blob: 1012fef78b29074912def28c1c057a483015e53d [file] [log] [blame]
Flavio Castrocc38a542016-03-03 13:15:46 -08001#!/usr/bin/python
2
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07003"""
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07004Copyright 2016 Open Networking Foundation ( ONF )
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07005
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
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070013 ( at your option ) any later version.
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -070014
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"""
Flavio Castrocc38a542016-03-03 13:15:46 -080023import os
Flavio Castroab163ca2016-07-07 14:05:00 -070024import re
Pierb95002f2016-12-05 15:20:42 -080025import math
Flavio Castrocc38a542016-03-03 13:15:46 -080026from optparse import OptionParser
Pierb95002f2016-12-05 15:20:42 -080027from ipaddress import IPv6Network, IPv4Network
Flavio Castrocc38a542016-03-03 13:15:46 -080028from mininet.net import Mininet
29from mininet.topo import Topo
Flavio Castroe168f7f2016-06-24 15:53:12 -070030from mininet.node import RemoteController, UserSwitch, Host, OVSBridge
Flavio Castrocc38a542016-03-03 13:15:46 -080031from mininet.link import TCLink
32from mininet.log import setLogLevel
33from mininet.cli import CLI
34
Flavio Castrocc38a542016-03-03 13:15:46 -080035
Flavio Castro5608a392016-06-22 17:02:35 -070036# Parse command line options and dump results
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070037def parseOptions():
38 "Parse command line options"
39 parser = OptionParser()
Flavio Castro5608a392016-06-22 17:02:35 -070040 parser.add_option( '--spine', dest='spine', type='int', default=2,
41 help='number of spine switches, default=2' )
42 parser.add_option( '--leaf', dest='leaf', type='int', default=2,
43 help='number of leaf switches, default=2' )
44 parser.add_option( '--fanout', dest='fanout', type='int', default=2,
45 help='number of hosts per leaf switch, default=2' )
46 parser.add_option( '--onos', dest='onos', type='int', default=0,
47 help='number of ONOS Instances, default=0, 0 means localhost, 1 will use OC1 and so on' )
Flavio Castroab163ca2016-07-07 14:05:00 -070048 parser.add_option( '--vlan', dest='vlan', type='int', default=-1,
49 help='vid of cross connect, default=-1, -1 means utilize default value' )
Pierb95002f2016-12-05 15:20:42 -080050 parser.add_option( '--ipv6', action="store_true", dest='ipv6',
51 help='hosts are capable to use also ipv6' )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070052 ( options, args ) = parser.parse_args()
Flavio Castrocc38a542016-03-03 13:15:46 -080053 return options, args
54
Flavio Castrocc38a542016-03-03 13:15:46 -080055
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070056opts, args = parseOptions()
Flavio Castro5608a392016-06-22 17:02:35 -070057
Pierb95002f2016-12-05 15:20:42 -080058IP6_SUBNET_CLASS = 120
59IP4_SUBNET_CLASS = 24
60
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070061
Pierb95002f2016-12-05 15:20:42 -080062class LeafAndSpine6( Topo ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070063
Pierb95002f2016-12-05 15:20:42 -080064 """
65 Create Leaf and Spine Topology for IPv4/IPv6 tests.
66 """
67 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
68 Topo.__init__( self, **opts )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070069 spines = {}
70 leafs = {}
Pierb95002f2016-12-05 15:20:42 -080071 """
72 We calculate the offset from /120 and from /24 in order to have
73 a number of /120 and /24 subnets == leaf
74 """
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070075 offset = int( math.ceil( math.sqrt( leaf ) ) )
Pierb95002f2016-12-05 15:20:42 -080076 """
77 We calculate the subnets to use and set options
78 """
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070079 ipv6SubnetClass = unicode( '2000::/%s' % ( IP6_SUBNET_CLASS - offset ) )
80 ipv6Subnets = list( IPv6Network( ipv6SubnetClass ).subnets( new_prefix=IP6_SUBNET_CLASS ) )
81 ipv4SubnetClass = unicode( '10.0.0.0/%s' % ( IP4_SUBNET_CLASS - offset ) )
82 ipv4Subnets = list( IPv4Network( ipv4SubnetClass ).subnets( new_prefix=IP4_SUBNET_CLASS ) )
83 linkopts = dict( bw=100 )
Pierb95002f2016-12-05 15:20:42 -080084 """
85 We create the spine switches
86 """
87 for s in range( spine ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070088 spines[ s ] = self.addSwitch( 'spine10%s' % ( s + 1 ),
89 dpid="00000000010%s" % ( s + 1 ) )
Pierb95002f2016-12-05 15:20:42 -080090 """
91 We create the leaf switches
92 """
93 for ls in range( leaf ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070094 leafs[ ls ] = self.addSwitch( 'leaf%s' % ( ls + 1 ),
95 dpid="00000000000%s" % ( 1 + ls ) )
96 ipv6Subnet = ipv6Subnets[ ls ]
97 ipv6Hosts = list( ipv6Subnet.hosts() )
98 ipv4Subnet = ipv4Subnets[ ls ]
99 ipv4Hosts = list( ipv4Subnet.hosts() )
Pierb95002f2016-12-05 15:20:42 -0800100 """
101 We add the hosts
102 """
103 for f in range( fanout ):
104 ipv6 = ipv6Hosts[ f ]
105 ipv6Gateway = ipv6Hosts[ len( ipv6Hosts ) - 1 ]
106 ipv4 = ipv4Hosts[ f ]
107 ipv4Gateway = ipv4Hosts[ len( ipv4Hosts ) - 1 ]
108 host = self.addHost(
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700109 name='h%s' % ( ls * fanout + f + 1 ),
Pierb95002f2016-12-05 15:20:42 -0800110 cls=Ipv6Host,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700111 ip="%s/%s" % ( ipv4, IP4_SUBNET_CLASS ),
Pierb95002f2016-12-05 15:20:42 -0800112 gateway='%s' % ipv4Gateway,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700113 ipv6="%s/%s" % ( ipv6, IP6_SUBNET_CLASS ),
Pierb95002f2016-12-05 15:20:42 -0800114 ipv6Gateway="%s" % ipv6Gateway
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700115 )
Pierb95002f2016-12-05 15:20:42 -0800116 self.addLink( host, leafs[ ls ], **linkopts )
117 """
118 Connect leaf to all spines
119 """
120 for s in range( spine ):
121 switch = spines[ s ]
122 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700123
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700124
Flavio Castro5608a392016-06-22 17:02:35 -0700125class LeafAndSpine( Topo ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700126
Flavio Castro5608a392016-06-22 17:02:35 -0700127 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800128 "Create Leaf and Spine Topo."
Flavio Castro5608a392016-06-22 17:02:35 -0700129 Topo.__init__( self, **opts )
Flavio Castrocc38a542016-03-03 13:15:46 -0800130 # Add spine switches
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700131 spines = {}
132 leafs = {}
Flavio Castro5608a392016-06-22 17:02:35 -0700133 for s in range( spine ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700134 spines[ s ] = self.addSwitch( 'spine10%s' % ( s + 1 ),
135 dpid="00000000010%s" % ( s + 1 ) )
Flavio Castrocc38a542016-03-03 13:15:46 -0800136 # Set link speeds to 100Mb/s
Flavio Castro5608a392016-06-22 17:02:35 -0700137 linkopts = dict( bw=100 )
Flavio Castrocc38a542016-03-03 13:15:46 -0800138 # Add Leaf switches
Flavio Castro5608a392016-06-22 17:02:35 -0700139 for ls in range( leaf ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700140 leafs[ ls ] = self.addSwitch( 'leaf%s' % ( ls + 1 ),
141 dpid="00000000000%s" % ( 1 + ls ) )
Flavio Castrocc38a542016-03-03 13:15:46 -0800142 # Add hosts under a leaf, fanout hosts per leaf switch
Flavio Castro5608a392016-06-22 17:02:35 -0700143 for f in range( fanout ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700144 host = self.addHost( 'h%s' % ( ls * fanout + f + 1 ),
Flavio Castro5608a392016-06-22 17:02:35 -0700145 cls=IpHost,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700146 ip='10.0.%s.%s/24' % ( ( ls + 1 ), ( f + 1 ) ),
147 gateway='10.0.%s.254' % ( ls + 1 ) )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700148 self.addLink( host, leafs[ ls ], **linkopts )
149 # Add Xconnect simulation
Flavio Castroab163ca2016-07-07 14:05:00 -0700150 if ls is 0:
151 in1 = self.addHost( 'in1', cls=IpHost, ip='10.0.1.9/24', mac="00:00:00:00:00:09" )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700152 self.addLink( in1, leafs[ 0 ], **linkopts )
Flavio Castroab163ca2016-07-07 14:05:00 -0700153 out1 = self.addHost( 'out1', cls=IpHost, ip='10.0.9.1/24', mac="00:00:00:00:09:01" )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700154 self.addLink( out1, leafs[ 0 ], **linkopts )
Flavio Castroab163ca2016-07-07 14:05:00 -0700155 br1 = self.addSwitch( 'br1', cls=OVSBridge )
156 self.addLink( br1, leafs[ 0 ], **linkopts )
157 vlans = [ 1, 5, 10 ]
158 for vid in vlans:
159 olt = self.addHost( 'olt%s' % vid, cls=VLANHost, vlan=vid,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700160 ip="10.%s.0.1/24" % vid, mac="00:00:%02d:00:00:01" % vid )
Flavio Castroab163ca2016-07-07 14:05:00 -0700161 vsg = self.addHost( 'vsg%s' % vid, cls=VLANHost, vlan=vid,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700162 ip="10.%s.0.2/24" % vid, mac="00:00:%02d:00:00:02" % vid )
Flavio Castroab163ca2016-07-07 14:05:00 -0700163 self.addLink( olt, leafs[ 0 ], **linkopts )
164 self.addLink( vsg, br1, **linkopts )
165 # Connect leaf to all spines
166 for s in range( spine ):
167 switch = spines[ s ]
168 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700169
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700170
Flavio Castro5608a392016-06-22 17:02:35 -0700171class IpHost( Host ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700172
Flavio Castroab163ca2016-07-07 14:05:00 -0700173 def __init__( self, name, *args, **kwargs ):
Flavio Castro5608a392016-06-22 17:02:35 -0700174 super( IpHost, self ).__init__( name, *args, **kwargs )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700175 gateway = re.split( '\.|/', kwargs[ 'ip' ] )
176 gateway[ 3 ] = '254'
177 self.gateway = '.'.join( gateway[ 0:4 ] )
Flavio Castrocc38a542016-03-03 13:15:46 -0800178
Flavio Castro5608a392016-06-22 17:02:35 -0700179 def config( self, **kwargs ):
180 Host.config( self, **kwargs )
181 mtu = "ifconfig " + self.name + "-eth0 mtu 1490"
182 self.cmd( mtu )
183 self.cmd( 'ip route add default via %s' % self.gateway )
Flavio Castrocc38a542016-03-03 13:15:46 -0800184
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700185
Pierb95002f2016-12-05 15:20:42 -0800186class Ipv6Host( IpHost ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700187
Pierb95002f2016-12-05 15:20:42 -0800188 """
189 Abstraction to model an augmented host with a ipv6
190 functionalities as well
191 """
192 def __init__( self, name, *args, **kwargs ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700193 IpHost.__init__( self, name, *args, **kwargs )
Pierb95002f2016-12-05 15:20:42 -0800194
195 def config( self, **kwargs ):
196 IpHost.config( self, **kwargs )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700197 ipv6Cmd = 'ifconfig %s-eth0 inet6 add %s' % ( self.name, kwargs[ 'ipv6' ] )
198 ipv6GatewayCmd = 'ip -6 route add default via %s' % kwargs[ 'ipv6Gateway' ]
199 ipv6MtuCmd = 'ifconfig %s-eth0 inet6 mtu 1490' % ( self.name )
Pierb95002f2016-12-05 15:20:42 -0800200 self.cmd( ipv6Cmd )
201 self.cmd( ipv6GatewayCmd )
202 self.cmd( ipv6MtuCmd )
Flavio Castro5608a392016-06-22 17:02:35 -0700203
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700204
Flavio Castroe168f7f2016-06-24 15:53:12 -0700205class VLANHost( Host ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700206
Flavio Castroe168f7f2016-06-24 15:53:12 -0700207 "Host connected to VLAN interface"
208
209 def config( self, vlan=100, **params ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700210 """Configure VLANHost according to ( optional ) parameters:
Flavio Castroe168f7f2016-06-24 15:53:12 -0700211 vlan: VLAN ID for default interface"""
212 r = super( VLANHost, self ).config( **params )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700213 intf = self.defaultIntf()
Flavio Castroe168f7f2016-06-24 15:53:12 -0700214 # remove IP from default, "physical" interface
215 self.cmd( 'ifconfig %s inet 0' % intf )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700216 intf = self.defaultIntf()
Flavio Castroe168f7f2016-06-24 15:53:12 -0700217 # create VLAN interface
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700218 self.cmd( 'vconfig add %s %d' % ( intf, vlan ) )
219 self.cmd( 'ifconfig %s.%d %s' % ( intf, vlan, params[ 'ip' ] ) )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700220 # update the intf name and host's intf map
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700221 self.cmd( 'ifconfig %s.%d mtu 1480' % ( intf, vlan ) )
222 newName = '%s.%d' % ( intf, vlan )
223 # update the ( Mininet ) interface to refer to VLAN interface name
Flavio Castroe168f7f2016-06-24 15:53:12 -0700224 intf.name = newName
225 # add VLAN interface to host's name to intf map
226 self.nameToIntf[ newName ] = intf
227
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700228
Flavio Castroab163ca2016-07-07 14:05:00 -0700229class ExtendedCLI( CLI ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700230
Flavio Castroab163ca2016-07-07 14:05:00 -0700231 """
232 Extends mininet CLI with the following commands:
233 addvlanhost
234 addiphost
235 """
236 def do_addhost( self, line ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700237 # Parsing args from CLI
238 args = line.split()
239 if len( args ) < 3 or len( args ):
Flavio Castroab163ca2016-07-07 14:05:00 -0700240 "usage: addhost hostname switch **params"
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700241 hostname, switch = args[ 0 ], args[ 1 ]
242 params = eval( line.split( ' ', 3 )[ 2 ] )
Flavio Castroab163ca2016-07-07 14:05:00 -0700243 if 'cls' in params:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700244 params[ 'cls' ] = eval( params[ 'cls' ] )
Flavio Castroab163ca2016-07-07 14:05:00 -0700245 if hostname in self.mn:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700246 # error( '%s already exists!\n' % hostname )
Flavio Castroab163ca2016-07-07 14:05:00 -0700247 return
248 if switch not in self.mn:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700249 # error( '%s does not exist!\n' % switch )
Flavio Castroab163ca2016-07-07 14:05:00 -0700250 return
251 print params
252 host = self.mn.addHostCfg( hostname, **params )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700253 # switch.attach( link.intf2 )
254 # host.config()
Flavio Castroab163ca2016-07-07 14:05:00 -0700255 link = self.mn.addLink( host, switch )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700256 host.config( **params )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700257
Pierb95002f2016-12-05 15:20:42 -0800258 def do_pingall6( self, line ):
259 "Ping6 between all hosts."
260 self.mn.pingAll6( line )
261
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700262
Flavio Castro5608a392016-06-22 17:02:35 -0700263def config( opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800264 spine = opts.spine
265 leaf = opts.leaf
Flavio Castro5608a392016-06-22 17:02:35 -0700266 fanout = opts.fanout
Flavio Castroab163ca2016-07-07 14:05:00 -0700267 vlan = opts.vlan
Pierb95002f2016-12-05 15:20:42 -0800268 ipv6 = opts.ipv6
Flavio Castro5608a392016-06-22 17:02:35 -0700269 controllers = [ os.environ[ 'OC%s' % i ] for i in
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700270 range( 1, opts.onos + 1 ) ] if ( opts.onos ) else [
Flavio Castro5608a392016-06-22 17:02:35 -0700271 '127.0.0.1' ]
Pierb95002f2016-12-05 15:20:42 -0800272 if not ipv6:
273 topo = LeafAndSpine(
274 spine=spine,
275 leaf=leaf,
276 fanout=fanout,
277 vlan=vlan,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700278 )
Pierb95002f2016-12-05 15:20:42 -0800279 else:
280 topo = LeafAndSpine6(
281 spine=spine,
282 leaf=leaf,
283 fanout=fanout,
284 vlan=vlan,
285 ipv6=ipv6
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700286 )
Flavio Castro5608a392016-06-22 17:02:35 -0700287 net = Mininet( topo=topo, link=TCLink, build=False,
Flavio Castroe168f7f2016-06-24 15:53:12 -0700288 switch=UserSwitch, controller=None, autoSetMacs=True )
Flavio Castrocc38a542016-03-03 13:15:46 -0800289 i = 0
290 for ip in controllers:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700291 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
292 i += 1
293 net.build()
294 net.start()
Pierb95002f2016-12-05 15:20:42 -0800295 if not ipv6:
296 out1 = net.get( 'out1' )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700297 out1.cmd( "arp -s 10.0.9.254 10:00:00:00:00:01 -i %s " % ( out1.intf() ) )
298 ExtendedCLI( net )
299 net.stop()
Flavio Castro5608a392016-06-22 17:02:35 -0700300
Flavio Castrocc38a542016-03-03 13:15:46 -0800301if __name__ == '__main__':
Flavio Castro5608a392016-06-22 17:02:35 -0700302 setLogLevel( 'info' )
303 config( opts )
304 os.system( 'sudo mn -c' )