Committed by
Yuta HIGUCHI
ONOS-3422 inter-domain cross connect
- Add network configuration about cross connect port for CO-ONOS - revised metro.py requires ecord.co app after (Change-Id: I3892e780bc6550f8a8d8be622b9fee5322c1dab5) to be loaded. - stop using onos-topo-cfg to send netcfg Change-Id: Ie90e69c4134d1f71893bf43ee6c290bdbd273aeb
Showing
3 changed files
with
76 additions
and
38 deletions
| ... | @@ -83,6 +83,7 @@ public class BasicNetworkConfigs { | ... | @@ -83,6 +83,7 @@ public class BasicNetworkConfigs { |
| 83 | return new BasicLinkConfig(); | 83 | return new BasicLinkConfig(); |
| 84 | } | 84 | } |
| 85 | }, | 85 | }, |
| 86 | + // TODO move this optical specific configuration out to optical app | ||
| 86 | new ConfigFactory<ConnectPoint, OpticalPortConfig>(CONNECT_POINT_SUBJECT_FACTORY, | 87 | new ConfigFactory<ConnectPoint, OpticalPortConfig>(CONNECT_POINT_SUBJECT_FACTORY, |
| 87 | OpticalPortConfig.class, | 88 | OpticalPortConfig.class, |
| 88 | "optical") { | 89 | "optical") { | ... | ... |
| 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
| 2 | 2 | ||
| 3 | +import json | ||
| 4 | + | ||
| 3 | from mininet.net import Mininet | 5 | from mininet.net import Mininet |
| 4 | from mininet.node import UserSwitch, DefaultController, RemoteController, Host | 6 | from mininet.node import UserSwitch, DefaultController, RemoteController, Host |
| 5 | from mininet.topo import Topo | 7 | from mininet.topo import Topo |
| 6 | -from mininet.log import setLogLevel, info | 8 | +from mininet.log import setLogLevel, info, error, warn |
| 7 | from mininet.cli import CLI | 9 | from mininet.cli import CLI |
| 8 | from mininet.link import OVSIntf | 10 | from mininet.link import OVSIntf |
| 11 | +from mininet.util import quietRun | ||
| 9 | 12 | ||
| 10 | from opticalUtils import LINCSwitch, LINCLink | 13 | from opticalUtils import LINCSwitch, LINCLink |
| 11 | 14 | ||
| ... | @@ -33,6 +36,7 @@ class Domain(object): | ... | @@ -33,6 +36,7 @@ class Domain(object): |
| 33 | self.__ctrls[name] = args if args else {} | 36 | self.__ctrls[name] = args if args else {} |
| 34 | return name | 37 | return name |
| 35 | 38 | ||
| 39 | + # Note: This method will return the name of the swich, not the switch object | ||
| 36 | def addSwitch(self, name, **args): | 40 | def addSwitch(self, name, **args): |
| 37 | self.__switches[name] = args if args else {} | 41 | self.__switches[name] = args if args else {} |
| 38 | return name | 42 | return name |
| ... | @@ -90,10 +94,12 @@ class OpticalDomain(Domain): | ... | @@ -90,10 +94,12 @@ class OpticalDomain(Domain): |
| 90 | oean = { "optical.regens": 0 } | 94 | oean = { "optical.regens": 0 } |
| 91 | self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch) | 95 | self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch) |
| 92 | 96 | ||
| 97 | + # ROADM port number OE"1" -> OE'2' = "1"'2'00 | ||
| 98 | + # leaving port number up to 100 open for use by Och port | ||
| 93 | an = { "durable": "true" } | 99 | an = { "durable": "true" } |
| 94 | - self.addLink('OE1', 'OE2', port1=50, port2=30, annotations=an, cls=LINCLink) | 100 | + self.addLink('OE1', 'OE2', port1=1200, port2=2100, annotations=an, cls=LINCLink) |
| 95 | - self.addLink('OE2', 'OE3', port1=50, port2=30, annotations=an, cls=LINCLink) | 101 | + self.addLink('OE2', 'OE3', port1=2300, port2=3200, annotations=an, cls=LINCLink) |
| 96 | - self.addLink('OE3', 'OE1', port1=50, port2=30, annotations=an, cls=LINCLink) | 102 | + self.addLink('OE3', 'OE1', port1=3100, port2=1300, annotations=an, cls=LINCLink) |
| 97 | 103 | ||
| 98 | class FabricDomain(Domain): | 104 | class FabricDomain(Domain): |
| 99 | """ | 105 | """ |
| ... | @@ -139,10 +145,11 @@ class FabricDomain(Domain): | ... | @@ -139,10 +145,11 @@ class FabricDomain(Domain): |
| 139 | domains to the core. name: the UserSwitch to connect the OVS to. | 145 | domains to the core. name: the UserSwitch to connect the OVS to. |
| 140 | """ | 146 | """ |
| 141 | self.__tether = self.addSwitch(tname, dpid=tdpid) | 147 | self.__tether = self.addSwitch(tname, dpid=tdpid) |
| 148 | + # Note: OVS port number '1' reserved for port facing the fabric | ||
| 142 | self.addLink(tname, name, port1=1) | 149 | self.addLink(tname, name, port1=1) |
| 143 | 150 | ||
| 144 | def getTether(self): | 151 | def getTether(self): |
| 145 | - """ get connection point of this fabric to the core """ | 152 | + """ get the switch name of this fabric facing the core """ |
| 146 | return self.__tether | 153 | return self.__tether |
| 147 | 154 | ||
| 148 | 155 | ||
| ... | @@ -157,10 +164,6 @@ class IpHost(Host): | ... | @@ -157,10 +164,6 @@ class IpHost(Host): |
| 157 | self.cmd(mtu) | 164 | self.cmd(mtu) |
| 158 | self.cmd('ip route add default via %s' % self.gateway) | 165 | self.cmd('ip route add default via %s' % self.gateway) |
| 159 | 166 | ||
| 160 | -# fixed port numbers for attachment points (APs) between CORD and metro domains | ||
| 161 | -OVS_AP=2 | ||
| 162 | -OE_AP=10 | ||
| 163 | - | ||
| 164 | def setup(argv): | 167 | def setup(argv): |
| 165 | domains = [] | 168 | domains = [] |
| 166 | ctlsets = sys.argv[1:] | 169 | ctlsets = sys.argv[1:] |
| ... | @@ -180,6 +183,16 @@ def setup(argv): | ... | @@ -180,6 +183,16 @@ def setup(argv): |
| 180 | for j in range (len(ctls)): | 183 | for j in range (len(ctls)): |
| 181 | f.addController('c%s%s' % (i,j), controller=RemoteController, ip=ctls[j]) | 184 | f.addController('c%s%s' % (i,j), controller=RemoteController, ip=ctls[j]) |
| 182 | 185 | ||
| 186 | + # netcfg for each domains | ||
| 187 | + # Note: Separate netcfg for domain0 is created in opticalUtils | ||
| 188 | + domainCfgs = [] | ||
| 189 | + for i in range (0,len(ctlsets)): | ||
| 190 | + cfg = {} | ||
| 191 | + cfg['devices'] = {} | ||
| 192 | + cfg['ports'] = {} | ||
| 193 | + cfg['links'] = {} | ||
| 194 | + domainCfgs.append(cfg) | ||
| 195 | + | ||
| 183 | # make/setup Mininet object | 196 | # make/setup Mininet object |
| 184 | net = Mininet() | 197 | net = Mininet() |
| 185 | for d in domains: | 198 | for d in domains: |
| ... | @@ -187,10 +200,19 @@ def setup(argv): | ... | @@ -187,10 +200,19 @@ def setup(argv): |
| 187 | d.injectInto(net) | 200 | d.injectInto(net) |
| 188 | 201 | ||
| 189 | # connect COs to core - sort of hard-wired at this moment | 202 | # connect COs to core - sort of hard-wired at this moment |
| 203 | + # adding cross-connect links | ||
| 190 | for i in range(1,len(domains)): | 204 | for i in range(1,len(domains)): |
| 191 | - an = { "bandwidth": 100000, "durable": "true" } | 205 | + # add 10 cross-connect links between domains |
| 192 | - net.addLink(domains[i].getTether(), d0.getSwitches('OE%s' % i), | 206 | + xcPortNo=2 |
| 193 | - port1=OVS_AP, port2=OE_AP, speed=10000, annotations=an, cls=LINCLink) | 207 | + ochPortNo=10 |
| 208 | + for j in range(0, 10): | ||
| 209 | + an = { "bandwidth": 10, "durable": "true" } | ||
| 210 | + net.addLink(domains[i].getTether(), d0.getSwitches('OE%s' % i), | ||
| 211 | + port1=xcPortNo+j, port2=ochPortNo+j, speed=10000, annotations=an, cls=LINCLink) | ||
| 212 | + | ||
| 213 | + xcId = 'of:' + domains[i].getSwitches(name=domains[i].getTether()).dpid + '/' + str(xcPortNo+j) | ||
| 214 | + ochId = 'of:' + d0.getSwitches('OE%s' % i).dpid + '/' + str(ochPortNo+j) | ||
| 215 | + domainCfgs[i]['ports'][xcId] = {'cross-connect': {'remote': ochId}} | ||
| 194 | 216 | ||
| 195 | # fire everything up | 217 | # fire everything up |
| 196 | net.build() | 218 | net.build() |
| ... | @@ -203,6 +225,22 @@ def setup(argv): | ... | @@ -203,6 +225,22 @@ def setup(argv): |
| 203 | cfgnet.controllers = d0.getControllers() | 225 | cfgnet.controllers = d0.getControllers() |
| 204 | LINCSwitch.bootOE(cfgnet, d0.getSwitches()) | 226 | LINCSwitch.bootOE(cfgnet, d0.getSwitches()) |
| 205 | 227 | ||
| 228 | + # send netcfg json to each CO-ONOS | ||
| 229 | + for i in range(1,len(domains)): | ||
| 230 | + info('*** Pushing Topology.json to CO-ONOS %d\n' % i) | ||
| 231 | + filename = 'Topology%d.json' % i | ||
| 232 | + with open(filename, 'w') as outfile: | ||
| 233 | + json.dump(domainCfgs[i], outfile, indent=4, separators=(',', ': ')) | ||
| 234 | + | ||
| 235 | + output = quietRun('%s/tools/test/bin/onos-netcfg %s %s &'\ | ||
| 236 | + % (LINCSwitch.onosDir, | ||
| 237 | + domains[i].getControllers()[0].ip, | ||
| 238 | + filename), shell=True) | ||
| 239 | + # successful output contains the two characters '{}' | ||
| 240 | + # if there is more output than this, there is an issue | ||
| 241 | + if output.strip('{}'): | ||
| 242 | + warn('***WARNING: Could not push topology file to ONOS: %s\n' % output) | ||
| 243 | + | ||
| 206 | CLI(net) | 244 | CLI(net) |
| 207 | net.stop() | 245 | net.stop() |
| 208 | LINCSwitch.shutdownOE() | 246 | LINCSwitch.shutdownOE() | ... | ... |
| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | ''' | 3 | ''' |
| 4 | Notes: | 4 | Notes: |
| 5 | 5 | ||
| 6 | -This file contains classes and methods useful for integrating LincOE with Mininet, | 6 | +This file contains classes and methods useful for integrating LincOE with Mininet, |
| 7 | such as startOE, stopOE, LINCLink, and OpticalSwitch | 7 | such as startOE, stopOE, LINCLink, and OpticalSwitch |
| 8 | 8 | ||
| 9 | - $ONOS_ROOT ust be set | 9 | - $ONOS_ROOT ust be set |
| ... | @@ -24,7 +24,7 @@ such as startOE, stopOE, LINCLink, and OpticalSwitch | ... | @@ -24,7 +24,7 @@ such as startOE, stopOE, LINCLink, and OpticalSwitch |
| 24 | ------------ | 24 | ------------ |
| 25 | - import LINCLink and OpticalSwitch from this module | 25 | - import LINCLink and OpticalSwitch from this module |
| 26 | - import startOE and stopOE from this module | 26 | - import startOE and stopOE from this module |
| 27 | - - create topology as you would a normal topology. when | 27 | + - create topology as you would a normal topology. when |
| 28 | to an optical switch with topo.addLink, always specify cls=LINCLink | 28 | to an optical switch with topo.addLink, always specify cls=LINCLink |
| 29 | - when creating an optical switch, use cls=OpticalSwitch in topo.addSwitch | 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 | 30 | - for annotations on links and switches, a dictionary must be passed in as |
| ... | @@ -41,13 +41,13 @@ we need to add another object to Mininet that contains all of the json object | ... | @@ -41,13 +41,13 @@ 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 | 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 | 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 | 43 | in the Mininet class object. We may also change the default switch class to add |
| 44 | -it's tap interfaces from lincOE during startup. The start() method for mininet would | 44 | +it's tap interfaces from lincOE during startup. The start() method for mininet would |
| 45 | grab all of the information from these switches and links, write configuration files | 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 | 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 | 47 | switch. The new start() method for each switch would parse through the sys.config |
| 48 | -file that was created and find the tap interface it needs to connect to, similar | 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 | 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 | 50 | +switches have been started, the new Mininet start() method should also push the |
| 51 | Topology configuration file to ONOS. | 51 | Topology configuration file to ONOS. |
| 52 | 52 | ||
| 53 | ''' | 53 | ''' |
| ... | @@ -119,7 +119,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -119,7 +119,7 @@ class LINCSwitch(OpticalSwitch): |
| 119 | if dpid: | 119 | if dpid: |
| 120 | dpids_to_ids[dpid.group().replace(':', '')] = switch_id | 120 | dpids_to_ids[dpid.group().replace(':', '')] = switch_id |
| 121 | switch_id += 1 | 121 | switch_id += 1 |
| 122 | - return dpids_to_ids | 122 | + return dpids_to_ids |
| 123 | except: | 123 | except: |
| 124 | print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info()) | 124 | print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info()) |
| 125 | fd.close() | 125 | fd.close() |
| ... | @@ -287,8 +287,8 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -287,8 +287,8 @@ class LINCSwitch(OpticalSwitch): |
| 287 | with open("crossConnect.json", 'w') as fd: | 287 | with open("crossConnect.json", 'w') as fd: |
| 288 | json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': ')) | 288 | json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': ')) |
| 289 | info('*** Pushing crossConnect.json to ONOS\n') | 289 | info('*** Pushing crossConnect.json to ONOS\n') |
| 290 | - output = quietRun('%s/tools/test/bin/onos-topo-cfg %s\ | 290 | + output = quietRun('%s/tools/test/bin/onos-netcfg %s\ |
| 291 | - Topology.json network/configuration/' % (self.onosDir, self.controllers[ 0 ].ip), shell=True) | 291 | + Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True) |
| 292 | 292 | ||
| 293 | def stop_oe(self): | 293 | def stop_oe(self): |
| 294 | ''' | 294 | ''' |
| ... | @@ -388,7 +388,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -388,7 +388,7 @@ class LINCSwitch(OpticalSwitch): |
| 388 | json.dump(topoJSON, outfile, indent=4, separators=(',', ': ')) | 388 | json.dump(topoJSON, outfile, indent=4, separators=(',', ': ')) |
| 389 | 389 | ||
| 390 | info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n') | 390 | info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file (no oecfg) \n') |
| 391 | - | 391 | + |
| 392 | topoConfigJson = {} | 392 | topoConfigJson = {} |
| 393 | 393 | ||
| 394 | topoConfigJson["switchConfig"] = LINCSwitch.getSwitchConfig(net.switches) | 394 | topoConfigJson["switchConfig"] = LINCSwitch.getSwitchConfig(net.switches) |
| ... | @@ -455,21 +455,20 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -455,21 +455,20 @@ class LINCSwitch(OpticalSwitch): |
| 455 | opener.open(url) | 455 | opener.open(url) |
| 456 | urllib2.install_opener(opener) | 456 | urllib2.install_opener(opener) |
| 457 | # focus on just checking the state of devices we're interested in | 457 | # focus on just checking the state of devices we're interested in |
| 458 | - devlist = map( lambda x: x['uri'], devices ) | 458 | + # expected devices availability map |
| 459 | + devMap = dict.fromkeys(map( lambda x: x['uri'], devices ), False) | ||
| 459 | while True: | 460 | while True: |
| 460 | response = json.load(urllib2.urlopen(url)) | 461 | response = json.load(urllib2.urlopen(url)) |
| 461 | devs = response.get('devices') | 462 | devs = response.get('devices') |
| 462 | 463 | ||
| 463 | - # Wait for all devices to be registered. There is a chance that this is only a subgraph. | 464 | + # update availability map |
| 464 | - if (len(devices) == len(devs)): | 465 | + for d in devs: |
| 466 | + if devMap.has_key(d['id']): | ||
| 467 | + devMap[d['id']] = d['available'] | ||
| 465 | 468 | ||
| 466 | - # Wait for all devices to available | 469 | + # Check if all devices we're interested became available |
| 467 | - available = True | 470 | + if all(devMap.viewvalues()): |
| 468 | - for d in devs: | 471 | + break; |
| 469 | - if d['id'] in devlist: | ||
| 470 | - available &= d['available'] | ||
| 471 | - if available: | ||
| 472 | - break | ||
| 473 | 472 | ||
| 474 | if (time >= TIMEOUT): | 473 | if (time >= TIMEOUT): |
| 475 | error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT) | 474 | error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT) |
| ... | @@ -480,7 +479,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -480,7 +479,7 @@ class LINCSwitch(OpticalSwitch): |
| 480 | 479 | ||
| 481 | info('*** Pushing Topology.json to ONOS\n') | 480 | info('*** Pushing Topology.json to ONOS\n') |
| 482 | for index in range(len(LINCSwitch.controllers)): | 481 | for index in range(len(LINCSwitch.controllers)): |
| 483 | - output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json network/configuration/ &'\ | 482 | + output = quietRun('%s/tools/test/bin/onos-netcfg %s Topology.json &'\ |
| 484 | % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True) | 483 | % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True) |
| 485 | # successful output contains the two characters '{}' | 484 | # successful output contains the two characters '{}' |
| 486 | # if there is more output than this, there is an issue | 485 | # if there is more output than this, there is an issue |
| ... | @@ -539,7 +538,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -539,7 +538,7 @@ class LINCSwitch(OpticalSwitch): |
| 539 | @staticmethod | 538 | @staticmethod |
| 540 | def getSwitchConfig(switches): | 539 | def getSwitchConfig(switches): |
| 541 | switchConfig = [] | 540 | switchConfig = [] |
| 542 | - | 541 | + |
| 543 | # Iterate through all switches and convert the ROADM switches to linc-oe format | 542 | # Iterate through all switches and convert the ROADM switches to linc-oe format |
| 544 | for switch in switches: | 543 | for switch in switches: |
| 545 | if isinstance(switch, LINCSwitch): | 544 | if isinstance(switch, LINCSwitch): |
| ... | @@ -566,7 +565,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -566,7 +565,7 @@ class LINCSwitch(OpticalSwitch): |
| 566 | @staticmethod | 565 | @staticmethod |
| 567 | def getLinkConfig(links): | 566 | def getLinkConfig(links): |
| 568 | linkConfig = [] | 567 | linkConfig = [] |
| 569 | - | 568 | + |
| 570 | # Iterate through all non-edge links and convert them to linc-oe format | 569 | # Iterate through all non-edge links and convert them to linc-oe format |
| 571 | for link in links: | 570 | for link in links: |
| 572 | if isinstance(link, LINCLink): | 571 | if isinstance(link, LINCLink): |
| ... | @@ -581,7 +580,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -581,7 +580,7 @@ class LINCSwitch(OpticalSwitch): |
| 581 | params = {} | 580 | params = {} |
| 582 | params["nodeName1"] = link.intf1.node.name | 581 | params["nodeName1"] = link.intf1.node.name |
| 583 | params["nodeName2"] = link.intf2.node.name | 582 | params["nodeName2"] = link.intf2.node.name |
| 584 | - | 583 | + |
| 585 | params["port1"] = link.port1 | 584 | params["port1"] = link.port1 |
| 586 | params["port2"] = link.port2 | 585 | params["port2"] = link.port2 |
| 587 | 586 | ||
| ... | @@ -609,7 +608,7 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -609,7 +608,7 @@ class LINCSwitch(OpticalSwitch): |
| 609 | for link in net.links: | 608 | for link in net.links: |
| 610 | if isinstance(link, LINCLink) and link.isCrossConnect(): | 609 | if isinstance(link, LINCLink) and link.isCrossConnect(): |
| 611 | tapCount += 1 | 610 | tapCount += 1 |
| 612 | - | 611 | + |
| 613 | while True: | 612 | while True: |
| 614 | # tapCount can be less than the actual number of taps if the optical network | 613 | # tapCount can be less than the actual number of taps if the optical network |
| 615 | # is a subgraph of a larger multidomain network. | 614 | # is a subgraph of a larger multidomain network. |
| ... | @@ -722,7 +721,7 @@ class LINCLink(Link): | ... | @@ -722,7 +721,7 @@ class LINCLink(Link): |
| 722 | node1.crossConnects.append(self) | 721 | node1.crossConnects.append(self) |
| 723 | else: | 722 | else: |
| 724 | cls1 = Intf | 723 | cls1 = Intf |
| 725 | - # bad hack to stop error message from appearing when we try to set up intf in a packet switch, | 724 | + # bad hack to stop error message from appearing when we try to set up intf in a packet switch, |
| 726 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up | 725 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up |
| 727 | intfName1 = 'lo' | 726 | intfName1 = 'lo' |
| 728 | if isinstance(node2, LINCSwitch): | 727 | if isinstance(node2, LINCSwitch): | ... | ... |
-
Please register or login to post a comment