blob: 6fc3c1e6a9292afc2142588922d716b0e6319be3 [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,
Marc De Leenheer6ff97642015-07-08 19:21:16 +00007such as startOE, stopOE, LINCLink, 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 ------------
Marc De Leenheer6ff97642015-07-08 19:21:16 +000025 - import LINCLink 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
Marc De Leenheer6ff97642015-07-08 19:21:16 +000028 to an optical switch with topo.addLink, always specify cls=LINCLink
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'''
Marc De Leenheer6ff97642015-07-08 19:21:16 +000054import sys
Brian O'Connoreb27c452014-12-07 02:43:58 -080055import re
56import json
57import os
58from time import sleep
Marc De Leenheer6ff97642015-07-08 19:21:16 +000059import urllib2
Brian O'Connoreb27c452014-12-07 02:43:58 -080060
61from mininet.node import Switch, RemoteController
62from mininet.topo import Topo
63from mininet.util import quietRun
64from mininet.net import Mininet
65from mininet.log import setLogLevel, info, error, warn
66from mininet.link import Link, Intf
67from mininet.cli import CLI
68
Marc De Leenheer6ff97642015-07-08 19:21:16 +000069# Sleep time and timeout values in seconds
70SLEEP_TIME = 2
71TIMEOUT = 60
Marc De Leenheer16f857b2015-05-05 20:50:24 -070072
Marc De Leenheer6ff97642015-07-08 19:21:16 +000073class OpticalSwitch(Switch):
74 """
75 For now, same as Switch class.
76 """
77 pass
78
79class OpticalIntf(Intf):
80 """
81 For now,same as Intf class.
82 """
83 pass
84
85class OpticalLink(Link):
86 """
87 For now, same as Link.
88 """
89 pass
90
91class LINCSwitch(OpticalSwitch):
92 """
93 LINCSwitch class
94 """
95 # FIXME:Sometimes LINC doesn't remove pipes and on restart increase the pipe
96 # number from erlang.pipe.1.* to erlang.pipe.2.*, so should read and write
97 # from latest pipe files. For now we are removing all the pipes before
98 # starting LINC.
99 ### User Name ###
100 user = os.getlogin()
101 ### pipes ###
102 readPipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.r".format(user)
103 writePipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.w".format(user)
104 ### sys.config path ###
105 sysConfig = "/home/{}/linc-oe/rel/linc/releases/1.0/sys.config".format(user)
106 ### method, mapping dpid to LINC switchId ###
107 @staticmethod
108 def dpids_to_ids(sysConfig):
109 '''
110 return the dict containing switch dpids as key and LINC switch id as values
111 '''
112 dpids_to_ids = {}
113 fd = None
114 try:
115 with open(sysConfig, 'r', 0) as fd:
116 switch_id = 1
117 for line in fd:
118 dpid = re.search(r'([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})+', line, re.I)
119 if dpid:
120 dpids_to_ids[dpid.group().replace(':', '')] = switch_id
121 switch_id += 1
122 return dpids_to_ids
123 except:
124 print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info())
125 fd.close()
126 return None
127 ### dict of containing dpids as key and corresponding LINC switchId as values ###
128 dpidsToLINCSwitchId = dpids_to_ids.__func__(sysConfig)
129 @staticmethod
130 def findDir(directory, userName):
131 "finds and returns the path of any directory in the user's home directory"
132 homeDir = '/home/' + userName
133 Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n')
134 DirList = Dir.split('\n')
135 if not Dir:
136 return None
137 elif len(DirList) > 1 :
138 warn('***WARNING: Found multiple instances of %s; using %s\n'
139 % (directory, DirList[ 0 ]))
140 return DirList[ 0 ]
141 else:
142 return Dir
143 ### ONOS Directory ###
144 try:
145 onosDir = os.environ[ 'ONOS_ROOT' ]
146 except:
147 onosDir = findDir('onos', user)
148 if not onosDir:
149 error('Please set ONOS_ROOT environment variable!\n')
150 else:
151 os.environ[ 'ONOS_ROOT' ] = onosDir
Ayaka Koshibeb0d70582015-09-11 11:29:36 -0700152 ### REST USER/PASS ###
153 try:
154 restUser = os.environ[ 'ONOS_WEB_USER' ]
155 restPass = os.environ[ 'ONOS_WEB_PASS' ]
156 except:
157 error('***WARNING: $ONOS_WEB_USER and $ONOS_WEB_PASS aren\'t set!\n')
158 error('***WARNING: Setting (probably) sane WEB user/pass values\n')
159 restUser = 'onos'
160 restPass = 'rocks'
161 os.environ[ 'ONOS_WEB_USER' ] = restUser
162 os.environ[ 'ONOS_WEB_PASS' ] = restPass
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000163 ### LINC-directory
164 lincDir = findDir.__func__('linc-oe', user)
165 if not lincDir:
166 error("***ERROR: Could not find linc-oe in user's home directory\n")
167 ### LINC config generator directory###
168 configGen = findDir.__func__('LINC-config-generator', user)
169 if not configGen:
170 error("***ERROR: Could not find LINC-config-generator in user's home directory\n")
171 # list of all the controllers
172 controllers = None
173 def __init__(self, name, dpid=None, allowed=True,
174 switchType='ROADM', topo=None, annotations={}, controller=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800175 params[ 'inNamespace' ] = False
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000176 Switch.__init__(self, name, dpid=dpid, **params)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800177 self.name = name
178 self.annotations = annotations
179 self.allowed = allowed
180 self.switchType = switchType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000181 self.configDict = {} # dictionary that holds all of the JSON configuration data
182 self.crossConnects = []
183 self.deletedCrossConnects = []
184 self.controller = controller
185 self.lincId = self._get_linc_id() # use to communicate with LINC
186 self.lincStarted = False
Brian O'Connoreb27c452014-12-07 02:43:58 -0800187
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000188 def start(self, *opts, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800189 '''Instead of starting a virtual switch, we build the JSON
190 dictionary for the emulated optical switch'''
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000191 # TODO:Once LINC has the ability to spawn network element dynamically
192 # we need to use this method to spawn new logical LINC switch rather then
193 # bulding JSON.
194 # if LINC is started then we can start and stop logical switches else create JSON
195 if self.lincStarted:
196 return self.start_oe()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800197 self.configDict[ 'uri' ] = 'of:' + self.dpid
198 self.configDict[ 'annotations' ] = self.annotations
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000199 self.configDict[ 'annotations' ].setdefault('name', self.name)
200 self.configDict[ 'hw' ] = 'LINC-OE'
Brian O'Connoreb27c452014-12-07 02:43:58 -0800201 self.configDict[ 'mfr' ] = 'Linc'
202 self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1]
203 self.configDict[ 'type' ] = self.switchType
204 self.configDict[ 'ports' ] = []
205 for port, intf in self.intfs.items():
206 if intf.name == 'lo':
207 continue
208 else:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000209 self.configDict[ 'ports' ].append(intf.json())
210 self.lincStarted = True
fahad44e62c72015-03-04 14:55:35 -0800211
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000212 def stop(self, deleteIntfs=False):
213 '''
214 stop the existing switch
215 '''
216 # TODO:Add support for deleteIntf
217 self.stop_oe()
fahad44e62c72015-03-04 14:55:35 -0800218
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000219 def dpctl( self, *args ):
220 "Run dpctl command: ignore for now"
fahad44e62c72015-03-04 14:55:35 -0800221 pass
222
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000223 def write_to_cli(self, command):
224 '''
225 send command to LINC
226 '''
227 fd = None
228 try:
229 fd = open(self.writePipe, 'w', 0)
230 fd.write(command)
231 fd.close()
232 except:
233 print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info())
234 if fd:
235 fd.close()
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700236
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000237 def read_from_cli(self):
238 '''
239 read the output from the LINC CLI
240 '''
241 response = None
242 fd = None
243 try:
244 fd = open(self.readPipe, 'r', 0)
245 fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read
246 # FIXME:Due to non-blocking read most for the time we read nothing
247 response = fd.read()
248 fd.close()
249 except :
250 # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info())
251 if fd:
252 fd.close()
253 return response
254
255 def _get_linc_id(self):
256 '''
257 return the corresponding LINC switchId.
258 '''
259 return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid)
260 #--------------------------------------------------------------------------
261 # LINC CLI commands
262 #--------------------------------------------------------------------------
263 def start_oe(self):
264 '''
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700265 existing LINC switch
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000266 '''
267 #starting Switch
268 cmd = "linc:start_switch({}).\r\n".format(self.lincId)
269 self.write_to_cli(cmd)
270 #hanlding taps interfaces related to the switch
271 crossConnectJSON = {}
272 linkConfig = []
273 for i in range(0,len(self.deletedCrossConnects)):
274 crossConnect = self.deletedCrossConnects.pop()
275 tap = None
276 if isinstance(crossConnect.intf1.node, LINCSwitch):
277 intf = crossConnect.intf2
278 tapPort = crossConnect.intf1.port
279 else:
280 intf = crossConnect.intf1
281 tapPort = crossConnect.intf2.port
282 tap = LINCSwitch.findTap(self, tapPort)
283 if tap:
284 LINCSwitch.setupInts([tap])
285 intf.node.attach(tap)
286 self.crossConnects.append(crossConnect)
287 linkConfig.append(crossConnect.json())
288 #Sending crossConnect info to the ONOS.
289 crossConnectJSON['links'] = linkConfig
290 with open("crossConnect.json", 'w') as fd:
291 json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': '))
292 info('*** Pushing crossConnect.json to ONOS\n')
293 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s\
294 Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True)
295
296 def stop_oe(self):
297 '''
298 stop the existing LINC switch
299 '''
300 cmd = "linc:stop_switch({}).\r\n".format(self.lincId)
301 self.write_to_cli(cmd)
302 #handling taps if any
303 for i in range(0, len(self.crossConnects)):
304 crossConnect = self.crossConnects.pop()
305 if isinstance(crossConnect.intf1.node, LINCSwitch):
306 intf = crossConnect.intf2
307 tapPort = crossConnect.intf1.port
308 else:
309 intf = crossConnect.intf1
310 tapPort = crossConnect.intf2.port
311 intf.node.detach(LINCSwitch.findTap(self, tapPort))
312 self.deletedCrossConnects.append(crossConnect)
313
314 def w_port_up(self, port):
315 '''
316 port_up
317 '''
318 cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port)
319 self.write_to_cli(cmd)
320
321 def w_port_down(self, port):
322 '''
323 port_down
324 '''
325 cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port)
326 self.write_to_cli(cmd)
327
328 # helper functions
329 @staticmethod
330 def switchJSON(switch):
331 "Returns the json configuration for a packet switch"
332 configDict = {}
333 configDict[ 'uri' ] = 'of:' + switch.dpid
334 configDict[ 'mac' ] = quietRun('cat /sys/class/net/%s/address' % switch.name).strip('\n').translate(None, ':')
335 configDict[ 'hw' ] = 'PK' # FIXME what about OVS?
336 configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS?
337 configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS?
338 annotations = switch.params.get('annotations', {})
339 annotations.setdefault('name', switch.name)
340 configDict[ 'annotations' ] = annotations
341 ports = []
342 for port, intf in switch.intfs.items():
343 if intf.name == 'lo':
344 continue
345 portDict = {}
346 portDict[ 'port' ] = port
347 portDict[ 'type' ] = 'FIBER' if isinstance(intf.link, LINCLink) else 'COPPER'
348 intfList = [ intf.link.intf1, intf.link.intf2 ]
349 intfList.remove(intf)
350 portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0
351 ports.append(portDict)
352 configDict[ 'ports' ] = ports
353 return configDict
354
355 @staticmethod
356 def bootOE(net):
357 "Start the LINC optical emulator within a mininet instance"
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700358 LINCSwitch.opticalJSON = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000359 linkConfig = []
360 devices = []
361 #setting up the controllers for LINCSwitch class
362 LINCSwitch.controllers = net.controllers
363
364 for switch in net.switches:
365 if isinstance(switch, OpticalSwitch):
366 devices.append(switch.json())
367 else:
368 devices.append(LINCSwitch.switchJSON(switch))
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700369 LINCSwitch.opticalJSON[ 'devices' ] = devices
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000370
371 for link in net.links:
372 if isinstance(link, LINCLink) :
373 linkConfig.append(link.json())
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700374 LINCSwitch.opticalJSON[ 'links' ] = linkConfig
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000375
376 info('*** Writing Topology.json file\n')
377 with open('Topology.json', 'w') as outfile:
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700378 json.dump(LINCSwitch.opticalJSON, outfile, indent=4, separators=(',', ': '))
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000379
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700380 info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n')
381
382 topoConfigJson = {};
383 dpIdToName = {};
384
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700385 topoConfigJson["switchConfig"] = LINCSwitch.getSwitchConfig(dpIdToName);
386 topoConfigJson["linkConfig"] = LINCSwitch.getLinkConfig(dpIdToName);
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700387
388 #Writing to TopoConfig.json
389 with open( 'TopoConfig.json', 'w' ) as outfile:
390 json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') )
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000391
392 info('*** Creating sys.config...\n')
393 output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s'
394 % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True)
395 if output:
396 error('***ERROR: Error creating sys.config file: %s\n' % output)
397 return False
398
399 info ('*** Setting multiple controllers in sys.config...\n')
400 searchStr = '\[{"Switch.*$'
401 ctrlStr = ''
402 for index in range(len(LINCSwitch.controllers)):
403 ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port)
404 replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma
405 sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr)
406 output = quietRun(sedCmd, shell=True)
407
408 info('*** Copying sys.config to linc-oe directory: ', output + '\n')
409 output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n')
410 info(output + '\n')
411
412 info('*** Adding taps and bringing them up...\n')
413 LINCSwitch.setupInts(LINCSwitch.getTaps())
414
415 info('*** removing pipes if any \n')
416 quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True)
417
418 info('*** Starting linc OE...\n')
419 output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True)
420 if output:
421 error('***ERROR: LINC-OE: %s' % output + '\n')
422 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
423 return False
424
425 info('*** Waiting for linc-oe to start...\n')
426 LINCSwitch.waitStarted(net)
427
428 info('*** Adding cross-connect (tap) interfaces to packet switches...\n')
429 for link in net.links:
430 if isinstance(link, LINCLink):
431 if link.annotations[ 'optical.type' ] == 'cross-connect':
432 for intf in [ link.intf1, link.intf2 ]:
433 if not isinstance(intf, LINCIntf):
434 intfList = [ intf.link.intf1, intf.link.intf2 ]
435 intfList.remove(intf)
436 intf2 = intfList[ 0 ]
437 intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
438
439 info('*** Waiting for all devices to be available in ONOS...\n')
440 url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
441 time = 0
Ayaka Koshibec9eed382015-09-03 14:38:55 -0700442 # Set up password authentication
443 pw_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
Ayaka Koshibeb0d70582015-09-11 11:29:36 -0700444 pw_mgr.add_password(None, url, LINCSwitch.restUser, LINCSwitch.restPass)
Ayaka Koshibec9eed382015-09-03 14:38:55 -0700445 handler = urllib2.HTTPBasicAuthHandler(pw_mgr)
446 opener = urllib2.build_opener(handler)
447 opener.open(url)
448 urllib2.install_opener(opener)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000449 while True:
450 response = json.load(urllib2.urlopen(url))
451 devs = response.get('devices')
452
453 # Wait for all devices to be registered
454 if (len(devices) != len(devs)):
455 continue
456
457 # Wait for all devices to available
458 available = True
459 for d in devs:
460 available &= d['available']
461 if available:
462 break
463
464 if (time >= TIMEOUT):
465 error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
466 break
467
468 time += SLEEP_TIME
469 sleep(SLEEP_TIME)
470
471 info('*** Pushing Topology.json to ONOS\n')
472 for index in range(len(LINCSwitch.controllers)):
473 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json &' % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True)
474 # successful output contains the two characters '{}'
475 # if there is more output than this, there is an issue
476 if output.strip('{}'):
477 warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
478
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700479 #converts node ids to linc-oe format, with colons every two chars
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700480 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700481 def dpId(id):
482 nodeDpid = ""
483 id = id.split("/", 1)[0]
484 for i in range(3, len(id) - 1, 2):
485 nodeDpid += (id[i:(i + 2):]) + ":"
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700486 return nodeDpid[0:-1];
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700487
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700488 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700489 def getSwitchConfig (dpIdToName):
490 switchConfig = [];
491 #Iterate through all switches and convert the ROADM switches to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700492 for switch in LINCSwitch.opticalJSON["devices"]:
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700493 if switch.get("type", "none") == "ROADM":
494 builtSwitch = {}
495
496 #set basic switch params based on annotations
497 builtSwitch["allowed"] = True;
498 builtSwitch["latitude"] = switch["annotations"].get("latitude", 0.0);
499 builtSwitch["longitude"] = switch["annotations"].get("longitude", 0.0);
500
501 #assumed that all switches have this entry
502 nodeId = switch["uri"]
503
504 #convert the nodeId to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700505 nodeDpid = LINCSwitch.dpId(nodeId);
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700506
507 builtSwitch["name"] = switch.get("name", "none");
508
509 #keep track of the name corresponding to each switch dpid
510 dpIdToName[nodeDpid] = builtSwitch["name"];
511
512 builtSwitch["nodeDpid"] = nodeDpid
513
514 #set switch params and type
515 builtSwitch["params"] = {};
516 builtSwitch["params"]["numregens"] = switch["annotations"].get("optical.regens", 0);
517 builtSwitch["type"] = "Roadm"
518
519 #append to list of switches
520 switchConfig.append(builtSwitch);
521 return switchConfig
522
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700523 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700524 def getLinkConfig (dpIdToName):
525 newLinkConfig = [];
526 #Iterate through all optical links and convert them to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700527 for link in LINCSwitch.opticalJSON["links"]:
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700528 if link.get("type", "none") == "OPTICAL":
529 builtLink = {}
530
531 #set basic link params for src and dst
532 builtLink["allowed"] = True;
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700533 builtLink["nodeDpid1"] = LINCSwitch.dpId(link["src"])
534 builtLink["nodeDpid2"] = LINCSwitch.dpId(link["dst"])
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700535
536 #set more params such as name/bandwidth/port/waves if they exist
537 params = {}
538 params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none")
539 params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none")
540
541 params["port1"] = int(link["src"].split("/")[1])
542 params["port2"] = int(link["dst"].split("/")[1])
543
544 if "bandwidth" in link["annotations"]:
545 params["bandwidth"] = link["annotations"]["bandwidth"]
546
547 if "optical.waves" in link["annotations"]:
548 params["numWaves"] = link["annotations"]["optical.waves"]
549
550 builtLink["params"] = params
551
552 #set type of link (WDM or pktOpt)
553 if link["annotations"].get("optical.type", "cross-connect") == "WDM":
554 builtLink["type"] = "wdmLink"
555 else:
556 builtLink["type"] = "pktOptLink"
557
558 newLinkConfig.append(builtLink);
559 return newLinkConfig
560
561
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000562 @staticmethod
563 def waitStarted(net, timeout=TIMEOUT):
564 "wait until all tap interfaces are available"
565 tapCount = 0
566 time = 0
567 for link in net.links:
568 if isinstance(link, LINCLink):
569 if link.annotations[ 'optical.type' ] == 'cross-connect':
570 tapCount += 1
571
572 while True:
573 if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
574 return True
575 if timeout:
576 if time >= TIMEOUT:
577 error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
578 return False
579 time += SLEEP_TIME
580 sleep(SLEEP_TIME)
581
582 @staticmethod
583 def shutdownOE():
584 "stop the optical emulator"
585 info('*** Stopping linc OE...\n')
586 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
587
588 @staticmethod
589 def setupInts(intfs):
590 '''
591 add taps and bring them up.
592 '''
593 for i in intfs:
594 quietRun('ip tuntap add dev %s mode tap' % i)
595 quietRun('ip link set dev %s up' % i)
596 info('*** Intf %s set\n' % i)
597
598 @staticmethod
599 def getTaps(path=None):
600 '''
601 return list of all the tops in sys.config
602 '''
603 if path is None:
604 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
605 fd = open(path, 'r', 0)
606 sys_data = fd.read()
607 taps = re.findall('tap\d+', sys_data)
608 fd.close()
609 return taps
610
611 @staticmethod
612 def findUser():
613 "Try to return logged-in (usually non-root) user"
614 try:
615 # If we're running sudo
616 return os.environ[ 'SUDO_USER' ]
617 except:
618 try:
619 # Logged-in user (if we have a tty)
620 return quietRun('who am i').split()[ 0 ]
621 except:
622 # Give up and return effective user
623 return quietRun('whoami')
624
625
626 @staticmethod
627 def findTap(node, port, path=None):
628 '''utility function to parse through a sys.config
629 file to find tap interfaces for a switch'''
630 switch = False
631 portLine = ''
632 intfLines = []
633
634 if path is None:
635 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
636
637 with open(path) as f:
638 for line in f:
639 if 'tap' in line:
640 intfLines.append(line)
641 if node.dpid in line.translate(None, ':'):
642 switch = True
643 continue
644 if switch:
645 if 'switch' in line:
646 switch = False
647 if 'port_no,%s}' % port in line:
648 portLine = line
649 break
650
651 if portLine:
652 m = re.search('port,\d+', portLine)
653 port = m.group(0).split(',')[ 1 ]
654 else:
655 error('***ERROR: Could not find any ports in sys.config\n')
656 return
657
658 for intfLine in intfLines:
659 if 'port,%s' % port in intfLine:
660 return re.findall('tap\d+', intfLine)[ 0 ]
661
662 def json(self):
663 "return json configuration dictionary for switch"
664 return self.configDict
665
666 def terminate(self):
667 pass
668
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700669
670
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000671class LINCLink(Link):
672 """
673 LINC link class
674 """
675 def __init__(self, node1, node2, port1=None, port2=None, allowed=True,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800676 intfName1=None, intfName2=None, linkType='OPTICAL',
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000677 annotations={}, speed1=0, speed2=0, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800678 "Creates a dummy link without a virtual ethernet pair."
679 self.allowed = allowed
680 self.annotations = annotations
681 self.linkType = linkType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000682 self.port1 = port1
683 self.port2 = port2
Brian O'Connoreb27c452014-12-07 02:43:58 -0800684 params1 = { 'speed': speed1 }
685 params2 = { 'speed': speed2 }
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000686 # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False
687 if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch):
688 self.isCrossConnect = False
689 else:
690 self.isCrossConnect = True
691 if isinstance(node1, LINCSwitch):
692 cls1 = LINCIntf
693 if self.isCrossConnect:
694 node1.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800695 else:
696 cls1 = Intf
697 # bad hack to stop error message from appearing when we try to set up intf in a packet switch,
698 # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up
699 intfName1 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000700 if isinstance(node2, LINCSwitch):
701 cls2 = LINCIntf
702 if self.isCrossConnect:
703 node2.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800704 else:
705 cls2 = Intf
706 intfName2 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000707 Link.__init__(self, node1, node2, port1=port1, port2=port2,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800708 intfName1=intfName1, intfName2=intfName2, cls1=cls1,
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000709 cls2=cls2, params1=params1, params2=params2)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800710
711 @classmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000712 def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800713 pass
714
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000715 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800716 "build and return the json configuration dictionary for this link"
717 configData = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000718 configData[ 'src' ] = ('of:' + self.intf1.node.dpid +
719 '/%s' % self.intf1.node.ports[ self.intf1 ])
720 configData[ 'dst' ] = ('of:' + self.intf2.node.dpid +
721 '/%s' % self.intf2.node.ports[ self.intf2 ])
Brian O'Connoreb27c452014-12-07 02:43:58 -0800722 configData[ 'type' ] = self.linkType
723 configData[ 'annotations' ] = self.annotations
724 return configData
725
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000726class LINCIntf(OpticalIntf):
727 """
728 LINC interface class
729 """
730 def __init__(self, name=None, node=None, speed=0,
731 port=None, link=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800732 self.node = node
733 self.speed = speed
734 self.port = port
735 self.link = link
736 self.name = name
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000737 node.addIntf(self, port=port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800738 self.params = params
739 self.ip = None
740
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000741 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800742 "build and return the JSON information for this interface( not used right now )"
743 configDict = {}
744 configDict[ 'port' ] = self.port
745 configDict[ 'speed' ] = self.speed
746 configDict[ 'type' ] = 'FIBER'
747 return configDict
748
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000749 def config(self, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800750 "dont configure a dummy interface"
751 pass
752
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000753 def ifconfig(self, status):
754 "configure the status"
755 if status == "up":
756 return self.node.w_port_up(self.port)
757 elif status == "down":
758 return self.node.w_port_down(self.port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800759
760
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000761class MininetOE(Mininet):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800762 "Mininet with Linc-OE support (starts and stops linc-oe)"
763
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000764 def start(self):
765 Mininet.start(self)
766 LINCSwitch.bootOE(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800767
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000768 def stop(self):
769 Mininet.stop(self)
770 LINCSwitch.shutdownOE()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800771
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000772 def addControllers(self, controllers):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800773 i = 0
774 for ctrl in controllers:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000775 self.addController(RemoteController('c%d' % i, ip=ctrl))
776 i += 1
Brian O'Connoreb27c452014-12-07 02:43:58 -0800777
778if __name__ == '__main__':
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000779 pass