Committed by
Yuta HIGUCHI
Script for emulating a four-domain metro network.
ref: https://wiki.onosproject.org/display/ONOS/Metro+Network+Emulation Change-Id: Icb8dd7a12587398af49e9acea23a7cb85fb84b88
Showing
1 changed file
with
217 additions
and
0 deletions
tools/test/topos/metro.py
0 → 100755
1 | +#!/usr/bin/env python | ||
2 | + | ||
3 | +from mininet.net import Mininet | ||
4 | +from mininet.node import UserSwitch, DefaultController, RemoteController, Host | ||
5 | +from mininet.topo import Topo | ||
6 | +from mininet.log import setLogLevel, info | ||
7 | +from mininet.cli import CLI | ||
8 | +from mininet.link import OVSIntf | ||
9 | + | ||
10 | +from opticalUtils import LINCSwitch, LINCLink | ||
11 | + | ||
12 | +class Domain(object): | ||
13 | + """ | ||
14 | + A container for switch, host, link, and controller information to be dumped | ||
15 | + into the Mininet mid-level API. | ||
16 | + """ | ||
17 | + | ||
18 | + def __init__ (self, did=0): | ||
19 | + # each Domain has a numeric ID for sanity/convenience | ||
20 | + self.__dId = did | ||
21 | + | ||
22 | + # information about network elements - for calling the "mid-level" APIs | ||
23 | + self.__ctrls = {} | ||
24 | + self.__switches = {} | ||
25 | + self.__hosts = {} | ||
26 | + self.__links = {} | ||
27 | + # maps of devices, hosts, and controller names to actual objects | ||
28 | + self.__smap = {} | ||
29 | + self.__hmap = {} | ||
30 | + self.__cmap = {} | ||
31 | + | ||
32 | + def addController(self, name, **args): | ||
33 | + self.__ctrls[name] = args if args else {} | ||
34 | + return name | ||
35 | + | ||
36 | + def addSwitch(self, name, **args): | ||
37 | + self.__switches[name] = args if args else {} | ||
38 | + return name | ||
39 | + | ||
40 | + def addHost(self, name, **args): | ||
41 | + self.__hosts[name] = args if args else {} | ||
42 | + return name | ||
43 | + | ||
44 | + def addLink(self, src, dst, **args): | ||
45 | + self.__links[(src, dst)] = args if args else {} | ||
46 | + return (src, dst) | ||
47 | + | ||
48 | + def getId( self): | ||
49 | + return self.__dId | ||
50 | + | ||
51 | + def getControllers(self, name=None): | ||
52 | + return self.__cmap.values() if not name else self.__cmap.get(name) | ||
53 | + | ||
54 | + def getSwitches(self, name=None): | ||
55 | + return self.__smap.values() if not name else self.__smap.get(name) | ||
56 | + | ||
57 | + def getHosts(self, name=None): | ||
58 | + return self.__hmap.values() if not name else self.__hmap.get(name) | ||
59 | + | ||
60 | + def injectInto(self, net): | ||
61 | + """ Adds available topology info to a supplied Mininet object. """ | ||
62 | + # add switches, hosts, then links to mininet object | ||
63 | + for sw, args in self.__switches.iteritems(): | ||
64 | + self.__smap[sw] = net.addSwitch(sw, **args) | ||
65 | + for h, args in self.__hosts.iteritems(): | ||
66 | + self.__hmap[h] = net.addHost(h, **args) | ||
67 | + for l, args in self.__links.iteritems(): | ||
68 | + src = self.__smap.get(l[0]) | ||
69 | + dst = self.__smap.get(l[1]) | ||
70 | + net.addLink(src if src else self.__hmap.get(l[0]), | ||
71 | + dst if dst else self.__hmap.get(l[1]), **args) | ||
72 | + # then controllers | ||
73 | + for c, args in self.__ctrls.iteritems(): | ||
74 | + self.__cmap[c] = net.addController(c, **args) | ||
75 | + | ||
76 | + def start(self): | ||
77 | + """ starts the switches with the correct controller. """ | ||
78 | + map(lambda c: c.start(), self.__cmap.values()) | ||
79 | + map(lambda s: s.start(self.__cmap.values()), self.__smap.values()) | ||
80 | + | ||
81 | + def build(self, *args): | ||
82 | + """ override for custom topology, similar to Topo """ | ||
83 | + pass | ||
84 | + | ||
85 | + | ||
86 | +class OpticalDomain(Domain): | ||
87 | + """ An emulated optical metro core. It is Domain 0. """ | ||
88 | + def build(self): | ||
89 | + oean = { "optical.regens": 0 } | ||
90 | + for i in range (1,4): | ||
91 | + self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch) | ||
92 | + | ||
93 | + an = { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } | ||
94 | + self.addLink('OE1', 'OE2', port1=50, port2=30, annotations=an, cls=LINCLink) | ||
95 | + self.addLink('OE2', 'OE3', port1=50, port2=30, annotations=an, cls=LINCLink) | ||
96 | + self.addLink('OE3', 'OE1', port1=50, port2=30, annotations=an, cls=LINCLink) | ||
97 | + | ||
98 | +class FabricDomain(Domain): | ||
99 | + """ | ||
100 | + An emulated CO fabric, which is basically a K(n,m) bipartite graph. | ||
101 | + | ||
102 | + Each FabricDomain should be given a unique Domain ID (did) to ensure unique | ||
103 | + names and addressing. | ||
104 | + """ | ||
105 | + def __init__(self, did): | ||
106 | + Domain.__init__(self, did) | ||
107 | + | ||
108 | + def build(self, n=2, m=3, f=2): | ||
109 | + # K(n,m) in bipartite graph | ||
110 | + l_nsw=[] | ||
111 | + l_msw=[] | ||
112 | + | ||
113 | + # create n spine switches | ||
114 | + for sw in range(n): | ||
115 | + l_nsw.append(self.addSwitch('swn%s%s' % (self.getId(), sw+1), cls=UserSwitch)) | ||
116 | + | ||
117 | + # create connection point to optical core (a leaf switch) | ||
118 | + tsw = self.addSwitch('swm%s01' % self.getId(), cls=UserSwitch) | ||
119 | + self.addTether(tsw, 'sw000%s' % self.getId(), '0000ffffffff000%s' % self.getId()) | ||
120 | + l_msw.append(tsw) | ||
121 | + | ||
122 | + # attach f hosts to last m-1 leaves | ||
123 | + for sw in range(1, m): | ||
124 | + msw = self.addSwitch('swm%s0%s' % (self.getId(), sw+1), cls=UserSwitch) | ||
125 | + l_msw.append(msw) | ||
126 | + for h in range(f): | ||
127 | + host = self.addHost('h%s%s' % (self.getId(), sw * f+h+1), cls=IpHost, | ||
128 | + ip='10.0.%s.%s/24' % ((self.getId()+sw+1), (f+1)), | ||
129 | + gateway='10.0.%s.254' % (self.getId()+sw+1)) | ||
130 | + self.addLink(host, msw) | ||
131 | + # link up spines and leaves | ||
132 | + for nsw in l_nsw: | ||
133 | + for msw in l_msw: | ||
134 | + self.addLink(nsw, msw) | ||
135 | + | ||
136 | + def addTether(self, name, tname, tdpid): | ||
137 | + """ | ||
138 | + add an OVS with name 'tname' and dpid 'tdpid' for connecting fabric | ||
139 | + domains to the core. name: the UserSwitch to connect the OVS to. | ||
140 | + """ | ||
141 | + self.__tether = self.addSwitch(tname, dpid=tdpid) | ||
142 | + self.addLink(tname, name, port1=1) | ||
143 | + | ||
144 | + def getTether(self): | ||
145 | + """ get connection point of this fabric to the core """ | ||
146 | + return self.__tether | ||
147 | + | ||
148 | + | ||
149 | +class IpHost(Host): | ||
150 | + def __init__(self, name, gateway, *args, **kwargs): | ||
151 | + super(IpHost, self).__init__(name, *args, **kwargs) | ||
152 | + self.gateway = gateway | ||
153 | + | ||
154 | + def config(self, **kwargs): | ||
155 | + Host.config(self, **kwargs) | ||
156 | + mtu = "ifconfig "+self.name+"-eth0 mtu 1490" | ||
157 | + self.cmd(mtu) | ||
158 | + self.cmd('ip route add default via %s' % self.gateway) | ||
159 | + | ||
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): | ||
165 | + domains = [] | ||
166 | + ctlsets = sys.argv[1:] | ||
167 | + | ||
168 | + # the controllers for the optical domain | ||
169 | + d0 = OpticalDomain() | ||
170 | + domains.append(d0) | ||
171 | + ctls = ctlsets[0].split(',') | ||
172 | + for i in range (len(ctls)): | ||
173 | + d0.addController('c0%s' % i, controller=RemoteController, ip=ctls[i]) | ||
174 | + | ||
175 | + # the fabric domains - position 1 for domain 1, 2 for 2 ... | ||
176 | + for i in range (1,len(ctlsets)): | ||
177 | + f = FabricDomain(i) | ||
178 | + domains.append(f) | ||
179 | + ctls = ctlsets[i].split(',') | ||
180 | + for j in range (len(ctls)): | ||
181 | + f.addController('c%s%s' % (i,j), controller=RemoteController, ip=ctls[j]) | ||
182 | + | ||
183 | + # make/setup Mininet object | ||
184 | + net = Mininet() | ||
185 | + for d in domains: | ||
186 | + d.build() | ||
187 | + d.injectInto(net) | ||
188 | + | ||
189 | + # connect COs to core - sort of hard-wired at this moment | ||
190 | + for i in range(1,len(domains)): | ||
191 | + an = { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } | ||
192 | + net.addLink(domains[i].getTether(), d0.getSwitches('OE%s' % i), | ||
193 | + port1=OVS_AP, port2=OE_AP, speed=10000, annotations=an, cls=LINCLink) | ||
194 | + | ||
195 | + # fire everything up | ||
196 | + net.build() | ||
197 | + map(lambda x: x.start(), domains) | ||
198 | + | ||
199 | + # create a minimal copy of the network for configuring LINC. | ||
200 | + cfgnet = Mininet() | ||
201 | + cfgnet.switches = net.switches | ||
202 | + cfgnet.links = net.links | ||
203 | + cfgnet.controllers = d0.getControllers() | ||
204 | + LINCSwitch.bootOE(cfgnet, d0.getSwitches()) | ||
205 | + | ||
206 | + CLI(net) | ||
207 | + net.stop() | ||
208 | + LINCSwitch.shutdownOE() | ||
209 | + | ||
210 | +if __name__ == '__main__': | ||
211 | + setLogLevel('info') | ||
212 | + import sys | ||
213 | + if len(sys.argv) < 5: | ||
214 | + print ("Usage: sudo -E ./metro.py ctl-set1 ... ctl-set4\n\n", | ||
215 | + "Where ctl-set are comma-separated controller IP's") | ||
216 | + else: | ||
217 | + setup(sys.argv) |
-
Please register or login to post a comment