blob: 56c7dcca0359f211acb67d94ccba59a613b6dcb4 [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 Casconef11513d2018-01-16 00:31:14 -080038
Carmelo Cascone499f3202019-02-08 22:54:33 -080039# Stratum paths relative to stratum repo root
40STRATUM_BMV2 = 'stratum_bmv2'
41STRATUM_BINARY = '/bazel-bin/stratum/hal/bin/bmv2/' + STRATUM_BMV2
42STRATUM_INIT_PIPELINE = '/stratum/hal/bin/bmv2/dummy.json'
43
44
45def getStratumRoot():
46 if 'STRATUM_ROOT' not in os.environ:
47 raise Exception("Env variable STRATUM_ROOT not set")
48 return os.environ['STRATUM_ROOT']
49
Carmelo Cascone46d360b2017-08-29 20:20:32 +020050
51def parseBoolean(value):
52 if value in ['1', 1, 'true', 'True']:
53 return True
54 else:
55 return False
Carmelo Cascone34433252017-08-25 20:27:18 +020056
57
Carmelo Casconef11513d2018-01-16 00:31:14 -080058def pickUnusedPort():
59 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
60 s.bind(('localhost', 0))
61 addr, port = s.getsockname()
62 s.close()
63 return port
64
65
66def writeToFile(path, value):
67 with open(path, "w") as f:
68 f.write(str(value))
69
70
71def watchDog(sw):
Carmelo Cascone499f3202019-02-08 22:54:33 -080072 try:
73 writeToFile(sw.keepaliveFile,
74 "Remove this file to terminate %s" % sw.name)
75 while True:
76 if ONOSBmv2Switch.mininet_exception == 1 \
77 or not os.path.isfile(sw.keepaliveFile):
78 sw.killBmv2(log=False)
Carmelo Casconef11513d2018-01-16 00:31:14 -080079 return
Carmelo Cascone499f3202019-02-08 22:54:33 -080080 if sw.stopped:
81 return
82 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
83 if s.connect_ex(('127.0.0.1', sw.grpcPort)) == 0:
84 time.sleep(1)
85 else:
86 warn("\n*** WARN: switch %s died ☠️ \n" % sw.name)
87 sw.printBmv2Log()
88 print ("-" * 80) + "\n"
89 return
90 except Exception as e:
91 warn("*** ERROR: " + e.message)
92 sw.killBmv2(log=True)
Carmelo Casconef11513d2018-01-16 00:31:14 -080093
94
Carmelo Cascone34433252017-08-25 20:27:18 +020095class ONOSHost(Host):
96 def __init__(self, name, inNamespace=True, **params):
97 Host.__init__(self, name, inNamespace=inNamespace, **params)
98
99 def config(self, **params):
100 r = super(Host, self).config(**params)
101 for off in ["rx", "tx", "sg"]:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800102 cmd = "/sbin/ethtool --offload %s %s off" \
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800103 % (self.defaultIntf(), off)
Carmelo Cascone34433252017-08-25 20:27:18 +0200104 self.cmd(cmd)
105 # disable IPv6
106 self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
107 self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
108 self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
109 return r
Carmelo Casconeb7524272017-06-05 16:53:13 -0400110
Carmelo Cascone785fada2016-06-16 18:34:16 -0700111
112class ONOSBmv2Switch(Switch):
Carmelo Casconeb7524272017-06-05 16:53:13 -0400113 """BMv2 software switch with gRPC server"""
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200114 # Shared value used to notify to all instances of this class that a Mininet
115 # exception occurred. Mininet exception handling doesn't call the stop()
116 # method, so the mn process would hang after clean-up since Bmv2 would still
117 # be running.
118 mininet_exception = multiprocessing.Value('i', 0)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700119
Carmelo Casconef11513d2018-01-16 00:31:14 -0800120 def __init__(self, name, json=None, debugger=False, loglevel="warn",
Carmelo Cascone76e63862018-09-04 14:25:49 -0700121 elogger=False, grpcport=None, cpuport=255, notifications=False,
Carmelo Casconeeaa8b1d2018-04-11 14:12:17 -0700122 thriftport=None, netcfg=True, dryrun=False, pipeconf="",
123 pktdump=False, valgrind=False, gnmi=False,
Carmelo Cascone499f3202019-02-08 22:54:33 -0800124 portcfg=True, onosdevid=None, stratum=False, **kwargs):
Carmelo Cascone785fada2016-06-16 18:34:16 -0700125 Switch.__init__(self, name, **kwargs)
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200126 self.grpcPort = grpcport
127 self.thriftPort = thriftport
Carmelo Casconeeaa8b1d2018-04-11 14:12:17 -0700128 self.cpuPort = cpuport
Carmelo Casconefb76b042017-07-17 19:42:00 -0400129 self.json = json
Carmelo Cascone499f3202019-02-08 22:54:33 -0800130 self.useStratum = parseBoolean(stratum)
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200131 self.debugger = parseBoolean(debugger)
Carmelo Cascone76e63862018-09-04 14:25:49 -0700132 self.notifications = parseBoolean(notifications)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700133 self.loglevel = loglevel
Carmelo Casconef11513d2018-01-16 00:31:14 -0800134 # Important: Mininet removes all /tmp/*.log files in case of exceptions.
135 # We want to be able to see the bmv2 log if anything goes wrong, hence
136 # avoid the .log extension.
Carmelo Casconec2821332018-05-14 18:15:33 -0700137 self.logfile = '/tmp/bmv2-%s-log' % self.name
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200138 self.elogger = parseBoolean(elogger)
139 self.pktdump = parseBoolean(pktdump)
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200140 self.netcfg = parseBoolean(netcfg)
141 self.dryrun = parseBoolean(dryrun)
142 self.valgrind = parseBoolean(valgrind)
Carmelo Casconec2821332018-05-14 18:15:33 -0700143 self.netcfgfile = '/tmp/bmv2-%s-netcfg.json' % self.name
Carmelo Casconeeaa8b1d2018-04-11 14:12:17 -0700144 self.pipeconfId = pipeconf
145 self.injectPorts = parseBoolean(portcfg)
146 self.withGnmi = parseBoolean(gnmi)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800147 self.longitude = kwargs['longitude'] if 'longitude' in kwargs else None
148 self.latitude = kwargs['latitude'] if 'latitude' in kwargs else None
Carmelo Cascone55965c62018-05-17 18:13:16 -0700149 if onosdevid is not None and len(onosdevid) > 0:
150 self.onosDeviceId = onosdevid
151 else:
152 self.onosDeviceId = "device:bmv2:%s" % self.name
Carmelo Casconef11513d2018-01-16 00:31:14 -0800153 self.logfd = None
154 self.bmv2popen = None
Carmelo Cascone499f3202019-02-08 22:54:33 -0800155 self.stopped = True
156 # In case of exceptions, mininet removes *.out files from /tmp. We use
157 # this as a signal to terminate the switch instance (if active).
158 self.keepaliveFile = '/tmp/bmv2-%s-watchdog.out' % self.name
159 self.targetName = STRATUM_BMV2 if self.useStratum else SIMPLE_SWITCH_GRPC
Yi Tseng7875cb72017-08-08 10:15:58 -0700160
Carmelo Casconef11513d2018-01-16 00:31:14 -0800161 # Remove files from previous executions
162 self.cleanupTmpFiles()
163
Carmelo Casconeb7524272017-06-05 16:53:13 -0400164 def getSourceIp(self, dstIP):
165 """
Carmelo Casconef11513d2018-01-16 00:31:14 -0800166 Queries the Linux routing table to get the source IP that can talk with
167 dstIP, and vice versa.
Carmelo Casconeb7524272017-06-05 16:53:13 -0400168 """
169 ipRouteOut = self.cmd('ip route get %s' % dstIP)
170 r = re.search(r"src (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", ipRouteOut)
171 return r.group(1) if r else None
172
Yi Tseng7875cb72017-08-08 10:15:58 -0700173 def getDeviceConfig(self, srcIP):
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200174
Yi Tseng7875cb72017-08-08 10:15:58 -0700175 basicCfg = {
Carmelo Casconef6cb3e82018-08-24 00:00:28 +0000176 "driver": "bmv2"
Yi Tseng7875cb72017-08-08 10:15:58 -0700177 }
178
179 if self.longitude and self.latitude:
180 basicCfg["longitude"] = self.longitude
181 basicCfg["latitude"] = self.latitude
182
183 cfgData = {
Carmelo Cascone34433252017-08-25 20:27:18 +0200184 "generalprovider": {
185 "p4runtime": {
186 "ip": srcIP,
187 "port": self.grpcPort,
Carmelo Casconec2821332018-05-14 18:15:33 -0700188 "deviceId": BMV2_DEFAULT_DEVICE_ID,
Carmelo Cascone34433252017-08-25 20:27:18 +0200189 "deviceKeyId": "p4runtime:%s" % self.onosDeviceId
Esin Karaman971fb7f2017-12-28 13:44:52 +0000190 },
Carmelo Cascone15693a22018-12-12 19:06:57 -0800191 # "bmv2-thrift": {
192 # "ip": srcIP,
193 # "port": self.thriftPort
194 # }
Carmelo Cascone34433252017-08-25 20:27:18 +0200195 },
196 "piPipeconf": {
197 "piPipeconfId": self.pipeconfId
198 },
Andrea Campanellabf9e5ce2017-12-06 14:26:36 +0100199 "basic": basicCfg
Yi Tseng7875cb72017-08-08 10:15:58 -0700200 }
201
Carmelo Cascone4f985cd2018-02-11 17:36:42 -0800202 if self.withGnmi:
203 cfgData["generalprovider"]["gnmi"] = {
204 "ip": srcIP,
205 "port": self.grpcPort
206 }
207
208 if self.injectPorts:
Andrea Campanellabf9e5ce2017-12-06 14:26:36 +0100209 portData = {}
210 portId = 1
211 for intfName in self.intfNames():
212 if intfName == 'lo':
213 continue
214 portData[str(portId)] = {
215 "number": portId,
216 "name": intfName,
217 "enabled": True,
218 "removed": False,
219 "type": "copper",
220 "speed": 10000
221 }
222 portId += 1
223
224 cfgData['ports'] = portData
225
Yi Tseng7875cb72017-08-08 10:15:58 -0700226 return cfgData
227
228 def doOnosNetcfg(self, controllerIP):
229 """
230 Notifies ONOS about the new device via Netcfg.
231 """
232 srcIP = self.getSourceIp(controllerIP)
233 if not srcIP:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800234 warn("*** WARN: unable to get switch IP address, won't do netcfg\n")
Yi Tseng7875cb72017-08-08 10:15:58 -0700235 return
236
Carmelo Casconea11279b2017-06-22 04:30:08 -0400237 cfgData = {
238 "devices": {
Yi Tseng7875cb72017-08-08 10:15:58 -0700239 self.onosDeviceId: self.getDeviceConfig(srcIP)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400240 }
Carmelo Casconea11279b2017-06-22 04:30:08 -0400241 }
Carmelo Casconeb7524272017-06-05 16:53:13 -0400242 with open(self.netcfgfile, 'w') as fp:
243 json.dump(cfgData, fp, indent=4)
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200244
245 if not self.netcfg:
246 # Do not push config to ONOS.
247 return
248
Brian O'Connor71167f92017-06-16 14:55:00 -0700249 # Build netcfg URL
250 url = 'http://%s:8181/onos/v1/network/configuration/' % controllerIP
251 # Instantiate password manager for HTTP auth
252 pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
Carmelo Casconef11513d2018-01-16 00:31:14 -0800253 pm.add_password(None, url,
254 os.environ['ONOS_WEB_USER'],
255 os.environ['ONOS_WEB_PASS'])
256 urllib2.install_opener(urllib2.build_opener(
257 urllib2.HTTPBasicAuthHandler(pm)))
Brian O'Connor71167f92017-06-16 14:55:00 -0700258 # Push config data to controller
Carmelo Casconef11513d2018-01-16 00:31:14 -0800259 req = urllib2.Request(url, json.dumps(cfgData),
260 {'Content-Type': 'application/json'})
Carmelo Casconea11279b2017-06-22 04:30:08 -0400261 try:
262 f = urllib2.urlopen(req)
263 print f.read()
264 f.close()
265 except urllib2.URLError as e:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800266 warn("*** WARN: unable to push config to ONOS (%s)\n" % e.reason)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400267
Carmelo Cascone785fada2016-06-16 18:34:16 -0700268 def start(self, controllers):
Carmelo Casconef11513d2018-01-16 00:31:14 -0800269
Carmelo Cascone499f3202019-02-08 22:54:33 -0800270 if not self.stopped:
271 warn("*** %s is already running!\n" % self.name)
272 return
273
274 # Remove files from previous executions (if we are restarting)
275 self.cleanupTmpFiles()
276
277 if self.grpcPort is None:
278 self.grpcPort = pickUnusedPort()
279 writeToFile("/tmp/bmv2-%s-grpc-port" % self.name, self.grpcPort)
280
281 if self.useStratum:
282 config_dir = "/tmp/bmv2-%s-stratum" % self.name
283 os.mkdir(config_dir)
284 cmdString = self.getStratumCmdString(config_dir)
285 else:
286 if self.thriftPort is None:
287 self.thriftPort = pickUnusedPort()
288 writeToFile("/tmp/bmv2-%s-thrift-port" % self.name, self.thriftPort)
289 cmdString = self.getBmv2CmdString()
Carmelo Casconef11513d2018-01-16 00:31:14 -0800290
291 if self.dryrun:
Carmelo Cascone499f3202019-02-08 22:54:33 -0800292 info("\n*** DRY RUN (not executing %s)\n" % self.targetName)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800293
Carmelo Cascone499f3202019-02-08 22:54:33 -0800294 debug("\n%s\n" % cmdString)
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200295
Carmelo Casconef11513d2018-01-16 00:31:14 -0800296 try:
297 if not self.dryrun:
298 # Start the switch
Carmelo Cascone499f3202019-02-08 22:54:33 -0800299 self.stopped = False
Carmelo Casconef11513d2018-01-16 00:31:14 -0800300 self.logfd = open(self.logfile, "w")
301 self.bmv2popen = self.popen(cmdString,
302 stdout=self.logfd,
303 stderr=self.logfd)
304 self.waitBmv2Start()
Carmelo Cascone499f3202019-02-08 22:54:33 -0800305 # We want to be notified if BMv2/Stratum dies...
Carmelo Casconef11513d2018-01-16 00:31:14 -0800306 threading.Thread(target=watchDog, args=[self]).start()
307
308 self.doOnosNetcfg(self.controllerIp(controllers))
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200309 except Exception:
310 ONOSBmv2Switch.mininet_exception = 1
Carmelo Casconef11513d2018-01-16 00:31:14 -0800311 self.killBmv2()
312 self.printBmv2Log()
Carmelo Cascone3c216fa2018-06-22 14:52:15 +0200313 raise
Carmelo Casconef11513d2018-01-16 00:31:14 -0800314
Carmelo Cascone499f3202019-02-08 22:54:33 -0800315 def getBmv2CmdString(self):
316 bmv2Args = [SIMPLE_SWITCH_GRPC] + self.bmv2Args()
317 if self.valgrind:
318 bmv2Args = VALGRIND_PREFIX.split() + bmv2Args
319 return " ".join(bmv2Args)
320
321 def getStratumCmdString(self, config_dir):
322 stratumRoot = getStratumRoot()
323 args = [
324 stratumRoot + STRATUM_BINARY,
325 '-device_id=%d' % BMV2_DEFAULT_DEVICE_ID,
326 '-forwarding_pipeline_configs_file=%s/config.txt' % config_dir,
327 '-persistent_config_dir=' + config_dir,
328 '-initial_pipeline=' + stratumRoot + STRATUM_INIT_PIPELINE,
329 '-cpu_port=%s' % self.cpuPort,
330 '-external_hercules_urls=0.0.0.0:%d' % self.grpcPort
331 ]
332 for port, intf in self.intfs.items():
333 if not intf.IP():
334 args.append('%d@%s' % (port, intf.name))
335 return " ".join(args)
336
337 def bmv2Args(self):
Carmelo Casconec2821332018-05-14 18:15:33 -0700338 args = ['--device-id %s' % str(BMV2_DEFAULT_DEVICE_ID)]
Carmelo Cascone785fada2016-06-16 18:34:16 -0700339 for port, intf in self.intfs.items():
340 if not intf.IP():
341 args.append('-i %d@%s' % (port, intf.name))
Carmelo Casconec2821332018-05-14 18:15:33 -0700342 args.append('--thrift-port %s' % self.thriftPort)
Carmelo Cascone76e63862018-09-04 14:25:49 -0700343 if self.notifications:
344 ntfaddr = 'ipc:///tmp/bmv2-%s-notifications.ipc' % self.name
345 args.append('--notifications-addr %s' % ntfaddr)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700346 if self.elogger:
Carmelo Casconec2821332018-05-14 18:15:33 -0700347 nanologaddr = 'ipc:///tmp/bmv2-%s-nanolog.ipc' % self.name
348 args.append('--nanolog %s' % nanologaddr)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700349 if self.debugger:
Carmelo Casconec2821332018-05-14 18:15:33 -0700350 dbgaddr = 'ipc:///tmp/bmv2-%s-debug.ipc' % self.name
351 args.append('--debugger-addr %s' % dbgaddr)
Carmelo Casconefb76b042017-07-17 19:42:00 -0400352 args.append('--log-console')
Carmelo Cascone34433252017-08-25 20:27:18 +0200353 if self.pktdump:
Carmelo Casconef11513d2018-01-16 00:31:14 -0800354 args.append('--pcap --dump-packet-data %s' % PKT_BYTES_TO_DUMP)
Carmelo Cascone9e6621f2017-06-27 16:06:33 -0400355 args.append('-L%s' % self.loglevel)
Carmelo Casconefb76b042017-07-17 19:42:00 -0400356 if not self.json:
357 args.append('--no-p4')
358 else:
359 args.append(self.json)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800360 # gRPC target-specific options
Carmelo Cascone785fada2016-06-16 18:34:16 -0700361 args.append('--')
Carmelo Casconef11513d2018-01-16 00:31:14 -0800362 args.append('--cpu-port %s' % self.cpuPort)
363 args.append('--grpc-server-addr 0.0.0.0:%s' % self.grpcPort)
364 return args
Carmelo Cascone785fada2016-06-16 18:34:16 -0700365
Carmelo Casconef11513d2018-01-16 00:31:14 -0800366 def waitBmv2Start(self):
367 # Wait for switch to open gRPC port, before sending ONOS the netcfg.
368 # Include time-out just in case something hangs.
369 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
370 endtime = time.time() + SWITCH_START_TIMEOUT
371 while True:
372 result = sock.connect_ex(('127.0.0.1', self.grpcPort))
373 if result == 0:
Carmelo Cascone499f3202019-02-08 22:54:33 -0800374 # No new line
375 sys.stdout.write("⚡️ %s @ %d" % (self.targetName, self.bmv2popen.pid))
376 sys.stdout.flush()
Carmelo Casconef11513d2018-01-16 00:31:14 -0800377 # The port is open. Let's go! (Close socket first)
378 sock.close()
379 break
380 # Port is not open yet. If there is time, we wait a bit.
381 if endtime > time.time():
Carmelo Cascone499f3202019-02-08 22:54:33 -0800382 sys.stdout.write('.')
383 sys.stdout.flush()
384 time.sleep(0.05)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800385 else:
386 # Time's up.
387 raise Exception("Switch did not start before timeout")
Carmelo Casconefb76b042017-07-17 19:42:00 -0400388
Carmelo Casconef11513d2018-01-16 00:31:14 -0800389 def printBmv2Log(self):
390 if os.path.isfile(self.logfile):
391 print "-" * 80
Carmelo Casconec2821332018-05-14 18:15:33 -0700392 print "%s log (from %s):" % (self.name, self.logfile)
Carmelo Casconef11513d2018-01-16 00:31:14 -0800393 with open(self.logfile, 'r') as f:
394 lines = f.readlines()
395 if len(lines) > BMV2_LOG_LINES:
396 print "..."
397 for line in lines[-BMV2_LOG_LINES:]:
398 print line.rstrip()
Carmelo Casconefb76b042017-07-17 19:42:00 -0400399
Carmelo Casconef11513d2018-01-16 00:31:14 -0800400 @staticmethod
401 def controllerIp(controllers):
402 try:
403 # onos.py
Carmelo Cascone46d360b2017-08-29 20:20:32 +0200404 clist = controllers[0].nodes()
405 except AttributeError:
406 clist = controllers
407 assert len(clist) > 0
Carmelo Cascone44448a52018-06-25 23:36:57 +0200408 return random.choice(clist).IP()
Keesjan Karsten8539f082018-01-04 17:03:31 +0100409
Carmelo Casconef11513d2018-01-16 00:31:14 -0800410 def killBmv2(self, log=False):
Carmelo Cascone499f3202019-02-08 22:54:33 -0800411 self.stopped = True
Carmelo Casconef11513d2018-01-16 00:31:14 -0800412 if self.bmv2popen is not None:
Carmelo Cascone499f3202019-02-08 22:54:33 -0800413 self.bmv2popen.terminate()
414 self.bmv2popen.wait()
415 self.bmv2popen = None
Carmelo Casconef11513d2018-01-16 00:31:14 -0800416 if self.logfd is not None:
417 if log:
418 self.logfd.write("*** PROCESS TERMINATED BY MININET ***\n")
419 self.logfd.close()
Carmelo Cascone499f3202019-02-08 22:54:33 -0800420 self.logfd = None
Keesjan Karsten8539f082018-01-04 17:03:31 +0100421
Carmelo Casconef11513d2018-01-16 00:31:14 -0800422 def cleanupTmpFiles(self):
Carmelo Cascone499f3202019-02-08 22:54:33 -0800423 self.cmd("rm -rf /tmp/bmv2-%s-*" % self.name)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400424
425 def stop(self, deleteIntfs=True):
426 """Terminate switch."""
Carmelo Casconef11513d2018-01-16 00:31:14 -0800427 self.killBmv2(log=True)
Carmelo Casconeb7524272017-06-05 16:53:13 -0400428 Switch.stop(self, deleteIntfs)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700429
430
Carmelo Cascone499f3202019-02-08 22:54:33 -0800431class ONOSStratumSwitch(ONOSBmv2Switch):
432 def __init__(self, name, **kwargs):
433 kwargs["stratum"] = True
434 super(ONOSStratumSwitch, self).__init__(name, **kwargs)
435
436
Carmelo Casconeb7524272017-06-05 16:53:13 -0400437# Exports for bin/mn
Carmelo Cascone499f3202019-02-08 22:54:33 -0800438switches = {
439 'onosbmv2': ONOSBmv2Switch,
440 'stratum': ONOSStratumSwitch,
441}
Carmelo Cascone34433252017-08-25 20:27:18 +0200442hosts = {'onoshost': ONOSHost}