blob: 3dc42fd61d7db175f25a79a21220b1a8cd3ba9f0 [file] [log] [blame]
Carmelo Cascone499f3202019-02-08 22:54:33 -08001# coding=utf-8
2"""
3Copyright 2019-present Open Networking Foundation
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16"""
Carmelo Cascone3c216fa2018-06-22 14:52:15 +020017import multiprocessing
Carmelo Casconeb7524272017-06-05 16:53:13 -040018import os
Carmelo Cascone15693a22018-12-12 19:06:57 -080019
20import json
Carmelo Cascone44448a52018-06-25 23:36:57 +020021import random
Carmelo Casconeb7524272017-06-05 16:53:13 -040022import re
Carmelo Cascone44448a52018-06-25 23:36:57 +020023import socket
Carmelo Cascone499f3202019-02-08 22:54:33 -080024import sys
Carmelo Casconef11513d2018-01-16 00:31:14 -080025import threading
Carmelo Cascone15693a22018-12-12 19:06:57 -080026import time
Carmelo Cascone44448a52018-06-25 23:36:57 +020027import urllib2
Carmelo Casconef11513d2018-01-16 00:31:14 -080028from contextlib import closing
Carmelo Cascone499f3202019-02-08 22:54:33 -080029from mininet.log import info, warn, debug
Carmelo Cascone34433252017-08-25 20:27:18 +020030from mininet.node import Switch, Host
Carmelo Cascone785fada2016-06-16 18:34:16 -070031
Carmelo Casconef11513d2018-01-16 00:31:14 -080032SIMPLE_SWITCH_GRPC = 'simple_switch_grpc'
Carmelo Cascone34433252017-08-25 20:27:18 +020033PKT_BYTES_TO_DUMP = 80
Carmelo Cascone46d360b2017-08-29 20:20:32 +020034VALGRIND_PREFIX = 'valgrind --leak-check=yes'
Carmelo Casconef11513d2018-01-16 00:31:14 -080035SWITCH_START_TIMEOUT = 5 # seconds
36BMV2_LOG_LINES = 5
Carmelo Cascone03ae0ac2018-10-11 08:31:59 -070037BMV2_DEFAULT_DEVICE_ID = 1
Carmelo Cascone3977ea42019-02-28 13:43:42 -080038DEFAULT_PIPECONF = "org.onosproject.pipelines.basic"
Carmelo Casconef11513d2018-01-16 00:31:14 -080039
Carmelo Cascone499f3202019-02-08 22:54:33 -080040# Stratum paths relative to stratum repo root
41STRATUM_BMV2 = 'stratum_bmv2'
42STRATUM_BINARY = '/bazel-bin/stratum/hal/bin/bmv2/' + STRATUM_BMV2
43STRATUM_INIT_PIPELINE = '/stratum/hal/bin/bmv2/dummy.json'
44
45
46def getStratumRoot():
47 if 'STRATUM_ROOT' not in os.environ:
48 raise Exception("Env variable STRATUM_ROOT not set")
49 return os.environ['STRATUM_ROOT']
50
Carmelo Cascone46d360b2017-08-29 20:20:32 +020051
52def parseBoolean(value):
53 if value in ['1', 1, 'true', 'True']:
54 return True
55 else:
56 return False
Carmelo Cascone34433252017-08-25 20:27:18 +020057
58
Carmelo Casconef11513d2018-01-16 00:31:14 -080059def pickUnusedPort():
60 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
61 s.bind(('localhost', 0))
62 addr, port = s.getsockname()
63 s.close()
64 return port
65
66
67def writeToFile(path, value):
68 with open(path, "w") as f:
69 f.write(str(value))
70
71
72def watchDog(sw):
Carmelo Cascone499f3202019-02-08 22:54:33 -080073 try:
74 writeToFile(sw.keepaliveFile,
75 "Remove this file to terminate %s" % sw.name)
76 while True:
77 if ONOSBmv2Switch.mininet_exception == 1 \
78 or not os.path.isfile(sw.keepaliveFile):
79 sw.killBmv2(log=False)
Carmelo Casconef11513d2018-01-16 00:31:14 -080080 return
Carmelo Cascone499f3202019-02-08 22:54:33 -080081 if sw.stopped:
82 return
83 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
84 if s.connect_ex(('127.0.0.1', sw.grpcPort)) == 0:
85 time.sleep(1)
86 else:
87 warn("\n*** WARN: switch %s died ☠️ \n" % sw.name)
88 sw.printBmv2Log()
89 print ("-" * 80) + "\n"
90 return
91 except Exception as e:
92 warn("*** ERROR: " + e.message)
93 sw.killBmv2(log=True)
Carmelo Casconef11513d2018-01-16 00:31:14 -080094
95
Carmelo Cascone34433252017-08-25 20:27:18 +020096class ONOSHost(Host):
97 def __init__(self, name, inNamespace=True, **params):
98 Host.__init__(self, name, inNamespace=inNamespace, **params)
99
100 def config(self, **params):
101 r = super(Host, self).config(**params)
102 for off in ["rx", "tx", "sg"]:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800103 cmd = "/sbin/ethtool --offload %s %s off" \
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800104 % (self.defaultIntf(), off)
Carmelo Cascone34433252017-08-25 20:27:18 +0200105 self.cmd(cmd)
106 # disable IPv6
107 self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
108 self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
109 self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
110 return r
Carmelo Casconeb7524272017-06-05 16:53:13 -0400111
Carmelo Cascone785fada2016-06-16 18:34:16 -0700112
113class ONOSBmv2Switch(Switch):
Carmelo Casconeb7524272017-06-05 16:53:13 -0400114 """BMv2 software switch with gRPC server"""
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200115 # Shared value used to notify to all instances of this class that a Mininet
116 # exception occurred. Mininet exception handling doesn't call the stop()
117 # method, so the mn process would hang after clean-up since Bmv2 would still
118 # be running.
119 mininet_exception = multiprocessing.Value('i', 0)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700120
Carmelo Casconef11513d2018-01-16 00:31:14 -0800121 def __init__(self, name, json=None, debugger=False, loglevel="warn",
Carmelo Cascone76e63862018-09-04 14:25:49 -0700122 elogger=False, grpcport=None, cpuport=255, notifications=False,
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800123 thriftport=None, netcfg=True, dryrun=False,
124 pipeconf=DEFAULT_PIPECONF, pktdump=False, valgrind=False,
125 gnmi=False, portcfg=True, onosdevid=None, stratum=False,
126 **kwargs):
Carmelo Cascone785fada2016-06-16 18:34:16 -0700127 Switch.__init__(self, name, **kwargs)
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200128 self.grpcPort = grpcport
129 self.thriftPort = thriftport
Carmelo Casconeeaa8b1d2018-04-11 14:12:17 -0700130 self.cpuPort = cpuport
Carmelo Casconefb76b042017-07-17 19:42:00 -0400131 self.json = json
Carmelo Cascone499f3202019-02-08 22:54:33 -0800132 self.useStratum = parseBoolean(stratum)
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200133 self.debugger = parseBoolean(debugger)
Carmelo Cascone76e63862018-09-04 14:25:49 -0700134 self.notifications = parseBoolean(notifications)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700135 self.loglevel = loglevel
Carmelo Casconef11513d2018-01-16 00:31:14 -0800136 # Important: Mininet removes all /tmp/*.log files in case of exceptions.
137 # We want to be able to see the bmv2 log if anything goes wrong, hence
138 # avoid the .log extension.
Carmelo Casconec2821332018-05-14 18:15:33 -0700139 self.logfile = '/tmp/bmv2-%s-log' % self.name
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200140 self.elogger = parseBoolean(elogger)
141 self.pktdump = parseBoolean(pktdump)
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200142 self.netcfg = parseBoolean(netcfg)
143 self.dryrun = parseBoolean(dryrun)
144 self.valgrind = parseBoolean(valgrind)
Carmelo Casconec2821332018-05-14 18:15:33 -0700145 self.netcfgfile = '/tmp/bmv2-%s-netcfg.json' % self.name
Carmelo Casconeeaa8b1d2018-04-11 14:12:17 -0700146 self.pipeconfId = pipeconf
147 self.injectPorts = parseBoolean(portcfg)
148 self.withGnmi = parseBoolean(gnmi)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800149 self.longitude = kwargs['longitude'] if 'longitude' in kwargs else None
150 self.latitude = kwargs['latitude'] if 'latitude' in kwargs else None
Carmelo Cascone55965c62018-05-17 18:13:16 -0700151 if onosdevid is not None and len(onosdevid) > 0:
152 self.onosDeviceId = onosdevid
153 else:
154 self.onosDeviceId = "device:bmv2:%s" % self.name
Carmelo Casconef11513d2018-01-16 00:31:14 -0800155 self.logfd = None
156 self.bmv2popen = None
Carmelo Cascone499f3202019-02-08 22:54:33 -0800157 self.stopped = True
158 # In case of exceptions, mininet removes *.out files from /tmp. We use
159 # this as a signal to terminate the switch instance (if active).
160 self.keepaliveFile = '/tmp/bmv2-%s-watchdog.out' % self.name
161 self.targetName = STRATUM_BMV2 if self.useStratum else SIMPLE_SWITCH_GRPC
Yi Tseng7875cb72017-08-08 10:15:58 -0700162
Carmelo Casconef11513d2018-01-16 00:31:14 -0800163 # Remove files from previous executions
164 self.cleanupTmpFiles()
165
Carmelo Casconeb7524272017-06-05 16:53:13 -0400166 def getSourceIp(self, dstIP):
167 """
Carmelo Casconef11513d2018-01-16 00:31:14 -0800168 Queries the Linux routing table to get the source IP that can talk with
169 dstIP, and vice versa.
Carmelo Casconeb7524272017-06-05 16:53:13 -0400170 """
171 ipRouteOut = self.cmd('ip route get %s' % dstIP)
172 r = re.search(r"src (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", ipRouteOut)
173 return r.group(1) if r else None
174
Yi Tseng7875cb72017-08-08 10:15:58 -0700175 def getDeviceConfig(self, srcIP):
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200176
Yi Tseng7875cb72017-08-08 10:15:58 -0700177 basicCfg = {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800178 "managementAddress": "grpc://%s:%d?device_id=%d" % (
179 srcIP, self.grpcPort, BMV2_DEFAULT_DEVICE_ID),
180 "driver": "bmv2",
181 "pipeconf": self.pipeconfId
Yi Tseng7875cb72017-08-08 10:15:58 -0700182 }
183
184 if self.longitude and self.latitude:
185 basicCfg["longitude"] = self.longitude
186 basicCfg["latitude"] = self.latitude
187
188 cfgData = {
Andrea Campanellabf9e5ce2017-12-06 14:26:36 +0100189 "basic": basicCfg
Yi Tseng7875cb72017-08-08 10:15:58 -0700190 }
191
Carmelo Cascone4f985cd2018-02-11 17:36:42 -0800192 if self.injectPorts:
Andrea Campanellabf9e5ce2017-12-06 14:26:36 +0100193 portData = {}
194 portId = 1
195 for intfName in self.intfNames():
196 if intfName == 'lo':
197 continue
198 portData[str(portId)] = {
199 "number": portId,
200 "name": intfName,
201 "enabled": True,
202 "removed": False,
203 "type": "copper",
204 "speed": 10000
205 }
206 portId += 1
207
208 cfgData['ports'] = portData
209
Yi Tseng7875cb72017-08-08 10:15:58 -0700210 return cfgData
211
212 def doOnosNetcfg(self, controllerIP):
213 """
214 Notifies ONOS about the new device via Netcfg.
215 """
216 srcIP = self.getSourceIp(controllerIP)
217 if not srcIP:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800218 warn("*** WARN: unable to get switch IP address, won't do netcfg\n")
Yi Tseng7875cb72017-08-08 10:15:58 -0700219 return
220
Carmelo Casconea11279b2017-06-22 04:30:08 -0400221 cfgData = {
222 "devices": {
Yi Tseng7875cb72017-08-08 10:15:58 -0700223 self.onosDeviceId: self.getDeviceConfig(srcIP)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400224 }
Carmelo Casconea11279b2017-06-22 04:30:08 -0400225 }
Carmelo Casconeb7524272017-06-05 16:53:13 -0400226 with open(self.netcfgfile, 'w') as fp:
227 json.dump(cfgData, fp, indent=4)
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200228
229 if not self.netcfg:
230 # Do not push config to ONOS.
231 return
232
Brian O'Connor71167f92017-06-16 14:55:00 -0700233 # Build netcfg URL
234 url = 'http://%s:8181/onos/v1/network/configuration/' % controllerIP
235 # Instantiate password manager for HTTP auth
236 pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
Carmelo Casconef11513d2018-01-16 00:31:14 -0800237 pm.add_password(None, url,
238 os.environ['ONOS_WEB_USER'],
239 os.environ['ONOS_WEB_PASS'])
240 urllib2.install_opener(urllib2.build_opener(
241 urllib2.HTTPBasicAuthHandler(pm)))
Brian O'Connor71167f92017-06-16 14:55:00 -0700242 # Push config data to controller
Carmelo Casconef11513d2018-01-16 00:31:14 -0800243 req = urllib2.Request(url, json.dumps(cfgData),
244 {'Content-Type': 'application/json'})
Carmelo Casconea11279b2017-06-22 04:30:08 -0400245 try:
246 f = urllib2.urlopen(req)
247 print f.read()
248 f.close()
249 except urllib2.URLError as e:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800250 warn("*** WARN: unable to push config to ONOS (%s)\n" % e.reason)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400251
Carmelo Cascone785fada2016-06-16 18:34:16 -0700252 def start(self, controllers):
Carmelo Casconef11513d2018-01-16 00:31:14 -0800253
Carmelo Cascone499f3202019-02-08 22:54:33 -0800254 if not self.stopped:
255 warn("*** %s is already running!\n" % self.name)
256 return
257
258 # Remove files from previous executions (if we are restarting)
259 self.cleanupTmpFiles()
260
261 if self.grpcPort is None:
262 self.grpcPort = pickUnusedPort()
263 writeToFile("/tmp/bmv2-%s-grpc-port" % self.name, self.grpcPort)
264
265 if self.useStratum:
266 config_dir = "/tmp/bmv2-%s-stratum" % self.name
267 os.mkdir(config_dir)
268 cmdString = self.getStratumCmdString(config_dir)
269 else:
270 if self.thriftPort is None:
271 self.thriftPort = pickUnusedPort()
272 writeToFile("/tmp/bmv2-%s-thrift-port" % self.name, self.thriftPort)
273 cmdString = self.getBmv2CmdString()
Carmelo Casconef11513d2018-01-16 00:31:14 -0800274
275 if self.dryrun:
Carmelo Cascone499f3202019-02-08 22:54:33 -0800276 info("\n*** DRY RUN (not executing %s)\n" % self.targetName)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800277
Carmelo Cascone499f3202019-02-08 22:54:33 -0800278 debug("\n%s\n" % cmdString)
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200279
Carmelo Casconef11513d2018-01-16 00:31:14 -0800280 try:
281 if not self.dryrun:
282 # Start the switch
Carmelo Cascone499f3202019-02-08 22:54:33 -0800283 self.stopped = False
Carmelo Casconef11513d2018-01-16 00:31:14 -0800284 self.logfd = open(self.logfile, "w")
285 self.bmv2popen = self.popen(cmdString,
286 stdout=self.logfd,
287 stderr=self.logfd)
288 self.waitBmv2Start()
Carmelo Cascone499f3202019-02-08 22:54:33 -0800289 # We want to be notified if BMv2/Stratum dies...
Carmelo Casconef11513d2018-01-16 00:31:14 -0800290 threading.Thread(target=watchDog, args=[self]).start()
291
292 self.doOnosNetcfg(self.controllerIp(controllers))
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200293 except Exception:
294 ONOSBmv2Switch.mininet_exception = 1
Carmelo Casconef11513d2018-01-16 00:31:14 -0800295 self.killBmv2()
296 self.printBmv2Log()
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200297 raise
Carmelo Casconef11513d2018-01-16 00:31:14 -0800298
Carmelo Cascone499f3202019-02-08 22:54:33 -0800299 def getBmv2CmdString(self):
300 bmv2Args = [SIMPLE_SWITCH_GRPC] + self.bmv2Args()
301 if self.valgrind:
302 bmv2Args = VALGRIND_PREFIX.split() + bmv2Args
303 return " ".join(bmv2Args)
304
305 def getStratumCmdString(self, config_dir):
306 stratumRoot = getStratumRoot()
307 args = [
308 stratumRoot + STRATUM_BINARY,
309 '-device_id=%d' % BMV2_DEFAULT_DEVICE_ID,
310 '-forwarding_pipeline_configs_file=%s/config.txt' % config_dir,
311 '-persistent_config_dir=' + config_dir,
312 '-initial_pipeline=' + stratumRoot + STRATUM_INIT_PIPELINE,
313 '-cpu_port=%s' % self.cpuPort,
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800314 '-external_hercules_urls=0.0.0.0:%d' % self.grpcPort,
315 '-max_num_controllers_per_node=10'
Carmelo Cascone499f3202019-02-08 22:54:33 -0800316 ]
317 for port, intf in self.intfs.items():
318 if not intf.IP():
319 args.append('%d@%s' % (port, intf.name))
320 return " ".join(args)
321
322 def bmv2Args(self):
Carmelo Casconec2821332018-05-14 18:15:33 -0700323 args = ['--device-id %s' % str(BMV2_DEFAULT_DEVICE_ID)]
Carmelo Cascone785fada2016-06-16 18:34:16 -0700324 for port, intf in self.intfs.items():
325 if not intf.IP():
326 args.append('-i %d@%s' % (port, intf.name))
Carmelo Casconec2821332018-05-14 18:15:33 -0700327 args.append('--thrift-port %s' % self.thriftPort)
Carmelo Cascone76e63862018-09-04 14:25:49 -0700328 if self.notifications:
329 ntfaddr = 'ipc:///tmp/bmv2-%s-notifications.ipc' % self.name
330 args.append('--notifications-addr %s' % ntfaddr)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700331 if self.elogger:
Carmelo Casconec2821332018-05-14 18:15:33 -0700332 nanologaddr = 'ipc:///tmp/bmv2-%s-nanolog.ipc' % self.name
333 args.append('--nanolog %s' % nanologaddr)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700334 if self.debugger:
Carmelo Casconec2821332018-05-14 18:15:33 -0700335 dbgaddr = 'ipc:///tmp/bmv2-%s-debug.ipc' % self.name
336 args.append('--debugger-addr %s' % dbgaddr)
Carmelo Casconefb76b042017-07-17 19:42:00 -0400337 args.append('--log-console')
Carmelo Cascone34433252017-08-25 20:27:18 +0200338 if self.pktdump:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800339 args.append('--pcap --dump-packet-data %s' % PKT_BYTES_TO_DUMP)
Carmelo Cascone9e6621f2017-06-27 16:06:33 -0400340 args.append('-L%s' % self.loglevel)
Carmelo Casconefb76b042017-07-17 19:42:00 -0400341 if not self.json:
342 args.append('--no-p4')
343 else:
344 args.append(self.json)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800345 # gRPC target-specific options
Carmelo Cascone785fada2016-06-16 18:34:16 -0700346 args.append('--')
Carmelo Casconef11513d2018-01-16 00:31:14 -0800347 args.append('--cpu-port %s' % self.cpuPort)
348 args.append('--grpc-server-addr 0.0.0.0:%s' % self.grpcPort)
349 return args
Carmelo Cascone785fada2016-06-16 18:34:16 -0700350
Carmelo Casconef11513d2018-01-16 00:31:14 -0800351 def waitBmv2Start(self):
352 # Wait for switch to open gRPC port, before sending ONOS the netcfg.
353 # Include time-out just in case something hangs.
354 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
355 endtime = time.time() + SWITCH_START_TIMEOUT
356 while True:
357 result = sock.connect_ex(('127.0.0.1', self.grpcPort))
358 if result == 0:
Carmelo Cascone499f3202019-02-08 22:54:33 -0800359 # No new line
360 sys.stdout.write("⚡️ %s @ %d" % (self.targetName, self.bmv2popen.pid))
361 sys.stdout.flush()
Carmelo Casconef11513d2018-01-16 00:31:14 -0800362 # The port is open. Let's go! (Close socket first)
363 sock.close()
364 break
365 # Port is not open yet. If there is time, we wait a bit.
366 if endtime > time.time():
Carmelo Cascone499f3202019-02-08 22:54:33 -0800367 sys.stdout.write('.')
368 sys.stdout.flush()
369 time.sleep(0.05)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800370 else:
371 # Time's up.
372 raise Exception("Switch did not start before timeout")
Carmelo Casconefb76b042017-07-17 19:42:00 -0400373
Carmelo Casconef11513d2018-01-16 00:31:14 -0800374 def printBmv2Log(self):
375 if os.path.isfile(self.logfile):
376 print "-" * 80
Carmelo Casconec2821332018-05-14 18:15:33 -0700377 print "%s log (from %s):" % (self.name, self.logfile)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800378 with open(self.logfile, 'r') as f:
379 lines = f.readlines()
380 if len(lines) > BMV2_LOG_LINES:
381 print "..."
382 for line in lines[-BMV2_LOG_LINES:]:
383 print line.rstrip()
Carmelo Casconefb76b042017-07-17 19:42:00 -0400384
Carmelo Casconef11513d2018-01-16 00:31:14 -0800385 @staticmethod
386 def controllerIp(controllers):
387 try:
388 # onos.py
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200389 clist = controllers[0].nodes()
390 except AttributeError:
391 clist = controllers
392 assert len(clist) > 0
Carmelo Cascone44448a52018-06-25 23:36:57 +0200393 return random.choice(clist).IP()
Keesjan Karsten8539f082018-01-04 17:03:31 +0100394
Carmelo Casconef11513d2018-01-16 00:31:14 -0800395 def killBmv2(self, log=False):
Carmelo Cascone499f3202019-02-08 22:54:33 -0800396 self.stopped = True
Carmelo Casconef11513d2018-01-16 00:31:14 -0800397 if self.bmv2popen is not None:
Carmelo Cascone499f3202019-02-08 22:54:33 -0800398 self.bmv2popen.terminate()
399 self.bmv2popen.wait()
400 self.bmv2popen = None
Carmelo Casconef11513d2018-01-16 00:31:14 -0800401 if self.logfd is not None:
402 if log:
403 self.logfd.write("*** PROCESS TERMINATED BY MININET ***\n")
404 self.logfd.close()
Carmelo Cascone499f3202019-02-08 22:54:33 -0800405 self.logfd = None
Keesjan Karsten8539f082018-01-04 17:03:31 +0100406
Carmelo Casconef11513d2018-01-16 00:31:14 -0800407 def cleanupTmpFiles(self):
Carmelo Cascone499f3202019-02-08 22:54:33 -0800408 self.cmd("rm -rf /tmp/bmv2-%s-*" % self.name)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400409
410 def stop(self, deleteIntfs=True):
411 """Terminate switch."""
Carmelo Casconef11513d2018-01-16 00:31:14 -0800412 self.killBmv2(log=True)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400413 Switch.stop(self, deleteIntfs)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700414
415
Carmelo Cascone499f3202019-02-08 22:54:33 -0800416class ONOSStratumSwitch(ONOSBmv2Switch):
417 def __init__(self, name, **kwargs):
418 kwargs["stratum"] = True
419 super(ONOSStratumSwitch, self).__init__(name, **kwargs)
420
421
Carmelo Casconeb7524272017-06-05 16:53:13 -0400422# Exports for bin/mn
Carmelo Cascone499f3202019-02-08 22:54:33 -0800423switches = {
424 'onosbmv2': ONOSBmv2Switch,
425 'stratum': ONOSStratumSwitch,
426}
Carmelo Cascone34433252017-08-25 20:27:18 +0200427hosts = {'onoshost': ONOSHost}