blob: 1c7e25819ec6a7799de5e6f57f56ac6cbe97c796 [file] [log] [blame]
Flavio Castrocc38a542016-03-03 13:15:46 -08001#!/usr/bin/python
2
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07003"""
4Copyright 2016 Open Networking Foundation (ONF)
5
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
9
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 (at your option) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TestON. If not, see <http://www.gnu.org/licenses/>.
22"""
23
Flavio Castrocc38a542016-03-03 13:15:46 -080024import os
Flavio Castroab163ca2016-07-07 14:05:00 -070025import re
Pierb95002f2016-12-05 15:20:42 -080026import math
Flavio Castrocc38a542016-03-03 13:15:46 -080027from optparse import OptionParser
Pierb95002f2016-12-05 15:20:42 -080028from ipaddress import IPv6Network, IPv4Network
Flavio Castrocc38a542016-03-03 13:15:46 -080029from mininet.net import Mininet
30from mininet.topo import Topo
Flavio Castroe168f7f2016-06-24 15:53:12 -070031from mininet.node import RemoteController, UserSwitch, Host, OVSBridge
Flavio Castrocc38a542016-03-03 13:15:46 -080032from mininet.link import TCLink
33from mininet.log import setLogLevel
34from mininet.cli import CLI
35
Flavio Castrocc38a542016-03-03 13:15:46 -080036
Flavio Castro5608a392016-06-22 17:02:35 -070037# Parse command line options and dump results
38def parseOptions( ):
39 """Parse command line options"""
40 parser = OptionParser( )
41 parser.add_option( '--spine', dest='spine', type='int', default=2,
42 help='number of spine switches, default=2' )
43 parser.add_option( '--leaf', dest='leaf', type='int', default=2,
44 help='number of leaf switches, default=2' )
45 parser.add_option( '--fanout', dest='fanout', type='int', default=2,
46 help='number of hosts per leaf switch, default=2' )
47 parser.add_option( '--onos', dest='onos', type='int', default=0,
48 help='number of ONOS Instances, default=0, 0 means localhost, 1 will use OC1 and so on' )
Flavio Castroab163ca2016-07-07 14:05:00 -070049 parser.add_option( '--vlan', dest='vlan', type='int', default=-1,
50 help='vid of cross connect, default=-1, -1 means utilize default value' )
Pierb95002f2016-12-05 15:20:42 -080051 parser.add_option( '--ipv6', action="store_true", dest='ipv6',
52 help='hosts are capable to use also ipv6' )
Flavio Castro5608a392016-06-22 17:02:35 -070053 (options, args) = parser.parse_args( )
Flavio Castrocc38a542016-03-03 13:15:46 -080054 return options, args
55
Flavio Castrocc38a542016-03-03 13:15:46 -080056
Flavio Castro5608a392016-06-22 17:02:35 -070057opts, args = parseOptions( )
58
Pierb95002f2016-12-05 15:20:42 -080059IP6_SUBNET_CLASS = 120
60IP4_SUBNET_CLASS = 24
61
62class LeafAndSpine6( Topo ):
63 """
64 Create Leaf and Spine Topology for IPv4/IPv6 tests.
65 """
66 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
67 Topo.__init__( self, **opts )
68 spines = {}
69 leafs = {}
70 """
71 We calculate the offset from /120 and from /24 in order to have
72 a number of /120 and /24 subnets == leaf
73 """
74 offset = int(math.ceil(math.sqrt( leaf )))
75 """
76 We calculate the subnets to use and set options
77 """
78 ipv6SubnetClass = unicode('2000::/%s' % (IP6_SUBNET_CLASS - offset))
79 ipv6Subnets = list(IPv6Network(ipv6SubnetClass).subnets( new_prefix = IP6_SUBNET_CLASS ))
80 ipv4SubnetClass = unicode('10.0.0.0/%s' % (IP4_SUBNET_CLASS - offset))
81 ipv4Subnets = list(IPv4Network(ipv4SubnetClass).subnets( new_prefix = IP4_SUBNET_CLASS ))
82 linkopts = dict( bw=100 )
83 """
84 We create the spine switches
85 """
86 for s in range( spine ):
87 spines[ s ] = self.addSwitch( 'spine10%s' % (s + 1),
88 dpid="00000000010%s" % (s + 1) )
89 """
90 We create the leaf switches
91 """
92 for ls in range( leaf ):
93 leafs[ ls ] = self.addSwitch( 'leaf%s' % (ls + 1),
94 dpid="00000000000%s" % (1 + ls) )
95 ipv6Subnet = ipv6Subnets[ ls ]
96 ipv6Hosts = list(ipv6Subnet.hosts())
97 ipv4Subnet = ipv4Subnets[ ls ]
98 ipv4Hosts = list(ipv4Subnet.hosts())
99 """
100 We add the hosts
101 """
102 for f in range( fanout ):
103 ipv6 = ipv6Hosts[ f ]
104 ipv6Gateway = ipv6Hosts[ len( ipv6Hosts ) - 1 ]
105 ipv4 = ipv4Hosts[ f ]
106 ipv4Gateway = ipv4Hosts[ len( ipv4Hosts ) - 1 ]
107 host = self.addHost(
108 name='h%s' % (ls * fanout + f + 1),
109 cls=Ipv6Host,
110 ip="%s/%s" %(ipv4, IP4_SUBNET_CLASS),
111 gateway='%s' % ipv4Gateway,
112 ipv6="%s/%s" %(ipv6, IP6_SUBNET_CLASS),
113 ipv6Gateway="%s" % ipv6Gateway
114 )
115 self.addLink( host, leafs[ ls ], **linkopts )
116 """
117 Connect leaf to all spines
118 """
119 for s in range( spine ):
120 switch = spines[ s ]
121 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700122
123class LeafAndSpine( Topo ):
124 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800125 "Create Leaf and Spine Topo."
Flavio Castro5608a392016-06-22 17:02:35 -0700126 Topo.__init__( self, **opts )
Flavio Castrocc38a542016-03-03 13:15:46 -0800127 # Add spine switches
Flavio Castro5608a392016-06-22 17:02:35 -0700128 spines = { }
Flavio Castroe168f7f2016-06-24 15:53:12 -0700129 leafs = { }
Flavio Castro5608a392016-06-22 17:02:35 -0700130 for s in range( spine ):
131 spines[ s ] = self.addSwitch( 'spine10%s' % (s + 1),
132 dpid="00000000010%s" % (s + 1) )
Flavio Castrocc38a542016-03-03 13:15:46 -0800133 # Set link speeds to 100Mb/s
Flavio Castro5608a392016-06-22 17:02:35 -0700134 linkopts = dict( bw=100 )
Flavio Castrocc38a542016-03-03 13:15:46 -0800135 # Add Leaf switches
Flavio Castro5608a392016-06-22 17:02:35 -0700136 for ls in range( leaf ):
Flavio Castroe168f7f2016-06-24 15:53:12 -0700137 leafs[ ls ] = self.addSwitch( 'leaf%s' % (ls + 1),
138 dpid="00000000000%s" % (1 + ls) )
Flavio Castrocc38a542016-03-03 13:15:46 -0800139 # Add hosts under a leaf, fanout hosts per leaf switch
Flavio Castro5608a392016-06-22 17:02:35 -0700140 for f in range( fanout ):
141 host = self.addHost( 'h%s' % (ls * fanout + f + 1),
142 cls=IpHost,
143 ip='10.0.%s.%s/24' % ((ls + 1), (f + 1)),
144 gateway='10.0.%s.254' % (ls + 1) )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700145 self.addLink( host, leafs[ ls ], **linkopts )
146 # Add Xconnect simulation
Flavio Castroab163ca2016-07-07 14:05:00 -0700147 if ls is 0:
148 in1 = self.addHost( 'in1', cls=IpHost, ip='10.0.1.9/24', mac="00:00:00:00:00:09" )
149 self.addLink( in1, leafs[0], **linkopts )
150 out1 = self.addHost( 'out1', cls=IpHost, ip='10.0.9.1/24', mac="00:00:00:00:09:01" )
151 self.addLink( out1, leafs[0], **linkopts )
152 br1 = self.addSwitch( 'br1', cls=OVSBridge )
153 self.addLink( br1, leafs[ 0 ], **linkopts )
154 vlans = [ 1, 5, 10 ]
155 for vid in vlans:
156 olt = self.addHost( 'olt%s' % vid, cls=VLANHost, vlan=vid,
157 ip="10.%s.0.1/24" % vid
158 , mac="00:00:%02d:00:00:01" % vid )
159 vsg = self.addHost( 'vsg%s' % vid, cls=VLANHost, vlan=vid,
160 ip="10.%s.0.2/24" % vid
161 , mac="00:00:%02d:00:00:02" % vid )
162 self.addLink( olt, leafs[ 0 ], **linkopts )
163 self.addLink( vsg, br1, **linkopts )
164 # Connect leaf to all spines
165 for s in range( spine ):
166 switch = spines[ s ]
167 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700168
169class IpHost( Host ):
Flavio Castroab163ca2016-07-07 14:05:00 -0700170 def __init__( self, name, *args, **kwargs ):
Flavio Castro5608a392016-06-22 17:02:35 -0700171 super( IpHost, self ).__init__( name, *args, **kwargs )
Flavio Castroab163ca2016-07-07 14:05:00 -0700172 gateway = re.split('\.|/', kwargs['ip'])
173 gateway[3] = '254'
174 self.gateway = '.'.join(gateway[0:4])
Flavio Castrocc38a542016-03-03 13:15:46 -0800175
Flavio Castro5608a392016-06-22 17:02:35 -0700176 def config( self, **kwargs ):
177 Host.config( self, **kwargs )
178 mtu = "ifconfig " + self.name + "-eth0 mtu 1490"
179 self.cmd( mtu )
180 self.cmd( 'ip route add default via %s' % self.gateway )
Flavio Castrocc38a542016-03-03 13:15:46 -0800181
Pierb95002f2016-12-05 15:20:42 -0800182class Ipv6Host( IpHost ):
183 """
184 Abstraction to model an augmented host with a ipv6
185 functionalities as well
186 """
187 def __init__( self, name, *args, **kwargs ):
188 IpHost.__init__(self, name, *args, **kwargs)
189
190 def config( self, **kwargs ):
191 IpHost.config( self, **kwargs )
192 ipv6Cmd = 'ifconfig %s-eth0 inet6 add %s' % (self.name, kwargs['ipv6'])
193 ipv6GatewayCmd = 'ip -6 route add default via %s' % kwargs['ipv6Gateway']
194 ipv6MtuCmd = 'ifconfig %s-eth0 inet6 mtu 1490' % (self.name)
195 self.cmd( ipv6Cmd )
196 self.cmd( ipv6GatewayCmd )
197 self.cmd( ipv6MtuCmd )
Flavio Castro5608a392016-06-22 17:02:35 -0700198
Flavio Castroe168f7f2016-06-24 15:53:12 -0700199class VLANHost( Host ):
200 "Host connected to VLAN interface"
201
202 def config( self, vlan=100, **params ):
203 """Configure VLANHost according to (optional) parameters:
204 vlan: VLAN ID for default interface"""
205 r = super( VLANHost, self ).config( **params )
206 intf = self.defaultIntf( )
207 # remove IP from default, "physical" interface
208 self.cmd( 'ifconfig %s inet 0' % intf )
209 intf = self.defaultIntf( )
210 # create VLAN interface
211 self.cmd( 'vconfig add %s %d' % (intf, vlan) )
212 self.cmd( 'ifconfig %s.%d %s' % (intf, vlan, params[ 'ip' ]) )
213 # update the intf name and host's intf map
214 self.cmd( 'ifconfig %s.%d mtu 1480' % (intf, vlan) )
215 newName = '%s.%d' % (intf, vlan)
216 # update the (Mininet) interface to refer to VLAN interface name
217 intf.name = newName
218 # add VLAN interface to host's name to intf map
219 self.nameToIntf[ newName ] = intf
220
Flavio Castroab163ca2016-07-07 14:05:00 -0700221class ExtendedCLI( CLI ):
222 """
223 Extends mininet CLI with the following commands:
224 addvlanhost
225 addiphost
226 """
227 def do_addhost( self, line ):
228 #Parsing args from CLI
229 args = line.split( )
230 if len( args ) < 3 or len( args ) :
231 "usage: addhost hostname switch **params"
232 hostname, switch = args[0], args[1]
233 params = eval(line.split( ' ', 3 )[2])
234 if 'cls' in params:
235 params['cls'] = eval( params[ 'cls' ] )
236 if hostname in self.mn:
237 #error( '%s already exists!\n' % hostname )
238 return
239 if switch not in self.mn:
240 #error( '%s does not exist!\n' % switch )
241 return
242 print params
243 host = self.mn.addHostCfg( hostname, **params )
244 #switch.attach( link.intf2 )
245 #host.config()
246 link = self.mn.addLink( host, switch )
247 host.config(**params)
Flavio Castroe168f7f2016-06-24 15:53:12 -0700248
Pierb95002f2016-12-05 15:20:42 -0800249 def do_pingall6( self, line ):
250 "Ping6 between all hosts."
251 self.mn.pingAll6( line )
252
Flavio Castro5608a392016-06-22 17:02:35 -0700253def config( opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800254 spine = opts.spine
255 leaf = opts.leaf
Flavio Castro5608a392016-06-22 17:02:35 -0700256 fanout = opts.fanout
Flavio Castroab163ca2016-07-07 14:05:00 -0700257 vlan = opts.vlan
Pierb95002f2016-12-05 15:20:42 -0800258 ipv6 = opts.ipv6
Flavio Castro5608a392016-06-22 17:02:35 -0700259 controllers = [ os.environ[ 'OC%s' % i ] for i in
260 range( 1, opts.onos + 1 ) ] if (opts.onos) else [
261 '127.0.0.1' ]
Pierb95002f2016-12-05 15:20:42 -0800262 if not ipv6:
263 topo = LeafAndSpine(
264 spine=spine,
265 leaf=leaf,
266 fanout=fanout,
267 vlan=vlan,
268 )
269 else:
270 topo = LeafAndSpine6(
271 spine=spine,
272 leaf=leaf,
273 fanout=fanout,
274 vlan=vlan,
275 ipv6=ipv6
276 )
Flavio Castro5608a392016-06-22 17:02:35 -0700277 net = Mininet( topo=topo, link=TCLink, build=False,
Flavio Castroe168f7f2016-06-24 15:53:12 -0700278 switch=UserSwitch, controller=None, autoSetMacs=True )
Flavio Castrocc38a542016-03-03 13:15:46 -0800279 i = 0
280 for ip in controllers:
Flavio Castro5608a392016-06-22 17:02:35 -0700281 net.addController( "c%s" % (i), controller=RemoteController, ip=ip )
Flavio Castrocc38a542016-03-03 13:15:46 -0800282 i += 1;
Flavio Castro5608a392016-06-22 17:02:35 -0700283 net.build( )
284 net.start( )
Pierb95002f2016-12-05 15:20:42 -0800285 if not ipv6:
286 out1 = net.get( 'out1' )
287 out1.cmd( "arp -s 10.0.9.254 10:00:00:00:00:01 -i %s " % (out1.intf()) )
288 ExtendedCLI(net)
Flavio Castro5608a392016-06-22 17:02:35 -0700289 net.stop( )
290
Flavio Castrocc38a542016-03-03 13:15:46 -0800291if __name__ == '__main__':
Flavio Castro5608a392016-06-22 17:02:35 -0700292 setLogLevel( 'info' )
293 config( opts )
294 os.system( 'sudo mn -c' )