blob: d56e0b2f448fde22e7c29fd2eaa75541d088be6e [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
Jon Hallf69e3162020-09-01 09:08:44 -070030from mininet.node import RemoteController, Host, OVSBridge, OVSSwitch
Flavio Castrocc38a542016-03-03 13:15:46 -080031from mininet.link import TCLink
32from mininet.log import setLogLevel
33from mininet.cli import CLI
34
Jon Hallf69e3162020-09-01 09:08:44 -070035from bmv2 import ONOSBmv2Switch
36from stratum import StratumBmv2Switch
Flavio Castrocc38a542016-03-03 13:15:46 -080037
Flavio Castro5608a392016-06-22 17:02:35 -070038# Parse command line options and dump results
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070039def parseOptions():
40 "Parse command line options"
41 parser = OptionParser()
Flavio Castro5608a392016-06-22 17:02:35 -070042 parser.add_option( '--spine', dest='spine', type='int', default=2,
43 help='number of spine switches, default=2' )
44 parser.add_option( '--leaf', dest='leaf', type='int', default=2,
45 help='number of leaf switches, default=2' )
46 parser.add_option( '--fanout', dest='fanout', type='int', default=2,
47 help='number of hosts per leaf switch, default=2' )
Jonghwan Hyun76a02b72018-01-30 16:40:48 +090048 parser.add_option( '--onos-ip', dest='onosIp', type='str', default='',
49 help='IP address list of ONOS instances, separated by comma(,). Overrides --onos option' )
Flavio Castroab163ca2016-07-07 14:05:00 -070050 parser.add_option( '--vlan', dest='vlan', type='int', default=-1,
51 help='vid of cross connect, default=-1, -1 means utilize default value' )
Pierb95002f2016-12-05 15:20:42 -080052 parser.add_option( '--ipv6', action="store_true", dest='ipv6',
53 help='hosts are capable to use also ipv6' )
Jon Hallf69e3162020-09-01 09:08:44 -070054 parser.add_option( '--switch', dest='switch', type='str', default='ovs',
55 help='Switch type: ovs, bmv2 (with fabric.p4), stratum' )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070056 ( options, args ) = parser.parse_args()
Flavio Castrocc38a542016-03-03 13:15:46 -080057 return options, args
58
Flavio Castrocc38a542016-03-03 13:15:46 -080059
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070060opts, args = parseOptions()
Flavio Castro5608a392016-06-22 17:02:35 -070061
Jon Hallf69e3162020-09-01 09:08:44 -070062FABRIC_PIPECONF = "org.onosproject.pipelines.fabric"
63
64SWITCH_TO_PARAMS_DICT = {
65 "ovs": dict(cls=OVSSwitch),
66 "bmv2": dict(cls=ONOSBmv2Switch, pipeconf=FABRIC_PIPECONF),
67 "stratum": dict(cls=StratumBmv2Switch, pipeconf=FABRIC_PIPECONF, loglevel='debug')
68}
69if opts.switch not in SWITCH_TO_PARAMS_DICT:
70 raise Exception("Unknown switch type '%s'" % opts.switch)
71SWITCH_PARAMS = SWITCH_TO_PARAMS_DICT[opts.switch]
72
Pierb95002f2016-12-05 15:20:42 -080073IP6_SUBNET_CLASS = 120
74IP4_SUBNET_CLASS = 24
75
76class LeafAndSpine6( Topo ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070077
Pierb95002f2016-12-05 15:20:42 -080078 """
79 Create Leaf and Spine Topology for IPv4/IPv6 tests.
80 """
81 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
82 Topo.__init__( self, **opts )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070083 spines = {}
84 leafs = {}
Pierb95002f2016-12-05 15:20:42 -080085 """
86 We calculate the offset from /120 and from /24 in order to have
87 a number of /120 and /24 subnets == leaf
88 """
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070089 offset = int( math.ceil( math.sqrt( leaf ) ) )
Pierb95002f2016-12-05 15:20:42 -080090 """
91 We calculate the subnets to use and set options
92 """
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070093 ipv6SubnetClass = unicode( '2000::/%s' % ( IP6_SUBNET_CLASS - offset ) )
94 ipv6Subnets = list( IPv6Network( ipv6SubnetClass ).subnets( new_prefix=IP6_SUBNET_CLASS ) )
95 ipv4SubnetClass = unicode( '10.0.0.0/%s' % ( IP4_SUBNET_CLASS - offset ) )
96 ipv4Subnets = list( IPv4Network( ipv4SubnetClass ).subnets( new_prefix=IP4_SUBNET_CLASS ) )
97 linkopts = dict( bw=100 )
Pierb95002f2016-12-05 15:20:42 -080098 """
99 We create the spine switches
100 """
101 for s in range( spine ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700102 spines[ s ] = self.addSwitch( 'spine10%s' % ( s + 1 ),
103 dpid="00000000010%s" % ( s + 1 ) )
Pierb95002f2016-12-05 15:20:42 -0800104 """
105 We create the leaf switches
106 """
107 for ls in range( leaf ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700108 leafs[ ls ] = self.addSwitch( 'leaf%s' % ( ls + 1 ),
109 dpid="00000000000%s" % ( 1 + ls ) )
110 ipv6Subnet = ipv6Subnets[ ls ]
111 ipv6Hosts = list( ipv6Subnet.hosts() )
112 ipv4Subnet = ipv4Subnets[ ls ]
113 ipv4Hosts = list( ipv4Subnet.hosts() )
Pierb95002f2016-12-05 15:20:42 -0800114 """
115 We add the hosts
116 """
117 for f in range( fanout ):
118 ipv6 = ipv6Hosts[ f ]
119 ipv6Gateway = ipv6Hosts[ len( ipv6Hosts ) - 1 ]
120 ipv4 = ipv4Hosts[ f ]
121 ipv4Gateway = ipv4Hosts[ len( ipv4Hosts ) - 1 ]
122 host = self.addHost(
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700123 name='h%s' % ( ls * fanout + f + 1 ),
Pierb95002f2016-12-05 15:20:42 -0800124 cls=Ipv6Host,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700125 ip="%s/%s" % ( ipv4, IP4_SUBNET_CLASS ),
Pierb95002f2016-12-05 15:20:42 -0800126 gateway='%s' % ipv4Gateway,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700127 ipv6="%s/%s" % ( ipv6, IP6_SUBNET_CLASS ),
Pierb95002f2016-12-05 15:20:42 -0800128 ipv6Gateway="%s" % ipv6Gateway
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700129 )
Pierb95002f2016-12-05 15:20:42 -0800130 self.addLink( host, leafs[ ls ], **linkopts )
131 """
132 Connect leaf to all spines
133 """
134 for s in range( spine ):
135 switch = spines[ s ]
136 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700137
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700138
Flavio Castro5608a392016-06-22 17:02:35 -0700139class LeafAndSpine( Topo ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700140
Flavio Castro5608a392016-06-22 17:02:35 -0700141 def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800142 "Create Leaf and Spine Topo."
Flavio Castro5608a392016-06-22 17:02:35 -0700143 Topo.__init__( self, **opts )
Flavio Castrocc38a542016-03-03 13:15:46 -0800144 # Add spine switches
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700145 spines = {}
146 leafs = {}
Flavio Castro5608a392016-06-22 17:02:35 -0700147 for s in range( spine ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700148 spines[ s ] = self.addSwitch( 'spine10%s' % ( s + 1 ),
Jon Hallf69e3162020-09-01 09:08:44 -0700149 dpid="00000000010%s" % ( s + 1 ),
150 **SWITCH_PARAMS )
Flavio Castrocc38a542016-03-03 13:15:46 -0800151 # Set link speeds to 100Mb/s
Flavio Castro5608a392016-06-22 17:02:35 -0700152 linkopts = dict( bw=100 )
Flavio Castrocc38a542016-03-03 13:15:46 -0800153 # Add Leaf switches
Flavio Castro5608a392016-06-22 17:02:35 -0700154 for ls in range( leaf ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700155 leafs[ ls ] = self.addSwitch( 'leaf%s' % ( ls + 1 ),
Jon Hallf69e3162020-09-01 09:08:44 -0700156 dpid="00000000000%s" % ( 1 + ls ),
157 **SWITCH_PARAMS )
Flavio Castrocc38a542016-03-03 13:15:46 -0800158 # Add hosts under a leaf, fanout hosts per leaf switch
Flavio Castro5608a392016-06-22 17:02:35 -0700159 for f in range( fanout ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700160 host = self.addHost( 'h%s' % ( ls * fanout + f + 1 ),
Flavio Castro5608a392016-06-22 17:02:35 -0700161 cls=IpHost,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700162 ip='10.0.%s.%s/24' % ( ( ls + 1 ), ( f + 1 ) ),
163 gateway='10.0.%s.254' % ( ls + 1 ) )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700164 self.addLink( host, leafs[ ls ], **linkopts )
165 # Add Xconnect simulation
Flavio Castroab163ca2016-07-07 14:05:00 -0700166 if ls is 0:
167 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 -0700168 self.addLink( in1, leafs[ 0 ], **linkopts )
Flavio Castroab163ca2016-07-07 14:05:00 -0700169 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 -0700170 self.addLink( out1, leafs[ 0 ], **linkopts )
Flavio Castroab163ca2016-07-07 14:05:00 -0700171 br1 = self.addSwitch( 'br1', cls=OVSBridge )
172 self.addLink( br1, leafs[ 0 ], **linkopts )
173 vlans = [ 1, 5, 10 ]
174 for vid in vlans:
175 olt = self.addHost( 'olt%s' % vid, cls=VLANHost, vlan=vid,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700176 ip="10.%s.0.1/24" % vid, mac="00:00:%02d:00:00:01" % vid )
Flavio Castroab163ca2016-07-07 14:05:00 -0700177 vsg = self.addHost( 'vsg%s' % vid, cls=VLANHost, vlan=vid,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700178 ip="10.%s.0.2/24" % vid, mac="00:00:%02d:00:00:02" % vid )
Flavio Castroab163ca2016-07-07 14:05:00 -0700179 self.addLink( olt, leafs[ 0 ], **linkopts )
180 self.addLink( vsg, br1, **linkopts )
181 # Connect leaf to all spines
182 for s in range( spine ):
183 switch = spines[ s ]
184 self.addLink( leafs[ ls ], switch, **linkopts )
Flavio Castro5608a392016-06-22 17:02:35 -0700185
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700186
Flavio Castro5608a392016-06-22 17:02:35 -0700187class IpHost( Host ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700188
Flavio Castroab163ca2016-07-07 14:05:00 -0700189 def __init__( self, name, *args, **kwargs ):
Flavio Castro5608a392016-06-22 17:02:35 -0700190 super( IpHost, self ).__init__( name, *args, **kwargs )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700191 gateway = re.split( '\.|/', kwargs[ 'ip' ] )
192 gateway[ 3 ] = '254'
193 self.gateway = '.'.join( gateway[ 0:4 ] )
Flavio Castrocc38a542016-03-03 13:15:46 -0800194
Flavio Castro5608a392016-06-22 17:02:35 -0700195 def config( self, **kwargs ):
196 Host.config( self, **kwargs )
197 mtu = "ifconfig " + self.name + "-eth0 mtu 1490"
198 self.cmd( mtu )
199 self.cmd( 'ip route add default via %s' % self.gateway )
Flavio Castrocc38a542016-03-03 13:15:46 -0800200
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700201
Pierb95002f2016-12-05 15:20:42 -0800202class Ipv6Host( IpHost ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700203
Pierb95002f2016-12-05 15:20:42 -0800204 """
205 Abstraction to model an augmented host with a ipv6
206 functionalities as well
207 """
208 def __init__( self, name, *args, **kwargs ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700209 IpHost.__init__( self, name, *args, **kwargs )
Pierb95002f2016-12-05 15:20:42 -0800210
211 def config( self, **kwargs ):
212 IpHost.config( self, **kwargs )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700213 ipv6Cmd = 'ifconfig %s-eth0 inet6 add %s' % ( self.name, kwargs[ 'ipv6' ] )
214 ipv6GatewayCmd = 'ip -6 route add default via %s' % kwargs[ 'ipv6Gateway' ]
215 ipv6MtuCmd = 'ifconfig %s-eth0 inet6 mtu 1490' % ( self.name )
Pierb95002f2016-12-05 15:20:42 -0800216 self.cmd( ipv6Cmd )
217 self.cmd( ipv6GatewayCmd )
218 self.cmd( ipv6MtuCmd )
Flavio Castro5608a392016-06-22 17:02:35 -0700219
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700220
Flavio Castroe168f7f2016-06-24 15:53:12 -0700221class VLANHost( Host ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700222
Flavio Castroe168f7f2016-06-24 15:53:12 -0700223 "Host connected to VLAN interface"
224
225 def config( self, vlan=100, **params ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700226 """Configure VLANHost according to ( optional ) parameters:
Flavio Castroe168f7f2016-06-24 15:53:12 -0700227 vlan: VLAN ID for default interface"""
228 r = super( VLANHost, self ).config( **params )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700229 intf = self.defaultIntf()
Flavio Castroe168f7f2016-06-24 15:53:12 -0700230 # remove IP from default, "physical" interface
231 self.cmd( 'ifconfig %s inet 0' % intf )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700232 intf = self.defaultIntf()
Flavio Castroe168f7f2016-06-24 15:53:12 -0700233 # create VLAN interface
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700234 self.cmd( 'vconfig add %s %d' % ( intf, vlan ) )
235 self.cmd( 'ifconfig %s.%d %s' % ( intf, vlan, params[ 'ip' ] ) )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700236 # update the intf name and host's intf map
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700237 self.cmd( 'ifconfig %s.%d mtu 1480' % ( intf, vlan ) )
238 newName = '%s.%d' % ( intf, vlan )
239 # update the ( Mininet ) interface to refer to VLAN interface name
Flavio Castroe168f7f2016-06-24 15:53:12 -0700240 intf.name = newName
241 # add VLAN interface to host's name to intf map
242 self.nameToIntf[ newName ] = intf
243
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700244
Flavio Castroab163ca2016-07-07 14:05:00 -0700245class ExtendedCLI( CLI ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700246
Flavio Castroab163ca2016-07-07 14:05:00 -0700247 """
248 Extends mininet CLI with the following commands:
249 addvlanhost
250 addiphost
251 """
252 def do_addhost( self, line ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700253 # Parsing args from CLI
254 args = line.split()
255 if len( args ) < 3 or len( args ):
Flavio Castroab163ca2016-07-07 14:05:00 -0700256 "usage: addhost hostname switch **params"
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700257 hostname, switch = args[ 0 ], args[ 1 ]
258 params = eval( line.split( ' ', 3 )[ 2 ] )
Flavio Castroab163ca2016-07-07 14:05:00 -0700259 if 'cls' in params:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700260 params[ 'cls' ] = eval( params[ 'cls' ] )
Flavio Castroab163ca2016-07-07 14:05:00 -0700261 if hostname in self.mn:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700262 # error( '%s already exists!\n' % hostname )
Flavio Castroab163ca2016-07-07 14:05:00 -0700263 return
264 if switch not in self.mn:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700265 # error( '%s does not exist!\n' % switch )
Flavio Castroab163ca2016-07-07 14:05:00 -0700266 return
267 print params
268 host = self.mn.addHostCfg( hostname, **params )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700269 # switch.attach( link.intf2 )
270 # host.config()
Flavio Castroab163ca2016-07-07 14:05:00 -0700271 link = self.mn.addLink( host, switch )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700272 host.config( **params )
Flavio Castroe168f7f2016-06-24 15:53:12 -0700273
Pierb95002f2016-12-05 15:20:42 -0800274 def do_pingall6( self, line ):
275 "Ping6 between all hosts."
276 self.mn.pingAll6( line )
277
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700278
Flavio Castro5608a392016-06-22 17:02:35 -0700279def config( opts ):
Flavio Castrocc38a542016-03-03 13:15:46 -0800280 spine = opts.spine
281 leaf = opts.leaf
Flavio Castro5608a392016-06-22 17:02:35 -0700282 fanout = opts.fanout
Flavio Castroab163ca2016-07-07 14:05:00 -0700283 vlan = opts.vlan
Pierb95002f2016-12-05 15:20:42 -0800284 ipv6 = opts.ipv6
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900285 if opts.onosIp != '':
286 controllers = opts.onosIp.split( ',' )
287 else:
288 controllers = ['127.0.0.1']
289
Pierb95002f2016-12-05 15:20:42 -0800290 if not ipv6:
291 topo = LeafAndSpine(
292 spine=spine,
293 leaf=leaf,
294 fanout=fanout,
295 vlan=vlan,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700296 )
Pierb95002f2016-12-05 15:20:42 -0800297 else:
298 topo = LeafAndSpine6(
299 spine=spine,
300 leaf=leaf,
301 fanout=fanout,
302 vlan=vlan,
303 ipv6=ipv6
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700304 )
Flavio Castro5608a392016-06-22 17:02:35 -0700305 net = Mininet( topo=topo, link=TCLink, build=False,
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900306 controller=None, autoSetMacs=True )
Flavio Castrocc38a542016-03-03 13:15:46 -0800307 i = 0
308 for ip in controllers:
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700309 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
310 i += 1
311 net.build()
312 net.start()
Pierb95002f2016-12-05 15:20:42 -0800313 if not ipv6:
314 out1 = net.get( 'out1' )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700315 out1.cmd( "arp -s 10.0.9.254 10:00:00:00:00:01 -i %s " % ( out1.intf() ) )
316 ExtendedCLI( net )
317 net.stop()
Flavio Castro5608a392016-06-22 17:02:35 -0700318
Flavio Castrocc38a542016-03-03 13:15:46 -0800319if __name__ == '__main__':
Flavio Castro5608a392016-06-22 17:02:35 -0700320 setLogLevel( 'info' )
321 config( opts )
322 os.system( 'sudo mn -c' )