#!/usr/bin/python ''' Notes: This file contains classes and methods useful for integrating LincOE with Mininet, such as startOE, stopOE, LINCLink, and OpticalSwitch - $ONOS_ROOT must be set - Need to run with sudo -E to preserve ONOS_ROOT env var - We assume LINC-Config-Generator is named LINC-Config-Generator - We also assume linc-oe is named linc-oe - LINC-config-generator and linc-oe must be subdirectories of the user's home directory TODO ----------- - clean up files after runtime - maybe save the old files in a separate directory? - modify script to allow startOE to run before net.start() - add ONOS as a controller in script Usage: ------------ - import LINCLink and OpticalSwitch from this module - import startOE and stopOE from this module - create topology as you would a normal topology. when to an optical switch with topo.addLink, always specify cls=LINCLink - when creating an optical switch, use cls=OpticalSwitch in topo.addSwitch - for annotations on links and switches, a dictionary must be passed in as the annotations argument - startOE must be run AFTER net.start() with net as an argument. - stopOE can be run at any time I created a separate function to start lincOE to avoid subclassing Mininet. In case anyone wants to write something that DOES subclass Mininet, I thought I would outline how: If we want an object that starts lincOE within the mininet class itself, we need to add another object to Mininet that contains all of the json object information for each switch. We would still subclass switch and link, but these classes would basically be dummy classes that store their own json information in the Mininet class object. We may also change the default switch class to add it's tap interfaces from lincOE during startup. The start() method for mininet would grab all of the information from these switches and links, write configuration files for lincOE using the json module, start lincOE, then run the start methodfor each switch. The new start() method for each switch would parse through the sys.config file that was created and find the tap interface it needs to connect to, similar to the findTap function that I currently use. After all of the controllers and switches have been started, the new Mininet start() method should also push the Topology configuration file to ONOS. ''' import sys import re import json import os from time import sleep import urllib2 from mininet.node import Switch, OVSSwitch, RemoteController from mininet.topo import Topo from mininet.util import quietRun from mininet.net import Mininet from mininet.log import setLogLevel, info, error, warn from mininet.link import Link, Intf from mininet.cli import CLI # Sleep time and timeout values in seconds SLEEP_TIME = 2 TIMEOUT = 60 ### method, mapping dpid to LINC switchId ### def dpids_to_ids(sysConfig): ''' return the dict containing switch dpids as key and LINC switch id as values ''' dpids_to_ids = {} fd = None try: with open(sysConfig, 'r', 0) as fd: switch_id = 1 for line in fd: dpid = re.search(r'([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})+', line, re.I) if dpid: dpids_to_ids[dpid.group().replace(':', '')] = switch_id switch_id += 1 return dpids_to_ids except: print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info()) fd.close() return None def findDir(directory, userName): "finds and returns the path of any directory in the user's home directory" homeDir = '/home/' + userName Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n') DirList = Dir.split('\n') if not Dir: return None elif len(DirList) > 1 : warn('***WARNING: Found multiple instances of %s; using %s\n' % (directory, DirList[ 0 ])) return DirList[ 0 ] else: return Dir def switchJSON(switch): "Returns the json configuration for a packet switch" configDict = {} configDict[ 'uri' ] = 'of:' + switch.dpid configDict[ 'type' ] = 'SWITCH' annotations = switch.params.get('annotations', {}) annotations.setdefault('name', switch.name) configDict[ 'annotations' ] = annotations ports = [] for port, intf in switch.intfs.items(): if intf.name == 'lo': continue portDict = {} portDict[ 'port' ] = port portType = 'COPPER' if isinstance(intf.link, LINCLink): portType = 'OCH' if intf.link.isCrossConnect() else 'OMS' portDict[ 'type' ] = portType intfList = [ intf.link.intf1, intf.link.intf2 ] intfList.remove(intf) portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0 ports.append(portDict) configDict[ 'ports' ] = ports return configDict def dpId(id): nodeDpid = "" id = id.split("/", 1)[0] for i in range(3, len(id) - 1, 2): nodeDpid += (id[i:(i + 2):]) + ":" return nodeDpid[0:-1] def getSwitchConfig(switches): switchConfig = [] # Iterate through all switches and convert the ROADM switches to linc-oe format for switch in switches: if isinstance(switch, LINCSwitch): builtSwitch = {} # Set basic switch params based on annotations builtSwitch["allowed"] = True builtSwitch["latitude"] = switch.annotations.get("latitude", 0.0) builtSwitch["longitude"] = switch.annotations.get("longitude", 0.0) # Convert dpid to linc-oe format builtSwitch["name"] = switch.name builtSwitch["nodeDpid"] = dpId('of:' + switch.dpid) # Set switch params and type builtSwitch["params"] = {} builtSwitch["params"]["numregens"] = switch.annotations.get("optical.regens", 0) builtSwitch["type"] = "Roadm" switchConfig.append(builtSwitch) return switchConfig def getLinkConfig(links): linkConfig = [] # Iterate through all non-edge links and convert them to linc-oe format for link in links: if isinstance(link, LINCLink): builtLink = {} # Set basic link params for src and dst builtLink["allowed"] = True builtLink["nodeDpid1"] = dpId('of:' + link.intf1.node.dpid) builtLink["nodeDpid2"] = dpId('of:' + link.intf2.node.dpid) # Set more params such as name/bandwidth/port if they exist params = {} params["nodeName1"] = link.intf1.node.name params["nodeName2"] = link.intf2.node.name params["port1"] = link.port1 params["port2"] = link.port2 if "bandwidth" in link.annotations: params["bandwidth"] = link.annotations["bandwidth"] builtLink["params"] = params # Set link type to WDM or packet (LINC-config-generator relies on it) if link.isTransportLayer(): builtLink["type"] = "wdmLink" else: builtLink["type"] = "pktOptLink" linkConfig.append(builtLink) return linkConfig def waitStarted(net, timeout=TIMEOUT): "wait until all tap interfaces are available" tapCount = 0 time = 0 for link in net.links: if isinstance(link, LINCLink) and link.isCrossConnect(): tapCount += 1 while True: # tapCount can be less than the actual number of taps if the optical network # is a subgraph of a larger multidomain network. tapNum = int(quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n')) if tapCount <= tapNum: return True if timeout: if time >= TIMEOUT: error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT) return False time += SLEEP_TIME sleep(SLEEP_TIME) def setupInts(intfs): ''' add taps and bring them up. ''' for i in intfs: quietRun('ip tuntap add dev %s mode tap' % i) quietRun('ip link set dev %s up' % i) info('*** Intf %s set\n' % i) class OpticalSwitch(Switch): """ For now, same as Switch class. """ pass class OpticalIntf(Intf): """ For now,same as Intf class. """ pass class OpticalLink(Link): """ For now, same as Link. """ pass class LINCSwitch(OpticalSwitch): """ LINCSwitch class """ # FIXME:Sometimes LINC doesn't remove pipes and on restart increase the pipe # number from erlang.pipe.1.* to erlang.pipe.2.*, so should read and write # from latest pipe files. For now we are removing all the pipes before # starting LINC. ### User Name ### user = os.getenv('SUDO_USER') ### pipes ### readPipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.r".format(user) writePipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.w".format(user) ### sys.config path ### sysConfig = "/home/{}/linc-oe/rel/linc/releases/1.0/sys.config".format(user) ### dict of containing dpids as key and corresponding LINC switchId as values ### dpidsToLINCSwitchId = dpids_to_ids(sysConfig) ### ONOS Directory ### try: onosDir = os.environ[ 'ONOS_ROOT' ] except: onosDir = findDir('onos', user) if not onosDir: error('Please set ONOS_ROOT environment variable!\n') else: os.environ[ 'ONOS_ROOT' ] = onosDir ### ONOS-netcfg directory ### try: runPackDir = os.environ[ 'RUN_PACK_PATH' ] except: runPackDir = onosDir+"/tools/package/runtime/bin" os.environ[ 'RUN_PACK_PATH' ] = runPackDir ### REST USER/PASS ### try: restUser = os.environ[ 'ONOS_WEB_USER' ] restPass = os.environ[ 'ONOS_WEB_PASS' ] except: error('***WARNING: $ONOS_WEB_USER and $ONOS_WEB_PASS aren\'t set!\n') error('***WARNING: Setting (probably) sane WEB user/pass values\n') restUser = 'onos' restPass = 'rocks' os.environ[ 'ONOS_WEB_USER' ] = restUser os.environ[ 'ONOS_WEB_PASS' ] = restPass ### LINC-directory lincDir = findDir('linc-oe', user) if not lincDir: error("***ERROR: Could not find linc-oe in user's home directory\n") ### LINC config generator directory### configGen = findDir('LINC-config-generator', user) if not configGen: error("***ERROR: Could not find LINC-config-generator in user's home directory\n") # list of all the controllers controllers = None def __init__(self, name, dpid=None, allowed=True, switchType='ROADM', topo=None, annotations={}, controller=None, **params): params[ 'inNamespace' ] = False Switch.__init__(self, name, dpid=dpid, **params) self.name = name self.annotations = annotations self.allowed = allowed self.switchType = switchType self.configDict = {} # dictionary that holds all of the JSON configuration data self.crossConnects = [] self.deletedCrossConnects = [] self.controller = controller self.lincId = self._get_linc_id() # use to communicate with LINC self.lincStarted = False def start(self, *opts, **params): '''Instead of starting a virtual switch, we build the JSON dictionary for the emulated optical switch''' # TODO:Once LINC has the ability to spawn network element dynamically # we need to use this method to spawn new logical LINC switch rather then # bulding JSON. # if LINC is started then we can start and stop logical switches else create JSON if self.lincStarted: return self.start_oe() self.configDict[ 'uri' ] = 'of:' + self.dpid self.configDict[ 'annotations' ] = self.annotations self.configDict[ 'annotations' ].setdefault('name', self.name) self.configDict[ 'type' ] = self.switchType self.configDict[ 'ports' ] = [] for port, intf in self.intfs.items(): if intf.name == 'lo': continue else: self.configDict[ 'ports' ].append(intf.json()) self.lincStarted = True def stop(self, deleteIntfs=False): ''' stop the existing switch ''' # TODO:Add support for deleteIntf self.stop_oe() def dpctl( self, *args ): "Run dpctl command: ignore for now" pass def write_to_cli(self, command): ''' send command to LINC ''' fd = None try: fd = open(self.writePipe, 'w', 0) fd.write(command) fd.close() except: print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info()) if fd: fd.close() def read_from_cli(self): ''' read the output from the LINC CLI ''' response = None fd = None try: fd = open(self.readPipe, 'r', 0) fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read # FIXME:Due to non-blocking read most for the time we read nothing response = fd.read() fd.close() except : # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info()) if fd: fd.close() return response def _get_linc_id(self): ''' return the corresponding LINC switchId. ''' return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid) #-------------------------------------------------------------------------- # LINC CLI commands #-------------------------------------------------------------------------- def start_oe(self): ''' existing LINC switch ''' #starting Switch cmd = "linc:start_switch({}).\r\n".format(self.lincId) self.write_to_cli(cmd) #hanlding taps interfaces related to the switch crossConnectJSON = {} linkConfig = [] for i in range(0,len(self.deletedCrossConnects)): crossConnect = self.deletedCrossConnects.pop() tap = None if isinstance(crossConnect.intf1.node, LINCSwitch): intf = crossConnect.intf2 tapPort = crossConnect.intf1.port else: intf = crossConnect.intf1 tapPort = crossConnect.intf2.port tap = LINCSwitch.findTap(self, tapPort) if tap: setupInts([tap]) intf.node.attach(tap) self.crossConnects.append(crossConnect) linkConfig.append(crossConnect.json()) #Sending crossConnect info to the ONOS. crossConnectJSON['links'] = linkConfig with open("crossConnect.json", 'w') as fd: json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': ')) info('*** Pushing crossConnect.json to ONOS\n') output = quietRun('%s/onos-netcfg %s\ Topology.json' % (self.runPackDir, self.controllers[ 0 ].ip), shell=True) def stop_oe(self): ''' stop the existing LINC switch ''' cmd = "linc:stop_switch({}).\r\n".format(self.lincId) self.write_to_cli(cmd) #handling taps if any for i in range(0, len(self.crossConnects)): crossConnect = self.crossConnects.pop() if isinstance(crossConnect.intf1.node, LINCSwitch): intf = crossConnect.intf2 tapPort = crossConnect.intf1.port else: intf = crossConnect.intf1 tapPort = crossConnect.intf2.port intf.node.detach(LINCSwitch.findTap(self, tapPort)) self.deletedCrossConnects.append(crossConnect) def w_port_up(self, port): ''' port_up ''' cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port) self.write_to_cli(cmd) def w_port_down(self, port): ''' port_down ''' cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port) self.write_to_cli(cmd) # helper functions @staticmethod def bootOE(net, domain=None): """ Start the LINC optical emulator within a mininet instance This involves 1. converting the information stored in Linc* to configs for both LINC and the network config system, 2. starting Linc, 3. connecting cross-connects, and finally pushing the network configs to ONOS. Inevitably, there are times when we have OVS switches that should not be under the control of the controller in charge of the Linc switches. We hint at these by passing domain information. """ LINCSwitch.opticalJSON = {} linkConfig = [] devices = [] #setting up the controllers for LINCSwitch class LINCSwitch.controllers = net.controllers for switch in net.switches: if domain and switch not in domain: continue if isinstance(switch, OpticalSwitch): devices.append(switch.json()) elif isinstance(switch, OVSSwitch): devices.append(switchJSON(switch)) LINCSwitch.opticalJSON[ 'devices' ] = devices for link in net.links: if isinstance(link, LINCLink) : linkConfig.append(link.json()) LINCSwitch.opticalJSON[ 'links' ] = linkConfig info('*** Writing Topology.json file\n') topoJSON = LINCSwitch.makeTopoJSON() with open('Topology.json', 'w') as outfile: json.dump(topoJSON, outfile, indent=4, separators=(',', ': ')) info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n') topoConfigJson = {} topoConfigJson["switchConfig"] = getSwitchConfig(net.switches) topoConfigJson["linkConfig"] = getLinkConfig(net.links) #Writing to TopoConfig.json with open( 'TopoConfig.json', 'w' ) as outfile: json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') ) info('*** Creating sys.config...\n') output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s' % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True) if output: error('***ERROR: Error creating sys.config file: %s\n' % output) return False info ('*** Setting multiple controllers in sys.config...\n') searchStr = '\[{"Switch.*$' ctrlStr = '' for index in range(len(LINCSwitch.controllers)): ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port) replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr) output = quietRun(sedCmd, shell=True) info('*** Copying sys.config to linc-oe directory: ', output + '\n') output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n') info(output + '\n') info('*** Adding taps and bringing them up...\n') setupInts(LINCSwitch.getTaps()) info('*** removing pipes if any \n') quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True) info('*** Starting linc OE...\n') output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True) if output: error('***ERROR: LINC-OE: %s' % output + '\n') quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True) return False info('*** Waiting for linc-oe to start...\n') waitStarted(net) info('*** Adding cross-connect (tap) interfaces to packet switches...\n') for link in net.links: if isinstance(link, LINCLink) and link.isCrossConnect(): for intf in [ link.intf1, link.intf2 ]: if not isinstance(intf, LINCIntf): intfList = [ intf.link.intf1, intf.link.intf2 ] intfList.remove(intf) intf2 = intfList[ 0 ] intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ])) info('*** Waiting for all devices to be available in ONOS...\n') url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip time = 0 # Set up password authentication pw_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pw_mgr.add_password(None, url, LINCSwitch.restUser, LINCSwitch.restPass) handler = urllib2.HTTPBasicAuthHandler(pw_mgr) opener = urllib2.build_opener(handler) opener.open(url) urllib2.install_opener(opener) # focus on just checking the state of devices we're interested in # expected devices availability map devMap = dict.fromkeys(map( lambda x: x['uri'], devices ), False) while True: response = json.load(urllib2.urlopen(url)) devs = response.get('devices') # update availability map for d in devs: if devMap.has_key(d['id']): devMap[d['id']] = d['available'] # Check if all devices we're interested became available if all(devMap.viewvalues()): break; if (time >= TIMEOUT): error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT) break time += SLEEP_TIME sleep(SLEEP_TIME) info('*** Pushing Topology.json to ONOS\n') for index in range(len(LINCSwitch.controllers)): output = quietRun('%s/onos-netcfg %s Topology.json &'\ % (LINCSwitch.runPackDir, LINCSwitch.controllers[ index ].ip), shell=True) # successful output contains the two characters '{}' # if there is more output than this, there is an issue if output.strip('{}'): warn('***WARNING: Could not push topology file to ONOS: %s\n' % output) @staticmethod def makeTopoJSON(): """ Builds ONOS network config system compatible dicts to be written as Topology.json file. """ topology = {} links = {} devices = {} ports = {} BasicDevConfigKeys = ['name', 'type', 'latitude', 'longitude', 'allowed', 'rackAddress', 'owner', 'driver', 'manufacturer', 'hwVersion', 'swVersion', 'serial', 'managementAddress'] for switch in LINCSwitch.opticalJSON[ 'devices' ]: # Build device entries - keyed on uri (DPID) and config key 'basic' # 'type' is necessary field, else ONOS assumes it's a SWITCH # Annotations hold switch name and latitude/longitude devDict = {} devDict[ 'type' ] = switch[ 'type' ] devDict.update({k: v for k, v in switch[ 'annotations' ].iteritems() if k in BasicDevConfigKeys}) devSubj = switch[ 'uri' ] devices[ devSubj ] = { 'basic': devDict } # Build port entries - keyed on "uri/port" and config key 'optical' for port in switch[ 'ports' ]: portSubj = devSubj + '/' + str(port[ 'port' ]) ports[ portSubj ] = { 'optical': port } # Build link entries - keyed on "uri/port-uri/port" and config key 'basic' # Annotations hold the 'durable' field, which is necessary as long as we don't discover optical links for link in LINCSwitch.opticalJSON[ 'links' ]: linkDict = {} linkDict[ 'type' ] = link[ 'type' ] linkDict.update(link[ 'annotations' ]) linkSubj = link[ 'src' ] + '-' + link[ 'dst' ] links[ linkSubj ] = { 'basic': linkDict } topology[ 'links' ] = links topology[ 'devices' ] = devices topology[ 'ports' ] = ports return topology @staticmethod def shutdownOE(): "stop the optical emulator" info('*** Stopping linc OE...\n') quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True) @staticmethod def getTaps(path=None): ''' return list of all the taps in sys.config ''' if path is None: path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir fd = open(path, 'r', 0) sys_data = fd.read() taps = re.findall('tap\d+', sys_data) fd.close() return taps @staticmethod def findTap(node, port, path=None): '''utility function to parse through a sys.config file to find tap interfaces for a switch''' switch = False portLine = '' intfLines = [] if path is None: path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir with open(path) as f: for line in f: if 'tap' in line: intfLines.append(line) if node.dpid in line.translate(None, ':'): switch = True continue if switch: if 'switch' in line: switch = False if 'port_no,%s}' % port in line: portLine = line break if portLine: m = re.search('port,\d+', portLine) port = m.group(0).split(',')[ 1 ] else: error('***ERROR: Could not find any ports in sys.config\n') return for intfLine in intfLines: if 'port,%s' % port in intfLine: return re.findall('tap\d+', intfLine)[ 0 ] def json(self): "return json configuration dictionary for switch" return self.configDict def terminate(self): pass class LINCLink(Link): """ LINC link class """ def __init__(self, node1, node2, port1=None, port2=None, allowed=True, intfName1=None, intfName2=None, linkType='OPTICAL', annotations={}, speed1=0, speed2=0, **params): "Creates a dummy link without a virtual ethernet pair." self.allowed = allowed self.annotations = annotations self.linkType = linkType self.port1 = port1 self.port2 = port2 params1 = { 'speed': speed1 } params2 = { 'speed': speed2 } if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch): self.isXC = False else: self.isXC = True if isinstance(node1, LINCSwitch): cls1 = LINCIntf if self.isXC: node1.crossConnects.append(self) else: cls1 = Intf # bad hack to stop error message from appearing when we try to set up intf in a packet switch, # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up intfName1 = 'lo' if isinstance(node2, LINCSwitch): cls2 = LINCIntf if self.isXC: node2.crossConnects.append(self) else: cls2 = Intf intfName2 = 'lo' Link.__init__(self, node1, node2, port1=port1, port2=port2, intfName1=intfName1, intfName2=intfName2, cls1=cls1, cls2=cls2, params1=params1, params2=params2) @classmethod def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs): pass def json(self): "build and return the json configuration dictionary for this link" configData = {} configData[ 'src' ] = ('of:' + self.intf1.node.dpid + '/%s' % self.intf1.node.ports[ self.intf1 ]) configData[ 'dst' ] = ('of:' + self.intf2.node.dpid + '/%s' % self.intf2.node.ports[ self.intf2 ]) configData[ 'type' ] = self.linkType configData[ 'annotations' ] = self.annotations return configData def isCrossConnect(self): if isinstance(self.intf1.node, LINCSwitch) ^ isinstance(self.intf2.node, LINCSwitch): return True return False def isTransportLayer(self): if isinstance(self.intf1.node, LINCSwitch) and isinstance(self.intf2.node, LINCSwitch): return True return False class LINCIntf(OpticalIntf): """ LINC interface class """ def __init__(self, name=None, node=None, speed=0, port=None, link=None, **params): self.node = node self.speed = speed self.port = port self.link = link self.name = name node.addIntf(self, port=port) self.params = params self.ip = None def json(self): "build and return the JSON information for this interface( not used right now )" configDict = {} configDict[ 'port' ] = self.port configDict[ 'speed' ] = self.speed portType = 'COPPER' if isinstance(self.link, LINCLink): portType = 'OCH' if self.link.isCrossConnect() else 'OMS' configDict[ 'type' ] = portType return configDict def config(self, *args, **kwargs): "dont configure a dummy interface" pass def ifconfig(self, status): "configure the status" if status == "up": return self.node.w_port_up(self.port) elif status == "down": return self.node.w_port_down(self.port) class MininetOE(Mininet): "Mininet with Linc-OE support (starts and stops linc-oe)" def start(self): Mininet.start(self) LINCSwitch.bootOE(self) def stop(self): Mininet.stop(self) LINCSwitch.shutdownOE() def addControllers(self, controllers): i = 0 for ctrl in controllers: self.addController(RemoteController('c%d' % i, ip=ctrl)) i += 1 if __name__ == '__main__': pass