blob: bef9deca993363b3c2e3e781b9cd906c6def706e [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
Ayaka Koshibec9eed382015-09-03 14:38:55 -070073REST_USER = 'onos'
74REST_PW = 'rocks'
75
Marc De Leenheer6ff97642015-07-08 19:21:16 +000076class OpticalSwitch(Switch):
77 """
78 For now, same as Switch class.
79 """
80 pass
81
82class OpticalIntf(Intf):
83 """
84 For now,same as Intf class.
85 """
86 pass
87
88class OpticalLink(Link):
89 """
90 For now, same as Link.
91 """
92 pass
93
94class LINCSwitch(OpticalSwitch):
95 """
96 LINCSwitch class
97 """
98 # FIXME:Sometimes LINC doesn't remove pipes and on restart increase the pipe
99 # number from erlang.pipe.1.* to erlang.pipe.2.*, so should read and write
100 # from latest pipe files. For now we are removing all the pipes before
101 # starting LINC.
102 ### User Name ###
103 user = os.getlogin()
104 ### pipes ###
105 readPipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.r".format(user)
106 writePipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.w".format(user)
107 ### sys.config path ###
108 sysConfig = "/home/{}/linc-oe/rel/linc/releases/1.0/sys.config".format(user)
109 ### method, mapping dpid to LINC switchId ###
110 @staticmethod
111 def dpids_to_ids(sysConfig):
112 '''
113 return the dict containing switch dpids as key and LINC switch id as values
114 '''
115 dpids_to_ids = {}
116 fd = None
117 try:
118 with open(sysConfig, 'r', 0) as fd:
119 switch_id = 1
120 for line in fd:
121 dpid = re.search(r'([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})+', line, re.I)
122 if dpid:
123 dpids_to_ids[dpid.group().replace(':', '')] = switch_id
124 switch_id += 1
125 return dpids_to_ids
126 except:
127 print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info())
128 fd.close()
129 return None
130 ### dict of containing dpids as key and corresponding LINC switchId as values ###
131 dpidsToLINCSwitchId = dpids_to_ids.__func__(sysConfig)
132 @staticmethod
133 def findDir(directory, userName):
134 "finds and returns the path of any directory in the user's home directory"
135 homeDir = '/home/' + userName
136 Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n')
137 DirList = Dir.split('\n')
138 if not Dir:
139 return None
140 elif len(DirList) > 1 :
141 warn('***WARNING: Found multiple instances of %s; using %s\n'
142 % (directory, DirList[ 0 ]))
143 return DirList[ 0 ]
144 else:
145 return Dir
146 ### ONOS Directory ###
147 try:
148 onosDir = os.environ[ 'ONOS_ROOT' ]
149 except:
150 onosDir = findDir('onos', user)
151 if not onosDir:
152 error('Please set ONOS_ROOT environment variable!\n')
153 else:
154 os.environ[ 'ONOS_ROOT' ] = onosDir
155 ### LINC-directory
156 lincDir = findDir.__func__('linc-oe', user)
157 if not lincDir:
158 error("***ERROR: Could not find linc-oe in user's home directory\n")
159 ### LINC config generator directory###
160 configGen = findDir.__func__('LINC-config-generator', user)
161 if not configGen:
162 error("***ERROR: Could not find LINC-config-generator in user's home directory\n")
163 # list of all the controllers
164 controllers = None
165 def __init__(self, name, dpid=None, allowed=True,
166 switchType='ROADM', topo=None, annotations={}, controller=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800167 params[ 'inNamespace' ] = False
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000168 Switch.__init__(self, name, dpid=dpid, **params)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800169 self.name = name
170 self.annotations = annotations
171 self.allowed = allowed
172 self.switchType = switchType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000173 self.configDict = {} # dictionary that holds all of the JSON configuration data
174 self.crossConnects = []
175 self.deletedCrossConnects = []
176 self.controller = controller
177 self.lincId = self._get_linc_id() # use to communicate with LINC
178 self.lincStarted = False
Brian O'Connoreb27c452014-12-07 02:43:58 -0800179
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000180 def start(self, *opts, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800181 '''Instead of starting a virtual switch, we build the JSON
182 dictionary for the emulated optical switch'''
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000183 # TODO:Once LINC has the ability to spawn network element dynamically
184 # we need to use this method to spawn new logical LINC switch rather then
185 # bulding JSON.
186 # if LINC is started then we can start and stop logical switches else create JSON
187 if self.lincStarted:
188 return self.start_oe()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800189 self.configDict[ 'uri' ] = 'of:' + self.dpid
190 self.configDict[ 'annotations' ] = self.annotations
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000191 self.configDict[ 'annotations' ].setdefault('name', self.name)
192 self.configDict[ 'hw' ] = 'LINC-OE'
Brian O'Connoreb27c452014-12-07 02:43:58 -0800193 self.configDict[ 'mfr' ] = 'Linc'
194 self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1]
195 self.configDict[ 'type' ] = self.switchType
196 self.configDict[ 'ports' ] = []
197 for port, intf in self.intfs.items():
198 if intf.name == 'lo':
199 continue
200 else:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000201 self.configDict[ 'ports' ].append(intf.json())
202 self.lincStarted = True
fahad44e62c72015-03-04 14:55:35 -0800203
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000204 def stop(self, deleteIntfs=False):
205 '''
206 stop the existing switch
207 '''
208 # TODO:Add support for deleteIntf
209 self.stop_oe()
fahad44e62c72015-03-04 14:55:35 -0800210
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000211 def dpctl( self, *args ):
212 "Run dpctl command: ignore for now"
fahad44e62c72015-03-04 14:55:35 -0800213 pass
214
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000215 def write_to_cli(self, command):
216 '''
217 send command to LINC
218 '''
219 fd = None
220 try:
221 fd = open(self.writePipe, 'w', 0)
222 fd.write(command)
223 fd.close()
224 except:
225 print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info())
226 if fd:
227 fd.close()
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700228
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000229 def read_from_cli(self):
230 '''
231 read the output from the LINC CLI
232 '''
233 response = None
234 fd = None
235 try:
236 fd = open(self.readPipe, 'r', 0)
237 fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read
238 # FIXME:Due to non-blocking read most for the time we read nothing
239 response = fd.read()
240 fd.close()
241 except :
242 # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info())
243 if fd:
244 fd.close()
245 return response
246
247 def _get_linc_id(self):
248 '''
249 return the corresponding LINC switchId.
250 '''
251 return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid)
252 #--------------------------------------------------------------------------
253 # LINC CLI commands
254 #--------------------------------------------------------------------------
255 def start_oe(self):
256 '''
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700257 existing LINC switch
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000258 '''
259 #starting Switch
260 cmd = "linc:start_switch({}).\r\n".format(self.lincId)
261 self.write_to_cli(cmd)
262 #hanlding taps interfaces related to the switch
263 crossConnectJSON = {}
264 linkConfig = []
265 for i in range(0,len(self.deletedCrossConnects)):
266 crossConnect = self.deletedCrossConnects.pop()
267 tap = None
268 if isinstance(crossConnect.intf1.node, LINCSwitch):
269 intf = crossConnect.intf2
270 tapPort = crossConnect.intf1.port
271 else:
272 intf = crossConnect.intf1
273 tapPort = crossConnect.intf2.port
274 tap = LINCSwitch.findTap(self, tapPort)
275 if tap:
276 LINCSwitch.setupInts([tap])
277 intf.node.attach(tap)
278 self.crossConnects.append(crossConnect)
279 linkConfig.append(crossConnect.json())
280 #Sending crossConnect info to the ONOS.
281 crossConnectJSON['links'] = linkConfig
282 with open("crossConnect.json", 'w') as fd:
283 json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': '))
284 info('*** Pushing crossConnect.json to ONOS\n')
285 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s\
286 Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True)
287
288 def stop_oe(self):
289 '''
290 stop the existing LINC switch
291 '''
292 cmd = "linc:stop_switch({}).\r\n".format(self.lincId)
293 self.write_to_cli(cmd)
294 #handling taps if any
295 for i in range(0, len(self.crossConnects)):
296 crossConnect = self.crossConnects.pop()
297 if isinstance(crossConnect.intf1.node, LINCSwitch):
298 intf = crossConnect.intf2
299 tapPort = crossConnect.intf1.port
300 else:
301 intf = crossConnect.intf1
302 tapPort = crossConnect.intf2.port
303 intf.node.detach(LINCSwitch.findTap(self, tapPort))
304 self.deletedCrossConnects.append(crossConnect)
305
306 def w_port_up(self, port):
307 '''
308 port_up
309 '''
310 cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port)
311 self.write_to_cli(cmd)
312
313 def w_port_down(self, port):
314 '''
315 port_down
316 '''
317 cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port)
318 self.write_to_cli(cmd)
319
320 # helper functions
321 @staticmethod
322 def switchJSON(switch):
323 "Returns the json configuration for a packet switch"
324 configDict = {}
325 configDict[ 'uri' ] = 'of:' + switch.dpid
326 configDict[ 'mac' ] = quietRun('cat /sys/class/net/%s/address' % switch.name).strip('\n').translate(None, ':')
327 configDict[ 'hw' ] = 'PK' # FIXME what about OVS?
328 configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS?
329 configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS?
330 annotations = switch.params.get('annotations', {})
331 annotations.setdefault('name', switch.name)
332 configDict[ 'annotations' ] = annotations
333 ports = []
334 for port, intf in switch.intfs.items():
335 if intf.name == 'lo':
336 continue
337 portDict = {}
338 portDict[ 'port' ] = port
339 portDict[ 'type' ] = 'FIBER' if isinstance(intf.link, LINCLink) else 'COPPER'
340 intfList = [ intf.link.intf1, intf.link.intf2 ]
341 intfList.remove(intf)
342 portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0
343 ports.append(portDict)
344 configDict[ 'ports' ] = ports
345 return configDict
346
347 @staticmethod
348 def bootOE(net):
349 "Start the LINC optical emulator within a mininet instance"
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700350 LINCSwitch.opticalJSON = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000351 linkConfig = []
352 devices = []
353 #setting up the controllers for LINCSwitch class
354 LINCSwitch.controllers = net.controllers
355
356 for switch in net.switches:
357 if isinstance(switch, OpticalSwitch):
358 devices.append(switch.json())
359 else:
360 devices.append(LINCSwitch.switchJSON(switch))
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700361 LINCSwitch.opticalJSON[ 'devices' ] = devices
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000362
363 for link in net.links:
364 if isinstance(link, LINCLink) :
365 linkConfig.append(link.json())
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700366 LINCSwitch.opticalJSON[ 'links' ] = linkConfig
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000367
368 info('*** Writing Topology.json file\n')
369 with open('Topology.json', 'w') as outfile:
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700370 json.dump(LINCSwitch.opticalJSON, outfile, indent=4, separators=(',', ': '))
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000371
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700372 info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n')
373
374 topoConfigJson = {};
375 dpIdToName = {};
376
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700377 topoConfigJson["switchConfig"] = LINCSwitch.getSwitchConfig(dpIdToName);
378 topoConfigJson["linkConfig"] = LINCSwitch.getLinkConfig(dpIdToName);
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700379
380 #Writing to TopoConfig.json
381 with open( 'TopoConfig.json', 'w' ) as outfile:
382 json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') )
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000383
384 info('*** Creating sys.config...\n')
385 output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s'
386 % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True)
387 if output:
388 error('***ERROR: Error creating sys.config file: %s\n' % output)
389 return False
390
391 info ('*** Setting multiple controllers in sys.config...\n')
392 searchStr = '\[{"Switch.*$'
393 ctrlStr = ''
394 for index in range(len(LINCSwitch.controllers)):
395 ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port)
396 replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma
397 sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr)
398 output = quietRun(sedCmd, shell=True)
399
400 info('*** Copying sys.config to linc-oe directory: ', output + '\n')
401 output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n')
402 info(output + '\n')
403
404 info('*** Adding taps and bringing them up...\n')
405 LINCSwitch.setupInts(LINCSwitch.getTaps())
406
407 info('*** removing pipes if any \n')
408 quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True)
409
410 info('*** Starting linc OE...\n')
411 output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True)
412 if output:
413 error('***ERROR: LINC-OE: %s' % output + '\n')
414 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
415 return False
416
417 info('*** Waiting for linc-oe to start...\n')
418 LINCSwitch.waitStarted(net)
419
420 info('*** Adding cross-connect (tap) interfaces to packet switches...\n')
421 for link in net.links:
422 if isinstance(link, LINCLink):
423 if link.annotations[ 'optical.type' ] == 'cross-connect':
424 for intf in [ link.intf1, link.intf2 ]:
425 if not isinstance(intf, LINCIntf):
426 intfList = [ intf.link.intf1, intf.link.intf2 ]
427 intfList.remove(intf)
428 intf2 = intfList[ 0 ]
429 intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
430
431 info('*** Waiting for all devices to be available in ONOS...\n')
432 url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
433 time = 0
Ayaka Koshibec9eed382015-09-03 14:38:55 -0700434 # Set up password authentication
435 pw_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
436 pw_mgr.add_password(None, url, REST_USER, REST_PW)
437 handler = urllib2.HTTPBasicAuthHandler(pw_mgr)
438 opener = urllib2.build_opener(handler)
439 opener.open(url)
440 urllib2.install_opener(opener)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000441 while True:
442 response = json.load(urllib2.urlopen(url))
443 devs = response.get('devices')
444
445 # Wait for all devices to be registered
446 if (len(devices) != len(devs)):
447 continue
448
449 # Wait for all devices to available
450 available = True
451 for d in devs:
452 available &= d['available']
453 if available:
454 break
455
456 if (time >= TIMEOUT):
457 error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
458 break
459
460 time += SLEEP_TIME
461 sleep(SLEEP_TIME)
462
463 info('*** Pushing Topology.json to ONOS\n')
464 for index in range(len(LINCSwitch.controllers)):
465 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json &' % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True)
466 # successful output contains the two characters '{}'
467 # if there is more output than this, there is an issue
468 if output.strip('{}'):
469 warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
470
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700471 #converts node ids to linc-oe format, with colons every two chars
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700472 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700473 def dpId(id):
474 nodeDpid = ""
475 id = id.split("/", 1)[0]
476 for i in range(3, len(id) - 1, 2):
477 nodeDpid += (id[i:(i + 2):]) + ":"
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700478 return nodeDpid[0:-1];
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700479
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700480 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700481 def getSwitchConfig (dpIdToName):
482 switchConfig = [];
483 #Iterate through all switches and convert the ROADM switches to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700484 for switch in LINCSwitch.opticalJSON["devices"]:
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700485 if switch.get("type", "none") == "ROADM":
486 builtSwitch = {}
487
488 #set basic switch params based on annotations
489 builtSwitch["allowed"] = True;
490 builtSwitch["latitude"] = switch["annotations"].get("latitude", 0.0);
491 builtSwitch["longitude"] = switch["annotations"].get("longitude", 0.0);
492
493 #assumed that all switches have this entry
494 nodeId = switch["uri"]
495
496 #convert the nodeId to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700497 nodeDpid = LINCSwitch.dpId(nodeId);
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700498
499 builtSwitch["name"] = switch.get("name", "none");
500
501 #keep track of the name corresponding to each switch dpid
502 dpIdToName[nodeDpid] = builtSwitch["name"];
503
504 builtSwitch["nodeDpid"] = nodeDpid
505
506 #set switch params and type
507 builtSwitch["params"] = {};
508 builtSwitch["params"]["numregens"] = switch["annotations"].get("optical.regens", 0);
509 builtSwitch["type"] = "Roadm"
510
511 #append to list of switches
512 switchConfig.append(builtSwitch);
513 return switchConfig
514
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700515 @staticmethod
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700516 def getLinkConfig (dpIdToName):
517 newLinkConfig = [];
518 #Iterate through all optical links and convert them to linc-oe format
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700519 for link in LINCSwitch.opticalJSON["links"]:
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700520 if link.get("type", "none") == "OPTICAL":
521 builtLink = {}
522
523 #set basic link params for src and dst
524 builtLink["allowed"] = True;
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700525 builtLink["nodeDpid1"] = LINCSwitch.dpId(link["src"])
526 builtLink["nodeDpid2"] = LINCSwitch.dpId(link["dst"])
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700527
528 #set more params such as name/bandwidth/port/waves if they exist
529 params = {}
530 params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none")
531 params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none")
532
533 params["port1"] = int(link["src"].split("/")[1])
534 params["port2"] = int(link["dst"].split("/")[1])
535
536 if "bandwidth" in link["annotations"]:
537 params["bandwidth"] = link["annotations"]["bandwidth"]
538
539 if "optical.waves" in link["annotations"]:
540 params["numWaves"] = link["annotations"]["optical.waves"]
541
542 builtLink["params"] = params
543
544 #set type of link (WDM or pktOpt)
545 if link["annotations"].get("optical.type", "cross-connect") == "WDM":
546 builtLink["type"] = "wdmLink"
547 else:
548 builtLink["type"] = "pktOptLink"
549
550 newLinkConfig.append(builtLink);
551 return newLinkConfig
552
553
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000554 @staticmethod
555 def waitStarted(net, timeout=TIMEOUT):
556 "wait until all tap interfaces are available"
557 tapCount = 0
558 time = 0
559 for link in net.links:
560 if isinstance(link, LINCLink):
561 if link.annotations[ 'optical.type' ] == 'cross-connect':
562 tapCount += 1
563
564 while True:
565 if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
566 return True
567 if timeout:
568 if time >= TIMEOUT:
569 error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
570 return False
571 time += SLEEP_TIME
572 sleep(SLEEP_TIME)
573
574 @staticmethod
575 def shutdownOE():
576 "stop the optical emulator"
577 info('*** Stopping linc OE...\n')
578 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
579
580 @staticmethod
581 def setupInts(intfs):
582 '''
583 add taps and bring them up.
584 '''
585 for i in intfs:
586 quietRun('ip tuntap add dev %s mode tap' % i)
587 quietRun('ip link set dev %s up' % i)
588 info('*** Intf %s set\n' % i)
589
590 @staticmethod
591 def getTaps(path=None):
592 '''
593 return list of all the tops in sys.config
594 '''
595 if path is None:
596 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
597 fd = open(path, 'r', 0)
598 sys_data = fd.read()
599 taps = re.findall('tap\d+', sys_data)
600 fd.close()
601 return taps
602
603 @staticmethod
604 def findUser():
605 "Try to return logged-in (usually non-root) user"
606 try:
607 # If we're running sudo
608 return os.environ[ 'SUDO_USER' ]
609 except:
610 try:
611 # Logged-in user (if we have a tty)
612 return quietRun('who am i').split()[ 0 ]
613 except:
614 # Give up and return effective user
615 return quietRun('whoami')
616
617
618 @staticmethod
619 def findTap(node, port, path=None):
620 '''utility function to parse through a sys.config
621 file to find tap interfaces for a switch'''
622 switch = False
623 portLine = ''
624 intfLines = []
625
626 if path is None:
627 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
628
629 with open(path) as f:
630 for line in f:
631 if 'tap' in line:
632 intfLines.append(line)
633 if node.dpid in line.translate(None, ':'):
634 switch = True
635 continue
636 if switch:
637 if 'switch' in line:
638 switch = False
639 if 'port_no,%s}' % port in line:
640 portLine = line
641 break
642
643 if portLine:
644 m = re.search('port,\d+', portLine)
645 port = m.group(0).split(',')[ 1 ]
646 else:
647 error('***ERROR: Could not find any ports in sys.config\n')
648 return
649
650 for intfLine in intfLines:
651 if 'port,%s' % port in intfLine:
652 return re.findall('tap\d+', intfLine)[ 0 ]
653
654 def json(self):
655 "return json configuration dictionary for switch"
656 return self.configDict
657
658 def terminate(self):
659 pass
660
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700661
662
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000663class LINCLink(Link):
664 """
665 LINC link class
666 """
667 def __init__(self, node1, node2, port1=None, port2=None, allowed=True,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800668 intfName1=None, intfName2=None, linkType='OPTICAL',
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000669 annotations={}, speed1=0, speed2=0, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800670 "Creates a dummy link without a virtual ethernet pair."
671 self.allowed = allowed
672 self.annotations = annotations
673 self.linkType = linkType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000674 self.port1 = port1
675 self.port2 = port2
Brian O'Connoreb27c452014-12-07 02:43:58 -0800676 params1 = { 'speed': speed1 }
677 params2 = { 'speed': speed2 }
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000678 # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False
679 if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch):
680 self.isCrossConnect = False
681 else:
682 self.isCrossConnect = True
683 if isinstance(node1, LINCSwitch):
684 cls1 = LINCIntf
685 if self.isCrossConnect:
686 node1.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800687 else:
688 cls1 = Intf
689 # bad hack to stop error message from appearing when we try to set up intf in a packet switch,
690 # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up
691 intfName1 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000692 if isinstance(node2, LINCSwitch):
693 cls2 = LINCIntf
694 if self.isCrossConnect:
695 node2.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800696 else:
697 cls2 = Intf
698 intfName2 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000699 Link.__init__(self, node1, node2, port1=port1, port2=port2,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800700 intfName1=intfName1, intfName2=intfName2, cls1=cls1,
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000701 cls2=cls2, params1=params1, params2=params2)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800702
703 @classmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000704 def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800705 pass
706
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000707 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800708 "build and return the json configuration dictionary for this link"
709 configData = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000710 configData[ 'src' ] = ('of:' + self.intf1.node.dpid +
711 '/%s' % self.intf1.node.ports[ self.intf1 ])
712 configData[ 'dst' ] = ('of:' + self.intf2.node.dpid +
713 '/%s' % self.intf2.node.ports[ self.intf2 ])
Brian O'Connoreb27c452014-12-07 02:43:58 -0800714 configData[ 'type' ] = self.linkType
715 configData[ 'annotations' ] = self.annotations
716 return configData
717
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000718class LINCIntf(OpticalIntf):
719 """
720 LINC interface class
721 """
722 def __init__(self, name=None, node=None, speed=0,
723 port=None, link=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800724 self.node = node
725 self.speed = speed
726 self.port = port
727 self.link = link
728 self.name = name
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000729 node.addIntf(self, port=port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800730 self.params = params
731 self.ip = None
732
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000733 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800734 "build and return the JSON information for this interface( not used right now )"
735 configDict = {}
736 configDict[ 'port' ] = self.port
737 configDict[ 'speed' ] = self.speed
738 configDict[ 'type' ] = 'FIBER'
739 return configDict
740
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000741 def config(self, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800742 "dont configure a dummy interface"
743 pass
744
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000745 def ifconfig(self, status):
746 "configure the status"
747 if status == "up":
748 return self.node.w_port_up(self.port)
749 elif status == "down":
750 return self.node.w_port_down(self.port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800751
752
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000753class MininetOE(Mininet):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800754 "Mininet with Linc-OE support (starts and stops linc-oe)"
755
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000756 def start(self):
757 Mininet.start(self)
758 LINCSwitch.bootOE(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800759
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000760 def stop(self):
761 Mininet.stop(self)
762 LINCSwitch.shutdownOE()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800763
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000764 def addControllers(self, controllers):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800765 i = 0
766 for ctrl in controllers:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000767 self.addController(RemoteController('c%d' % i, ip=ctrl))
768 i += 1
Brian O'Connoreb27c452014-12-07 02:43:58 -0800769
770if __name__ == '__main__':
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000771 pass