blob: 3315ce68004e6751eac5a276c531b8ee317e03dc [file] [log] [blame]
Brian O'Connoreb27c452014-12-07 02:43:58 -08001#!/usr/bin/python
2
3'''
4Notes:
5
6This file contains classes and methods useful for integrating LincOE with Mininet,
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -07007such as startOE, stopOE, OpticalLink, and OpticalSwitch
Brian O'Connoreb27c452014-12-07 02:43:58 -08008
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 Cheerla5f8f8f02015-07-07 16:01:17 -070025 - import OpticalLink and OpticalSwitch from this module
Brian O'Connoreb27c452014-12-07 02:43:58 -080026 - import startOE and stopOE from this module
27 - create topology as you would a normal topology. when
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070028 to an optical switch with topo.addLink, always specify cls=OpticalLink
Brian O'Connoreb27c452014-12-07 02:43:58 -080029 - 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
35I created a separate function to start lincOE to avoid subclassing Mininet.
36In case anyone wants to write something that DOES subclass Mininet, I
37thought I would outline how:
38
39If we want an object that starts lincOE within the mininet class itself,
40we need to add another object to Mininet that contains all of the json object
41information for each switch. We would still subclass switch and link, but these
42classes would basically be dummy classes that store their own json information
43in the Mininet class object. We may also change the default switch class to add
44it's tap interfaces from lincOE during startup. The start() method for mininet would
45grab all of the information from these switches and links, write configuration files
46for lincOE using the json module, start lincOE, then run the start methodfor each
47switch. The new start() method for each switch would parse through the sys.config
48file that was created and find the tap interface it needs to connect to, similar
49to the findTap function that I currently use. After all of the controllers and
50switches have been started, the new Mininet start() method should also push the
51Topology configuration file to ONOS.
52
53'''
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070054
Brian O'Connoreb27c452014-12-07 02:43:58 -080055import re
56import json
57import os
58from time import sleep
59
60from mininet.node import Switch, RemoteController
61from mininet.topo import Topo
62from mininet.util import quietRun
63from mininet.net import Mininet
64from mininet.log import setLogLevel, info, error, warn
65from mininet.link import Link, Intf
66from mininet.cli import CLI
67
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070068class OpticalSwitch( Switch ):
Marc De Leenheer16f857b2015-05-05 20:50:24 -070069
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070070 def __init__( self, name, dpid=None, allowed=True,
71 switchType='ROADM', annotations={}, **params ):
Brian O'Connoreb27c452014-12-07 02:43:58 -080072 params[ 'inNamespace' ] = False
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070073 Switch.__init__( self, name, dpid=dpid, **params )
Brian O'Connoreb27c452014-12-07 02:43:58 -080074 self.name = name
75 self.annotations = annotations
76 self.allowed = allowed
77 self.switchType = switchType
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070078 self.configDict = {} # dictionary that holds all of the JSON configuration data
Brian O'Connoreb27c452014-12-07 02:43:58 -080079
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070080 def start( self, *opts, **params ):
Brian O'Connoreb27c452014-12-07 02:43:58 -080081 '''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 Cheerla5f8f8f02015-07-07 16:01:17 -070085 self.configDict[ 'annotations' ].setdefault( 'name', self.name )
86 self.configDict[ 'hw' ] = 'OE'
Brian O'Connoreb27c452014-12-07 02:43:58 -080087 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 Cheerla5f8f8f02015-07-07 16:01:17 -070095 self.configDict[ 'ports' ].append( intf.json() )
fahad44e62c72015-03-04 14:55:35 -080096
97
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -070098 def json( self ):
fahad44e62c72015-03-04 14:55:35 -080099 "return json configuration dictionary for switch"
100 return self.configDict
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700101
102 def terminate( self ):
fahad44e62c72015-03-04 14:55:35 -0800103 pass
104
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700105class OpticalLink( Link ):
106
107 def __init__( self, node1, node2, port1=None, port2=None, allowed=True,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800108 intfName1=None, intfName2=None, linkType='OPTICAL',
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700109 annotations={}, speed1=0, speed2=0, **params ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800110 "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 Cheerla5f8f8f02015-07-07 16:01:17 -0700116
117 if isinstance( node1, OpticalSwitch ):
118 cls1 = OpticalIntf
Brian O'Connoreb27c452014-12-07 02:43:58 -0800119 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 Cheerla5f8f8f02015-07-07 16:01:17 -0700124 if isinstance( node2, OpticalSwitch ):
125 cls2 = OpticalIntf
Brian O'Connoreb27c452014-12-07 02:43:58 -0800126 else:
127 cls2 = Intf
128 intfName2 = 'lo'
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700129 Link.__init__( self, node1, node2, port1=port1, port2=port2,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800130 intfName1=intfName1, intfName2=intfName2, cls1=cls1,
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700131 cls2=cls2, params1=params1, params2=params2 )
132
Brian O'Connoreb27c452014-12-07 02:43:58 -0800133
134 @classmethod
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700135 def makeIntfPair( _cls, intfName1, intfName2, *args, **kwargs ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800136 pass
137
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700138 def json( self ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800139 "build and return the json configuration dictionary for this link"
140 configData = {}
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700141 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'Connoreb27c452014-12-07 02:43:58 -0800145 configData[ 'type' ] = self.linkType
146 configData[ 'annotations' ] = self.annotations
147 return configData
148
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700149class OpticalIntf( Intf ):
150
151 def __init__( self, name=None, node=None, speed=0,
152 port=None, link=None, **params ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800153 self.node = node
154 self.speed = speed
155 self.port = port
156 self.link = link
157 self.name = name
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700158 node.addIntf( self, port=port )
Brian O'Connoreb27c452014-12-07 02:43:58 -0800159 self.params = params
160 self.ip = None
161
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700162 def json( self ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800163 "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 Cheerla5f8f8f02015-07-07 16:01:17 -0700170 def config( self, *args, **kwargs ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800171 "dont configure a dummy interface"
172 pass
173
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700174def 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'Connoreb27c452014-12-07 02:43:58 -0800198
199
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700200def 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
371def 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
378def 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
397def 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
403def 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
418def 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
432def 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
472class MininetOE( Mininet ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800473 "Mininet with Linc-OE support (starts and stops linc-oe)"
474
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700475 def start( self ):
476 Mininet.start( self )
477 startOE( self )
Brian O'Connoreb27c452014-12-07 02:43:58 -0800478
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700479 def stop( self ):
480 Mininet.stop( self )
481 stopOE()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800482
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700483 def addControllers( self, controllers ):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800484 i = 0
485 for ctrl in controllers:
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700486 self.addController( RemoteController( 'c%d' % i, ip=ctrl ) )
487
488
489class 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'Connoreb27c452014-12-07 02:43:58 -0800507
508if __name__ == '__main__':
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700509 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()