blob: 2c9180679bb91066bd2225f73ad91cc4a408f761 [file] [log] [blame]
Flavio Castrocc38a542016-03-03 13:15:46 -08001#!/usr/bin/python
2
3import os
Flavio Castroab163ca2016-07-07 14:05:00 -07004import re
Pierb95002f2016-12-05 15:20:42 -08005import math
Flavio Castrocc38a542016-03-03 13:15:46 -08006from optparse import OptionParser
Pierb95002f2016-12-05 15:20:42 -08007from ipaddress import IPv6Network, IPv4Network
Flavio Castrocc38a542016-03-03 13:15:46 -08008from mininet.net import Mininet
9from mininet.topo import Topo
Flavio Castroe168f7f2016-06-24 15:53:12 -070010from mininet.node import RemoteController, UserSwitch, Host, OVSBridge
Flavio Castrocc38a542016-03-03 13:15:46 -080011from mininet.link import TCLink
12from mininet.log import setLogLevel
13from mininet.cli import CLI
14
Flavio Castrocc38a542016-03-03 13:15:46 -080015
Flavio Castro5608a392016-06-22 17:02:35 -070016# Parse command line options and dump results
17def parseOptions( ):
18 """Parse command line options"""
19 parser = OptionParser( )
20 parser.add_option( '--spine', dest='spine', type='int', default=2,
21 help='number of spine switches, default=2' )
22 parser.add_option( '--leaf', dest='leaf', type='int', default=2,
23 help='number of leaf switches, default=2' )
24 parser.add_option( '--fanout', dest='fanout', type='int', default=2,
25 help='number of hosts per leaf switch, default=2' )
26 parser.add_option( '--onos', dest='onos', type='int', default=0,
27 help='number of ONOS Instances, default=0, 0 means localhost, 1 will use OC1 and so on' )
Flavio Castroab163ca2016-07-07 14:05:00 -070028 parser.add_option( '--vlan', dest='vlan', type='int', default=-1,
29 help='vid of cross connect, default=-1, -1 means utilize default value' )
Pierb95002f2016-12-05 15:20:42 -080030 parser.add_option( '--ipv6', action="store_true", dest='ipv6',
31 help='hosts are capable to use also ipv6' )
Flavio Castro5608a392016-06-22 17:02:35 -070032 (options, args) = parser.parse_args( )
Flavio Castrocc38a542016-03-03 13:15:46 -080033 return options, args
34
Flavio Castrocc38a542016-03-03 13:15:46 -080035
Flavio Castro5608a392016-06-22 17:02:35 -070036opts, args = parseOptions( )
37
Pierb95002f2016-12-05 15:20:42 -080038IP6_SUBNET_CLASS = 120
39IP4_SUBNET_CLASS = 24
40
41class LeafAndSpine6( Topo ):
42 """
43 Create Leaf and Spine Topology for IPv4/IPv6 tests.
44 """
45 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
46 Topo.__init__( self, **opts )
47 spines = {}
48 leafs = {}
49 """
50 We calculate the offset from /120 and from /24 in order to have
51 a number of /120 and /24 subnets == leaf
52 """
53 offset = int(math.ceil(math.sqrt( leaf )))
54 """
55 We calculate the subnets to use and set options
56 """
57 ipv6SubnetClass = unicode('2000::/%s' % (IP6_SUBNET_CLASS - offset))
58 ipv6Subnets = list(IPv6Network(ipv6SubnetClass).subnets( new_prefix = IP6_SUBNET_CLASS ))
59 ipv4SubnetClass = unicode('10.0.0.0/%s' % (IP4_SUBNET_CLASS - offset))
60 ipv4Subnets = list(IPv4Network(ipv4SubnetClass).subnets( new_prefix = IP4_SUBNET_CLASS ))
61 linkopts = dict( bw=100 )
62 """
63 We create the spine switches
64 """
65 for s in range( spine ):
66 spines[ s ] = self.addSwitch( 'spine10%s' % (s + 1),
67 dpid="00000000010%s" % (s + 1) )
68 """
69 We create the leaf switches
70 """
71 for ls in range( leaf ):
72 leafs[ ls ] = self.addSwitch( 'leaf%s' % (ls + 1),
73 dpid="00000000000%s" % (1 + ls) )
74 ipv6Subnet = ipv6Subnets[ ls ]
75 ipv6Hosts = list(ipv6Subnet.hosts())
76 ipv4Subnet = ipv4Subnets[ ls ]
77 ipv4Hosts = list(ipv4Subnet.hosts())
78 """
79 We add the hosts
80 """
81 for f in range( fanout ):
82 ipv6 = ipv6Hosts[ f ]
83 ipv6Gateway = ipv6Hosts[ len( ipv6Hosts ) - 1 ]
84 ipv4 = ipv4Hosts[ f ]
85 ipv4Gateway = ipv4Hosts[ len( ipv4Hosts ) - 1 ]
86 host = self.addHost(
87 name='h%s' % (ls * fanout + f + 1),
88 cls=Ipv6Host,
89 ip="%s/%s" %(ipv4, IP4_SUBNET_CLASS),
90 gateway='%s' % ipv4Gateway,
91 ipv6="%s/%s" %(ipv6, IP6_SUBNET_CLASS),
92 ipv6Gateway="%s" % ipv6Gateway
93 )
94 self.addLink( host, leafs[ ls ], **linkopts )
95 """
96 Connect leaf to all spines
97 """
98 for s in range( spine ):
99 switch = spines[ s ]
100 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700101
102class LeafAndSpine( Topo ):
103 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800104 "Create Leaf and Spine Topo."
Flavio Castro5608a392016-06-22 17:02:35 -0700105 Topo.__init__( self, **opts )
Flavio Castrocc38a542016-03-03 13:15:46 -0800106 # Add spine switches
Flavio Castro5608a392016-06-22 17:02:35 -0700107 spines = { }
Flavio Castroe168f7f2016-06-24 15:53:12 -0700108 leafs = { }
Flavio Castro5608a392016-06-22 17:02:35 -0700109 for s in range( spine ):
110 spines[ s ] = self.addSwitch( 'spine10%s' % (s + 1),
111 dpid="00000000010%s" % (s + 1) )
Flavio Castrocc38a542016-03-03 13:15:46 -0800112 # Set link speeds to 100Mb/s
Flavio Castro5608a392016-06-22 17:02:35 -0700113 linkopts = dict( bw=100 )
Flavio Castrocc38a542016-03-03 13:15:46 -0800114 # Add Leaf switches
Flavio Castro5608a392016-06-22 17:02:35 -0700115 for ls in range( leaf ):
Flavio Castroe168f7f2016-06-24 15:53:12 -0700116 leafs[ ls ] = self.addSwitch( 'leaf%s' % (ls + 1),
117 dpid="00000000000%s" % (1 + ls) )
Flavio Castrocc38a542016-03-03 13:15:46 -0800118 # Add hosts under a leaf, fanout hosts per leaf switch
Flavio Castro5608a392016-06-22 17:02:35 -0700119 for f in range( fanout ):
120 host = self.addHost( 'h%s' % (ls * fanout + f + 1),
121 cls=IpHost,
122 ip='10.0.%s.%s/24' % ((ls + 1), (f + 1)),
123 gateway='10.0.%s.254' % (ls + 1) )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700124 self.addLink( host, leafs[ ls ], **linkopts )
125 # Add Xconnect simulation
Flavio Castroab163ca2016-07-07 14:05:00 -0700126 if ls is 0:
127 in1 = self.addHost( 'in1', cls=IpHost, ip='10.0.1.9/24', mac="00:00:00:00:00:09" )
128 self.addLink( in1, leafs[0], **linkopts )
129 out1 = self.addHost( 'out1', cls=IpHost, ip='10.0.9.1/24', mac="00:00:00:00:09:01" )
130 self.addLink( out1, leafs[0], **linkopts )
131 br1 = self.addSwitch( 'br1', cls=OVSBridge )
132 self.addLink( br1, leafs[ 0 ], **linkopts )
133 vlans = [ 1, 5, 10 ]
134 for vid in vlans:
135 olt = self.addHost( 'olt%s' % vid, cls=VLANHost, vlan=vid,
136 ip="10.%s.0.1/24" % vid
137 , mac="00:00:%02d:00:00:01" % vid )
138 vsg = self.addHost( 'vsg%s' % vid, cls=VLANHost, vlan=vid,
139 ip="10.%s.0.2/24" % vid
140 , mac="00:00:%02d:00:00:02" % vid )
141 self.addLink( olt, leafs[ 0 ], **linkopts )
142 self.addLink( vsg, br1, **linkopts )
143 # Connect leaf to all spines
144 for s in range( spine ):
145 switch = spines[ s ]
146 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700147
148class IpHost( Host ):
Flavio Castroab163ca2016-07-07 14:05:00 -0700149 def __init__( self, name, *args, **kwargs ):
Flavio Castro5608a392016-06-22 17:02:35 -0700150 super( IpHost, self ).__init__( name, *args, **kwargs )
Flavio Castroab163ca2016-07-07 14:05:00 -0700151 gateway = re.split('\.|/', kwargs['ip'])
152 gateway[3] = '254'
153 self.gateway = '.'.join(gateway[0:4])
Flavio Castrocc38a542016-03-03 13:15:46 -0800154
Flavio Castro5608a392016-06-22 17:02:35 -0700155 def config( self, **kwargs ):
156 Host.config( self, **kwargs )
157 mtu = "ifconfig " + self.name + "-eth0 mtu 1490"
158 self.cmd( mtu )
159 self.cmd( 'ip route add default via %s' % self.gateway )
Flavio Castrocc38a542016-03-03 13:15:46 -0800160
Pierb95002f2016-12-05 15:20:42 -0800161class Ipv6Host( IpHost ):
162 """
163 Abstraction to model an augmented host with a ipv6
164 functionalities as well
165 """
166 def __init__( self, name, *args, **kwargs ):
167 IpHost.__init__(self, name, *args, **kwargs)
168
169 def config( self, **kwargs ):
170 IpHost.config( self, **kwargs )
171 ipv6Cmd = 'ifconfig %s-eth0 inet6 add %s' % (self.name, kwargs['ipv6'])
172 ipv6GatewayCmd = 'ip -6 route add default via %s' % kwargs['ipv6Gateway']
173 ipv6MtuCmd = 'ifconfig %s-eth0 inet6 mtu 1490' % (self.name)
174 self.cmd( ipv6Cmd )
175 self.cmd( ipv6GatewayCmd )
176 self.cmd( ipv6MtuCmd )
Flavio Castro5608a392016-06-22 17:02:35 -0700177
Flavio Castroe168f7f2016-06-24 15:53:12 -0700178class VLANHost( Host ):
179 "Host connected to VLAN interface"
180
181 def config( self, vlan=100, **params ):
182 """Configure VLANHost according to (optional) parameters:
183 vlan: VLAN ID for default interface"""
184 r = super( VLANHost, self ).config( **params )
185 intf = self.defaultIntf( )
186 # remove IP from default, "physical" interface
187 self.cmd( 'ifconfig %s inet 0' % intf )
188 intf = self.defaultIntf( )
189 # create VLAN interface
190 self.cmd( 'vconfig add %s %d' % (intf, vlan) )
191 self.cmd( 'ifconfig %s.%d %s' % (intf, vlan, params[ 'ip' ]) )
192 # update the intf name and host's intf map
193 self.cmd( 'ifconfig %s.%d mtu 1480' % (intf, vlan) )
194 newName = '%s.%d' % (intf, vlan)
195 # update the (Mininet) interface to refer to VLAN interface name
196 intf.name = newName
197 # add VLAN interface to host's name to intf map
198 self.nameToIntf[ newName ] = intf
199
Flavio Castroab163ca2016-07-07 14:05:00 -0700200class ExtendedCLI( CLI ):
201 """
202 Extends mininet CLI with the following commands:
203 addvlanhost
204 addiphost
205 """
206 def do_addhost( self, line ):
207 #Parsing args from CLI
208 args = line.split( )
209 if len( args ) < 3 or len( args ) :
210 "usage: addhost hostname switch **params"
211 hostname, switch = args[0], args[1]
212 params = eval(line.split( ' ', 3 )[2])
213 if 'cls' in params:
214 params['cls'] = eval( params[ 'cls' ] )
215 if hostname in self.mn:
216 #error( '%s already exists!\n' % hostname )
217 return
218 if switch not in self.mn:
219 #error( '%s does not exist!\n' % switch )
220 return
221 print params
222 host = self.mn.addHostCfg( hostname, **params )
223 #switch.attach( link.intf2 )
224 #host.config()
225 link = self.mn.addLink( host, switch )
226 host.config(**params)
Flavio Castroe168f7f2016-06-24 15:53:12 -0700227
Pierb95002f2016-12-05 15:20:42 -0800228 def do_pingall6( self, line ):
229 "Ping6 between all hosts."
230 self.mn.pingAll6( line )
231
Flavio Castro5608a392016-06-22 17:02:35 -0700232def config( opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800233 spine = opts.spine
234 leaf = opts.leaf
Flavio Castro5608a392016-06-22 17:02:35 -0700235 fanout = opts.fanout
Flavio Castroab163ca2016-07-07 14:05:00 -0700236 vlan = opts.vlan
Pierb95002f2016-12-05 15:20:42 -0800237 ipv6 = opts.ipv6
Flavio Castro5608a392016-06-22 17:02:35 -0700238 controllers = [ os.environ[ 'OC%s' % i ] for i in
239 range( 1, opts.onos + 1 ) ] if (opts.onos) else [
240 '127.0.0.1' ]
Pierb95002f2016-12-05 15:20:42 -0800241 if not ipv6:
242 topo = LeafAndSpine(
243 spine=spine,
244 leaf=leaf,
245 fanout=fanout,
246 vlan=vlan,
247 )
248 else:
249 topo = LeafAndSpine6(
250 spine=spine,
251 leaf=leaf,
252 fanout=fanout,
253 vlan=vlan,
254 ipv6=ipv6
255 )
Flavio Castro5608a392016-06-22 17:02:35 -0700256 net = Mininet( topo=topo, link=TCLink, build=False,
Flavio Castroe168f7f2016-06-24 15:53:12 -0700257 switch=UserSwitch, controller=None, autoSetMacs=True )
Flavio Castrocc38a542016-03-03 13:15:46 -0800258 i = 0
259 for ip in controllers:
Flavio Castro5608a392016-06-22 17:02:35 -0700260 net.addController( "c%s" % (i), controller=RemoteController, ip=ip )
Flavio Castrocc38a542016-03-03 13:15:46 -0800261 i += 1;
Flavio Castro5608a392016-06-22 17:02:35 -0700262 net.build( )
263 net.start( )
Pierb95002f2016-12-05 15:20:42 -0800264 if not ipv6:
265 out1 = net.get( 'out1' )
266 out1.cmd( "arp -s 10.0.9.254 10:00:00:00:00:01 -i %s " % (out1.intf()) )
267 ExtendedCLI(net)
Flavio Castro5608a392016-06-22 17:02:35 -0700268 net.stop( )
269
Flavio Castrocc38a542016-03-03 13:15:46 -0800270if __name__ == '__main__':
Flavio Castro5608a392016-06-22 17:02:35 -0700271 setLogLevel( 'info' )
272 config( opts )
273 os.system( 'sudo mn -c' )