blob: d1058ab21c20f162c2a53bb32cc6889f7c5f24df [file] [log] [blame]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -07001#!/usr/bin/python
2import os
3import re
4from optparse import OptionParser
5
6from ipaddress import ip_network
You Wang53dba1e2018-02-02 17:45:44 -08007from mininet.node import RemoteController, OVSBridge, Host, OVSSwitch
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -07008from mininet.link import TCLink
9from mininet.log import setLogLevel
10from mininet.net import Mininet
11from mininet.topo import Topo
12from mininet.nodelib import NAT
13from mininet.cli import CLI
14
You Wang5102af12018-02-08 12:30:12 -080015from routinglib import BgpRouter
You Wang53dba1e2018-02-02 17:45:44 -080016from trellislib import TrellisHost, DhcpRelay
17from functools import partial
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070018
19# Parse command line options and dump results
20def parseOptions():
21 "Parse command line options"
22 parser = OptionParser()
23 parser.add_option( '--spine', dest='spine', type='int', default=2,
24 help='number of spine switches, default=2' )
25 parser.add_option( '--leaf', dest='leaf', type='int', default=2,
26 help='number of leaf switches, default=2' )
27 parser.add_option( '--fanout', dest='fanout', type='int', default=2,
28 help='number of hosts per leaf switch, default=2' )
29 parser.add_option( '--onos-ip', dest='onosIp', type='str', default='',
30 help='IP address list of ONOS instances, separated by comma(,). Overrides --onos option' )
31 parser.add_option( '--ipv6', action="store_true", dest='ipv6',
32 help='hosts are capable to use also ipv6' )
33 parser.add_option( '--dual-homed', action="store_true", dest='dualhomed', default=False,
34 help='True if the topology is dual-homed, default=False' )
35 parser.add_option( '--vlan', dest='vlan', type='str', default='',
36 help='list of vlan id for hosts, separated by comma(,).'
37 'Empty or id with 0 will be unconfigured.' )
You Wang53dba1e2018-02-02 17:45:44 -080038 parser.add_option( '--dhcp-client', action="store_true", dest='dhcpClient', default=False,
39 help='Set hosts as DhcpClient if True' )
40 parser.add_option( '--dhcp-relay', action="store_true", dest='dhcpRelay', default=False,
41 help='Connect half of the hosts to switch indirectly (via DHCP relay) if True' )
42 parser.add_option( '--multiple-dhcp-server', action="store_true", dest='multipleServer', default=False,
43 help='Use another DHCP server for indirectly connected DHCP clients if True' )
44 parser.add_option( '--remote-dhcp-server', action="store_true", dest='remoteServer', default=False,
45 help='Connect DHCP server indirectly (via gateway) if True' )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070046 ( options, args ) = parser.parse_args()
47 return options, args
48
49
50opts, args = parseOptions()
51
52IP6_SUBNET_CLASS = 120
53IP4_SUBNET_CLASS = 24
54
55# TODO: DHCP support
56class IpHost( Host ):
57
58 def __init__( self, name, *args, **kwargs ):
59 super( IpHost, self ).__init__( name, *args, **kwargs )
60 gateway = re.split( '\.|/', kwargs[ 'ip' ] )
61 gateway[ 3 ] = '254'
62 self.gateway = '.'.join( gateway[ 0:4 ] )
63
64 def config( self, **kwargs ):
65 Host.config( self, **kwargs )
66 mtu = "ifconfig " + self.name + "-eth0 mtu 1490"
67 self.cmd( mtu )
68 self.cmd( 'ip route add default via %s' % self.gateway )
69
70class DualHomedIpHost(IpHost):
71 def __init__(self, name, *args, **kwargs):
72 super(DualHomedIpHost, self).__init__(name, **kwargs)
73 self.bond0 = None
74
75 def config(self, **kwargs):
76 super(DualHomedIpHost, self).config(**kwargs)
77 intf0 = self.intfs[0].name
78 intf1 = self.intfs[1].name
79 self.bond0 = "%s-bond0" % self.name
80 self.cmd('modprobe bonding')
81 self.cmd('ip link add %s type bond' % self.bond0)
82 self.cmd('ip link set %s down' % intf0)
83 self.cmd('ip link set %s down' % intf1)
84 self.cmd('ip link set %s master %s' % (intf0, self.bond0))
85 self.cmd('ip link set %s master %s' % (intf1, self.bond0))
86 self.cmd('ip addr flush dev %s' % intf0)
87 self.cmd('ip addr flush dev %s' % intf1)
88 self.cmd('ip link set %s up' % self.bond0)
89
90 def terminate(self, **kwargs):
91 self.cmd('ip link set %s down' % self.bond0)
92 self.cmd('ip link delete %s' % self.bond0)
93 self.cmd('kill -9 `cat %s`' % self.pidFile)
94 self.cmd('rm -rf %s' % self.pidFile)
95 super(DualHomedIpHost, self).terminate()
96
97
98# TODO: Implement IPv6 support
99class DualHomedLeafSpineFabric (Topo) :
You Wang53dba1e2018-02-02 17:45:44 -0800100 def __init__(self, spine = 2, leaf = 2, fanout = 2, vlan_id = [], ipv6 = False,
101 dhcp_client = False, dhcp_relay = False,
102 multiple_server = False, remote_server = False, **opts):
103 # TODO: add support to dhcp_relay, multiple_server and remote_server
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700104 Topo.__init__(self, **opts)
105 spines = dict()
106 leafs = dict()
107
108 # leaf should be 2 or 4
109
110 # calculate the subnets to use and set options
111 linkopts = dict( bw=100 )
112 # Create spine switches
113 for s in range(spine):
114 spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
115
116 # Create leaf switches
117 for ls in range(leaf):
118 leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
119
120 # Connect leaf to all spines with dual link
121 for s in range( spine ):
122 switch = spines[ s ]
123 self.addLink(leafs[ls], switch, **linkopts)
124 self.addLink(leafs[ls], switch, **linkopts)
125
126 # Add hosts after paired ToR switches are added.
127 if ls % 2 == 0:
128 continue
129
130 # Add leaf-leaf link
131 self.addLink(leafs[ls], leafs[ls-1])
132
133 dual_ls = ls / 2
134 # Add hosts
135 for f in range(fanout):
You Wang53dba1e2018-02-02 17:45:44 -0800136 name = 'h%s%s' % (dual_ls * fanout + f + 1, "v6" if ipv6 else "")
137 if ipv6:
138 ips = ['2000::%d0%d/%d' % (dual_ls+2, f+1, IP6_SUBNET_CLASS)]
139 gateway = '2000::%dff' % (dual_ls+2)
140 mac = '00:bb:00:00:00:%02x' % (dual_ls * fanout + f + 1)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700141 else:
You Wang53dba1e2018-02-02 17:45:44 -0800142 ips = ['10.0.%d.%d/%d' % (dual_ls+2, f+1, IP4_SUBNET_CLASS)]
143 gateway = '10.0.%d.254' % (dual_ls+2)
144 mac = '00:aa:00:00:00:%02x' % (dual_ls * fanout + f + 1)
145 host = self.addHost( name=name, cls=TrellisHost, ips=ips, gateway=gateway, mac=mac,
146 vlan=vlan_id[ dual_ls*fanout + f ] if vlan_id[dual_ls * fanout + f] != 0 else None,
147 dhcpClient=dhcp_client, ipv6=ipv6, dualHomed=True )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700148 self.addLink(host, leafs[ls], **linkopts)
149 self.addLink(host, leafs[ls-1], **linkopts)
150
151 last_ls = leafs[leaf-2]
152 last_paired_ls = leafs[leaf-1]
153 # Create common components
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700154 # Control plane switch (for DHCP servers)
155 cs1 = self.addSwitch('cs1', cls=OVSBridge)
156 self.addLink(cs1, last_ls)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700157
158 # Control plane switch (for quagga fpm)
159 cs0 = self.addSwitch('cs0', cls=OVSBridge)
160
161 # Control plane NAT (for quagga fpm)
162 nat = self.addHost('nat', cls=NAT,
163 ip='172.16.0.1/12',
164 subnet=str(ip_network(u'172.16.0.0/12')), inNamespace=False)
165 self.addLink(cs0, nat)
166
167 # Internal Quagga bgp1
168 intfs = {'bgp1-eth0': {'ipAddrs': ['10.0.1.2/24', '2000::102/120'], 'mac': '00:88:00:00:00:02'},
169 'bgp1-eth1': {'ipAddrs': ['172.16.0.2/12']}}
170 bgp1 = self.addHost('bgp1', cls=BgpRouter,
171 interfaces=intfs,
You Wang53dba1e2018-02-02 17:45:44 -0800172 quaggaConfFile='./bgpdbgp1.conf',
173 zebraConfFile='./zebradbgp1.conf')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700174 self.addLink(bgp1, last_ls)
175 self.addLink(bgp1, cs0)
176
177 # Internal Quagga bgp2
178 intfs = {'bgp2-eth0': [{'ipAddrs': ['10.0.5.2/24', '2000::502/120'], 'mac': '00:88:00:00:00:04', 'vlan': '150'},
179 {'ipAddrs': ['10.0.6.2/24', '2000::602/120'], 'mac': '00:88:00:00:00:04', 'vlan': '160'}],
180 'bgp2-eth1': {'ipAddrs': ['172.16.0.4/12']}}
181 bgp2 = self.addHost('bgp2', cls=BgpRouter,
182 interfaces=intfs,
You Wang53dba1e2018-02-02 17:45:44 -0800183 quaggaConfFile='./bgpdbgp2.conf',
184 zebraConfFile='./zebradbgp2.conf')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700185 self.addLink(bgp2, last_paired_ls)
186 self.addLink(bgp2, cs0)
187
188 # External Quagga r1
189 intfs = {'r1-eth0': {'ipAddrs': ['10.0.1.1/24', '2000::101/120'], 'mac': '00:88:00:00:00:01'},
190 'r1-eth1': {'ipAddrs': ['10.0.5.1/24', '2000::501/120'], 'mac': '00:88:00:00:00:11'},
191 'r1-eth2': {'ipAddrs': ['10.0.99.1/16']}}
192 r1 = self.addHost('r1', cls=BgpRouter,
193 interfaces=intfs,
You Wang53dba1e2018-02-02 17:45:44 -0800194 quaggaConfFile='./bgpdr1.conf')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700195 self.addLink(r1, last_ls)
196 self.addLink(r1, last_paired_ls)
197
198 # External IPv4 Host behind r1
You Wang5102af12018-02-08 12:30:12 -0800199 rh1 = self.addHost('rh1', cls=TrellisHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700200 self.addLink(r1, rh1)
201
202 # External Quagga r2
203 intfs = {'r2-eth0': {'ipAddrs': ['10.0.6.1/24', '2000::601/120'], 'mac': '00:88:00:00:00:02'},
204 'r2-eth1': {'ipAddrs': ['10.0.7.1/24', '2000::701/120'], 'mac': '00:88:00:00:00:22'},
205 'r2-eth2': {'ipAddrs': ['10.0.99.1/16']}}
206 r2 = self.addHost('r2', cls=BgpRouter,
207 interfaces=intfs,
You Wang53dba1e2018-02-02 17:45:44 -0800208 quaggaConfFile='./bgpdr2.conf')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700209 self.addLink(r2, last_ls)
210 self.addLink(r2, last_paired_ls)
211
212 # External IPv4 Host behind r2
You Wang5102af12018-02-08 12:30:12 -0800213 rh2 = self.addHost('rh2', cls=TrellisHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700214 self.addLink(r2, rh2)
215
You Wang53dba1e2018-02-02 17:45:44 -0800216 # DHCP server
217 if ipv6:
218 dhcp = self.addHost('dhcp', cls=TrellisHost, mac='00:99:00:00:00:01',
219 ips=['2000::3fd/120'], gateway='2000::3ff',
220 dhcpServer=True, ipv6=True)
221 self.addLink(dhcp, cs1)
222 else:
223 dhcp = self.addHost('dhcp', cls=TrellisHost, mac='00:99:00:00:00:01',
224 ips=['10.0.3.253/24'], gateway='10.0.3.254',
225 dhcpServer=True)
226 self.addLink(dhcp, cs1)
227
228
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700229class LeafSpineFabric (Topo) :
You Wang53dba1e2018-02-02 17:45:44 -0800230 def __init__(self, spine = 2, leaf = 2, fanout = 2, vlan_id = [], ipv6 = False,
231 dhcp_client = False, dhcp_relay = False,
232 multiple_server = False, remote_server = False, **opts):
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700233 Topo.__init__(self, **opts)
234 spines = dict()
235 leafs = dict()
236
237 # TODO: support IPv6 hosts
238 linkopts = dict( bw=100 )
239
240 # Create spine switches
241 for s in range(spine):
242 spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
243
244 # Create leaf switches
245 for ls in range(leaf):
246 leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
247
248 # Connect leaf to all spines
249 for s in range( spine ):
250 switch = spines[ s ]
251 self.addLink( leafs[ ls ], switch, **linkopts )
252
253 # If dual-homed ToR, add hosts only when adding second switch at each edge-pair
254 # When the number of leaf switches is odd, leave the last switch as a single ToR
255
256 # Add hosts
257 for f in range(fanout):
You Wang53dba1e2018-02-02 17:45:44 -0800258 name = 'h%s%s' % (ls * fanout + f + 1, "v6" if ipv6 else "")
259 if ipv6:
260 ips = ['2000::%d0%d/%d' % (ls+2, f+1, IP6_SUBNET_CLASS)]
261 gateway = '2000::%dff' % (ls+2)
262 mac = '00:bb:00:00:00:%02x' % (ls * fanout + f + 1)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700263 else:
You Wang53dba1e2018-02-02 17:45:44 -0800264 ips = ['10.0.%d.%d/%d' % (ls+2, f+1, IP4_SUBNET_CLASS)]
265 gateway = '10.0.%d.254' % (ls+2)
266 mac = '00:aa:00:00:00:%02x' % (ls * fanout + f + 1)
267 host = self.addHost( name=name, cls=TrellisHost, ips=ips, gateway=gateway, mac=mac,
268 vlan=vlan_id[ ls*fanout + f ] if vlan_id[ls * fanout + f] != 0 else None,
269 dhcpClient=dhcp_client, ipv6=ipv6 )
270 if dhcp_relay and f % 2:
271 relayIndex = ls * fanout + f + 1
272 if ipv6:
273 intfs = {
274 'relay%s-eth0' % relayIndex: { 'ipAddrs': ['2000::%dff/%d' % (leaf + ls + 2, IP6_SUBNET_CLASS)] },
275 'relay%s-eth1' % relayIndex: { 'ipAddrs': ['2000::%d5%d/%d' % (ls + 2, f, IP6_SUBNET_CLASS)] }
276 }
277 if remote_server:
278 serverIp = '2000::99fd'
279 elif multiple_server:
280 serverIp = '2000::3fc'
281 else:
282 serverIp = '2000::3fd'
283 dhcpRelay = self.addHost(name='relay%s' % relayIndex, cls=DhcpRelay, serverIp=serverIp,
284 gateway='2000::%dff' % (ls+2), interfaces=intfs)
285 else:
286 intfs = {
287 'relay%s-eth0' % relayIndex: { 'ipAddrs': ['10.0.%d.254/%d' % (leaf + ls + 2, IP4_SUBNET_CLASS)] },
288 'relay%s-eth1' % relayIndex: { 'ipAddrs': ['10.0.%d.%d/%d' % (ls + 2, f + 99, IP4_SUBNET_CLASS)] }
289 }
290 if remote_server:
291 serverIp = '10.0.99.3'
292 elif multiple_server:
293 serverIp = '10.0.3.252'
294 else:
295 serverIp = '10.0.3.253'
296 dhcpRelay = self.addHost(name='relay%s' % relayIndex, cls=DhcpRelay, serverIp=serverIp,
297 gateway='10.0.%d.254' % (ls+2), interfaces=intfs)
298 self.addLink(host, dhcpRelay, **linkopts)
299 self.addLink(dhcpRelay, leafs[ls], **linkopts)
300 else:
301 self.addLink(host, leafs[ls], **linkopts)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700302
303 last_ls = leafs[leaf-1]
304 # Create common components
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700305 # Control plane switch (for DHCP servers)
306 cs1 = self.addSwitch('cs1', cls=OVSBridge)
307 self.addLink(cs1, last_ls)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700308
309 # Control plane switch (for quagga fpm)
310 cs0 = self.addSwitch('cs0', cls=OVSBridge)
311
312 # Control plane NAT (for quagga fpm)
313 nat = self.addHost('nat', cls=NAT,
314 ip='172.16.0.1/12',
315 subnet=str(ip_network(u'172.16.0.0/12')), inNamespace=False)
316 self.addLink(cs0, nat)
317
318 # Internal Quagga bgp1
319 intfs = {'bgp1-eth0': {'ipAddrs': ['10.0.1.2/24', '2000::102/120'], 'mac': '00:88:00:00:00:02'},
320 'bgp1-eth1': {'ipAddrs': ['172.16.0.2/12']}}
321 bgp1 = self.addHost('bgp1', cls=BgpRouter,
322 interfaces=intfs,
You Wang53dba1e2018-02-02 17:45:44 -0800323 quaggaConfFile='./bgpdbgp1.conf',
324 zebraConfFile='./zebradbgp1.conf')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700325 self.addLink(bgp1, last_ls)
326 self.addLink(bgp1, cs0)
327
328 # External Quagga r1
329 intfs = {'r1-eth0': {'ipAddrs': ['10.0.1.1/24', '2000::101/120'], 'mac': '00:88:00:00:00:01'},
330 'r1-eth1': {'ipAddrs': ['10.0.99.1/16']},
331 'r1-eth2': {'ipAddrs': ['2000::9901/120']}}
332 r1 = self.addHost('r1', cls=BgpRouter,
333 interfaces=intfs,
You Wang53dba1e2018-02-02 17:45:44 -0800334 quaggaConfFile='./bgpdr1.conf')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700335 self.addLink(r1, last_ls)
336
You Wang53dba1e2018-02-02 17:45:44 -0800337 # External switch behind r1
338 rs0 = self.addSwitch('rs0', cls=OVSBridge)
339 self.addLink(r1, rs0)
340
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700341 # External IPv4 Host behind r1
You Wang5102af12018-02-08 12:30:12 -0800342 rh1 = self.addHost('rh1', cls=TrellisHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700343 self.addLink(r1, rh1)
344
You Wang53dba1e2018-02-02 17:45:44 -0800345 # External IPv6 Host behind r1
346 rh1v6 = self.addHost('rh1v6', cls=TrellisHost, ips=['2000::9902/120'], gateway='2000::9901')
347 self.addLink(r1, rh1v6)
348
349 # DHCP server
350 if ipv6:
351 if remote_server:
352 dhcp = self.addHost('dhcp', cls=TrellisHost, mac='00:99:00:00:00:01',
353 ips=['2000::99fd/120'], gateway='2000::9901',
354 dhcpServer=True, ipv6=True)
355 self.addLink(rs0, dhcp)
356 else:
357 dhcp = self.addHost('dhcp', cls=TrellisHost, mac='00:99:00:00:00:01',
358 ips=['2000::3fd/120'], gateway='2000::3ff',
359 dhcpServer=True, ipv6=True)
360 self.addLink(dhcp, cs1)
361 if multiple_server:
362 dhcp2 = self.addHost('dhcp2', cls=TrellisHost, mac='00:99:00:00:00:02',
363 ips=['2000::3fc/120'], gateway='2000::3ff',
364 dhcpServer=True, ipv6=True)
365 self.addLink(dhcp2, cs1)
366 else:
367 if remote_server:
368 dhcp = self.addHost('dhcp', cls=TrellisHost, mac='00:99:00:00:00:01',
369 ips=['10.0.99.3/24'], gateway='10.0.99.1',
370 dhcpServer=True)
371 self.addLink(rs0, dhcp)
372 else:
373 dhcp = self.addHost('dhcp', cls=TrellisHost, mac='00:99:00:00:00:01',
374 ips=['10.0.3.253/24'], gateway='10.0.3.254',
375 dhcpServer=True)
376 self.addLink(dhcp, cs1)
377 if multiple_server:
378 dhcp2 = self.addHost('dhcp2', cls=TrellisHost, mac='00:99:00:00:00:02',
379 ips=['10.0.3.252/24'], gateway='10.0.3.254',
380 dhcpServer=True)
381 self.addLink(dhcp2, cs1)
382
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700383def config( opts ):
384 spine = opts.spine
385 leaf = opts.leaf
386 fanout = opts.fanout
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700387 dualhomed = opts.dualhomed
388 if opts.vlan == '':
389 vlan = [0] * (((leaf / 2) if dualhomed else leaf) * fanout)
390 else:
391 vlan = [int(vlan_id) if vlan_id != '' else 0 for vlan_id in opts.vlan.split(',')]
392
393 if opts.onosIp != '':
394 controllers = opts.onosIp.split( ',' )
395 else:
396 controllers = ['127.0.0.1']
397
398 if len(vlan) != ((leaf / 2) if dualhomed else leaf ) * fanout:
399 print "Invalid vlan configuration is given."
400 return
401
You Wang53dba1e2018-02-02 17:45:44 -0800402 if dualhomed:
403 if leaf % 2 == 1 or leaf == 0:
404 print "Even number of leaf switches (at least two) are needed to build dual-homed topology."
405 return
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700406 else:
You Wang53dba1e2018-02-02 17:45:44 -0800407 topo = DualHomedLeafSpineFabric(spine=spine, leaf=leaf, fanout=fanout, vlan_id=vlan,
408 ipv6=opts.ipv6,
409 dhcp_client=opts.dhcpClient,
410 dhcp_relay=opts.dhcpRelay,
411 multiple_server=opts.multipleServer,
412 remote_server=opts.remoteServer)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700413 else:
You Wang53dba1e2018-02-02 17:45:44 -0800414 topo = LeafSpineFabric(spine=spine, leaf=leaf, fanout=fanout, vlan_id=vlan, ipv6=opts.ipv6,
415 dhcp_client=opts.dhcpClient,
416 dhcp_relay=opts.dhcpRelay,
417 multiple_server=opts.multipleServer,
418 remote_server=opts.remoteServer)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700419
420 net = Mininet( topo=topo, link=TCLink, build=False,
421 controller=None, autoSetMacs=True )
422 i = 0
423 for ip in controllers:
424 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
425 i += 1
426 net.build()
427 net.start()
428 CLI( net )
429 net.stop()
430
431if __name__ == '__main__':
432 setLogLevel('info')
433 config(opts)
434 os.system('sudo mn -c')