blob: 65a57bd1f8ff4a89b6ceae10b9fa33cb7f417b4c [file] [log] [blame]
Ayaka Koshibe4f23b0a2015-11-18 17:30:42 -08001#!/usr/bin/env python
2
3from mininet.net import Mininet
4from mininet.node import UserSwitch, DefaultController, RemoteController, Host
5from mininet.topo import Topo
6from mininet.log import setLogLevel, info
7from mininet.cli import CLI
8from mininet.link import OVSIntf
9
10from opticalUtils import LINCSwitch, LINCLink
11
12class 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
86class OpticalDomain(Domain):
87 """ An emulated optical metro core. It is Domain 0. """
88 def build(self):
Ayaka Koshibe4f23b0a2015-11-18 17:30:42 -080089 for i in range (1,4):
HIGUCHI Yuta099d75f2015-12-04 17:28:21 -080090 oean = { "optical.regens": 0 }
Ayaka Koshibe4f23b0a2015-11-18 17:30:42 -080091 self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch)
92
Marc De Leenheer32fc3d22015-12-15 21:36:39 -080093 an = { "durable": "true" }
Ayaka Koshibe4f23b0a2015-11-18 17:30:42 -080094 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
98class 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
149class 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
161OVS_AP=2
162OE_AP=10
163
164def 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)):
Marc De Leenheer32fc3d22015-12-15 21:36:39 -0800191 an = { "bandwidth": 100000, "durable": "true" }
Ayaka Koshibe4f23b0a2015-11-18 17:30:42 -0800192 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
210if __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)