blob: 814b6fbcb2aab1dab9061e74582b18bc7f75bb06 [file] [log] [blame]
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -07001#!/usr/bin/python
2import os
3import re
4from optparse import OptionParser
5from ipaddress import ip_network
You Wang68568b12019-03-04 11:49:57 -08006from mininet.node import RemoteController, OVSBridge, Host, OVSSwitch
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -07007from mininet.link import TCLink
8from mininet.log import setLogLevel
9from mininet.net import Mininet
10from mininet.topo import Topo
11from mininet.nodelib import NAT
12from mininet.cli import CLI
13
14from routinglib import BgpRouter, RoutedHost
15from trellislib import DhcpServer, TaggedRoutedHost, DualHomedRoutedHost, DualHomedTaggedRoutedHost, DhcpClient, Dhcp6Client, DhcpServer, Dhcp6Server, TrellisHost
16
You Wang68568b12019-03-04 11:49:57 -080017from bmv2 import ONOSBmv2Switch
18
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -070019# Parse command line options and dump results
20def parseOptions():
21 "Parse command line options"
22 parser = OptionParser()
23 parser.add_option( '--dhcp', dest='dhcp', type='int', default=0,
24 help='Configure hosts with dhcp or not' )
25 parser.add_option( '--routers', dest='routers', type='int', default=0,
26 help='Configure external routers or not in the topology' )
27 parser.add_option( '--ipv6', dest='ipv6', type='int', default=0,
28 help='Configure hosts with ipv6 or not' )
29 parser.add_option( '--ipv4', dest='ipv4', type='int', default=1,
30 help='Configure hosts with ipv4 or not' )
31 parser.add_option( '--onos-ip', dest='onosIp', type='str', default='',
You Wang68568b12019-03-04 11:49:57 -080032 help='IP address list of ONOS instances, separated by comma(,). Overrides --onos option' )
33 parser.add_option( '--switch', dest='switch', type='str', default='ovs',
34 help='Switch type: ovs, bmv2 (with fabric.p4)' )
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -070035
36 ( options, args ) = parser.parse_args()
37 return options, args
38
39opts, args = parseOptions()
40
You Wang68568b12019-03-04 11:49:57 -080041FABRIC_PIPECONF = "org.onosproject.pipelines.fabric"
42
43SWITCH_TO_PARAMS_DICT = {
44 "ovs": dict(cls=OVSSwitch),
45 "bmv2": dict(cls=ONOSBmv2Switch, pipeconf=FABRIC_PIPECONF)
46}
47if opts.switch not in SWITCH_TO_PARAMS_DICT:
48 raise Exception("Unknown switch type '%s'" % opts.switch)
49SWITCH_PARAMS = SWITCH_TO_PARAMS_DICT[opts.switch]
50
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -070051class ComcastLeafSpineFabric(Topo):
52
53 spines = dict()
54 leafs = dict()
55 hosts_dict = dict()
56
57 def createIpv4Hosts(self, dhcp):
58
59 h1 = self.addHost('h1v4', cls=TrellisHost,
60 mac='00:aa:00:00:00:01', ips=['10.1.0.1/24'],
61 gateway='10.1.0.254', dhcpClient=dhcp)
62 self.addLink(h1, self.leafs[0])
63 self.hosts_dict['h1v4'] = h1
64
65 h2 = self.addHost('h2v4', cls=TrellisHost,
66 mac='00:aa:00:00:01:01', ips=['10.1.10.1/24'],
67 gateway='10.1.10.254', dhcpClient=dhcp)
68 self.addLink(h2, self.leafs[0])
69 self.hosts_dict['h2v4'] = h2
70
71 h3 = self.addHost('h3v4', cls=TrellisHost,
72 mac='00:aa:00:00:00:02', ips=['10.2.0.1/24'],
73 gateway='10.2.0.254', dhcpClient=dhcp)
74 self.addLink(h3, self.leafs[1])
75 self.hosts_dict['h3v4'] = h3
76
77 h4 = self.addHost('h4v4', cls=TrellisHost,
78 mac='00:aa:00:00:00:03', ips=['10.2.30.1/24'],
79 gateway='10.2.30.254', dhcpClient=dhcp,
80 dualHomed=True)
81 self.addLink(h4, self.leafs[1])
82 self.addLink(h4, self.leafs[2])
83 self.hosts_dict['h4v4'] = h4
84
85 h5 = self.addHost('h5v4', cls=TrellisHost,
86 mac='00:aa:00:00:00:04', ips=['10.2.20.1/24'],
87 gateway='10.2.20.254', dhcpClient=dhcp, vlan=30,
88 dualHomed=True)
89 self.addLink(h5, self.leafs[1])
90 self.addLink(h5, self.leafs[2])
91 self.hosts_dict['h5v4'] = h5
92
93 h6 = self.addHost('h6v4', cls=TrellisHost,
94 mac='00:aa:00:00:00:05', ips=['10.2.10.1/24'],
95 gateway='10.2.10.254', dhcpClient=dhcp, vlan=20)
96 self.addLink(h6, self.leafs[2])
97 self.hosts_dict['h6v4'] = h6
98
99 h7 = self.addHost('h7v4', cls=TrellisHost,
100 mac='00:aa:00:00:01:05', ips=['10.2.40.1/24'],
101 gateway='10.2.40.254', dhcpClient=dhcp, vlan=40)
102 self.addLink(h7, self.leafs[2])
103 self.hosts_dict['h7v4'] = h7
104
105 h8 = self.addHost('h8v4', cls=TrellisHost,
106 mac='00:aa:00:00:00:06', ips=['10.3.0.1/24'],
107 gateway='10.3.0.254', dhcpClient=dhcp)
108 self.addLink(h8, self.leafs[3])
109 self.hosts_dict['h8v4'] = h8
110
111 h9 = self.addHost('h9v4', cls=TrellisHost,
112 mac='00:aa:00:00:00:07', ips=['10.3.10.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700113 gateway='10.3.10.254', dhcpClient=dhcp, vlan=50,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700114 dualHomed=True)
115 self.addLink(h9, self.leafs[3])
116 self.addLink(h9, self.leafs[4])
117 self.hosts_dict['h9v4'] = h9
118
119 h10 = self.addHost('h10v4', cls=TrellisHost,
120 mac='00:aa:00:00:00:08', ips=['10.3.30.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700121 gateway='10.3.30.254', dhcpClient=dhcp, vlan=60,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700122 dualHomed=True)
123 self.addLink(h10, self.leafs[3])
124 self.addLink(h10, self.leafs[4])
125 self.hosts_dict['h10v4'] = h10
126
127 h11 = self.addHost('h11v4', cls=TrellisHost,
128 mac='00:aa:00:00:00:0a', ips=['10.3.20.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700129 gateway='10.3.20.254', dhcpClient=dhcp, vlan=70)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700130 self.addLink(h11, self.leafs[4])
131 self.hosts_dict['h11v4'] = h11
132
133 h12 = self.addHost('h12v4', cls=TrellisHost,
134 mac='00:aa:00:00:02:01', ips=['10.5.10.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700135 gateway='10.5.10.254', dhcpClient=dhcp, vlan=80)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700136 self.addLink(h12, self.leafs[5])
137 self.hosts_dict['h12v4'] = h12
138
139 h13 = self.addHost('h13v4', cls=TrellisHost,
140 mac='00:aa:00:00:02:02', ips=['10.5.20.1/24'],
141 gateway='10.5.20.254', dhcpClient=dhcp)
142 self.addLink(h13, self.leafs[5])
143 self.hosts_dict['h13v4'] = h13
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700144 return
145
146 def createIpv6Hosts(self, dhcp):
147
148 h1 = self.addHost('h1v6', cls=TrellisHost,
149 mac='00:bb:00:00:00:01', ips=["1000::3fe/120"],
150 gateway='1000::3ff', dhcpClient=dhcp, ipv6=1)
151 self.addLink(h1, self.leafs[0])
152 self.hosts_dict['h1v6'] = h1
153
154 h2 = self.addHost('h2v6', cls=TrellisHost,
155 mac='00:bb:00:00:01:01', ips=['1001::3fe/120'],
156 gateway='1001::3ff', dhcpClient=dhcp, ipv6=1)
157 self.addLink(h2, self.leafs[0])
158 self.hosts_dict['h2v6'] = h2
159
160 h3 = self.addHost('h3v6', cls=TrellisHost,
161 mac='00:bb:00:00:00:02', ips=['1002::3fe/120'],
162 gateway='1002::3ff', dhcpClient=dhcp, ipv6=1)
163 self.addLink(h3, self.leafs[1])
164 self.hosts_dict['h3v6'] = h3
165
166 h4 = self.addHost('h4v6', cls=TrellisHost,
167 mac='00:bb:00:00:00:03', ips=['1003::3fe/120'],
168 gateway='1003::3ff', dhcpClient=dhcp, ipv6=1,
169 dualHomed=True)
170 self.addLink(h4, self.leafs[1])
171 self.addLink(h4, self.leafs[2])
172 self.hosts_dict['h4v6'] = h4
173
174 h5 = self.addHost('h5v6', cls=TrellisHost,
175 mac='00:bb:00:00:00:04', ips=['1004::3fe/120'],
176 gateway='1004::3ff', dhcpClient=False, ipv6=1,
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700177 vlan=121,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700178 dualHomed=True)
179 self.addLink(h5, self.leafs[1])
180 self.addLink(h5, self.leafs[2])
181 self.hosts_dict['h5v6'] = h5
182
183 h6 = self.addHost('h6v6', cls=TrellisHost,
184 mac='00:bb:00:00:00:05', ips=['1005::3fe/120'],
185 gateway='1005::3ff', dhcpClient=False, ipv6=1,
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700186 vlan=122)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700187 self.addLink(h6, self.leafs[2])
188 self.hosts_dict['h6v6'] = h6
189
190 h7 = self.addHost('h7v6', cls=TrellisHost,
191 mac='00:bb:00:00:01:05', ips=['1006::3fe/120'],
192 gateway='1006::3ff', dhcpClient=False, ipv6=1,
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700193 vlan=123)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700194 self.addLink(h7, self.leafs[2])
195 self.hosts_dict['h7v6'] = h7
196
197 h8 = self.addHost('h8v6', cls=TrellisHost,
198 mac='00:bb:00:00:00:06', ips=['1007::3fe/120'],
199 gateway='1007::3ff', dhcpClient=dhcp, ipv6=1)
200 self.addLink(h8, self.leafs[3])
201 self.hosts_dict['h8v6'] = h8
202
203 h9 = self.addHost('h9v6', cls=TrellisHost,
204 mac='00:bb:00:00:00:07', ips=['1008::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700205 gateway='1008::3ff', dhcpClient=False, vlan=124,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700206 dualHomed=True, ipv6=1)
207 self.addLink(h9, self.leafs[3])
208 self.addLink(h9, self.leafs[4])
209 self.hosts_dict['h9v6'] = h9
210
211 h10 = self.addHost('h10v6', cls=TrellisHost,
212 mac='00:bb:00:00:00:08', ips=['1009::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700213 gateway='1009::3ff', dhcpClient=False, vlan=125,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700214 dualHomed=True, ipv6=1)
215 self.addLink(h10, self.leafs[3])
216 self.addLink(h10, self.leafs[4])
217 self.hosts_dict['h10v6'] = h10
218
219 h11 = self.addHost('h11v6', cls=TrellisHost,
220 mac='00:bb:00:00:00:0a', ips=['1010::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700221 gateway='1010::3ff', dhcpClient=False, vlan=126,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700222 ipv6=1)
223 self.addLink(h11, self.leafs[4])
224 self.hosts_dict['h11v6'] = h11
225
226 h12 = self.addHost('h12v6', cls=TrellisHost,
227 mac='00:bb:00:00:01:0a', ips=['1011::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700228 gateway='1011::3ff', dhcpClient=False, vlan=127,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700229 ipv6=1)
230 self.addLink(h12, self.leafs[5])
231 self.hosts_dict['h12v6'] = h12
232
233 h13 = self.addHost('h13v6', cls=TrellisHost,
234 mac='00:bb:00:00:02:0a', ips=['1012::3fe/120'],
235 gateway='1012::3ff', dhcpClient=dhcp, ipv6=1)
236 self.addLink(h13, self.leafs[5])
237 self.hosts_dict['h13v6'] = h13
238
239 return
240
241 '''
242 Creates the HAGG topology employed by Comcast.
243 '''
244 def __init__(self, dhcp=False, routers=False, ipv4=False, ipv6=False, **opts):
245 Topo.__init__(self, **opts)
246
247 linkopts = dict( bw=10 )
248
249 spine = 4
250 leaf = 6
251
252 # Create spine switches
253 for s in range(spine):
You Wang68568b12019-03-04 11:49:57 -0800254 self.spines[s] = self.addSwitch( 'spine10%s' % (s + 1),
255 dpid = "00000000010%s" % (s + 1),
256 **SWITCH_PARAMS )
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700257
258 # Create leaf switches
259 for ls in range(leaf):
You Wang68568b12019-03-04 11:49:57 -0800260 self.leafs[ls] = self.addSwitch( 'leaf%s' % (ls + 1),
261 dpid = "00000000000%s" % (ls + 1),
262 **SWITCH_PARAMS )
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700263
264 # connecting leaf and spines, leafs 1-5 have double links
265 for s in range(2):
266 spine_switch = self.spines[s]
267
268 for ls in range(1, 5):
269 leaf_switch = self.leafs[ls]
270
271 self.addLink( spine_switch, leaf_switch, **linkopts )
272 self.addLink( spine_switch, leaf_switch, **linkopts )
273
274 # connect paired leafs
275 self.addLink(self.leafs[1], self.leafs[2], **linkopts)
276 self.addLink(self.leafs[3], self.leafs[4], **linkopts)
277
278 # build second fabric with single links
279 for s in range(2, 4):
280 spine_switch = self.spines[s]
281
282 for ls in [0, 5]:
283 leaf_switch = self.leafs[ls]
284 self.addLink( spine_switch, leaf_switch, **linkopts )
285
286 # connect spines together
287 self.addLink(self.spines[2], self.spines[0], **linkopts)
288 self.addLink(self.spines[3], self.spines[1], **linkopts)
289
290 # create hosts
291 if ipv6:
292 self.createIpv6Hosts(dhcp)
293
294 if ipv4:
295 self.createIpv4Hosts(dhcp)
296
297 if not ipv4 and not ipv6:
298 print("No hosts were created!")
299
300 # create quagga routers
301 # Note: Change "fpm connection ip" to $OC1 in zebradbgp1.conf and zebradbgp2.conf to make quagga work correctly
302 if routers:
303 last_ls = self.leafs[4]
304 last_paired_ls = self.leafs[3]
305
306 # Control plane switch (for quagga fpm)
307 cs0 = self.addSwitch('cs0', cls=OVSBridge)
308
309 # Control plane NAT (for quagga fpm)
310 nat = self.addHost('nat', cls=NAT,
311 ip='172.16.0.1/12',
312 subnet=str(ip_network(u'172.16.0.0/12')), inNamespace=False)
313 self.addLink(cs0, nat)
314
315 # Internal Quagga bgp1
316 intfs = {'bgp1-eth0': [{'ipAddrs': ['10.0.1.2/24', '2000::102/120'], 'mac': '00:88:00:00:00:03', 'vlan': '110'},
317 {'ipAddrs': ['10.0.7.2/24', '2000::702/120'], 'mac': '00:88:00:00:00:03', 'vlan': '170'}],
318 'bgp1-eth1': {'ipAddrs': ['172.16.0.3/12']}}
319 bgp1 = self.addHost('bgp1', cls=BgpRouter,
320 interfaces=intfs,
321 quaggaConfFile='./bgpdbgp1.conf',
322 zebraConfFile='./zebradbgp1.conf')
323 self.addLink(bgp1, last_paired_ls)
324 self.addLink(bgp1, cs0)
325
326 # Internal Quagga bgp2
327 intfs = {'bgp2-eth0': [{'ipAddrs': ['10.0.5.2/24', '2000::502/120'], 'mac': '00:88:00:00:00:04', 'vlan': '150'},
328 {'ipAddrs': ['10.0.6.2/24', '2000::602/120'], 'mac': '00:88:00:00:00:04', 'vlan': '160'}],
329 'bgp2-eth1': {'ipAddrs': ['172.16.0.4/12']}}
330 bgp2 = self.addHost('bgp2', cls=BgpRouter,
331 interfaces=intfs,
332 quaggaConfFile='./bgpdbgp2.conf',
333 zebraConfFile='./zebradbgp2.conf')
334 self.addLink(bgp2, last_ls)
335 self.addLink(bgp2, cs0)
336
337 # External Quagga r1
338 intfs = {'r1-eth0': {'ipAddrs': ['10.0.1.1/24', '2000::101/120'], 'mac': '00:88:00:00:00:01'},
339 'r1-eth1': {'ipAddrs': ['10.0.5.1/24', '2000::501/120'], 'mac': '00:88:00:00:00:11'},
340 'r1-eth2': {'ipAddrs': ['10.0.99.1/16']},
341 'r1-eth3': {'ipAddrs': ['2000::9901/120']},
342 'r1-eth4': {'ipAddrs': ['2000::7701/120']},
343 'r1-eth5': {'ipAddrs': ['10.0.88.1/24']},
344 'r1-eth6': {'ipAddrs': ['2000::8701/120']}}
345 r1 = self.addHost('r1', cls=BgpRouter,
346 interfaces=intfs,
347 quaggaConfFile='./bgpdr1.conf')
348 self.addLink(r1, last_paired_ls)
349 self.addLink(r1, last_ls)
350
351 # External IPv4 Host behind r1
352 rh1v4 = self.addHost('rh1v4', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
353 self.addLink(r1, rh1v4)
354
355 # External IPv6 Host behind r1
356 rh1v6 = self.addHost('rh1v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
357 self.addLink(r1, rh1v6)
358
359 # Another external IPv6 Host behind r1
360 rh11v6 = self.addHost('rh11v6', cls=RoutedHost, ips=['2000::7702/120'], gateway='2000::7701')
361 self.addLink(r1, rh11v6)
362
363 # Add an external ipv4 hosts that is not configured in the bgp conf
364 # files
365 rh5v4 = self.addHost('rh5v4', cls=RoutedHost, ips=['10.0.88.2/24'], gateway='10.0.88.1')
366 self.addLink(r1, rh5v4)
367
368 # Add an external ipv6 hosts that is not configured in the bgp conf
369 # files
370 rh5v6 = self.addHost('rh5v6', cls=RoutedHost, ips=['2000::8702/120'], gateway='2000::8701')
371 self.addLink(r1, rh5v6)
372
373 # External Quagga r2
374 intfs = {'r2-eth0': {'ipAddrs': ['10.0.6.1/24', '2000::601/120'], 'mac': '00:88:00:00:00:02'},
375 'r2-eth1': {'ipAddrs': ['10.0.7.1/24', '2000::701/120'], 'mac': '00:88:00:00:00:22'},
376 'r2-eth2': {'ipAddrs': ['10.0.99.1/16']},
377 'r2-eth3': {'ipAddrs': ['2000::9901/120']},
378 'r2-eth4': {'ipAddrs': ['2000::8801/120']}}
379 r2 = self.addHost('r2', cls=BgpRouter,
380 interfaces=intfs,
381 quaggaConfFile='./bgpdr2.conf')
382 self.addLink(r2, last_ls)
383 self.addLink(r2, last_paired_ls)
384
385 # External IPv4 Host behind r2
386 rh2v4 = self.addHost('rh2v4', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
387 self.addLink(r2, rh2v4)
388
389 # External IPv6 Host behind r2
390 rh2v6 = self.addHost('rh2v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
391 self.addLink(r2, rh2v6)
392
393 # Another external IPv6 Host behind r1
394 rh22v6 = self.addHost('rh22v6', cls=RoutedHost, ips=['2000::8802/120'], gateway='2000::8801')
395 self.addLink(r2, rh22v6)
396
397 # create dhcp servers
398 if dhcp:
399 cs1 = self.addSwitch('cs1', cls=OVSBridge)
400 self.addLink(cs1, self.leafs[4])
401 if ipv4:
402 dhcp4 = self.addHost( 'dhcp', cls=TrellisHost,
403 mac="00:cc:00:00:00:01", ips=["10.0.3.253/24"],
404 gateway="10.0.3.254", dhcpServer=True)
405 self.addLink(dhcp4, cs1, **linkopts)
406 if ipv6:
407 dhcp6 = self.addHost( 'dhcp6', cls=TrellisHost,
408 mac="00:dd:00:00:00:01", ips=["2000::3fd/120"],
409 gateway="2000::3ff", dhcpServer=True, ipv6=True)
410 self.addLink(dhcp6, cs1, **linkopts)
411
412
413def config( opts ):
414
415 dhcp = bool(opts.dhcp)
416 routers = bool(opts.routers)
417 ipv6 = bool(opts.ipv6)
418 ipv4 = bool(opts.ipv4)
419
420 if opts.onosIp != '':
421 controllers = opts.onosIp.split( ',' )
422 else:
423 controllers = ['127.0.0.1']
424 topo = ComcastLeafSpineFabric(dhcp=dhcp, routers=routers, ipv6=ipv6,
425 ipv4=ipv4)
426
427 net = Mininet( topo=topo, link=TCLink, build=False,
428 controller=None, autoSetMacs=True )
429 i = 0
430 for ip in controllers:
431 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
432 i += 1
433
434 net.build()
435 net.start()
436 CLI( net )
437 net.stop()
438
439
440if __name__ == '__main__':
441 setLogLevel('info')
442 config(opts)
443 os.system('sudo mn -c')
444