diff --git a/examples/README.md b/examples/README.md index 74e25b8..56c82d3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -13,7 +13,7 @@ process running in a namespace. Doesn't use OpenFlow. #### consoles.py: -This example creates a grid of console windows, one for each node, +This example creates a grid of console windows, one for each node, and allows interaction with and monitoring of each console, including graphical monitoring. @@ -65,9 +65,14 @@ that uses Linux IP forwarding. This example demonstrates creating a network via a graphical editor. +#### mobility.py + +This example demonstrates detaching an interface from one switch and +attaching it another as a basic way to move a host around a network. + #### multiping.py: -This example demonstrates one method for +This example demonstrates one method for monitoring output from multiple hosts, using `node.monitor()`. #### multipoll.py: diff --git a/examples/mobility.py b/examples/mobility.py new file mode 100755 index 0000000..0e3e557 --- /dev/null +++ b/examples/mobility.py @@ -0,0 +1,136 @@ +#!/usr/bin/python + +""" +Simple example of Mobility with Mininet +(aka enough rope to hang yourself.) + +We move a host from s1 to s2, s2 to s3, and then back to s1. + +Gotchas: + +The reference controller doesn't support mobility, so we need to +manually flush the switch flow tables! + +Good luck! + +to-do: + +- think about wifi/hub behavior +- think about clearing last hop - why doesn't that work? +""" + +from mininet.net import Mininet +from mininet.node import OVSSwitch +from mininet.topo import LinearTopo +from mininet.util import quietRun +from mininet.log import output, warn + +from random import randint +from re import findall + + +class MobilitySwitch( OVSSwitch ): + "Switch that can reattach and rename interfaces" + + def delIntf( self, intf ): + "Remove (and detach) an interface" + port = self.ports[ intf ] + del self.ports[ intf ] + del self.intfs[ port ] + del self.nameToIntf[ intf.name ] + + def addIntf( self, intf, rename=False, **kwargs ): + "Add (and reparent) an interface" + OVSSwitch.addIntf( self, intf, **kwargs ) + intf.node = self + if rename: + self.renameIntf( intf ) + + def attach( self, intf ): + "Attach an interface and set its port" + port = self.ports[ intf ] + if port: + if self.isOldOVS(): + self.cmd( 'ovs-vsctl add-port', self, intf ) + else: + self.cmd( 'ovs-vsctl add-port', self, intf, + '-- set Interface', intf, + 'ofport_request=%s' % port ) + self.validatePort( intf ) + + def validatePort( self, intf ): + "Validate intf's OF port number" + ofport = int( self.cmd( 'ovs-vsctl get Interface', intf, + 'ofport' ) ) + if ofport != self.ports[ intf ]: + warn( 'WARNING: ofport for', intf, 'is actually', ofport, + '\n' ) + + def renameIntf( self, intf, newname='' ): + "Rename an interface (to its canonical name)" + intf.ifconfig( 'down' ) + if not newname: + newname = '%s-eth%d' % ( self.name, self.ports[ intf ] ) + intf.cmd( 'ip link set', intf, 'name', newname ) + del self.nameToIntf[ intf.name ] + intf.name = newname + self.nameToIntf[ intf.name ] = intf + intf.ifconfig( 'up' ) + + def moveIntf( self, intf, switch, port=None, rename=True ): + "Move one of our interfaces to another switch" + self.detach( intf ) + self.delIntf( intf ) + switch.addIntf( intf, port=port, rename=rename ) + switch.attach( intf ) + + +def printConnections( switches ): + "Compactly print connected nodes to each switch" + for sw in switches: + output( '%s: ' % sw ) + for intf in sw.intfList(): + link = intf.link + if link: + intf1, intf2 = link.intf1, link.intf2 + remote = intf1 if intf1.node != sw else intf2 + output( '%s(%s) ' % ( remote.node, sw.ports[ intf ] ) ) + output( '\n' ) + + +def moveHost( host, oldSwitch, newSwitch, newPort=None ): + "Move a host from old switch to new switch" + hintf, sintf = host.connectionsTo( oldSwitch )[ 0 ] + oldSwitch.moveIntf( sintf, newSwitch, port=newPort ) + return hintf, sintf + + +def mobilityTest(): + "A simple test of mobility" + print '* Simple mobility test' + net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch ) + print '* Starting network:' + net.start() + printConnections( net.switches ) + print '* Testing network' + net.pingAll() + print '* Identifying switch interface for h1' + h1, old = net.get( 'h1', 's1' ) + for s in 2, 3, 1: + new = net[ 's%d' % s ] + port = randint( 10, 20 ) + print '* Moving', h1, 'from', old, 'to', new, 'port', port + hintf, sintf = moveHost( h1, old, new, newPort=port ) + print '*', hintf, 'is now connected to', sintf + print '* Clearing out old flows' + for sw in net.switches: + sw.dpctl( 'del-flows' ) + print '* New network:' + printConnections( net.switches ) + print '* Testing connectivity:' + net.pingAll() + old = new + net.stop() + +if __name__ == '__main__': + mobilityTest() diff --git a/examples/test/test_mobility.py b/examples/test/test_mobility.py new file mode 100755 index 0000000..6eb28ee --- /dev/null +++ b/examples/test/test_mobility.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +""" +Test for mobility.py +""" + +import unittest +from subprocess import check_output + +class testMobility( unittest.TestCase ): + + def testMobility( self ): + "Run the example and verify its 4 ping results" + cmd = 'python -m mininet.examples.mobility 2>&1' + grep = ' | grep -c " 0% dropped" ' + result = check_output( cmd + grep, shell=True ) + assert int( result ) == 4 + +if __name__ == '__main__': + unittest.main()