Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | ''' |
| 4 | Notes: |
| 5 | |
| 6 | This file contains classes and methods useful for integrating LincOE with Mininet, |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 7 | such as startOE, stopOE, OpticalLink, and OpticalSwitch |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 8 | |
| 9 | - $ONOS_ROOT ust be set |
| 10 | - Need to run with sudo -E to preserve ONOS_ROOT env var |
| 11 | - We assume LINC-Config-Generator is named LINC-Config-Generator |
| 12 | - We also assume linc-oe is named linc-oe |
| 13 | - LINC-config-generator and linc-oe must be subdirectories of the user's |
| 14 | home directory |
| 15 | |
| 16 | TODO |
| 17 | ----------- |
| 18 | - clean up files after runtime |
| 19 | - maybe save the old files in a separate directory? |
| 20 | - modify script to allow startOE to run before net.start() |
| 21 | - add ONOS as a controller in script |
| 22 | |
| 23 | Usage: |
| 24 | ------------ |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 25 | - import OpticalLink and OpticalSwitch from this module |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 26 | - import startOE and stopOE from this module |
| 27 | - create topology as you would a normal topology. when |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 28 | to an optical switch with topo.addLink, always specify cls=OpticalLink |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 29 | - when creating an optical switch, use cls=OpticalSwitch in topo.addSwitch |
| 30 | - for annotations on links and switches, a dictionary must be passed in as |
| 31 | the annotations argument |
| 32 | - startOE must be run AFTER net.start() with net as an argument. |
| 33 | - stopOE can be run at any time |
| 34 | |
| 35 | I created a separate function to start lincOE to avoid subclassing Mininet. |
| 36 | In case anyone wants to write something that DOES subclass Mininet, I |
| 37 | thought I would outline how: |
| 38 | |
| 39 | If we want an object that starts lincOE within the mininet class itself, |
| 40 | we need to add another object to Mininet that contains all of the json object |
| 41 | information for each switch. We would still subclass switch and link, but these |
| 42 | classes would basically be dummy classes that store their own json information |
| 43 | in the Mininet class object. We may also change the default switch class to add |
| 44 | it's tap interfaces from lincOE during startup. The start() method for mininet would |
| 45 | grab all of the information from these switches and links, write configuration files |
| 46 | for lincOE using the json module, start lincOE, then run the start methodfor each |
| 47 | switch. The new start() method for each switch would parse through the sys.config |
| 48 | file that was created and find the tap interface it needs to connect to, similar |
| 49 | to the findTap function that I currently use. After all of the controllers and |
| 50 | switches have been started, the new Mininet start() method should also push the |
| 51 | Topology configuration file to ONOS. |
| 52 | |
| 53 | ''' |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 54 | |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 55 | import re |
| 56 | import json |
| 57 | import os |
| 58 | from time import sleep |
| 59 | |
| 60 | from mininet.node import Switch, RemoteController |
| 61 | from mininet.topo import Topo |
| 62 | from mininet.util import quietRun |
| 63 | from mininet.net import Mininet |
| 64 | from mininet.log import setLogLevel, info, error, warn |
| 65 | from mininet.link import Link, Intf |
| 66 | from mininet.cli import CLI |
| 67 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 68 | class OpticalSwitch( Switch ): |
Marc De Leenheer | 16f857b | 2015-05-05 20:50:24 -0700 | [diff] [blame] | 69 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 70 | def __init__( self, name, dpid=None, allowed=True, |
| 71 | switchType='ROADM', annotations={}, **params ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 72 | params[ 'inNamespace' ] = False |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 73 | Switch.__init__( self, name, dpid=dpid, **params ) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 74 | self.name = name |
| 75 | self.annotations = annotations |
| 76 | self.allowed = allowed |
| 77 | self.switchType = switchType |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 78 | self.configDict = {} # dictionary that holds all of the JSON configuration data |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 79 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 80 | def start( self, *opts, **params ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 81 | '''Instead of starting a virtual switch, we build the JSON |
| 82 | dictionary for the emulated optical switch''' |
| 83 | self.configDict[ 'uri' ] = 'of:' + self.dpid |
| 84 | self.configDict[ 'annotations' ] = self.annotations |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 85 | self.configDict[ 'annotations' ].setdefault( 'name', self.name ) |
| 86 | self.configDict[ 'hw' ] = 'OE' |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 87 | self.configDict[ 'mfr' ] = 'Linc' |
| 88 | self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1] |
| 89 | self.configDict[ 'type' ] = self.switchType |
| 90 | self.configDict[ 'ports' ] = [] |
| 91 | for port, intf in self.intfs.items(): |
| 92 | if intf.name == 'lo': |
| 93 | continue |
| 94 | else: |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 95 | self.configDict[ 'ports' ].append( intf.json() ) |
fahad | 44e62c7 | 2015-03-04 14:55:35 -0800 | [diff] [blame] | 96 | |
| 97 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 98 | def json( self ): |
fahad | 44e62c7 | 2015-03-04 14:55:35 -0800 | [diff] [blame] | 99 | "return json configuration dictionary for switch" |
| 100 | return self.configDict |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 101 | |
| 102 | def terminate( self ): |
fahad | 44e62c7 | 2015-03-04 14:55:35 -0800 | [diff] [blame] | 103 | pass |
| 104 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 105 | class OpticalLink( Link ): |
| 106 | |
| 107 | def __init__( self, node1, node2, port1=None, port2=None, allowed=True, |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 108 | intfName1=None, intfName2=None, linkType='OPTICAL', |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 109 | annotations={}, speed1=0, speed2=0, **params ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 110 | "Creates a dummy link without a virtual ethernet pair." |
| 111 | self.allowed = allowed |
| 112 | self.annotations = annotations |
| 113 | self.linkType = linkType |
| 114 | params1 = { 'speed': speed1 } |
| 115 | params2 = { 'speed': speed2 } |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 116 | |
| 117 | if isinstance( node1, OpticalSwitch ): |
| 118 | cls1 = OpticalIntf |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 119 | else: |
| 120 | cls1 = Intf |
| 121 | # bad hack to stop error message from appearing when we try to set up intf in a packet switch, |
| 122 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up |
| 123 | intfName1 = 'lo' |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 124 | if isinstance( node2, OpticalSwitch ): |
| 125 | cls2 = OpticalIntf |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 126 | else: |
| 127 | cls2 = Intf |
| 128 | intfName2 = 'lo' |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 129 | Link.__init__( self, node1, node2, port1=port1, port2=port2, |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 130 | intfName1=intfName1, intfName2=intfName2, cls1=cls1, |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 131 | cls2=cls2, params1=params1, params2=params2 ) |
| 132 | |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 133 | |
| 134 | @classmethod |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 135 | def makeIntfPair( _cls, intfName1, intfName2, *args, **kwargs ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 136 | pass |
| 137 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 138 | def json( self ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 139 | "build and return the json configuration dictionary for this link" |
| 140 | configData = {} |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 141 | configData[ 'src' ] = ( 'of:' + self.intf1.node.dpid + |
| 142 | '/%s' % self.intf1.node.ports[ self.intf1 ] ) |
| 143 | configData[ 'dst' ] = ( 'of:' + self.intf2.node.dpid + |
| 144 | '/%s' % self.intf2.node.ports[ self.intf2 ] ) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 145 | configData[ 'type' ] = self.linkType |
| 146 | configData[ 'annotations' ] = self.annotations |
| 147 | return configData |
| 148 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 149 | class OpticalIntf( Intf ): |
| 150 | |
| 151 | def __init__( self, name=None, node=None, speed=0, |
| 152 | port=None, link=None, **params ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 153 | self.node = node |
| 154 | self.speed = speed |
| 155 | self.port = port |
| 156 | self.link = link |
| 157 | self.name = name |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 158 | node.addIntf( self, port=port ) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 159 | self.params = params |
| 160 | self.ip = None |
| 161 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 162 | def json( self ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 163 | "build and return the JSON information for this interface( not used right now )" |
| 164 | configDict = {} |
| 165 | configDict[ 'port' ] = self.port |
| 166 | configDict[ 'speed' ] = self.speed |
| 167 | configDict[ 'type' ] = 'FIBER' |
| 168 | return configDict |
| 169 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 170 | def config( self, *args, **kwargs ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 171 | "dont configure a dummy interface" |
| 172 | pass |
| 173 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 174 | def switchJSON( switch ): |
| 175 | "Returns the json configuration for a packet switch" |
| 176 | configDict = {} |
| 177 | configDict[ 'uri' ] = 'of:' + switch.dpid |
| 178 | configDict[ 'mac' ] = quietRun( 'cat /sys/class/net/%s/address' % switch.name ).strip( '\n' ).translate( None, ':' ) |
| 179 | configDict[ 'hw' ] = 'PK' # FIXME what about OVS? |
| 180 | configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS? |
| 181 | configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS? |
| 182 | annotations = switch.params.get( 'annotations', {} ) |
| 183 | annotations.setdefault( 'name', switch.name ) |
| 184 | configDict[ 'annotations' ] = annotations |
| 185 | ports = [] |
| 186 | for port, intf in switch.intfs.items(): |
| 187 | if intf.name == 'lo': |
| 188 | continue |
| 189 | portDict = {} |
| 190 | portDict[ 'port' ] = port |
| 191 | portDict[ 'type' ] = 'FIBER' if isinstance( intf.link, OpticalLink ) else 'COPPER' |
| 192 | intfList = [ intf.link.intf1, intf.link.intf2 ] |
| 193 | intfList.remove( intf ) |
| 194 | portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance( intf.link, OpticalLink ) else 0 |
| 195 | ports.append( portDict ) |
| 196 | configDict[ 'ports' ] = ports |
| 197 | return configDict |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 198 | |
| 199 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 200 | def startOE( net ): |
| 201 | "Start the LINC optical emulator within a mininet instance" |
| 202 | opticalJSON = {} |
| 203 | linkConfig = [] |
| 204 | devices = [] |
| 205 | |
| 206 | for switch in net.switches: |
| 207 | if isinstance( switch, OpticalSwitch ): |
| 208 | devices.append( switch.json() ) |
| 209 | else: |
| 210 | devices.append( switchJSON( switch ) ) |
| 211 | opticalJSON[ 'devices' ] = devices |
| 212 | |
| 213 | for link in net.links: |
| 214 | if isinstance( link, OpticalLink ) : |
| 215 | linkConfig.append( link.json() ) |
| 216 | |
| 217 | opticalJSON[ 'links' ] = linkConfig |
| 218 | |
| 219 | try: |
| 220 | onosDir = os.environ[ 'ONOS_ROOT' ] |
| 221 | except: |
| 222 | onosDir = findDir( 'onos' ) |
| 223 | if not onosDir: |
| 224 | error( 'Please set ONOS_ROOT environment variable!\n' ) |
| 225 | return False |
| 226 | else: |
| 227 | os.environ[ 'ONOS_ROOT' ] = onosDir |
| 228 | |
| 229 | info( '*** Writing Topology.json file\n' ) |
| 230 | with open( 'Topology.json', 'w' ) as outfile: |
| 231 | json.dump( opticalJSON, outfile, indent=4, separators=(',', ': ') ) |
| 232 | |
| 233 | info( '*** Converting Topology.json to linc-oe format (TopoConfig.json) file (not using oecfg) \n' ) |
| 234 | |
| 235 | topoConfigJson = {}; |
| 236 | newLinkConfig = []; |
| 237 | switchConfig = []; |
| 238 | dpIdToName = {}; |
| 239 | |
| 240 | #Iterate through all switches and convert the ROADM switches to linc-oe format |
| 241 | for switch in opticalJSON["devices"]: |
| 242 | if switch["type"] == "ROADM": |
| 243 | builtSwitch = {} |
| 244 | |
| 245 | #set basic switch params based on annotations |
| 246 | builtSwitch["allowed"] = True; |
| 247 | builtSwitch["latitude"] = switch["annotations"]["latitude"]; |
| 248 | builtSwitch["longitude"] = switch["annotations"]["longitude"]; |
| 249 | |
| 250 | nodeId = switch["uri"] |
| 251 | |
| 252 | #convert the nodeId to linc-oe format |
| 253 | nodeDpid = dpId(nodeId); |
| 254 | |
| 255 | if "name" in switch: |
| 256 | builtSwitch["name"] = switch["name"] |
| 257 | else: |
| 258 | builtSwitch["name"] = "none" |
| 259 | |
| 260 | #keep track of the name corresponding to each switch dpid |
| 261 | dpIdToName[nodeDpid] = builtSwitch["name"]; |
| 262 | |
| 263 | builtSwitch["nodeDpid"] = nodeDpid |
| 264 | |
| 265 | #set switch params and type |
| 266 | builtSwitch["params"] = {}; |
| 267 | builtSwitch["params"]["numregens"] = switch["annotations"]["optical.regens"]; |
| 268 | builtSwitch["type"] = "Roadm" |
| 269 | |
| 270 | #append to list of switches |
| 271 | switchConfig.append(builtSwitch); |
| 272 | |
| 273 | topoConfigJson["switchConfig"] = switchConfig; |
| 274 | |
| 275 | #Iterate through all optical links and convert them to linc-oe format |
| 276 | for link in opticalJSON["links"]: |
| 277 | if link["type"] == "OPTICAL": |
| 278 | builtLink = {} |
| 279 | |
| 280 | #set basic link params for src and dst |
| 281 | builtLink["allowed"] = True; |
| 282 | builtLink["nodeDpid1"] = dpId(link["src"]) |
| 283 | builtLink["nodeDpid2"] = dpId(link["dst"]) |
| 284 | |
| 285 | #set more params such as name/bandwidth/port/waves if they exist |
| 286 | params = {} |
| 287 | params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none") |
| 288 | params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none") |
| 289 | if "bandwidth" in link["annotations"]: |
| 290 | params["bandwidth"] = link["annotations"]["bandwidth"] |
| 291 | params["port1"] = int(link["src"].split("/")[1]) |
| 292 | params["port2"] = int(link["dst"].split("/")[1]) |
| 293 | |
| 294 | if "optical.waves" in link["annotations"]: |
| 295 | params["numWaves"] = link["annotations"]["optical.waves"] |
| 296 | builtLink["params"] = params |
| 297 | |
| 298 | #set type of link (WDM or pktOpt) |
| 299 | if link["annotations"]["optical.type"] == "WDM": |
| 300 | builtLink["type"] = "wdmLink" |
| 301 | else: |
| 302 | builtLink["type"] = "pktOptLink" |
| 303 | |
| 304 | newLinkConfig.append(builtLink); |
| 305 | |
| 306 | topoConfigJson["linkConfig"] = newLinkConfig; |
| 307 | |
| 308 | #Writing to TopoConfig.json |
| 309 | with open( 'TopoConfig.json', 'w' ) as outfile: |
| 310 | json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') ) |
| 311 | |
| 312 | info( '*** Creating sys.config...\n' ) |
| 313 | configGen = findDir( 'LINC-config-generator' ) |
| 314 | if not configGen: |
| 315 | error( "***ERROR: Could not find LINC-config-generator in user's home directory\n" ) |
| 316 | return False |
| 317 | output = quietRun( '%s/config_generator TopoConfig.json %s/sys.config.template %s %s' |
| 318 | % ( configGen, configGen, net.controllers[ 0 ].ip, net.controllers[ 0 ].port ), shell=True ) |
| 319 | if output: |
| 320 | error( '***ERROR: Error creating sys.config file: %s\n' % output ) |
| 321 | return False |
| 322 | |
| 323 | info ('*** Setting multiple controllers in sys.config...\n' ) |
| 324 | searchStr = '{controllers,.*$' |
| 325 | ctrlStr = '' |
| 326 | for index in range(len(net.controllers)): |
| 327 | ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port) |
| 328 | replaceStr = '{controllers,[%s]},' % ctrlStr[:-1] # Cut off last comma |
| 329 | sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr) |
| 330 | output = quietRun( sedCmd, shell=True ) |
| 331 | |
| 332 | info( '*** Copying sys.config to linc-oe directory: ', output + '\n' ) |
| 333 | lincDir = findDir( 'linc-oe' ) |
| 334 | if not lincDir: |
| 335 | error( "***ERROR: Could not find linc-oe in user's home directory\n" ) |
| 336 | return False |
| 337 | output = quietRun( 'cp -v sys.config %s/rel/linc/releases/1.0/' % lincDir, shell=True ).strip( '\n' ) |
| 338 | info( output + '\n' ) |
| 339 | |
| 340 | info( '*** Starting linc OE...\n' ) |
| 341 | output = quietRun( '%s/rel/linc/bin/linc start' % lincDir, shell=True ) |
| 342 | if output: |
| 343 | error( '***ERROR: LINC-OE: %s' % output + '\n' ) |
| 344 | quietRun( '%s/rel/linc/bin/linc stop' % lincDir, shell=True ) |
| 345 | return False |
| 346 | |
| 347 | info( '*** Waiting for linc-oe to start...\n' ) |
| 348 | waitStarted( net ) |
| 349 | |
| 350 | info( '*** Adding cross-connect (tap) interfaces to packet switches...\n' ) |
| 351 | for link in net.links: |
| 352 | if isinstance( link, OpticalLink ): |
| 353 | if link.annotations[ 'optical.type' ] == 'cross-connect': |
| 354 | for intf in [ link.intf1, link.intf2 ]: |
| 355 | if not isinstance( intf, OpticalIntf ): |
| 356 | intfList = [ intf.link.intf1, intf.link.intf2 ] |
| 357 | intfList.remove( intf ) |
| 358 | intf2 = intfList[ 0 ] |
| 359 | intf.node.attach( findTap( intf2.node, intf2.node.ports[ intf2 ] ) ) |
| 360 | |
| 361 | info( '*** Press ENTER to push Topology.json to onos...\n' ) |
| 362 | raw_input() # FIXME... we should eventually remove this |
| 363 | info( '*** Pushing Topology.json to ONOS\n' ) |
| 364 | output = quietRun( '%s/tools/test/bin/onos-topo-cfg %s Topology.json' % ( onosDir, net.controllers[ 0 ].ip ), shell=True ) |
| 365 | # successful output contains the two characters '{}' |
| 366 | # if there is more output than this, there is an issue |
| 367 | if output.strip( '{}' ): |
| 368 | warn( '***WARNING: Could not push topology file to ONOS: %s' % output ) |
| 369 | |
| 370 | #converts node ids to linc-oe format, with colons every two chars |
| 371 | def dpId(id): |
| 372 | nodeDpid = "" |
| 373 | id = id.split("/", 1)[0] |
| 374 | for i in range(3, len(id) - 1, 2): |
| 375 | nodeDpid += (id[i:(i + 2):]) + ":" |
| 376 | return nodeDpid[0:(len(nodeDpid) - 1)]; |
| 377 | |
| 378 | def waitStarted( net, timeout=None ): |
| 379 | "wait until all tap interfaces are available" |
| 380 | tapCount = 0 |
| 381 | time = 0 |
| 382 | for link in net.links: |
| 383 | if isinstance( link, OpticalLink ): |
| 384 | if link.annotations[ 'optical.type' ] == 'cross-connect': |
| 385 | tapCount += 1 |
| 386 | |
| 387 | while True: |
| 388 | if str( tapCount ) == quietRun( 'ip addr | grep tap | wc -l', shell=True ).strip( '\n' ): |
| 389 | return True |
| 390 | if timeout: |
| 391 | if time >= timeout: |
| 392 | error( '***ERROR: Linc OE did not start within %s seconds' % timeout ) |
| 393 | return False |
| 394 | time += .5 |
| 395 | sleep( .5 ) |
| 396 | |
| 397 | def stopOE(): |
| 398 | "stop the optical emulator" |
| 399 | info( '*** Stopping linc OE...\n' ) |
| 400 | lincDir = findDir( 'linc-oe' ) |
| 401 | quietRun( '%s/rel/linc/bin/linc stop' % lincDir, shell=True ) |
| 402 | |
| 403 | def findDir( directory ): |
| 404 | "finds and returns the path of any directory in the user's home directory" |
| 405 | user = findUser() |
| 406 | homeDir = '/home/' + user |
| 407 | Dir = quietRun( 'find %s -maxdepth 1 -name %s -type d' % ( homeDir, directory ) ).strip( '\n' ) |
| 408 | DirList = Dir.split( '\n' ) |
| 409 | if not Dir: |
| 410 | return None |
| 411 | elif len( DirList ) > 1 : |
| 412 | warn( '***WARNING: Found multiple instances of %s; using %s\n' |
| 413 | % ( directory, DirList[ 0 ] ) ) |
| 414 | return DirList[ 0 ] |
| 415 | else: |
| 416 | return Dir |
| 417 | |
| 418 | def findUser(): |
| 419 | "Try to return logged-in (usually non-root) user" |
| 420 | try: |
| 421 | # If we're running sudo |
| 422 | return os.environ[ 'SUDO_USER' ] |
| 423 | except: |
| 424 | try: |
| 425 | # Logged-in user (if we have a tty) |
| 426 | return quietRun( 'who am i' ).split()[ 0 ] |
| 427 | except: |
| 428 | # Give up and return effective user |
| 429 | return quietRun( 'whoami' ) |
| 430 | |
| 431 | |
| 432 | def findTap( node, port, path=None ): |
| 433 | '''utility function to parse through a sys.config |
| 434 | file to find tap interfaces for a switch''' |
| 435 | switch=False |
| 436 | portLine = '' |
| 437 | intfLines = [] |
| 438 | |
| 439 | if path is None: |
| 440 | lincDir = findDir( 'linc-oe' ) |
| 441 | if not lincDir: |
| 442 | error( '***ERROR: Could not find linc-oe in users home directory\n' ) |
| 443 | return None |
| 444 | path = '%s/rel/linc/releases/1.0/sys.config' % lincDir |
| 445 | |
| 446 | with open( path ) as f: |
| 447 | for line in f: |
| 448 | if 'tap' in line: |
| 449 | intfLines.append( line ) |
| 450 | if node.dpid in line.translate( None, ':' ): |
| 451 | switch=True |
| 452 | continue |
| 453 | if switch: |
| 454 | if 'switch' in line: |
| 455 | switch = False |
| 456 | if 'port_no,%s}' % port in line: |
| 457 | portLine = line |
| 458 | break |
| 459 | |
| 460 | if portLine: |
| 461 | m = re.search( 'port,\d+', portLine ) |
| 462 | port = m.group( 0 ).split( ',' )[ 1 ] |
| 463 | else: |
| 464 | error( '***ERROR: Could not find any ports in sys.config\n' ) |
| 465 | return |
| 466 | |
| 467 | for intfLine in intfLines: |
| 468 | if 'port,%s' % port in intfLine: |
| 469 | return re.findall( 'tap\d+', intfLine )[ 0 ] |
| 470 | |
| 471 | |
| 472 | class MininetOE( Mininet ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 473 | "Mininet with Linc-OE support (starts and stops linc-oe)" |
| 474 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 475 | def start( self ): |
| 476 | Mininet.start( self ) |
| 477 | startOE( self ) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 478 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 479 | def stop( self ): |
| 480 | Mininet.stop( self ) |
| 481 | stopOE() |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 482 | |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 483 | def addControllers( self, controllers ): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 484 | i = 0 |
| 485 | for ctrl in controllers: |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 486 | self.addController( RemoteController( 'c%d' % i, ip=ctrl ) ) |
| 487 | |
| 488 | |
| 489 | class OpticalTestTopo( Topo ): |
| 490 | |
| 491 | def build( self ): |
| 492 | opticalAnn = { 'optical.waves': 80, 'optical.type': "WDM", 'durable': True } |
| 493 | switchAnn = { 'bandwidth': 100000, 'optical.type': 'cross-connect', 'durable': True } |
| 494 | h1 = self.addHost( 'h1' ) |
| 495 | h2 = self.addHost( 'h2' ) |
| 496 | s1 = self.addSwitch( 's1' ) |
| 497 | s2 = self.addSwitch( 's2' ) |
| 498 | O4 = self.addSwitch( 'O4', cls=OpticalSwitch ) |
| 499 | O5 = self.addSwitch( 'O5', cls=OpticalSwitch ) |
| 500 | O6 = self.addSwitch( 'O6', cls=OpticalSwitch ) |
| 501 | self.addLink( O4, O5, cls=OpticalLink, annotations=opticalAnn ) |
| 502 | self.addLink( O5, O6, cls=OpticalLink, annotations=opticalAnn ) |
| 503 | self.addLink( s1, O4, cls=OpticalLink, annotations=switchAnn ) |
| 504 | self.addLink( s2, O6, cls=OpticalLink, annotations=switchAnn ) |
| 505 | self.addLink( h1, s1 ) |
| 506 | self.addLink( h2, s2 ) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 507 | |
| 508 | if __name__ == '__main__': |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 509 | import sys |
| 510 | if len( sys.argv ) >= 2: |
| 511 | controllers = sys.argv[1:] |
| 512 | else: |
| 513 | print 'Usage: ./opticalUtils.py (<Controller IP>)+' |
| 514 | print 'Using localhost...\n' |
| 515 | controllers = [ '127.0.0.1' ] |
| 516 | |
| 517 | setLogLevel( 'info' ) |
| 518 | net = MininetOE( topo=OpticalTestTopo(), controller=None, autoSetMacs=True ) |
| 519 | net.addControllers( controllers ) |
| 520 | net.start() |
| 521 | CLI( net ) |
| 522 | net.stop() |