HIGUCHI Yuta
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
...@@ -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):
......