Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | import os |
Flavio Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 4 | import re |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 5 | import math |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 6 | from optparse import OptionParser |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 7 | from ipaddress import IPv6Network, IPv4Network |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 8 | from mininet.net import Mininet |
| 9 | from mininet.topo import Topo |
Flavio Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 10 | from mininet.node import RemoteController, UserSwitch, Host, OVSBridge |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 11 | from mininet.link import TCLink |
| 12 | from mininet.log import setLogLevel |
| 13 | from mininet.cli import CLI |
| 14 | |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 15 | |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 16 | # Parse command line options and dump results |
| 17 | def 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 Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 28 | parser.add_option( '--vlan', dest='vlan', type='int', default=-1, |
| 29 | help='vid of cross connect, default=-1, -1 means utilize default value' ) |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 30 | parser.add_option( '--ipv6', action="store_true", dest='ipv6', |
| 31 | help='hosts are capable to use also ipv6' ) |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 32 | (options, args) = parser.parse_args( ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 33 | return options, args |
| 34 | |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 35 | |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 36 | opts, args = parseOptions( ) |
| 37 | |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 38 | IP6_SUBNET_CLASS = 120 |
| 39 | IP4_SUBNET_CLASS = 24 |
| 40 | |
| 41 | class 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 Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 101 | |
| 102 | class LeafAndSpine( Topo ): |
| 103 | def __init__( self, spine=2, leaf=2, fanout=2, **opts ): |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 104 | "Create Leaf and Spine Topo." |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 105 | Topo.__init__( self, **opts ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 106 | # Add spine switches |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 107 | spines = { } |
Flavio Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 108 | leafs = { } |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 109 | for s in range( spine ): |
| 110 | spines[ s ] = self.addSwitch( 'spine10%s' % (s + 1), |
| 111 | dpid="00000000010%s" % (s + 1) ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 112 | # Set link speeds to 100Mb/s |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 113 | linkopts = dict( bw=100 ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 114 | # Add Leaf switches |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 115 | for ls in range( leaf ): |
Flavio Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 116 | leafs[ ls ] = self.addSwitch( 'leaf%s' % (ls + 1), |
| 117 | dpid="00000000000%s" % (1 + ls) ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 118 | # Add hosts under a leaf, fanout hosts per leaf switch |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 119 | 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 Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 124 | self.addLink( host, leafs[ ls ], **linkopts ) |
| 125 | # Add Xconnect simulation |
Flavio Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 126 | 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 Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 147 | |
| 148 | class IpHost( Host ): |
Flavio Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 149 | def __init__( self, name, *args, **kwargs ): |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 150 | super( IpHost, self ).__init__( name, *args, **kwargs ) |
Flavio Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 151 | gateway = re.split('\.|/', kwargs['ip']) |
| 152 | gateway[3] = '254' |
| 153 | self.gateway = '.'.join(gateway[0:4]) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 154 | |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 155 | 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 Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 160 | |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 161 | class 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 Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 177 | |
Flavio Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 178 | class 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 Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 200 | class 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 Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 227 | |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 228 | def do_pingall6( self, line ): |
| 229 | "Ping6 between all hosts." |
| 230 | self.mn.pingAll6( line ) |
| 231 | |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 232 | def config( opts ): |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 233 | spine = opts.spine |
| 234 | leaf = opts.leaf |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 235 | fanout = opts.fanout |
Flavio Castro | ab163ca | 2016-07-07 14:05:00 -0700 | [diff] [blame] | 236 | vlan = opts.vlan |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 237 | ipv6 = opts.ipv6 |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 238 | controllers = [ os.environ[ 'OC%s' % i ] for i in |
| 239 | range( 1, opts.onos + 1 ) ] if (opts.onos) else [ |
| 240 | '127.0.0.1' ] |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 241 | 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 Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 256 | net = Mininet( topo=topo, link=TCLink, build=False, |
Flavio Castro | e168f7f | 2016-06-24 15:53:12 -0700 | [diff] [blame] | 257 | switch=UserSwitch, controller=None, autoSetMacs=True ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 258 | i = 0 |
| 259 | for ip in controllers: |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 260 | net.addController( "c%s" % (i), controller=RemoteController, ip=ip ) |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 261 | i += 1; |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 262 | net.build( ) |
| 263 | net.start( ) |
Pier | b95002f | 2016-12-05 15:20:42 -0800 | [diff] [blame] | 264 | 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 Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 268 | net.stop( ) |
| 269 | |
Flavio Castro | cc38a54 | 2016-03-03 13:15:46 -0800 | [diff] [blame] | 270 | if __name__ == '__main__': |
Flavio Castro | 5608a39 | 2016-06-22 17:02:35 -0700 | [diff] [blame] | 271 | setLogLevel( 'info' ) |
| 272 | config( opts ) |
| 273 | os.system( 'sudo mn -c' ) |