Ayaka Koshibe
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
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)