blob: ddbe19aa31196ade2d747d8a803c47ed5862690b [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 '''
254 start the existing LINC switch
255 '''
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"
347 opticalJSON = {}
348 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))
358 opticalJSON[ 'devices' ] = devices
359
360 for link in net.links:
361 if isinstance(link, LINCLink) :
362 linkConfig.append(link.json())
363 opticalJSON[ 'links' ] = linkConfig
364
365 info('*** Writing Topology.json file\n')
366 with open('Topology.json', 'w') as outfile:
367 json.dump(opticalJSON, outfile, indent=4, separators=(',', ': '))
368
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
374 topoConfigJson["switchConfig"] = getSwitchConfig(dpIdToName);
375 topoConfigJson["linkConfig"] = getLinkConfig(dpIdToName);
376
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
462 def dpId(id):
463 nodeDpid = ""
464 id = id.split("/", 1)[0]
465 for i in range(3, len(id) - 1, 2):
466 nodeDpid += (id[i:(i + 2):]) + ":"
467 return nodeDpid[0:(len(nodeDpid) - 1)];
468
469 def getSwitchConfig (dpIdToName):
470 switchConfig = [];
471 #Iterate through all switches and convert the ROADM switches to linc-oe format
472 for switch in opticalJSON["devices"]:
473 if switch.get("type", "none") == "ROADM":
474 builtSwitch = {}
475
476 #set basic switch params based on annotations
477 builtSwitch["allowed"] = True;
478 builtSwitch["latitude"] = switch["annotations"].get("latitude", 0.0);
479 builtSwitch["longitude"] = switch["annotations"].get("longitude", 0.0);
480
481 #assumed that all switches have this entry
482 nodeId = switch["uri"]
483
484 #convert the nodeId to linc-oe format
485 nodeDpid = dpId(nodeId);
486
487 builtSwitch["name"] = switch.get("name", "none");
488
489 #keep track of the name corresponding to each switch dpid
490 dpIdToName[nodeDpid] = builtSwitch["name"];
491
492 builtSwitch["nodeDpid"] = nodeDpid
493
494 #set switch params and type
495 builtSwitch["params"] = {};
496 builtSwitch["params"]["numregens"] = switch["annotations"].get("optical.regens", 0);
497 builtSwitch["type"] = "Roadm"
498
499 #append to list of switches
500 switchConfig.append(builtSwitch);
501 return switchConfig
502
503
504 def getLinkConfig (dpIdToName):
505 newLinkConfig = [];
506 #Iterate through all optical links and convert them to linc-oe format
507 for link in opticalJSON["links"]:
508 if link.get("type", "none") == "OPTICAL":
509 builtLink = {}
510
511 #set basic link params for src and dst
512 builtLink["allowed"] = True;
513 builtLink["nodeDpid1"] = dpId(link["src"])
514 builtLink["nodeDpid2"] = dpId(link["dst"])
515
516 #set more params such as name/bandwidth/port/waves if they exist
517 params = {}
518 params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none")
519 params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none")
520
521 params["port1"] = int(link["src"].split("/")[1])
522 params["port2"] = int(link["dst"].split("/")[1])
523
524 if "bandwidth" in link["annotations"]:
525 params["bandwidth"] = link["annotations"]["bandwidth"]
526
527 if "optical.waves" in link["annotations"]:
528 params["numWaves"] = link["annotations"]["optical.waves"]
529
530 builtLink["params"] = params
531
532 #set type of link (WDM or pktOpt)
533 if link["annotations"].get("optical.type", "cross-connect") == "WDM":
534 builtLink["type"] = "wdmLink"
535 else:
536 builtLink["type"] = "pktOptLink"
537
538 newLinkConfig.append(builtLink);
539 return newLinkConfig
540
541
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000542 @staticmethod
543 def waitStarted(net, timeout=TIMEOUT):
544 "wait until all tap interfaces are available"
545 tapCount = 0
546 time = 0
547 for link in net.links:
548 if isinstance(link, LINCLink):
549 if link.annotations[ 'optical.type' ] == 'cross-connect':
550 tapCount += 1
551
552 while True:
553 if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
554 return True
555 if timeout:
556 if time >= TIMEOUT:
557 error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
558 return False
559 time += SLEEP_TIME
560 sleep(SLEEP_TIME)
561
562 @staticmethod
563 def shutdownOE():
564 "stop the optical emulator"
565 info('*** Stopping linc OE...\n')
566 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
567
568 @staticmethod
569 def setupInts(intfs):
570 '''
571 add taps and bring them up.
572 '''
573 for i in intfs:
574 quietRun('ip tuntap add dev %s mode tap' % i)
575 quietRun('ip link set dev %s up' % i)
576 info('*** Intf %s set\n' % i)
577
578 @staticmethod
579 def getTaps(path=None):
580 '''
581 return list of all the tops in sys.config
582 '''
583 if path is None:
584 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
585 fd = open(path, 'r', 0)
586 sys_data = fd.read()
587 taps = re.findall('tap\d+', sys_data)
588 fd.close()
589 return taps
590
591 @staticmethod
592 def findUser():
593 "Try to return logged-in (usually non-root) user"
594 try:
595 # If we're running sudo
596 return os.environ[ 'SUDO_USER' ]
597 except:
598 try:
599 # Logged-in user (if we have a tty)
600 return quietRun('who am i').split()[ 0 ]
601 except:
602 # Give up and return effective user
603 return quietRun('whoami')
604
605
606 @staticmethod
607 def findTap(node, port, path=None):
608 '''utility function to parse through a sys.config
609 file to find tap interfaces for a switch'''
610 switch = False
611 portLine = ''
612 intfLines = []
613
614 if path is None:
615 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
616
617 with open(path) as f:
618 for line in f:
619 if 'tap' in line:
620 intfLines.append(line)
621 if node.dpid in line.translate(None, ':'):
622 switch = True
623 continue
624 if switch:
625 if 'switch' in line:
626 switch = False
627 if 'port_no,%s}' % port in line:
628 portLine = line
629 break
630
631 if portLine:
632 m = re.search('port,\d+', portLine)
633 port = m.group(0).split(',')[ 1 ]
634 else:
635 error('***ERROR: Could not find any ports in sys.config\n')
636 return
637
638 for intfLine in intfLines:
639 if 'port,%s' % port in intfLine:
640 return re.findall('tap\d+', intfLine)[ 0 ]
641
642 def json(self):
643 "return json configuration dictionary for switch"
644 return self.configDict
645
646 def terminate(self):
647 pass
648
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700649
650
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000651class LINCLink(Link):
652 """
653 LINC link class
654 """
655 def __init__(self, node1, node2, port1=None, port2=None, allowed=True,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800656 intfName1=None, intfName2=None, linkType='OPTICAL',
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000657 annotations={}, speed1=0, speed2=0, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800658 "Creates a dummy link without a virtual ethernet pair."
659 self.allowed = allowed
660 self.annotations = annotations
661 self.linkType = linkType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000662 self.port1 = port1
663 self.port2 = port2
Brian O'Connoreb27c452014-12-07 02:43:58 -0800664 params1 = { 'speed': speed1 }
665 params2 = { 'speed': speed2 }
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000666 # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False
667 if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch):
668 self.isCrossConnect = False
669 else:
670 self.isCrossConnect = True
671 if isinstance(node1, LINCSwitch):
672 cls1 = LINCIntf
673 if self.isCrossConnect:
674 node1.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800675 else:
676 cls1 = Intf
677 # bad hack to stop error message from appearing when we try to set up intf in a packet switch,
678 # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up
679 intfName1 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000680 if isinstance(node2, LINCSwitch):
681 cls2 = LINCIntf
682 if self.isCrossConnect:
683 node2.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800684 else:
685 cls2 = Intf
686 intfName2 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000687 Link.__init__(self, node1, node2, port1=port1, port2=port2,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800688 intfName1=intfName1, intfName2=intfName2, cls1=cls1,
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000689 cls2=cls2, params1=params1, params2=params2)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800690
691 @classmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000692 def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800693 pass
694
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000695 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800696 "build and return the json configuration dictionary for this link"
697 configData = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000698 configData[ 'src' ] = ('of:' + self.intf1.node.dpid +
699 '/%s' % self.intf1.node.ports[ self.intf1 ])
700 configData[ 'dst' ] = ('of:' + self.intf2.node.dpid +
701 '/%s' % self.intf2.node.ports[ self.intf2 ])
Brian O'Connoreb27c452014-12-07 02:43:58 -0800702 configData[ 'type' ] = self.linkType
703 configData[ 'annotations' ] = self.annotations
704 return configData
705
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000706class LINCIntf(OpticalIntf):
707 """
708 LINC interface class
709 """
710 def __init__(self, name=None, node=None, speed=0,
711 port=None, link=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800712 self.node = node
713 self.speed = speed
714 self.port = port
715 self.link = link
716 self.name = name
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000717 node.addIntf(self, port=port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800718 self.params = params
719 self.ip = None
720
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000721 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800722 "build and return the JSON information for this interface( not used right now )"
723 configDict = {}
724 configDict[ 'port' ] = self.port
725 configDict[ 'speed' ] = self.speed
726 configDict[ 'type' ] = 'FIBER'
727 return configDict
728
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000729 def config(self, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800730 "dont configure a dummy interface"
731 pass
732
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000733 def ifconfig(self, status):
734 "configure the status"
735 if status == "up":
736 return self.node.w_port_up(self.port)
737 elif status == "down":
738 return self.node.w_port_down(self.port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800739
740
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000741class MininetOE(Mininet):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800742 "Mininet with Linc-OE support (starts and stops linc-oe)"
743
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000744 def start(self):
745 Mininet.start(self)
746 LINCSwitch.bootOE(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800747
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000748 def stop(self):
749 Mininet.stop(self)
750 LINCSwitch.shutdownOE()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800751
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000752 def addControllers(self, controllers):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800753 i = 0
754 for ctrl in controllers:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000755 self.addController(RemoteController('c%d' % i, ip=ctrl))
756 i += 1
Brian O'Connoreb27c452014-12-07 02:43:58 -0800757
758if __name__ == '__main__':
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000759 pass