import socket from mininet.log import error, info from mininet.node import Switch from os import environ from os.path import isfile class ONOSBmv2Switch(Switch): """BMv2 software switch """ deviceId = 0 instanceCount = 0 def __init__(self, name, thriftPort=None, deviceId=None, debugger=False, loglevel="warn", elogger=False, persistent=True, **kwargs): Switch.__init__(self, name, **kwargs) self.swPath = environ['BMV2_EXE'] self.jsonPath = environ['BMV2_JSON'] if thriftPort: self.thriftPort = thriftPort else: self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not deviceId: if self.dpid: self.deviceId = int(self.dpid, 0 if 'x' in self.dpid else 16) else: self.deviceId = ONOSBmv2Switch.deviceId ONOSBmv2Switch.deviceId += 1 else: self.deviceId = deviceId ONOSBmv2Switch.deviceId = max(deviceId, ONOSBmv2Switch.deviceId) self.debugger = debugger self.loglevel = loglevel self.logfile = '/tmp/bmv2-%d.log' % self.deviceId self.output = open(self.logfile, 'w') self.elogger = elogger self.persistent = persistent if persistent: self.exectoken = "/tmp/bmv2-%d-exec-token" % self.deviceId self.cmd("touch %s" % self.exectoken) # Store thrift port for future uses. self.cmd("echo %d > /tmp/bmv2-%d-thrift-port" % (self.thriftPort, self.deviceId)) @classmethod def pickUnusedPort(cls): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() return port @classmethod def setup(cls): err = False if 'BMV2_EXE' not in environ: error("ERROR! environment var $BMV2_EXE not set\n") err = True elif not isfile(environ['BMV2_EXE']): error("ERROR! BMV2_EXE=%s: no such file\n" % environ['BMV2_EXE']) err = True if 'BMV2_JSON' not in environ: error("ERROR! environment var $BMV2_JSON not set\n") err = True elif not isfile(environ['BMV2_JSON']): error("ERROR! BMV2_JSON=%s: no such file\n" % environ['BMV2_JSON']) err = True if err: exit(1) def start(self, controllers): args = [self.swPath, '--device-id %s' % str(self.deviceId)] for port, intf in self.intfs.items(): if not intf.IP(): args.append('-i %d@%s' % (port, intf.name)) if self.thriftPort: args.append('--thrift-port %d' % self.thriftPort) if self.elogger: nanomsg = 'ipc:///tmp/bmv2-%d-log.ipc' % self.deviceId args.append('--nanolog %s' % nanomsg) if self.debugger: args.append('--debugger') args.append('--log-console -L%s' % self.loglevel) args.append(self.jsonPath) try: # onos.py clist = controllers[0].nodes() except AttributeError: clist = controllers assert len(clist) > 0 # BMv2 can't connect to multiple controllers. # Uniformly balance connections among available ones. cip = clist[ONOSBmv2Switch.instanceCount % len(clist)].IP() ONOSBmv2Switch.instanceCount += 1 # BMv2 controler port is hardcoded here as it is hardcoded also in ONOS. cport = 40123 args.append('--') args.append('--controller-ip %s' % cip) args.append('--controller-port %d' % cport) bmv2cmd = " ".join(args) info("\nStarting BMv2 target: %s\n" % bmv2cmd) if self.persistent: # Re-exec the switch if it crashes. cmdStr = "(while [ -e {} ]; " \ "do {} ; " \ "sleep 1; " \ "done;) > {} 2>&1 &".format(self.exectoken, bmv2cmd, self.logfile) else: cmdStr = "{} > {} 2>&1 &".format(bmv2cmd, self.logfile) self.cmd(cmdStr) def stop(self): "Terminate switch." self.output.flush() self.cmd("rm -f /tmp/bmv2-%d-*" % self.deviceId) self.cmd("rm -f /tmp/bmv2-%d.log" % self.deviceId) self.cmd('kill %' + self.swPath) self.deleteIntfs() ### Exports for bin/mn switches = {'onosbmv2': ONOSBmv2Switch}