blob: ef0b9a0f242fc3a133a68c88e2bec8bb5e9105cb [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
152 ### LINC-directory
153 lincDir = findDir.__func__('linc-oe', user)
154 if not lincDir:
155 error("***ERROR: Could not find linc-oe in user's home directory\n")
156 ### LINC config generator directory###
157 configGen = findDir.__func__('LINC-config-generator', user)
158 if not configGen:
159 error("***ERROR: Could not find LINC-config-generator in user's home directory\n")
160 # list of all the controllers
161 controllers = None
162 def __init__(self, name, dpid=None, allowed=True,
163 switchType='ROADM', topo=None, annotations={}, controller=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800164 params[ 'inNamespace' ] = False
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000165 Switch.__init__(self, name, dpid=dpid, **params)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800166 self.name = name
167 self.annotations = annotations
168 self.allowed = allowed
169 self.switchType = switchType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000170 self.configDict = {} # dictionary that holds all of the JSON configuration data
171 self.crossConnects = []
172 self.deletedCrossConnects = []
173 self.controller = controller
174 self.lincId = self._get_linc_id() # use to communicate with LINC
175 self.lincStarted = False
Brian O'Connoreb27c452014-12-07 02:43:58 -0800176
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000177 def start(self, *opts, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800178 '''Instead of starting a virtual switch, we build the JSON
179 dictionary for the emulated optical switch'''
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000180 # TODO:Once LINC has the ability to spawn network element dynamically
181 # we need to use this method to spawn new logical LINC switch rather then
182 # bulding JSON.
183 # if LINC is started then we can start and stop logical switches else create JSON
184 if self.lincStarted:
185 return self.start_oe()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800186 self.configDict[ 'uri' ] = 'of:' + self.dpid
187 self.configDict[ 'annotations' ] = self.annotations
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000188 self.configDict[ 'annotations' ].setdefault('name', self.name)
189 self.configDict[ 'hw' ] = 'LINC-OE'
Brian O'Connoreb27c452014-12-07 02:43:58 -0800190 self.configDict[ 'mfr' ] = 'Linc'
191 self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1]
192 self.configDict[ 'type' ] = self.switchType
193 self.configDict[ 'ports' ] = []
194 for port, intf in self.intfs.items():
195 if intf.name == 'lo':
196 continue
197 else:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000198 self.configDict[ 'ports' ].append(intf.json())
199 self.lincStarted = True
fahad44e62c72015-03-04 14:55:35 -0800200
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000201 def stop(self, deleteIntfs=False):
202 '''
203 stop the existing switch
204 '''
205 # TODO:Add support for deleteIntf
206 self.stop_oe()
fahad44e62c72015-03-04 14:55:35 -0800207
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000208 def dpctl( self, *args ):
209 "Run dpctl command: ignore for now"
fahad44e62c72015-03-04 14:55:35 -0800210 pass
211
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000212 def write_to_cli(self, command):
213 '''
214 send command to LINC
215 '''
216 fd = None
217 try:
218 fd = open(self.writePipe, 'w', 0)
219 fd.write(command)
220 fd.close()
221 except:
222 print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info())
223 if fd:
224 fd.close()
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700225
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000226 def read_from_cli(self):
227 '''
228 read the output from the LINC CLI
229 '''
230 response = None
231 fd = None
232 try:
233 fd = open(self.readPipe, 'r', 0)
234 fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read
235 # FIXME:Due to non-blocking read most for the time we read nothing
236 response = fd.read()
237 fd.close()
238 except :
239 # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info())
240 if fd:
241 fd.close()
242 return response
243
244 def _get_linc_id(self):
245 '''
246 return the corresponding LINC switchId.
247 '''
248 return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid)
249 #--------------------------------------------------------------------------
250 # LINC CLI commands
251 #--------------------------------------------------------------------------
252 def start_oe(self):
253 '''
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700254 existing LINC switch
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000255 '''
256 #starting Switch
257 cmd = "linc:start_switch({}).\r\n".format(self.lincId)
258 self.write_to_cli(cmd)
259 #hanlding taps interfaces related to the switch
260 crossConnectJSON = {}
261 linkConfig = []
262 for i in range(0,len(self.deletedCrossConnects)):
263 crossConnect = self.deletedCrossConnects.pop()
264 tap = None
265 if isinstance(crossConnect.intf1.node, LINCSwitch):
266 intf = crossConnect.intf2
267 tapPort = crossConnect.intf1.port
268 else:
269 intf = crossConnect.intf1
270 tapPort = crossConnect.intf2.port
271 tap = LINCSwitch.findTap(self, tapPort)
272 if tap:
273 LINCSwitch.setupInts([tap])
274 intf.node.attach(tap)
275 self.crossConnects.append(crossConnect)
276 linkConfig.append(crossConnect.json())
277 #Sending crossConnect info to the ONOS.
278 crossConnectJSON['links'] = linkConfig
279 with open("crossConnect.json", 'w') as fd:
280 json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': '))
281 info('*** Pushing crossConnect.json to ONOS\n')
282 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s\
283 Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True)
284
285 def stop_oe(self):
286 '''
287 stop the existing LINC switch
288 '''
289 cmd = "linc:stop_switch({}).\r\n".format(self.lincId)
290 self.write_to_cli(cmd)
291 #handling taps if any
292 for i in range(0, len(self.crossConnects)):
293 crossConnect = self.crossConnects.pop()
294 if isinstance(crossConnect.intf1.node, LINCSwitch):
295 intf = crossConnect.intf2
296 tapPort = crossConnect.intf1.port
297 else:
298 intf = crossConnect.intf1
299 tapPort = crossConnect.intf2.port
300 intf.node.detach(LINCSwitch.findTap(self, tapPort))
301 self.deletedCrossConnects.append(crossConnect)
302
303 def w_port_up(self, port):
304 '''
305 port_up
306 '''
307 cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port)
308 self.write_to_cli(cmd)
309
310 def w_port_down(self, port):
311 '''
312 port_down
313 '''
314 cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port)
315 self.write_to_cli(cmd)
316
317 # helper functions
318 @staticmethod
319 def switchJSON(switch):
320 "Returns the json configuration for a packet switch"
321 configDict = {}
322 configDict[ 'uri' ] = 'of:' + switch.dpid
323 configDict[ 'mac' ] = quietRun('cat /sys/class/net/%s/address' % switch.name).strip('\n').translate(None, ':')
324 configDict[ 'hw' ] = 'PK' # FIXME what about OVS?
325 configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS?
326 configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS?
327 annotations = switch.params.get('annotations', {})
328 annotations.setdefault('name', switch.name)
329 configDict[ 'annotations' ] = annotations
330 ports = []
331 for port, intf in switch.intfs.items():
332 if intf.name == 'lo':
333 continue
334 portDict = {}
335 portDict[ 'port' ] = port
336 portDict[ 'type' ] = 'FIBER' if isinstance(intf.link, LINCLink) else 'COPPER'
337 intfList = [ intf.link.intf1, intf.link.intf2 ]
338 intfList.remove(intf)
339 portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0
340 ports.append(portDict)
341 configDict[ 'ports' ] = ports
342 return configDict
343
344 @staticmethod
345 def bootOE(net):
346 "Start the LINC optical emulator within a mininet instance"
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700347 LINCSwitch.opticalJSON = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000348 linkConfig = []
349 devices = []
350 #setting up the controllers for LINCSwitch class
351 LINCSwitch.controllers = net.controllers
352
353 for switch in net.switches:
354 if isinstance(switch, OpticalSwitch):
355 devices.append(switch.json())
356 else:
357 devices.append(LINCSwitch.switchJSON(switch))
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700358 LINCSwitch.opticalJSON[ 'devices' ] = devices
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000359
360 for link in net.links:
361 if isinstance(link, LINCLink) :
362 linkConfig.append(link.json())
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700363 LINCSwitch.opticalJSON[ 'links' ] = linkConfig
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000364
365 info('*** Writing Topology.json file\n')
366 with open('Topology.json', 'w') as outfile:
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700367 json.dump(LINCSwitch.opticalJSON, outfile, indent=4, separators=(',', ': '))
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000368
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700369 info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n')
370
371 topoConfigJson = {};
372 dpIdToName = {};
373
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700374 topoConfigJson["switchConfig"] = LINCSwitch.getSwitchConfig(dpIdToName);
375 topoConfigJson["linkConfig"] = LINCSwitch.getLinkConfig(dpIdToName);
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700376
377 #Writing to TopoConfig.json
378 with open( 'TopoConfig.json', 'w' ) as outfile:
379 json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') )
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000380
381 info('*** Creating sys.config...\n')
382 output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s'
383 % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True)
384 if output:
385 error('***ERROR: Error creating sys.config file: %s\n' % output)
386 return False
387
388 info ('*** Setting multiple controllers in sys.config...\n')
389 searchStr = '\[{"Switch.*$'
390 ctrlStr = ''
391 for index in range(len(LINCSwitch.controllers)):
392 ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port)
393 replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma
394 sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr)
395 output = quietRun(sedCmd, shell=True)
396
397 info('*** Copying sys.config to linc-oe directory: ', output + '\n')
398 output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n')
399 info(output + '\n')
400
401 info('*** Adding taps and bringing them up...\n')
402 LINCSwitch.setupInts(LINCSwitch.getTaps())
403
404 info('*** removing pipes if any \n')
405 quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True)
406
407 info('*** Starting linc OE...\n')
408 output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True)
409 if output:
410 error('***ERROR: LINC-OE: %s' % output + '\n')
411 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
412 return False
413
414 info('*** Waiting for linc-oe to start...\n')
415 LINCSwitch.waitStarted(net)
416
417 info('*** Adding cross-connect (tap) interfaces to packet switches...\n')
418 for link in net.links:
419 if isinstance(link, LINCLink):
420 if link.annotations[ 'optical.type' ] == 'cross-connect':
421 for intf in [ link.intf1, link.intf2 ]:
422 if not isinstance(intf, LINCIntf):
423 intfList = [ intf.link.intf1, intf.link.intf2 ]
424 intfList.remove(intf)
425 intf2 = intfList[ 0 ]
426 intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
427
428 info('*** Waiting for all devices to be available in ONOS...\n')
429 url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
430 time = 0
431 while True:
432 response = json.load(urllib2.urlopen(url))
433 devs = response.get('devices')
434
435 # Wait for all devices to be registered
436 if (len(devices) != len(devs)):
437 continue
438
439 # Wait for all devices to available
440 available = True
441 for d in devs:
442 available &= d['available']
443 if available:
444 break
445
446 if (time >= TIMEOUT):
447 error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
448 break
449
450 time += SLEEP_TIME
451 sleep(SLEEP_TIME)
452
453 info('*** Pushing Topology.json to ONOS\n')
454 for index in range(len(LINCSwitch.controllers)):
455 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json &' % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True)
456 # successful output contains the two characters '{}'
457 # if there is more output than this, there is an issue
458 if output.strip('{}'):
459 warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
460
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700461 #converts node ids to linc-oe format, with colons every two chars
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700462 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700463 def dpId(id):
464 nodeDpid = ""
465 id = id.split("/", 1)[0]
466 for i in range(3, len(id) - 1, 2):
467 nodeDpid += (id[i:(i + 2):]) + ":"
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700468 return nodeDpid[0:-1];
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700469
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700470 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700471 def getSwitchConfig (dpIdToName):
472 switchConfig = [];
473 #Iterate through all switches and convert the ROADM switches to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700474 for switch in LINCSwitch.opticalJSON["devices"]:
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700475 if switch.get("type", "none") == "ROADM":
476 builtSwitch = {}
477
478 #set basic switch params based on annotations
479 builtSwitch["allowed"] = True;
480 builtSwitch["latitude"] = switch["annotations"].get("latitude", 0.0);
481 builtSwitch["longitude"] = switch["annotations"].get("longitude", 0.0);
482
483 #assumed that all switches have this entry
484 nodeId = switch["uri"]
485
486 #convert the nodeId to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700487 nodeDpid = LINCSwitch.dpId(nodeId);
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700488
489 builtSwitch["name"] = switch.get("name", "none");
490
491 #keep track of the name corresponding to each switch dpid
492 dpIdToName[nodeDpid] = builtSwitch["name"];
493
494 builtSwitch["nodeDpid"] = nodeDpid
495
496 #set switch params and type
497 builtSwitch["params"] = {};
498 builtSwitch["params"]["numregens"] = switch["annotations"].get("optical.regens", 0);
499 builtSwitch["type"] = "Roadm"
500
501 #append to list of switches
502 switchConfig.append(builtSwitch);
503 return switchConfig
504
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700505 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700506 def getLinkConfig (dpIdToName):
507 newLinkConfig = [];
508 #Iterate through all optical links and convert them to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700509 for link in LINCSwitch.opticalJSON["links"]:
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700510 if link.get("type", "none") == "OPTICAL":
511 builtLink = {}
512
513 #set basic link params for src and dst
514 builtLink["allowed"] = True;
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700515 builtLink["nodeDpid1"] = LINCSwitch.dpId(link["src"])
516 builtLink["nodeDpid2"] = LINCSwitch.dpId(link["dst"])
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700517
518 #set more params such as name/bandwidth/port/waves if they exist
519 params = {}
520 params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none")
521 params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none")
522
523 params["port1"] = int(link["src"].split("/")[1])
524 params["port2"] = int(link["dst"].split("/")[1])
525
526 if "bandwidth" in link["annotations"]:
527 params["bandwidth"] = link["annotations"]["bandwidth"]
528
529 if "optical.waves" in link["annotations"]:
530 params["numWaves"] = link["annotations"]["optical.waves"]
531
532 builtLink["params"] = params
533
534 #set type of link (WDM or pktOpt)
535 if link["annotations"].get("optical.type", "cross-connect") == "WDM":
536 builtLink["type"] = "wdmLink"
537 else:
538 builtLink["type"] = "pktOptLink"
539
540 newLinkConfig.append(builtLink);
541 return newLinkConfig
542
543
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000544 @staticmethod
545 def waitStarted(net, timeout=TIMEOUT):
546 "wait until all tap interfaces are available"
547 tapCount = 0
548 time = 0
549 for link in net.links:
550 if isinstance(link, LINCLink):
551 if link.annotations[ 'optical.type' ] == 'cross-connect':
552 tapCount += 1
553
554 while True:
555 if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
556 return True
557 if timeout:
558 if time >= TIMEOUT:
559 error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
560 return False
561 time += SLEEP_TIME
562 sleep(SLEEP_TIME)
563
564 @staticmethod
565 def shutdownOE():
566 "stop the optical emulator"
567 info('*** Stopping linc OE...\n')
568 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
569
570 @staticmethod
571 def setupInts(intfs):
572 '''
573 add taps and bring them up.
574 '''
575 for i in intfs:
576 quietRun('ip tuntap add dev %s mode tap' % i)
577 quietRun('ip link set dev %s up' % i)
578 info('*** Intf %s set\n' % i)
579
580 @staticmethod
581 def getTaps(path=None):
582 '''
583 return list of all the tops in sys.config
584 '''
585 if path is None:
586 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
587 fd = open(path, 'r', 0)
588 sys_data = fd.read()
589 taps = re.findall('tap\d+', sys_data)
590 fd.close()
591 return taps
592
593 @staticmethod
594 def findUser():
595 "Try to return logged-in (usually non-root) user"
596 try:
597 # If we're running sudo
598 return os.environ[ 'SUDO_USER' ]
599 except:
600 try:
601 # Logged-in user (if we have a tty)
602 return quietRun('who am i').split()[ 0 ]
603 except:
604 # Give up and return effective user
605 return quietRun('whoami')
606
607
608 @staticmethod
609 def findTap(node, port, path=None):
610 '''utility function to parse through a sys.config
611 file to find tap interfaces for a switch'''
612 switch = False
613 portLine = ''
614 intfLines = []
615
616 if path is None:
617 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
618
619 with open(path) as f:
620 for line in f:
621 if 'tap' in line:
622 intfLines.append(line)
623 if node.dpid in line.translate(None, ':'):
624 switch = True
625 continue
626 if switch:
627 if 'switch' in line:
628 switch = False
629 if 'port_no,%s}' % port in line:
630 portLine = line
631 break
632
633 if portLine:
634 m = re.search('port,\d+', portLine)
635 port = m.group(0).split(',')[ 1 ]
636 else:
637 error('***ERROR: Could not find any ports in sys.config\n')
638 return
639
640 for intfLine in intfLines:
641 if 'port,%s' % port in intfLine:
642 return re.findall('tap\d+', intfLine)[ 0 ]
643
644 def json(self):
645 "return json configuration dictionary for switch"
646 return self.configDict
647
648 def terminate(self):
649 pass
650
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700651
652
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000653class LINCLink(Link):
654 """
655 LINC link class
656 """
657 def __init__(self, node1, node2, port1=None, port2=None, allowed=True,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800658 intfName1=None, intfName2=None, linkType='OPTICAL',
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000659 annotations={}, speed1=0, speed2=0, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800660 "Creates a dummy link without a virtual ethernet pair."
661 self.allowed = allowed
662 self.annotations = annotations
663 self.linkType = linkType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000664 self.port1 = port1
665 self.port2 = port2
Brian O'Connoreb27c452014-12-07 02:43:58 -0800666 params1 = { 'speed': speed1 }
667 params2 = { 'speed': speed2 }
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000668 # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False
669 if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch):
670 self.isCrossConnect = False
671 else:
672 self.isCrossConnect = True
673 if isinstance(node1, LINCSwitch):
674 cls1 = LINCIntf
675 if self.isCrossConnect:
676 node1.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800677 else:
678 cls1 = Intf
679 # bad hack to stop error message from appearing when we try to set up intf in a packet switch,
680 # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up
681 intfName1 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000682 if isinstance(node2, LINCSwitch):
683 cls2 = LINCIntf
684 if self.isCrossConnect:
685 node2.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800686 else:
687 cls2 = Intf
688 intfName2 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000689 Link.__init__(self, node1, node2, port1=port1, port2=port2,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800690 intfName1=intfName1, intfName2=intfName2, cls1=cls1,
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000691 cls2=cls2, params1=params1, params2=params2)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800692
693 @classmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000694 def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800695 pass
696
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000697 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800698 "build and return the json configuration dictionary for this link"
699 configData = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000700 configData[ 'src' ] = ('of:' + self.intf1.node.dpid +
701 '/%s' % self.intf1.node.ports[ self.intf1 ])
702 configData[ 'dst' ] = ('of:' + self.intf2.node.dpid +
703 '/%s' % self.intf2.node.ports[ self.intf2 ])
Brian O'Connoreb27c452014-12-07 02:43:58 -0800704 configData[ 'type' ] = self.linkType
705 configData[ 'annotations' ] = self.annotations
706 return configData
707
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000708class LINCIntf(OpticalIntf):
709 """
710 LINC interface class
711 """
712 def __init__(self, name=None, node=None, speed=0,
713 port=None, link=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800714 self.node = node
715 self.speed = speed
716 self.port = port
717 self.link = link
718 self.name = name
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000719 node.addIntf(self, port=port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800720 self.params = params
721 self.ip = None
722
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000723 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800724 "build and return the JSON information for this interface( not used right now )"
725 configDict = {}
726 configDict[ 'port' ] = self.port
727 configDict[ 'speed' ] = self.speed
728 configDict[ 'type' ] = 'FIBER'
729 return configDict
730
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000731 def config(self, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800732 "dont configure a dummy interface"
733 pass
734
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000735 def ifconfig(self, status):
736 "configure the status"
737 if status == "up":
738 return self.node.w_port_up(self.port)
739 elif status == "down":
740 return self.node.w_port_down(self.port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800741
742
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000743class MininetOE(Mininet):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800744 "Mininet with Linc-OE support (starts and stops linc-oe)"
745
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000746 def start(self):
747 Mininet.start(self)
748 LINCSwitch.bootOE(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800749
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000750 def stop(self):
751 Mininet.stop(self)
752 LINCSwitch.shutdownOE()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800753
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000754 def addControllers(self, controllers):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800755 i = 0
756 for ctrl in controllers:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000757 self.addController(RemoteController('c%d' % i, ip=ctrl))
758 i += 1
Brian O'Connoreb27c452014-12-07 02:43:58 -0800759
760if __name__ == '__main__':
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000761 pass