Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | ''' |
| 4 | Notes: |
| 5 | |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 6 | This file contains classes and methods useful for integrating LincOE with Mininet, |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 7 | such as startOE, stopOE, LINCLink, and OpticalSwitch |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 8 | |
| 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 Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 25 | - import LINCLink and OpticalSwitch from this module |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 26 | - import startOE and stopOE from this module |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 27 | - create topology as you would a normal topology. when |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 28 | to an optical switch with topo.addLink, always specify cls=LINCLink |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 29 | - 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 | |
| 35 | I created a separate function to start lincOE to avoid subclassing Mininet. |
| 36 | In case anyone wants to write something that DOES subclass Mininet, I |
| 37 | thought I would outline how: |
| 38 | |
| 39 | If we want an object that starts lincOE within the mininet class itself, |
| 40 | we need to add another object to Mininet that contains all of the json object |
| 41 | information for each switch. We would still subclass switch and link, but these |
| 42 | classes would basically be dummy classes that store their own json information |
| 43 | in the Mininet class object. We may also change the default switch class to add |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 44 | it's tap interfaces from lincOE during startup. The start() method for mininet would |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 45 | grab all of the information from these switches and links, write configuration files |
| 46 | for lincOE using the json module, start lincOE, then run the start methodfor each |
| 47 | switch. The new start() method for each switch would parse through the sys.config |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 48 | file that was created and find the tap interface it needs to connect to, similar |
| 49 | to the findTap function that I currently use. After all of the controllers and |
| 50 | switches have been started, the new Mininet start() method should also push the |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 51 | Topology configuration file to ONOS. |
| 52 | |
| 53 | ''' |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 54 | import sys |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 55 | import re |
| 56 | import json |
| 57 | import os |
| 58 | from time import sleep |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 59 | import urllib2 |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 60 | |
Ayaka Koshibe | 143b1c7 | 2015-11-18 17:19:04 -0800 | [diff] [blame] | 61 | from mininet.node import Switch, OVSSwitch, RemoteController |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 62 | from mininet.topo import Topo |
| 63 | from mininet.util import quietRun |
| 64 | from mininet.net import Mininet |
| 65 | from mininet.log import setLogLevel, info, error, warn |
| 66 | from mininet.link import Link, Intf |
| 67 | from mininet.cli import CLI |
| 68 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 69 | # Sleep time and timeout values in seconds |
| 70 | SLEEP_TIME = 2 |
| 71 | TIMEOUT = 60 |
Marc De Leenheer | 16f857b | 2015-05-05 20:50:24 -0700 | [diff] [blame] | 72 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 73 | class OpticalSwitch(Switch): |
| 74 | """ |
| 75 | For now, same as Switch class. |
| 76 | """ |
| 77 | pass |
| 78 | |
| 79 | class OpticalIntf(Intf): |
| 80 | """ |
| 81 | For now,same as Intf class. |
| 82 | """ |
| 83 | pass |
| 84 | |
| 85 | class OpticalLink(Link): |
| 86 | """ |
| 87 | For now, same as Link. |
| 88 | """ |
| 89 | pass |
| 90 | |
| 91 | class 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 |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 122 | return dpids_to_ids |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 123 | except: |
| 124 | print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info()) |
| 125 | fd.close() |
| 126 | return None |
| 127 | ### dict of containing dpids as key and corresponding LINC switchId as values ### |
| 128 | dpidsToLINCSwitchId = dpids_to_ids.__func__(sysConfig) |
| 129 | @staticmethod |
| 130 | def findDir(directory, userName): |
| 131 | "finds and returns the path of any directory in the user's home directory" |
| 132 | homeDir = '/home/' + userName |
| 133 | Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n') |
| 134 | DirList = Dir.split('\n') |
| 135 | if not Dir: |
| 136 | return None |
| 137 | elif len(DirList) > 1 : |
| 138 | warn('***WARNING: Found multiple instances of %s; using %s\n' |
| 139 | % (directory, DirList[ 0 ])) |
| 140 | return DirList[ 0 ] |
| 141 | else: |
| 142 | return Dir |
| 143 | ### ONOS Directory ### |
| 144 | try: |
| 145 | onosDir = os.environ[ 'ONOS_ROOT' ] |
| 146 | except: |
| 147 | onosDir = findDir('onos', user) |
| 148 | if not onosDir: |
| 149 | error('Please set ONOS_ROOT environment variable!\n') |
| 150 | else: |
| 151 | os.environ[ 'ONOS_ROOT' ] = onosDir |
Ayaka Koshibe | b0d7058 | 2015-09-11 11:29:36 -0700 | [diff] [blame] | 152 | ### REST USER/PASS ### |
| 153 | try: |
| 154 | restUser = os.environ[ 'ONOS_WEB_USER' ] |
| 155 | restPass = os.environ[ 'ONOS_WEB_PASS' ] |
| 156 | except: |
| 157 | error('***WARNING: $ONOS_WEB_USER and $ONOS_WEB_PASS aren\'t set!\n') |
| 158 | error('***WARNING: Setting (probably) sane WEB user/pass values\n') |
| 159 | restUser = 'onos' |
| 160 | restPass = 'rocks' |
| 161 | os.environ[ 'ONOS_WEB_USER' ] = restUser |
| 162 | os.environ[ 'ONOS_WEB_PASS' ] = restPass |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 163 | ### LINC-directory |
| 164 | lincDir = findDir.__func__('linc-oe', user) |
| 165 | if not lincDir: |
| 166 | error("***ERROR: Could not find linc-oe in user's home directory\n") |
| 167 | ### LINC config generator directory### |
| 168 | configGen = findDir.__func__('LINC-config-generator', user) |
| 169 | if not configGen: |
| 170 | error("***ERROR: Could not find LINC-config-generator in user's home directory\n") |
| 171 | # list of all the controllers |
| 172 | controllers = None |
| 173 | def __init__(self, name, dpid=None, allowed=True, |
| 174 | switchType='ROADM', topo=None, annotations={}, controller=None, **params): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 175 | params[ 'inNamespace' ] = False |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 176 | Switch.__init__(self, name, dpid=dpid, **params) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 177 | self.name = name |
| 178 | self.annotations = annotations |
| 179 | self.allowed = allowed |
| 180 | self.switchType = switchType |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 181 | self.configDict = {} # dictionary that holds all of the JSON configuration data |
| 182 | self.crossConnects = [] |
| 183 | self.deletedCrossConnects = [] |
| 184 | self.controller = controller |
| 185 | self.lincId = self._get_linc_id() # use to communicate with LINC |
| 186 | self.lincStarted = False |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 187 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 188 | def start(self, *opts, **params): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 189 | '''Instead of starting a virtual switch, we build the JSON |
| 190 | dictionary for the emulated optical switch''' |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 191 | # TODO:Once LINC has the ability to spawn network element dynamically |
| 192 | # we need to use this method to spawn new logical LINC switch rather then |
| 193 | # bulding JSON. |
| 194 | # if LINC is started then we can start and stop logical switches else create JSON |
| 195 | if self.lincStarted: |
| 196 | return self.start_oe() |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 197 | self.configDict[ 'uri' ] = 'of:' + self.dpid |
| 198 | self.configDict[ 'annotations' ] = self.annotations |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 199 | self.configDict[ 'annotations' ].setdefault('name', self.name) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 200 | self.configDict[ 'type' ] = self.switchType |
| 201 | self.configDict[ 'ports' ] = [] |
| 202 | for port, intf in self.intfs.items(): |
| 203 | if intf.name == 'lo': |
| 204 | continue |
| 205 | else: |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 206 | self.configDict[ 'ports' ].append(intf.json()) |
| 207 | self.lincStarted = True |
fahad | 44e62c7 | 2015-03-04 14:55:35 -0800 | [diff] [blame] | 208 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 209 | def stop(self, deleteIntfs=False): |
| 210 | ''' |
| 211 | stop the existing switch |
| 212 | ''' |
| 213 | # TODO:Add support for deleteIntf |
| 214 | self.stop_oe() |
fahad | 44e62c7 | 2015-03-04 14:55:35 -0800 | [diff] [blame] | 215 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 216 | def dpctl( self, *args ): |
| 217 | "Run dpctl command: ignore for now" |
fahad | 44e62c7 | 2015-03-04 14:55:35 -0800 | [diff] [blame] | 218 | pass |
| 219 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 220 | def write_to_cli(self, command): |
| 221 | ''' |
| 222 | send command to LINC |
| 223 | ''' |
| 224 | fd = None |
| 225 | try: |
| 226 | fd = open(self.writePipe, 'w', 0) |
| 227 | fd.write(command) |
| 228 | fd.close() |
| 229 | except: |
| 230 | print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info()) |
| 231 | if fd: |
| 232 | fd.close() |
Nikhil Cheerla | 5f8f8f0 | 2015-07-07 16:01:17 -0700 | [diff] [blame] | 233 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 234 | def read_from_cli(self): |
| 235 | ''' |
| 236 | read the output from the LINC CLI |
| 237 | ''' |
| 238 | response = None |
| 239 | fd = None |
| 240 | try: |
| 241 | fd = open(self.readPipe, 'r', 0) |
| 242 | fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read |
| 243 | # FIXME:Due to non-blocking read most for the time we read nothing |
| 244 | response = fd.read() |
| 245 | fd.close() |
| 246 | except : |
| 247 | # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info()) |
| 248 | if fd: |
| 249 | fd.close() |
| 250 | return response |
| 251 | |
| 252 | def _get_linc_id(self): |
| 253 | ''' |
| 254 | return the corresponding LINC switchId. |
| 255 | ''' |
| 256 | return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid) |
| 257 | #-------------------------------------------------------------------------- |
| 258 | # LINC CLI commands |
| 259 | #-------------------------------------------------------------------------- |
| 260 | def start_oe(self): |
| 261 | ''' |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 262 | existing LINC switch |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 263 | ''' |
| 264 | #starting Switch |
| 265 | cmd = "linc:start_switch({}).\r\n".format(self.lincId) |
| 266 | self.write_to_cli(cmd) |
| 267 | #hanlding taps interfaces related to the switch |
| 268 | crossConnectJSON = {} |
| 269 | linkConfig = [] |
| 270 | for i in range(0,len(self.deletedCrossConnects)): |
| 271 | crossConnect = self.deletedCrossConnects.pop() |
| 272 | tap = None |
| 273 | if isinstance(crossConnect.intf1.node, LINCSwitch): |
| 274 | intf = crossConnect.intf2 |
| 275 | tapPort = crossConnect.intf1.port |
| 276 | else: |
| 277 | intf = crossConnect.intf1 |
| 278 | tapPort = crossConnect.intf2.port |
| 279 | tap = LINCSwitch.findTap(self, tapPort) |
| 280 | if tap: |
| 281 | LINCSwitch.setupInts([tap]) |
| 282 | intf.node.attach(tap) |
| 283 | self.crossConnects.append(crossConnect) |
| 284 | linkConfig.append(crossConnect.json()) |
| 285 | #Sending crossConnect info to the ONOS. |
| 286 | crossConnectJSON['links'] = linkConfig |
| 287 | with open("crossConnect.json", 'w') as fd: |
| 288 | json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': ')) |
| 289 | info('*** Pushing crossConnect.json to ONOS\n') |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 290 | output = quietRun('%s/tools/test/bin/onos-netcfg %s\ |
| 291 | Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True) |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 292 | |
| 293 | def stop_oe(self): |
| 294 | ''' |
| 295 | stop the existing LINC switch |
| 296 | ''' |
| 297 | cmd = "linc:stop_switch({}).\r\n".format(self.lincId) |
| 298 | self.write_to_cli(cmd) |
| 299 | #handling taps if any |
| 300 | for i in range(0, len(self.crossConnects)): |
| 301 | crossConnect = self.crossConnects.pop() |
| 302 | if isinstance(crossConnect.intf1.node, LINCSwitch): |
| 303 | intf = crossConnect.intf2 |
| 304 | tapPort = crossConnect.intf1.port |
| 305 | else: |
| 306 | intf = crossConnect.intf1 |
| 307 | tapPort = crossConnect.intf2.port |
| 308 | intf.node.detach(LINCSwitch.findTap(self, tapPort)) |
| 309 | self.deletedCrossConnects.append(crossConnect) |
| 310 | |
| 311 | def w_port_up(self, port): |
| 312 | ''' |
| 313 | port_up |
| 314 | ''' |
| 315 | cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port) |
| 316 | self.write_to_cli(cmd) |
| 317 | |
| 318 | def w_port_down(self, port): |
| 319 | ''' |
| 320 | port_down |
| 321 | ''' |
| 322 | cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port) |
| 323 | self.write_to_cli(cmd) |
| 324 | |
| 325 | # helper functions |
| 326 | @staticmethod |
| 327 | def switchJSON(switch): |
| 328 | "Returns the json configuration for a packet switch" |
| 329 | configDict = {} |
| 330 | configDict[ 'uri' ] = 'of:' + switch.dpid |
Marc De Leenheer | 8b5aae8 | 2015-12-22 11:27:01 -0800 | [diff] [blame] | 331 | configDict[ 'type' ] = 'SWITCH' |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 332 | annotations = switch.params.get('annotations', {}) |
| 333 | annotations.setdefault('name', switch.name) |
| 334 | configDict[ 'annotations' ] = annotations |
| 335 | ports = [] |
| 336 | for port, intf in switch.intfs.items(): |
| 337 | if intf.name == 'lo': |
| 338 | continue |
| 339 | portDict = {} |
| 340 | portDict[ 'port' ] = port |
Ayaka Koshibe | 144bab0 | 2015-10-22 12:56:59 -0700 | [diff] [blame] | 341 | portType = 'COPPER' |
| 342 | if isinstance(intf.link, LINCLink): |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 343 | portType = 'OCH' if intf.link.isCrossConnect() else 'OMS' |
Ayaka Koshibe | 144bab0 | 2015-10-22 12:56:59 -0700 | [diff] [blame] | 344 | portDict[ 'type' ] = portType |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 345 | intfList = [ intf.link.intf1, intf.link.intf2 ] |
| 346 | intfList.remove(intf) |
| 347 | portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0 |
| 348 | ports.append(portDict) |
| 349 | configDict[ 'ports' ] = ports |
| 350 | return configDict |
| 351 | |
| 352 | @staticmethod |
Ayaka Koshibe | a879a04 | 2015-11-19 17:04:03 -0800 | [diff] [blame] | 353 | def bootOE(net, domain=None): |
Ayaka Koshibe | 143b1c7 | 2015-11-18 17:19:04 -0800 | [diff] [blame] | 354 | """ |
| 355 | Start the LINC optical emulator within a mininet instance |
| 356 | |
| 357 | This involves 1. converting the information stored in Linc* to configs |
| 358 | for both LINC and the network config system, 2. starting Linc, 3. connecting |
| 359 | cross-connects, and finally pushing the network configs to ONOS. |
Ayaka Koshibe | a879a04 | 2015-11-19 17:04:03 -0800 | [diff] [blame] | 360 | |
| 361 | Inevitably, there are times when we have OVS switches that should not be |
| 362 | under the control of the controller in charge of the Linc switches. We |
| 363 | hint at these by passing domain information. |
Ayaka Koshibe | 143b1c7 | 2015-11-18 17:19:04 -0800 | [diff] [blame] | 364 | """ |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 365 | LINCSwitch.opticalJSON = {} |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 366 | linkConfig = [] |
| 367 | devices = [] |
| 368 | #setting up the controllers for LINCSwitch class |
| 369 | LINCSwitch.controllers = net.controllers |
| 370 | |
| 371 | for switch in net.switches: |
Ayaka Koshibe | a879a04 | 2015-11-19 17:04:03 -0800 | [diff] [blame] | 372 | if domain and switch not in domain: |
| 373 | continue |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 374 | if isinstance(switch, OpticalSwitch): |
| 375 | devices.append(switch.json()) |
Ayaka Koshibe | 143b1c7 | 2015-11-18 17:19:04 -0800 | [diff] [blame] | 376 | elif isinstance(switch, OVSSwitch): |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 377 | devices.append(LINCSwitch.switchJSON(switch)) |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 378 | LINCSwitch.opticalJSON[ 'devices' ] = devices |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 379 | |
| 380 | for link in net.links: |
| 381 | if isinstance(link, LINCLink) : |
| 382 | linkConfig.append(link.json()) |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 383 | LINCSwitch.opticalJSON[ 'links' ] = linkConfig |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 384 | |
| 385 | info('*** Writing Topology.json file\n') |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 386 | topoJSON = LINCSwitch.makeTopoJSON() |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 387 | with open('Topology.json', 'w') as outfile: |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 388 | json.dump(topoJSON, outfile, indent=4, separators=(',', ': ')) |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 389 | |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 390 | info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n') |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 391 | |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 392 | topoConfigJson = {} |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 393 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 394 | topoConfigJson["switchConfig"] = LINCSwitch.getSwitchConfig(net.switches) |
| 395 | topoConfigJson["linkConfig"] = LINCSwitch.getLinkConfig(net.links) |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 396 | |
| 397 | #Writing to TopoConfig.json |
| 398 | with open( 'TopoConfig.json', 'w' ) as outfile: |
| 399 | json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') ) |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 400 | |
| 401 | info('*** Creating sys.config...\n') |
| 402 | output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s' |
| 403 | % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True) |
| 404 | if output: |
| 405 | error('***ERROR: Error creating sys.config file: %s\n' % output) |
| 406 | return False |
| 407 | |
| 408 | info ('*** Setting multiple controllers in sys.config...\n') |
| 409 | searchStr = '\[{"Switch.*$' |
| 410 | ctrlStr = '' |
| 411 | for index in range(len(LINCSwitch.controllers)): |
| 412 | ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port) |
| 413 | replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma |
| 414 | sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr) |
| 415 | output = quietRun(sedCmd, shell=True) |
| 416 | |
| 417 | info('*** Copying sys.config to linc-oe directory: ', output + '\n') |
| 418 | output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n') |
| 419 | info(output + '\n') |
| 420 | |
| 421 | info('*** Adding taps and bringing them up...\n') |
| 422 | LINCSwitch.setupInts(LINCSwitch.getTaps()) |
| 423 | |
| 424 | info('*** removing pipes if any \n') |
| 425 | quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True) |
| 426 | |
| 427 | info('*** Starting linc OE...\n') |
| 428 | output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True) |
| 429 | if output: |
| 430 | error('***ERROR: LINC-OE: %s' % output + '\n') |
| 431 | quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True) |
| 432 | return False |
| 433 | |
| 434 | info('*** Waiting for linc-oe to start...\n') |
| 435 | LINCSwitch.waitStarted(net) |
| 436 | |
| 437 | info('*** Adding cross-connect (tap) interfaces to packet switches...\n') |
| 438 | for link in net.links: |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 439 | if isinstance(link, LINCLink) and link.isCrossConnect(): |
| 440 | for intf in [ link.intf1, link.intf2 ]: |
| 441 | if not isinstance(intf, LINCIntf): |
| 442 | intfList = [ intf.link.intf1, intf.link.intf2 ] |
| 443 | intfList.remove(intf) |
| 444 | intf2 = intfList[ 0 ] |
| 445 | intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ])) |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 446 | |
| 447 | info('*** Waiting for all devices to be available in ONOS...\n') |
| 448 | url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip |
| 449 | time = 0 |
Ayaka Koshibe | c9eed38 | 2015-09-03 14:38:55 -0700 | [diff] [blame] | 450 | # Set up password authentication |
| 451 | pw_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() |
Ayaka Koshibe | b0d7058 | 2015-09-11 11:29:36 -0700 | [diff] [blame] | 452 | pw_mgr.add_password(None, url, LINCSwitch.restUser, LINCSwitch.restPass) |
Ayaka Koshibe | c9eed38 | 2015-09-03 14:38:55 -0700 | [diff] [blame] | 453 | handler = urllib2.HTTPBasicAuthHandler(pw_mgr) |
| 454 | opener = urllib2.build_opener(handler) |
| 455 | opener.open(url) |
| 456 | urllib2.install_opener(opener) |
Ayaka Koshibe | 143b1c7 | 2015-11-18 17:19:04 -0800 | [diff] [blame] | 457 | # focus on just checking the state of devices we're interested in |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 458 | # expected devices availability map |
| 459 | devMap = dict.fromkeys(map( lambda x: x['uri'], devices ), False) |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 460 | while True: |
| 461 | response = json.load(urllib2.urlopen(url)) |
| 462 | devs = response.get('devices') |
| 463 | |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 464 | # update availability map |
| 465 | for d in devs: |
| 466 | if devMap.has_key(d['id']): |
| 467 | devMap[d['id']] = d['available'] |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 468 | |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 469 | # Check if all devices we're interested became available |
| 470 | if all(devMap.viewvalues()): |
| 471 | break; |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 472 | |
| 473 | if (time >= TIMEOUT): |
| 474 | error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT) |
| 475 | break |
| 476 | |
| 477 | time += SLEEP_TIME |
| 478 | sleep(SLEEP_TIME) |
| 479 | |
| 480 | info('*** Pushing Topology.json to ONOS\n') |
| 481 | for index in range(len(LINCSwitch.controllers)): |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 482 | output = quietRun('%s/tools/test/bin/onos-netcfg %s Topology.json &'\ |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 483 | % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True) |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 484 | # successful output contains the two characters '{}' |
| 485 | # if there is more output than this, there is an issue |
| 486 | if output.strip('{}'): |
| 487 | warn('***WARNING: Could not push topology file to ONOS: %s\n' % output) |
| 488 | |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 489 | #converts node ids to linc-oe format, with colons every two chars |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 490 | @staticmethod |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 491 | def dpId(id): |
| 492 | nodeDpid = "" |
| 493 | id = id.split("/", 1)[0] |
| 494 | for i in range(3, len(id) - 1, 2): |
| 495 | nodeDpid += (id[i:(i + 2):]) + ":" |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 496 | return nodeDpid[0:-1] |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 497 | |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 498 | @staticmethod |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 499 | def makeTopoJSON(): |
| 500 | """ |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 501 | Builds ONOS network config system compatible dicts to be written as Topology.json file. |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 502 | """ |
| 503 | topology = {} |
| 504 | links = {} |
| 505 | devices = {} |
| 506 | ports = {} |
HIGUCHI Yuta | 3fd2f94 | 2016-01-16 20:26:47 -0800 | [diff] [blame] | 507 | BasicDevConfigKeys = ['name', 'type', 'latitude', 'longitude', 'allowed', |
| 508 | 'rackAddress', 'owner', 'driver', 'manufacturer', |
| 509 | 'hwVersion', 'swVersion', 'serial', |
| 510 | 'managementAddress'] |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 511 | |
| 512 | for switch in LINCSwitch.opticalJSON[ 'devices' ]: |
Marc De Leenheer | 8b5aae8 | 2015-12-22 11:27:01 -0800 | [diff] [blame] | 513 | # Build device entries - keyed on uri (DPID) and config key 'basic' |
| 514 | # 'type' is necessary field, else ONOS assumes it's a SWITCH |
| 515 | # Annotations hold switch name and latitude/longitude |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 516 | devDict = {} |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 517 | devDict[ 'type' ] = switch[ 'type' ] |
HIGUCHI Yuta | 3fd2f94 | 2016-01-16 20:26:47 -0800 | [diff] [blame] | 518 | devDict.update({k: v for k, v in switch[ 'annotations' ].iteritems() if k in BasicDevConfigKeys}) |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 519 | devSubj = switch[ 'uri' ] |
| 520 | devices[ devSubj ] = { 'basic': devDict } |
| 521 | |
Marc De Leenheer | 8b5aae8 | 2015-12-22 11:27:01 -0800 | [diff] [blame] | 522 | # Build port entries - keyed on "uri/port" and config key 'optical' |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 523 | for port in switch[ 'ports' ]: |
| 524 | portSubj = devSubj + '/' + str(port[ 'port' ]) |
| 525 | ports[ portSubj ] = { 'optical': port } |
| 526 | |
Marc De Leenheer | 8b5aae8 | 2015-12-22 11:27:01 -0800 | [diff] [blame] | 527 | # Build link entries - keyed on "uri/port-uri/port" and config key 'basic' |
| 528 | # Annotations hold the 'durable' field, which is necessary as long as we don't discover optical links |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 529 | for link in LINCSwitch.opticalJSON[ 'links' ]: |
| 530 | linkDict = {} |
| 531 | linkDict[ 'type' ] = link[ 'type' ] |
Marc De Leenheer | 8b5aae8 | 2015-12-22 11:27:01 -0800 | [diff] [blame] | 532 | linkDict.update(link[ 'annotations' ]) |
Ayaka Koshibe | d88b81d | 2015-09-17 17:52:27 -0700 | [diff] [blame] | 533 | linkSubj = link[ 'src' ] + '-' + link[ 'dst' ] |
| 534 | links[ linkSubj ] = { 'basic': linkDict } |
| 535 | |
| 536 | topology[ 'links' ] = links |
| 537 | topology[ 'devices' ] = devices |
| 538 | topology[ 'ports' ] = ports |
| 539 | |
| 540 | return topology |
| 541 | |
| 542 | @staticmethod |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 543 | def getSwitchConfig(switches): |
| 544 | switchConfig = [] |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 545 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 546 | # Iterate through all switches and convert the ROADM switches to linc-oe format |
| 547 | for switch in switches: |
| 548 | if isinstance(switch, LINCSwitch): |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 549 | builtSwitch = {} |
| 550 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 551 | # Set basic switch params based on annotations |
| 552 | builtSwitch["allowed"] = True |
| 553 | builtSwitch["latitude"] = switch.annotations.get("latitude", 0.0) |
| 554 | builtSwitch["longitude"] = switch.annotations.get("longitude", 0.0) |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 555 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 556 | # Convert dpid to linc-oe format |
| 557 | builtSwitch["name"] = switch.name |
| 558 | builtSwitch["nodeDpid"] = LINCSwitch.dpId('of:' + switch.dpid) |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 559 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 560 | # Set switch params and type |
| 561 | builtSwitch["params"] = {} |
| 562 | builtSwitch["params"]["numregens"] = switch.annotations.get("optical.regens", 0) |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 563 | builtSwitch["type"] = "Roadm" |
| 564 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 565 | switchConfig.append(builtSwitch) |
| 566 | |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 567 | return switchConfig |
| 568 | |
Nikhil Cheerla | f9391e1 | 2015-07-21 10:49:12 -0700 | [diff] [blame] | 569 | @staticmethod |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 570 | def getLinkConfig(links): |
| 571 | linkConfig = [] |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 572 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 573 | # Iterate through all non-edge links and convert them to linc-oe format |
| 574 | for link in links: |
| 575 | if isinstance(link, LINCLink): |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 576 | builtLink = {} |
| 577 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 578 | # Set basic link params for src and dst |
| 579 | builtLink["allowed"] = True |
| 580 | builtLink["nodeDpid1"] = LINCSwitch.dpId('of:' + link.intf1.node.dpid) |
| 581 | builtLink["nodeDpid2"] = LINCSwitch.dpId('of:' + link.intf2.node.dpid) |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 582 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 583 | # Set more params such as name/bandwidth/port if they exist |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 584 | params = {} |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 585 | params["nodeName1"] = link.intf1.node.name |
| 586 | params["nodeName2"] = link.intf2.node.name |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 587 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 588 | params["port1"] = link.port1 |
| 589 | params["port2"] = link.port2 |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 590 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 591 | if "bandwidth" in link.annotations: |
| 592 | params["bandwidth"] = link.annotations["bandwidth"] |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 593 | |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 594 | builtLink["params"] = params |
| 595 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 596 | # Set link type to WDM or packet (LINC-config-generator relies on it) |
| 597 | if link.isTransportLayer(): |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 598 | builtLink["type"] = "wdmLink" |
| 599 | else: |
| 600 | builtLink["type"] = "pktOptLink" |
| 601 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 602 | linkConfig.append(builtLink) |
| 603 | |
| 604 | return linkConfig |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 605 | |
| 606 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 607 | @staticmethod |
| 608 | def waitStarted(net, timeout=TIMEOUT): |
| 609 | "wait until all tap interfaces are available" |
| 610 | tapCount = 0 |
| 611 | time = 0 |
| 612 | for link in net.links: |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 613 | if isinstance(link, LINCLink) and link.isCrossConnect(): |
| 614 | tapCount += 1 |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 615 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 616 | while True: |
Ayaka Koshibe | 143b1c7 | 2015-11-18 17:19:04 -0800 | [diff] [blame] | 617 | # tapCount can be less than the actual number of taps if the optical network |
| 618 | # is a subgraph of a larger multidomain network. |
| 619 | tapNum = int(quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n')) |
| 620 | if tapCount <= tapNum: |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 621 | return True |
| 622 | if timeout: |
| 623 | if time >= TIMEOUT: |
| 624 | error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT) |
| 625 | return False |
| 626 | time += SLEEP_TIME |
| 627 | sleep(SLEEP_TIME) |
| 628 | |
| 629 | @staticmethod |
| 630 | def shutdownOE(): |
| 631 | "stop the optical emulator" |
| 632 | info('*** Stopping linc OE...\n') |
| 633 | quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True) |
| 634 | |
| 635 | @staticmethod |
| 636 | def setupInts(intfs): |
| 637 | ''' |
| 638 | add taps and bring them up. |
| 639 | ''' |
| 640 | for i in intfs: |
| 641 | quietRun('ip tuntap add dev %s mode tap' % i) |
| 642 | quietRun('ip link set dev %s up' % i) |
| 643 | info('*** Intf %s set\n' % i) |
| 644 | |
| 645 | @staticmethod |
| 646 | def getTaps(path=None): |
| 647 | ''' |
Marc De Leenheer | 8b5aae8 | 2015-12-22 11:27:01 -0800 | [diff] [blame] | 648 | return list of all the taps in sys.config |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 649 | ''' |
| 650 | if path is None: |
| 651 | path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir |
| 652 | fd = open(path, 'r', 0) |
| 653 | sys_data = fd.read() |
| 654 | taps = re.findall('tap\d+', sys_data) |
| 655 | fd.close() |
| 656 | return taps |
| 657 | |
| 658 | @staticmethod |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 659 | def findTap(node, port, path=None): |
| 660 | '''utility function to parse through a sys.config |
| 661 | file to find tap interfaces for a switch''' |
| 662 | switch = False |
| 663 | portLine = '' |
| 664 | intfLines = [] |
| 665 | |
| 666 | if path is None: |
| 667 | path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir |
| 668 | |
| 669 | with open(path) as f: |
| 670 | for line in f: |
| 671 | if 'tap' in line: |
| 672 | intfLines.append(line) |
| 673 | if node.dpid in line.translate(None, ':'): |
| 674 | switch = True |
| 675 | continue |
| 676 | if switch: |
| 677 | if 'switch' in line: |
| 678 | switch = False |
| 679 | if 'port_no,%s}' % port in line: |
| 680 | portLine = line |
| 681 | break |
| 682 | |
| 683 | if portLine: |
| 684 | m = re.search('port,\d+', portLine) |
| 685 | port = m.group(0).split(',')[ 1 ] |
| 686 | else: |
| 687 | error('***ERROR: Could not find any ports in sys.config\n') |
| 688 | return |
| 689 | |
| 690 | for intfLine in intfLines: |
| 691 | if 'port,%s' % port in intfLine: |
| 692 | return re.findall('tap\d+', intfLine)[ 0 ] |
| 693 | |
| 694 | def json(self): |
| 695 | "return json configuration dictionary for switch" |
| 696 | return self.configDict |
| 697 | |
| 698 | def terminate(self): |
| 699 | pass |
| 700 | |
Nikhil Cheerla | 7d7f3be | 2015-07-09 13:13:40 -0700 | [diff] [blame] | 701 | |
| 702 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 703 | class LINCLink(Link): |
| 704 | """ |
| 705 | LINC link class |
| 706 | """ |
| 707 | def __init__(self, node1, node2, port1=None, port2=None, allowed=True, |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 708 | intfName1=None, intfName2=None, linkType='OPTICAL', |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 709 | annotations={}, speed1=0, speed2=0, **params): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 710 | "Creates a dummy link without a virtual ethernet pair." |
| 711 | self.allowed = allowed |
| 712 | self.annotations = annotations |
| 713 | self.linkType = linkType |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 714 | self.port1 = port1 |
| 715 | self.port2 = port2 |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 716 | params1 = { 'speed': speed1 } |
| 717 | params2 = { 'speed': speed2 } |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 718 | if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch): |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 719 | self.isXC = False |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 720 | else: |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 721 | self.isXC = True |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 722 | if isinstance(node1, LINCSwitch): |
| 723 | cls1 = LINCIntf |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 724 | if self.isXC: |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 725 | node1.crossConnects.append(self) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 726 | else: |
| 727 | cls1 = Intf |
HIGUCHI Yuta | d95f3cd | 2015-12-15 15:05:22 -0800 | [diff] [blame] | 728 | # bad hack to stop error message from appearing when we try to set up intf in a packet switch, |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 729 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up |
| 730 | intfName1 = 'lo' |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 731 | if isinstance(node2, LINCSwitch): |
| 732 | cls2 = LINCIntf |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 733 | if self.isXC: |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 734 | node2.crossConnects.append(self) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 735 | else: |
| 736 | cls2 = Intf |
| 737 | intfName2 = 'lo' |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 738 | Link.__init__(self, node1, node2, port1=port1, port2=port2, |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 739 | intfName1=intfName1, intfName2=intfName2, cls1=cls1, |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 740 | cls2=cls2, params1=params1, params2=params2) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 741 | |
| 742 | @classmethod |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 743 | def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 744 | pass |
| 745 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 746 | def json(self): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 747 | "build and return the json configuration dictionary for this link" |
| 748 | configData = {} |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 749 | configData[ 'src' ] = ('of:' + self.intf1.node.dpid + |
| 750 | '/%s' % self.intf1.node.ports[ self.intf1 ]) |
| 751 | configData[ 'dst' ] = ('of:' + self.intf2.node.dpid + |
| 752 | '/%s' % self.intf2.node.ports[ self.intf2 ]) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 753 | configData[ 'type' ] = self.linkType |
| 754 | configData[ 'annotations' ] = self.annotations |
| 755 | return configData |
| 756 | |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 757 | def isCrossConnect(self): |
| 758 | if isinstance(self.intf1.node, LINCSwitch) ^ isinstance(self.intf2.node, LINCSwitch): |
| 759 | return True |
| 760 | |
| 761 | return False |
| 762 | |
| 763 | def isTransportLayer(self): |
| 764 | if isinstance(self.intf1.node, LINCSwitch) and isinstance(self.intf2.node, LINCSwitch): |
| 765 | return True |
| 766 | |
| 767 | return False |
| 768 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 769 | class LINCIntf(OpticalIntf): |
| 770 | """ |
| 771 | LINC interface class |
| 772 | """ |
| 773 | def __init__(self, name=None, node=None, speed=0, |
| 774 | port=None, link=None, **params): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 775 | self.node = node |
| 776 | self.speed = speed |
| 777 | self.port = port |
| 778 | self.link = link |
| 779 | self.name = name |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 780 | node.addIntf(self, port=port) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 781 | self.params = params |
| 782 | self.ip = None |
| 783 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 784 | def json(self): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 785 | "build and return the JSON information for this interface( not used right now )" |
| 786 | configDict = {} |
| 787 | configDict[ 'port' ] = self.port |
| 788 | configDict[ 'speed' ] = self.speed |
Ayaka Koshibe | 144bab0 | 2015-10-22 12:56:59 -0700 | [diff] [blame] | 789 | portType = 'COPPER' |
| 790 | if isinstance(self.link, LINCLink): |
Marc De Leenheer | 32fc3d2 | 2015-12-15 21:36:39 -0800 | [diff] [blame] | 791 | portType = 'OCH' if self.link.isCrossConnect() else 'OMS' |
Ayaka Koshibe | 144bab0 | 2015-10-22 12:56:59 -0700 | [diff] [blame] | 792 | configDict[ 'type' ] = portType |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 793 | return configDict |
| 794 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 795 | def config(self, *args, **kwargs): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 796 | "dont configure a dummy interface" |
| 797 | pass |
| 798 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 799 | def ifconfig(self, status): |
| 800 | "configure the status" |
| 801 | if status == "up": |
| 802 | return self.node.w_port_up(self.port) |
| 803 | elif status == "down": |
| 804 | return self.node.w_port_down(self.port) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 805 | |
| 806 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 807 | class MininetOE(Mininet): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 808 | "Mininet with Linc-OE support (starts and stops linc-oe)" |
| 809 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 810 | def start(self): |
| 811 | Mininet.start(self) |
| 812 | LINCSwitch.bootOE(self) |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 813 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 814 | def stop(self): |
| 815 | Mininet.stop(self) |
| 816 | LINCSwitch.shutdownOE() |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 817 | |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 818 | def addControllers(self, controllers): |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 819 | i = 0 |
| 820 | for ctrl in controllers: |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 821 | self.addController(RemoteController('c%d' % i, ip=ctrl)) |
| 822 | i += 1 |
Brian O'Connor | eb27c45 | 2014-12-07 02:43:58 -0800 | [diff] [blame] | 823 | |
| 824 | if __name__ == '__main__': |
Marc De Leenheer | 6ff9764 | 2015-07-08 19:21:16 +0000 | [diff] [blame] | 825 | pass |