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