blob: 37acfc5d72aca96abee1bdb02416db5a8ee16e7c [file] [log] [blame]
Yuta HIGUCHI991caf92017-06-02 11:12:03 -07001#!/usr/bin/env python
2
3import json
4
5from mininet.net import Mininet
6from mininet.node import UserSwitch, DefaultController, RemoteController, Host
7from mininet.topo import Topo
8from mininet.log import setLogLevel, info, error, warn
9from mininet.cli import CLI
10from mininet.link import OVSIntf
11from mininet.util import quietRun
12
13from opticalUtils import LINCSwitch, LINCLink
14
15"""XXX: separate out into domainlib"""
16class Domain(object):
17 """
18 A container for switch, host, link, and controller information to be dumped
19 into the Mininet mid-level API.
20 """
21
22 def __init__ (self, did=0):
23 # each Domain has a numeric ID for sanity/convenience
24 self.__dId = did
25
26 # information about network elements - for calling the "mid-level" APIs
27 self.__ctrls = {}
28 self.__switches = {}
29 self.__hosts = {}
30 self.__links = {}
31 # maps of devices, hosts, and controller names to actual objects
32 self.__smap = {}
33 self.__hmap = {}
34 self.__cmap = {}
35
36 def addController(self, name, **args):
37 self.__ctrls[name] = args if args else {}
38 return name
39
40 # Note: This method will return the name of the swich, not the switch object
41 def addSwitch(self, name, **args):
42 self.__switches[name] = args if args else {}
43 return name
44
45 def addHost(self, name, **args):
46 self.__hosts[name] = args if args else {}
47 return name
48
49 def addLink(self, src, dst, **args):
50 self.__links[(src, dst)] = args if args else {}
51 return (src, dst)
52
53 def getId( self):
54 return self.__dId
55
56 def getControllers(self, name=None):
57 return self.__cmap.values() if not name else self.__cmap.get(name)
58
59 def getSwitches(self, name=None):
60 return self.__smap.values() if not name else self.__smap.get(name)
61
62 def getHosts(self, name=None):
63 return self.__hmap.values() if not name else self.__hmap.get(name)
64
65 def injectInto(self, net):
66 """ Adds available topology info to a supplied Mininet object. """
67 # add switches, hosts, then links to mininet object
68 for sw, args in self.__switches.iteritems():
69 self.__smap[sw] = net.addSwitch(sw, **args)
70 for h, args in self.__hosts.iteritems():
71 self.__hmap[h] = net.addHost(h, **args)
72 for l, args in self.__links.iteritems():
73 src = self.__smap.get(l[0])
74 dst = self.__smap.get(l[1])
75 net.addLink(src if src else self.__hmap.get(l[0]),
76 dst if dst else self.__hmap.get(l[1]), **args)
77 # then controllers
78 for c, args in self.__ctrls.iteritems():
79 self.__cmap[c] = net.addController(c, **args)
80
81 def start(self):
82 """ starts the switches with the correct controller. """
83 map(lambda c: c.start(), self.__cmap.values())
84 map(lambda s: s.start(self.__cmap.values()), self.__smap.values())
85
86 def build(self, *args):
87 """ override for custom topology, similar to Topo """
88 pass
89
90
91class OpticalDomain(Domain):
92 """ An emulated optical metro core. It is Domain 0. """
93 def build(self):
94 for i in range (1,4):
95 oean = { "optical.regens": 0 }
96 self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch)
97
98 # ROADM port number OE"1" -> OE'2' = "1"'2'00
99 # leaving port number up to 100 open for use by Och port
100 an = { "durable": "true" }
101 self.addLink('OE1', 'OE2', port1=1200, port2=2100, annotations=an, cls=LINCLink)
102 self.addLink('OE2', 'OE3', port1=2300, port2=3200, annotations=an, cls=LINCLink)
103 self.addLink('OE3', 'OE1', port1=3100, port2=1300, annotations=an, cls=LINCLink)
104
105class FabricDomain(Domain):
106 """
107 An emulated CO fabric, which is basically a K(n,m) bipartite graph.
108
109 Each FabricDomain should be given a unique Domain ID (did) to ensure unique
110 names and addressing.
111 """
112 def __init__(self, did):
113 Domain.__init__(self, did)
114
115 def build(self):
116
117 # CpQD switches and OVS b/c brokenness.
118 sw1 = self.addSwitch('cpqd%s1' % self.getId(), cls=UserSwitch, dpopts='--no-local-port')
119 sw2 = self.addSwitch('ovs%s01' % self.getId())
120
121 # make sw2 the tether point
122 self.__tether = sw2
123
124 # sw1-sw2-> to metro core
125 self.addLink(sw1, sw2, port2=1)
126
127 # h-sw1
128 h = self.addHost('h%s' % self.getId(), cls=IpHost, ip='10.0.0.%s/24' % self.getId(),
129 gateway='10.0.0.254')
130 self.addLink(h, sw1)
131
132 def getTether(self):
133 """ get the switch name of this fabric facing the core """
134 return self.__tether
135
136
137class IpHost(Host):
138 def __init__(self, name, gateway, *args, **kwargs):
139 super(IpHost, self).__init__(name, *args, **kwargs)
140 self.gateway = gateway
141
142 def config(self, **kwargs):
143 Host.config(self, **kwargs)
144 mtu = "ifconfig "+self.name+"-eth0 mtu 1490"
145 self.cmd(mtu)
146 self.cmd('ip route add default via %s' % self.gateway)
147
148def setup(argv):
149 domains = []
150 ctlsets = sys.argv[1:]
151
152 # the controllers for the optical domain
153 d0 = OpticalDomain()
154 f0 = FabricDomain(1)
155 f1 = FabricDomain(2)
156 domains.extend([ d0, f0, f1 ])
157
158 for i in range(len(domains)):
159 ctls = ctlsets[i].split(',')
160 for c in range(len(ctls)):
161 domains[i].addController('c%s%s' % (i, c), controller=RemoteController, ip=ctls[c])
162
163 # netcfg for each domains
164 # Note: Separate netcfg for domain0 is created in opticalUtils
165 domainCfgs = []
166 for i in range (0,len(ctlsets)):
167 cfg = {}
168 cfg['devices'] = {}
169 cfg['ports'] = {}
170 cfg['links'] = {}
171 domainCfgs.append(cfg)
172
173 # make/setup Mininet object
174 net = Mininet()
175 for d in domains:
176 d.build()
177 d.injectInto(net)
178
179 # connect COs to core - sort of hard-wired at this moment
180 # adding cross-connect links
181 for i in range(1,len(domains)):
182 # add 10 cross-connect links between domains
183 xcPortNo=2
184 ochPortNo=10
185
186 an = { "bandwidth": 10, "durable": "true" }
187 net.addLink(domains[i].getTether(), d0.getSwitches('OE%s' % i),
188 port1=xcPortNo, port2=ochPortNo, speed=10000, annotations=an, cls=LINCLink)
189
190 xcId = 'of:' + domains[i].getSwitches(name=domains[i].getTether()).dpid + '/' + str(xcPortNo)
191 ochId = 'of:' + d0.getSwitches('OE%s' % i).dpid + '/' + str(ochPortNo)
192 domainCfgs[i]['ports'][xcId] = {'cross-connect': {'remote': ochId}}
193
194 # fire everything up
195 net.build()
196 map(lambda x: x.start(), domains)
197
198 # create a minimal copy of the network for configuring LINC.
199 cfgnet = Mininet()
200 cfgnet.switches = net.switches
201 cfgnet.links = net.links
202 cfgnet.controllers = d0.getControllers()
203 LINCSwitch.bootOE(cfgnet, d0.getSwitches())
204
205 # send netcfg json to each CO-ONOS
206 for i in range(1,len(domains)):
207 info('*** Pushing Topology.json to CO-ONOS %d\n' % i)
208 filename = 'Topology%d.json' % i
209 with open(filename, 'w') as outfile:
210 json.dump(domainCfgs[i], outfile, indent=4, separators=(',', ': '))
211
Devin Lim0d944e22017-06-23 15:17:53 -0700212 output = quietRun('%s/onos-netcfg %s %s &'\
213 % (LINCSwitch.runPackDir,
Yuta HIGUCHI991caf92017-06-02 11:12:03 -0700214 domains[i].getControllers()[0].ip,
215 filename), shell=True)
216 # successful output contains the two characters '{}'
217 # if there is more output than this, there is an issue
218 if output.strip('{}'):
219 warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
220
221 CLI(net)
222 net.stop()
223 LINCSwitch.shutdownOE()
224
225if __name__ == '__main__':
226 setLogLevel('info')
227 import sys
228 if len(sys.argv) < 4:
229 print ("Usage: sudo -E ./ectest.py ctl-set1 ... ctl-set4\n\n",
230 "Where ctl-set are comma-separated controller IP's")
231 else:
232 setup(sys.argv)