blob: 6730b806f9c371f2ab781f7bd500ee79ffe29fa4 [file] [log] [blame]
Brian O'Connoreb27c452014-12-07 02:43:58 -08001#!/usr/bin/python
2
3'''
4Notes:
5
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -08006This 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
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -080027 - 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
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -080044it's tap interfaces from lincOE during startup. The start() method for mininet would
Brian O'Connoreb27c452014-12-07 02:43:58 -080045grab 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
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -080048file 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
Brian O'Connoreb27c452014-12-07 02:43:58 -080051Topology 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
Ayaka Koshibe143b1c72015-11-18 17:19:04 -080061from mininet.node import Switch, OVSSwitch, RemoteController
Brian O'Connoreb27c452014-12-07 02:43:58 -080062from 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
Ching-Ting Sun60445dd2017-01-05 23:56:40 -080073### method, mapping dpid to LINC switchId ###
74def dpids_to_ids(sysConfig):
75 '''
76 return the dict containing switch dpids as key and LINC switch id as values
77 '''
78 dpids_to_ids = {}
79 fd = None
80 try:
81 with open(sysConfig, 'r', 0) as fd:
82 switch_id = 1
83 for line in fd:
84 dpid = re.search(r'([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})+', line, re.I)
85 if dpid:
86 dpids_to_ids[dpid.group().replace(':', '')] = switch_id
87 switch_id += 1
88 return dpids_to_ids
89 except:
90 print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info())
91 fd.close()
92 return None
93
94def findDir(directory, userName):
95 "finds and returns the path of any directory in the user's home directory"
96 homeDir = '/home/' + userName
97 Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n')
98 DirList = Dir.split('\n')
99 if not Dir:
100 return None
101 elif len(DirList) > 1 :
102 warn('***WARNING: Found multiple instances of %s; using %s\n'
103 % (directory, DirList[ 0 ]))
104 return DirList[ 0 ]
105 else:
106 return Dir
107
108def switchJSON(switch):
109 "Returns the json configuration for a packet switch"
110 configDict = {}
111 configDict[ 'uri' ] = 'of:' + switch.dpid
112 configDict[ 'type' ] = 'SWITCH'
113 annotations = switch.params.get('annotations', {})
114 annotations.setdefault('name', switch.name)
115 configDict[ 'annotations' ] = annotations
116 ports = []
117 for port, intf in switch.intfs.items():
118 if intf.name == 'lo':
119 continue
120 portDict = {}
121 portDict[ 'port' ] = port
122 portType = 'COPPER'
123 if isinstance(intf.link, LINCLink):
124 portType = 'OCH' if intf.link.isCrossConnect() else 'OMS'
125 portDict[ 'type' ] = portType
126 intfList = [ intf.link.intf1, intf.link.intf2 ]
127 intfList.remove(intf)
128 portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0
129 ports.append(portDict)
130 configDict[ 'ports' ] = ports
131 return configDict
132
133def dpId(id):
134 nodeDpid = ""
135 id = id.split("/", 1)[0]
136 for i in range(3, len(id) - 1, 2):
137 nodeDpid += (id[i:(i + 2):]) + ":"
138 return nodeDpid[0:-1]
139
140def getSwitchConfig(switches):
141 switchConfig = []
142
143 # Iterate through all switches and convert the ROADM switches to linc-oe format
144 for switch in switches:
145 if isinstance(switch, LINCSwitch):
146 builtSwitch = {}
147
148 # Set basic switch params based on annotations
149 builtSwitch["allowed"] = True
150 builtSwitch["latitude"] = switch.annotations.get("latitude", 0.0)
151 builtSwitch["longitude"] = switch.annotations.get("longitude", 0.0)
152
153 # Convert dpid to linc-oe format
154 builtSwitch["name"] = switch.name
155 builtSwitch["nodeDpid"] = dpId('of:' + switch.dpid)
156
157 # Set switch params and type
158 builtSwitch["params"] = {}
159 builtSwitch["params"]["numregens"] = switch.annotations.get("optical.regens", 0)
160 builtSwitch["type"] = "Roadm"
161
162 switchConfig.append(builtSwitch)
163
164 return switchConfig
165
166def getLinkConfig(links):
167 linkConfig = []
168
169 # Iterate through all non-edge links and convert them to linc-oe format
170 for link in links:
171 if isinstance(link, LINCLink):
172 builtLink = {}
173
174 # Set basic link params for src and dst
175 builtLink["allowed"] = True
176 builtLink["nodeDpid1"] = dpId('of:' + link.intf1.node.dpid)
177 builtLink["nodeDpid2"] = dpId('of:' + link.intf2.node.dpid)
178
179 # Set more params such as name/bandwidth/port if they exist
180 params = {}
181 params["nodeName1"] = link.intf1.node.name
182 params["nodeName2"] = link.intf2.node.name
183
184 params["port1"] = link.port1
185 params["port2"] = link.port2
186
187 if "bandwidth" in link.annotations:
188 params["bandwidth"] = link.annotations["bandwidth"]
189
190 builtLink["params"] = params
191
192 # Set link type to WDM or packet (LINC-config-generator relies on it)
193 if link.isTransportLayer():
194 builtLink["type"] = "wdmLink"
195 else:
196 builtLink["type"] = "pktOptLink"
197
198 linkConfig.append(builtLink)
199
200 return linkConfig
201
202def waitStarted(net, timeout=TIMEOUT):
203 "wait until all tap interfaces are available"
204 tapCount = 0
205 time = 0
206 for link in net.links:
207 if isinstance(link, LINCLink) and link.isCrossConnect():
208 tapCount += 1
209
210 while True:
211 # tapCount can be less than the actual number of taps if the optical network
212 # is a subgraph of a larger multidomain network.
213 tapNum = int(quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'))
214 if tapCount <= tapNum:
215 return True
216 if timeout:
217 if time >= TIMEOUT:
218 error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
219 return False
220 time += SLEEP_TIME
221 sleep(SLEEP_TIME)
222
223def setupInts(intfs):
224 '''
225 add taps and bring them up.
226 '''
227 for i in intfs:
228 quietRun('ip tuntap add dev %s mode tap' % i)
229 quietRun('ip link set dev %s up' % i)
230 info('*** Intf %s set\n' % i)
231
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000232class OpticalSwitch(Switch):
233 """
234 For now, same as Switch class.
235 """
236 pass
237
238class OpticalIntf(Intf):
239 """
240 For now,same as Intf class.
241 """
242 pass
243
244class OpticalLink(Link):
245 """
246 For now, same as Link.
247 """
248 pass
249
250class LINCSwitch(OpticalSwitch):
251 """
252 LINCSwitch class
253 """
254 # FIXME:Sometimes LINC doesn't remove pipes and on restart increase the pipe
255 # number from erlang.pipe.1.* to erlang.pipe.2.*, so should read and write
256 # from latest pipe files. For now we are removing all the pipes before
257 # starting LINC.
258 ### User Name ###
259 user = os.getlogin()
260 ### pipes ###
261 readPipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.r".format(user)
262 writePipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.w".format(user)
263 ### sys.config path ###
264 sysConfig = "/home/{}/linc-oe/rel/linc/releases/1.0/sys.config".format(user)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000265 ### dict of containing dpids as key and corresponding LINC switchId as values ###
Yuta HIGUCHI991caf92017-06-02 11:12:03 -0700266 dpidsToLINCSwitchId = dpids_to_ids(sysConfig)
Ching-Ting Sun60445dd2017-01-05 23:56:40 -0800267
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000268 ### ONOS Directory ###
269 try:
270 onosDir = os.environ[ 'ONOS_ROOT' ]
271 except:
272 onosDir = findDir('onos', user)
273 if not onosDir:
274 error('Please set ONOS_ROOT environment variable!\n')
275 else:
276 os.environ[ 'ONOS_ROOT' ] = onosDir
Ayaka Koshibeb0d70582015-09-11 11:29:36 -0700277 ### REST USER/PASS ###
278 try:
279 restUser = os.environ[ 'ONOS_WEB_USER' ]
280 restPass = os.environ[ 'ONOS_WEB_PASS' ]
281 except:
282 error('***WARNING: $ONOS_WEB_USER and $ONOS_WEB_PASS aren\'t set!\n')
283 error('***WARNING: Setting (probably) sane WEB user/pass values\n')
284 restUser = 'onos'
285 restPass = 'rocks'
286 os.environ[ 'ONOS_WEB_USER' ] = restUser
287 os.environ[ 'ONOS_WEB_PASS' ] = restPass
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000288 ### LINC-directory
Yuta HIGUCHI991caf92017-06-02 11:12:03 -0700289 lincDir = findDir('linc-oe', user)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000290 if not lincDir:
291 error("***ERROR: Could not find linc-oe in user's home directory\n")
292 ### LINC config generator directory###
Yuta HIGUCHI991caf92017-06-02 11:12:03 -0700293 configGen = findDir('LINC-config-generator', user)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000294 if not configGen:
295 error("***ERROR: Could not find LINC-config-generator in user's home directory\n")
296 # list of all the controllers
297 controllers = None
298 def __init__(self, name, dpid=None, allowed=True,
299 switchType='ROADM', topo=None, annotations={}, controller=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800300 params[ 'inNamespace' ] = False
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000301 Switch.__init__(self, name, dpid=dpid, **params)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800302 self.name = name
303 self.annotations = annotations
304 self.allowed = allowed
305 self.switchType = switchType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000306 self.configDict = {} # dictionary that holds all of the JSON configuration data
307 self.crossConnects = []
308 self.deletedCrossConnects = []
309 self.controller = controller
310 self.lincId = self._get_linc_id() # use to communicate with LINC
311 self.lincStarted = False
Brian O'Connoreb27c452014-12-07 02:43:58 -0800312
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000313 def start(self, *opts, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800314 '''Instead of starting a virtual switch, we build the JSON
315 dictionary for the emulated optical switch'''
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000316 # TODO:Once LINC has the ability to spawn network element dynamically
317 # we need to use this method to spawn new logical LINC switch rather then
318 # bulding JSON.
319 # if LINC is started then we can start and stop logical switches else create JSON
320 if self.lincStarted:
321 return self.start_oe()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800322 self.configDict[ 'uri' ] = 'of:' + self.dpid
323 self.configDict[ 'annotations' ] = self.annotations
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000324 self.configDict[ 'annotations' ].setdefault('name', self.name)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800325 self.configDict[ 'type' ] = self.switchType
326 self.configDict[ 'ports' ] = []
327 for port, intf in self.intfs.items():
328 if intf.name == 'lo':
329 continue
330 else:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000331 self.configDict[ 'ports' ].append(intf.json())
332 self.lincStarted = True
fahad44e62c72015-03-04 14:55:35 -0800333
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000334 def stop(self, deleteIntfs=False):
335 '''
336 stop the existing switch
337 '''
338 # TODO:Add support for deleteIntf
339 self.stop_oe()
fahad44e62c72015-03-04 14:55:35 -0800340
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000341 def dpctl( self, *args ):
342 "Run dpctl command: ignore for now"
fahad44e62c72015-03-04 14:55:35 -0800343 pass
344
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000345 def write_to_cli(self, command):
346 '''
347 send command to LINC
348 '''
349 fd = None
350 try:
351 fd = open(self.writePipe, 'w', 0)
352 fd.write(command)
353 fd.close()
354 except:
355 print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info())
356 if fd:
357 fd.close()
Nikhil Cheerla5f8f8f02015-07-07 16:01:17 -0700358
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000359 def read_from_cli(self):
360 '''
361 read the output from the LINC CLI
362 '''
363 response = None
364 fd = None
365 try:
366 fd = open(self.readPipe, 'r', 0)
367 fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read
368 # FIXME:Due to non-blocking read most for the time we read nothing
369 response = fd.read()
370 fd.close()
371 except :
372 # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info())
373 if fd:
374 fd.close()
375 return response
376
377 def _get_linc_id(self):
378 '''
379 return the corresponding LINC switchId.
380 '''
381 return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid)
382 #--------------------------------------------------------------------------
383 # LINC CLI commands
384 #--------------------------------------------------------------------------
385 def start_oe(self):
386 '''
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700387 existing LINC switch
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000388 '''
389 #starting Switch
390 cmd = "linc:start_switch({}).\r\n".format(self.lincId)
391 self.write_to_cli(cmd)
392 #hanlding taps interfaces related to the switch
393 crossConnectJSON = {}
394 linkConfig = []
395 for i in range(0,len(self.deletedCrossConnects)):
396 crossConnect = self.deletedCrossConnects.pop()
397 tap = None
398 if isinstance(crossConnect.intf1.node, LINCSwitch):
399 intf = crossConnect.intf2
400 tapPort = crossConnect.intf1.port
401 else:
402 intf = crossConnect.intf1
403 tapPort = crossConnect.intf2.port
404 tap = LINCSwitch.findTap(self, tapPort)
405 if tap:
Ching-Ting Sun60445dd2017-01-05 23:56:40 -0800406 setupInts([tap])
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000407 intf.node.attach(tap)
408 self.crossConnects.append(crossConnect)
409 linkConfig.append(crossConnect.json())
410 #Sending crossConnect info to the ONOS.
411 crossConnectJSON['links'] = linkConfig
412 with open("crossConnect.json", 'w') as fd:
413 json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': '))
414 info('*** Pushing crossConnect.json to ONOS\n')
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800415 output = quietRun('%s/tools/test/bin/onos-netcfg %s\
416 Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000417
418 def stop_oe(self):
419 '''
420 stop the existing LINC switch
421 '''
422 cmd = "linc:stop_switch({}).\r\n".format(self.lincId)
423 self.write_to_cli(cmd)
424 #handling taps if any
425 for i in range(0, len(self.crossConnects)):
426 crossConnect = self.crossConnects.pop()
427 if isinstance(crossConnect.intf1.node, LINCSwitch):
428 intf = crossConnect.intf2
429 tapPort = crossConnect.intf1.port
430 else:
431 intf = crossConnect.intf1
432 tapPort = crossConnect.intf2.port
433 intf.node.detach(LINCSwitch.findTap(self, tapPort))
434 self.deletedCrossConnects.append(crossConnect)
435
436 def w_port_up(self, port):
437 '''
438 port_up
439 '''
440 cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port)
441 self.write_to_cli(cmd)
442
443 def w_port_down(self, port):
444 '''
445 port_down
446 '''
447 cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port)
448 self.write_to_cli(cmd)
449
450 # helper functions
451 @staticmethod
Ayaka Koshibea879a042015-11-19 17:04:03 -0800452 def bootOE(net, domain=None):
Ayaka Koshibe143b1c72015-11-18 17:19:04 -0800453 """
454 Start the LINC optical emulator within a mininet instance
455
456 This involves 1. converting the information stored in Linc* to configs
457 for both LINC and the network config system, 2. starting Linc, 3. connecting
458 cross-connects, and finally pushing the network configs to ONOS.
Ayaka Koshibea879a042015-11-19 17:04:03 -0800459
460 Inevitably, there are times when we have OVS switches that should not be
461 under the control of the controller in charge of the Linc switches. We
462 hint at these by passing domain information.
Ayaka Koshibe143b1c72015-11-18 17:19:04 -0800463 """
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700464 LINCSwitch.opticalJSON = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000465 linkConfig = []
466 devices = []
467 #setting up the controllers for LINCSwitch class
468 LINCSwitch.controllers = net.controllers
469
470 for switch in net.switches:
Ayaka Koshibea879a042015-11-19 17:04:03 -0800471 if domain and switch not in domain:
472 continue
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000473 if isinstance(switch, OpticalSwitch):
474 devices.append(switch.json())
Ayaka Koshibe143b1c72015-11-18 17:19:04 -0800475 elif isinstance(switch, OVSSwitch):
Ching-Ting Sun60445dd2017-01-05 23:56:40 -0800476 devices.append(switchJSON(switch))
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700477 LINCSwitch.opticalJSON[ 'devices' ] = devices
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000478
479 for link in net.links:
480 if isinstance(link, LINCLink) :
481 linkConfig.append(link.json())
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700482 LINCSwitch.opticalJSON[ 'links' ] = linkConfig
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000483
484 info('*** Writing Topology.json file\n')
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700485 topoJSON = LINCSwitch.makeTopoJSON()
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000486 with open('Topology.json', 'w') as outfile:
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700487 json.dump(topoJSON, outfile, indent=4, separators=(',', ': '))
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000488
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700489 info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n')
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800490
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700491 topoConfigJson = {}
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700492
Yuta HIGUCHI991caf92017-06-02 11:12:03 -0700493 topoConfigJson["switchConfig"] = getSwitchConfig(net.switches)
Ching-Ting Sun60445dd2017-01-05 23:56:40 -0800494 topoConfigJson["linkConfig"] = getLinkConfig(net.links)
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700495
496 #Writing to TopoConfig.json
497 with open( 'TopoConfig.json', 'w' ) as outfile:
498 json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') )
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000499
500 info('*** Creating sys.config...\n')
501 output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s'
502 % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True)
503 if output:
504 error('***ERROR: Error creating sys.config file: %s\n' % output)
505 return False
506
507 info ('*** Setting multiple controllers in sys.config...\n')
508 searchStr = '\[{"Switch.*$'
509 ctrlStr = ''
510 for index in range(len(LINCSwitch.controllers)):
511 ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port)
512 replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma
513 sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr)
514 output = quietRun(sedCmd, shell=True)
515
516 info('*** Copying sys.config to linc-oe directory: ', output + '\n')
517 output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n')
518 info(output + '\n')
519
520 info('*** Adding taps and bringing them up...\n')
Ching-Ting Sun60445dd2017-01-05 23:56:40 -0800521 setupInts(LINCSwitch.getTaps())
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000522
523 info('*** removing pipes if any \n')
524 quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True)
525
526 info('*** Starting linc OE...\n')
527 output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True)
528 if output:
529 error('***ERROR: LINC-OE: %s' % output + '\n')
530 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
531 return False
532
533 info('*** Waiting for linc-oe to start...\n')
Ching-Ting Sun60445dd2017-01-05 23:56:40 -0800534 waitStarted(net)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000535
536 info('*** Adding cross-connect (tap) interfaces to packet switches...\n')
537 for link in net.links:
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800538 if isinstance(link, LINCLink) and link.isCrossConnect():
539 for intf in [ link.intf1, link.intf2 ]:
540 if not isinstance(intf, LINCIntf):
541 intfList = [ intf.link.intf1, intf.link.intf2 ]
542 intfList.remove(intf)
543 intf2 = intfList[ 0 ]
544 intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000545
546 info('*** Waiting for all devices to be available in ONOS...\n')
547 url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
548 time = 0
Ayaka Koshibec9eed382015-09-03 14:38:55 -0700549 # Set up password authentication
550 pw_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
Ayaka Koshibeb0d70582015-09-11 11:29:36 -0700551 pw_mgr.add_password(None, url, LINCSwitch.restUser, LINCSwitch.restPass)
Ayaka Koshibec9eed382015-09-03 14:38:55 -0700552 handler = urllib2.HTTPBasicAuthHandler(pw_mgr)
553 opener = urllib2.build_opener(handler)
554 opener.open(url)
555 urllib2.install_opener(opener)
Ayaka Koshibe143b1c72015-11-18 17:19:04 -0800556 # focus on just checking the state of devices we're interested in
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800557 # expected devices availability map
558 devMap = dict.fromkeys(map( lambda x: x['uri'], devices ), False)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000559 while True:
560 response = json.load(urllib2.urlopen(url))
561 devs = response.get('devices')
562
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800563 # update availability map
564 for d in devs:
565 if devMap.has_key(d['id']):
566 devMap[d['id']] = d['available']
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000567
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800568 # Check if all devices we're interested became available
569 if all(devMap.viewvalues()):
570 break;
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000571
572 if (time >= TIMEOUT):
573 error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
574 break
575
576 time += SLEEP_TIME
577 sleep(SLEEP_TIME)
578
579 info('*** Pushing Topology.json to ONOS\n')
580 for index in range(len(LINCSwitch.controllers)):
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800581 output = quietRun('%s/tools/test/bin/onos-netcfg %s Topology.json &'\
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700582 % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True)
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000583 # successful output contains the two characters '{}'
584 # if there is more output than this, there is an issue
585 if output.strip('{}'):
586 warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
587
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700588
Nikhil Cheerlaf9391e12015-07-21 10:49:12 -0700589 @staticmethod
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700590 def makeTopoJSON():
591 """
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800592 Builds ONOS network config system compatible dicts to be written as Topology.json file.
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700593 """
594 topology = {}
595 links = {}
596 devices = {}
597 ports = {}
HIGUCHI Yuta3fd2f942016-01-16 20:26:47 -0800598 BasicDevConfigKeys = ['name', 'type', 'latitude', 'longitude', 'allowed',
599 'rackAddress', 'owner', 'driver', 'manufacturer',
600 'hwVersion', 'swVersion', 'serial',
601 'managementAddress']
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700602
603 for switch in LINCSwitch.opticalJSON[ 'devices' ]:
Marc De Leenheer8b5aae82015-12-22 11:27:01 -0800604 # Build device entries - keyed on uri (DPID) and config key 'basic'
605 # 'type' is necessary field, else ONOS assumes it's a SWITCH
606 # Annotations hold switch name and latitude/longitude
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700607 devDict = {}
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700608 devDict[ 'type' ] = switch[ 'type' ]
HIGUCHI Yuta3fd2f942016-01-16 20:26:47 -0800609 devDict.update({k: v for k, v in switch[ 'annotations' ].iteritems() if k in BasicDevConfigKeys})
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700610 devSubj = switch[ 'uri' ]
611 devices[ devSubj ] = { 'basic': devDict }
612
Marc De Leenheer8b5aae82015-12-22 11:27:01 -0800613 # Build port entries - keyed on "uri/port" and config key 'optical'
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700614 for port in switch[ 'ports' ]:
615 portSubj = devSubj + '/' + str(port[ 'port' ])
616 ports[ portSubj ] = { 'optical': port }
617
Marc De Leenheer8b5aae82015-12-22 11:27:01 -0800618 # Build link entries - keyed on "uri/port-uri/port" and config key 'basic'
619 # Annotations hold the 'durable' field, which is necessary as long as we don't discover optical links
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700620 for link in LINCSwitch.opticalJSON[ 'links' ]:
621 linkDict = {}
622 linkDict[ 'type' ] = link[ 'type' ]
Marc De Leenheer8b5aae82015-12-22 11:27:01 -0800623 linkDict.update(link[ 'annotations' ])
Ayaka Koshibed88b81d2015-09-17 17:52:27 -0700624 linkSubj = link[ 'src' ] + '-' + link[ 'dst' ]
625 links[ linkSubj ] = { 'basic': linkDict }
626
627 topology[ 'links' ] = links
628 topology[ 'devices' ] = devices
629 topology[ 'ports' ] = ports
630
631 return topology
632
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000633
634 @staticmethod
635 def shutdownOE():
636 "stop the optical emulator"
637 info('*** Stopping linc OE...\n')
638 quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
639
640 @staticmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000641 def getTaps(path=None):
642 '''
Marc De Leenheer8b5aae82015-12-22 11:27:01 -0800643 return list of all the taps in sys.config
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000644 '''
645 if path is None:
646 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
647 fd = open(path, 'r', 0)
648 sys_data = fd.read()
649 taps = re.findall('tap\d+', sys_data)
650 fd.close()
651 return taps
652
653 @staticmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000654 def findTap(node, port, path=None):
655 '''utility function to parse through a sys.config
656 file to find tap interfaces for a switch'''
657 switch = False
658 portLine = ''
659 intfLines = []
660
661 if path is None:
662 path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
663
664 with open(path) as f:
665 for line in f:
666 if 'tap' in line:
667 intfLines.append(line)
668 if node.dpid in line.translate(None, ':'):
669 switch = True
670 continue
671 if switch:
672 if 'switch' in line:
673 switch = False
674 if 'port_no,%s}' % port in line:
675 portLine = line
676 break
677
678 if portLine:
679 m = re.search('port,\d+', portLine)
680 port = m.group(0).split(',')[ 1 ]
681 else:
682 error('***ERROR: Could not find any ports in sys.config\n')
683 return
684
685 for intfLine in intfLines:
686 if 'port,%s' % port in intfLine:
687 return re.findall('tap\d+', intfLine)[ 0 ]
688
689 def json(self):
690 "return json configuration dictionary for switch"
691 return self.configDict
692
693 def terminate(self):
694 pass
695
Nikhil Cheerla7d7f3be2015-07-09 13:13:40 -0700696
697
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000698class LINCLink(Link):
699 """
700 LINC link class
701 """
702 def __init__(self, node1, node2, port1=None, port2=None, allowed=True,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800703 intfName1=None, intfName2=None, linkType='OPTICAL',
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000704 annotations={}, speed1=0, speed2=0, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800705 "Creates a dummy link without a virtual ethernet pair."
706 self.allowed = allowed
707 self.annotations = annotations
708 self.linkType = linkType
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000709 self.port1 = port1
710 self.port2 = port2
Brian O'Connoreb27c452014-12-07 02:43:58 -0800711 params1 = { 'speed': speed1 }
712 params2 = { 'speed': speed2 }
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000713 if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch):
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800714 self.isXC = False
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000715 else:
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800716 self.isXC = True
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000717 if isinstance(node1, LINCSwitch):
718 cls1 = LINCIntf
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800719 if self.isXC:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000720 node1.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800721 else:
722 cls1 = Intf
HIGUCHI Yutad95f3cd2015-12-15 15:05:22 -0800723 # bad hack to stop error message from appearing when we try to set up intf in a packet switch,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800724 # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up
725 intfName1 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000726 if isinstance(node2, LINCSwitch):
727 cls2 = LINCIntf
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800728 if self.isXC:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000729 node2.crossConnects.append(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800730 else:
731 cls2 = Intf
732 intfName2 = 'lo'
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000733 Link.__init__(self, node1, node2, port1=port1, port2=port2,
Brian O'Connoreb27c452014-12-07 02:43:58 -0800734 intfName1=intfName1, intfName2=intfName2, cls1=cls1,
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000735 cls2=cls2, params1=params1, params2=params2)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800736
737 @classmethod
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000738 def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800739 pass
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 configuration dictionary for this link"
743 configData = {}
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000744 configData[ 'src' ] = ('of:' + self.intf1.node.dpid +
745 '/%s' % self.intf1.node.ports[ self.intf1 ])
746 configData[ 'dst' ] = ('of:' + self.intf2.node.dpid +
747 '/%s' % self.intf2.node.ports[ self.intf2 ])
Brian O'Connoreb27c452014-12-07 02:43:58 -0800748 configData[ 'type' ] = self.linkType
749 configData[ 'annotations' ] = self.annotations
750 return configData
751
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800752 def isCrossConnect(self):
753 if isinstance(self.intf1.node, LINCSwitch) ^ isinstance(self.intf2.node, LINCSwitch):
754 return True
755
756 return False
757
758 def isTransportLayer(self):
759 if isinstance(self.intf1.node, LINCSwitch) and isinstance(self.intf2.node, LINCSwitch):
760 return True
761
762 return False
763
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000764class LINCIntf(OpticalIntf):
765 """
766 LINC interface class
767 """
768 def __init__(self, name=None, node=None, speed=0,
769 port=None, link=None, **params):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800770 self.node = node
771 self.speed = speed
772 self.port = port
773 self.link = link
774 self.name = name
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000775 node.addIntf(self, port=port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800776 self.params = params
777 self.ip = None
778
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000779 def json(self):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800780 "build and return the JSON information for this interface( not used right now )"
781 configDict = {}
782 configDict[ 'port' ] = self.port
783 configDict[ 'speed' ] = self.speed
Ayaka Koshibe144bab02015-10-22 12:56:59 -0700784 portType = 'COPPER'
785 if isinstance(self.link, LINCLink):
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800786 portType = 'OCH' if self.link.isCrossConnect() else 'OMS'
Ayaka Koshibe144bab02015-10-22 12:56:59 -0700787 configDict[ 'type' ] = portType
Brian O'Connoreb27c452014-12-07 02:43:58 -0800788 return configDict
789
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000790 def config(self, *args, **kwargs):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800791 "dont configure a dummy interface"
792 pass
793
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000794 def ifconfig(self, status):
795 "configure the status"
796 if status == "up":
797 return self.node.w_port_up(self.port)
798 elif status == "down":
799 return self.node.w_port_down(self.port)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800800
801
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000802class MininetOE(Mininet):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800803 "Mininet with Linc-OE support (starts and stops linc-oe)"
804
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000805 def start(self):
806 Mininet.start(self)
807 LINCSwitch.bootOE(self)
Brian O'Connoreb27c452014-12-07 02:43:58 -0800808
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000809 def stop(self):
810 Mininet.stop(self)
811 LINCSwitch.shutdownOE()
Brian O'Connoreb27c452014-12-07 02:43:58 -0800812
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000813 def addControllers(self, controllers):
Brian O'Connoreb27c452014-12-07 02:43:58 -0800814 i = 0
815 for ctrl in controllers:
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000816 self.addController(RemoteController('c%d' % i, ip=ctrl))
817 i += 1
Brian O'Connoreb27c452014-12-07 02:43:58 -0800818
819if __name__ == '__main__':
Marc De Leenheer6ff97642015-07-08 19:21:16 +0000820 pass