From dd6424fee8322168a6261efa31102852546bb342 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 9 Oct 2013 15:54:27 -0700 Subject: [PATCH 1/6] Simple mobility example. --- examples/README.md | 9 +++- examples/mobility.py | 93 ++++++++++++++++++++++++++++++++++ examples/test/test_mobility.py | 20 ++++++++ 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100755 examples/mobility.py create mode 100755 examples/test/test_mobility.py diff --git a/examples/README.md b/examples/README.md index 96e30d7..1931964 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. @@ -60,9 +60,14 @@ by subclassing Topo, and how to run a series of tests on it. 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..dda400c --- /dev/null +++ b/examples/mobility.py @@ -0,0 +1,93 @@ +#!/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: + +1. The interfaces are not renamed; this + means that s1-eth1 will show up on other + switches. + +2. The reference controller doesn't support + mobility, so we need to flush the switch + flow tables. + +3. The port numbers reported by the switch + may not match the actual OpenFlow port + numbers used by OVS. + +Good luck! +""" + +from mininet.net import Mininet +from mininet.node import OVSSwitch, Controller +from mininet.topo import LinearTopo +from mininet.cli import CLI +from mininet.util import dumpNetConnections +from time import sleep + +class MobilitySwitch( OVSSwitch ): + "Switch that can delete interfaces" + + def delIntf( self, intf ): + "Remove an interface" + port = self.ports[ intf ] + del self.ports[ intf ] + del self.intfs[ port ] + del self.nameToIntf[ intf.name ] + self.detach( intf ) + + +def printConnections( switches ): + "Compactly print connected nodes to each switch" + for sw in switches: + print '%s:' % sw, + for intf in sw.intfList(): + link = intf.link + if link: + intfs = [ link.intf1, link.intf2 ] + if intfs[ 0 ].node != sw: + intfs.reverse() + local, remote = intfs + print remote.node, + print + + +def mobilityTest(): + "A simple test of mobility" + print '* Simple mobility test' + net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch ) + net.start() + print '* Starting network:' + printConnections( net.switches ) + net.pingAll() + print '* Identifying switch interface for h1' + h1, s1 = net.get( 'h1', 's1' ) + hintf, sintf = h1.connectionsTo( s1 )[ 0 ] + last = s1 + for s in 2, 3, 1: + next = net['s%d' % s ] + print '* Moving', sintf, 'from', last, 'to', next + last.detach( sintf ) + last.delIntf( sintf ) + next.attach( sintf ) + next.addIntf( sintf ) + sintf.node = next + 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() + last = next + 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() From 08d83d136d26e0c4849b31c31f0fda8091dfa797 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Oct 2013 17:30:42 -0700 Subject: [PATCH 2/6] Rename intfs, and add simple moveHost() function --- examples/mobility.py | 87 ++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/examples/mobility.py b/examples/mobility.py index dda400c..f59b7ae 100755 --- a/examples/mobility.py +++ b/examples/mobility.py @@ -4,22 +4,12 @@ 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. +We move a host from s1 to s2, s2 to s3, and then back to s1. Gotchas: -1. The interfaces are not renamed; this - means that s1-eth1 will show up on other - switches. - -2. The reference controller doesn't support - mobility, so we need to flush the switch - flow tables. - -3. The port numbers reported by the switch - may not match the actual OpenFlow port - numbers used by OVS. +The reference controller doesn't support mobility, so we need to +manually flush the switch flow tables! Good luck! """ @@ -29,33 +19,71 @@ from mininet.node import OVSSwitch, Controller from mininet.topo import LinearTopo from mininet.cli import CLI from mininet.util import dumpNetConnections +from mininet.log import output from time import sleep + class MobilitySwitch( OVSSwitch ): - "Switch that can delete interfaces" + "Switch that can reattach and rename interfaces" def delIntf( self, intf ): - "Remove an interface" + "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=True, **kwargs ): + "Add (and reparent) an interface" + OVSSwitch.addIntf( self, intf, **kwargs ) + intf.node = self + if rename: + self.renameIntf( intf ) + + 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 validatePort( self, intf ): + "Validate intf's OF port number" + ofport = int( intf.cmd( 'ovs-vsctl get Interface', intf, + 'ofport' ) ) + assert ofport == self.ports[ intf ] + + def moveIntf( self, intf, switch, rename=True ): + "Move one of our interfaces to another switch" self.detach( intf ) + self.delIntf( intf ) + switch.addIntf( intf, rename=True ) + switch.attach( intf ) + switch.validatePort( intf ) def printConnections( switches ): "Compactly print connected nodes to each switch" for sw in switches: - print '%s:' % sw, + output( '%s: ' % sw ) for intf in sw.intfList(): link = intf.link if link: - intfs = [ link.intf1, link.intf2 ] - if intfs[ 0 ].node != sw: - intfs.reverse() - local, remote = intfs - print remote.node, - print + 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 ): + "Move a host from old switch to new switch" + hintf, sintf = host.connectionsTo( oldSwitch )[ 0 ] + oldSwitch.moveIntf( sintf, newSwitch ) + return hintf, sintf def mobilityTest(): @@ -67,17 +95,12 @@ def mobilityTest(): printConnections( net.switches ) net.pingAll() print '* Identifying switch interface for h1' - h1, s1 = net.get( 'h1', 's1' ) - hintf, sintf = h1.connectionsTo( s1 )[ 0 ] - last = s1 + h1, last = net.get( 'h1', 's1' ) for s in 2, 3, 1: - next = net['s%d' % s ] - print '* Moving', sintf, 'from', last, 'to', next - last.detach( sintf ) - last.delIntf( sintf ) - next.attach( sintf ) - next.addIntf( sintf ) - sintf.node = next + next = net[ 's%d' % s ] + print '* Moving', h1, 'from', last, 'to', next + hintf, sintf = moveHost( h1, last, next ) + print '*', hintf, 'is now connected to', sintf print '* Clearing out old flows' for sw in net.switches: sw.dpctl( 'del-flows' ) From 212399feaf5b98d012a1ff8636713cb2eb4dc486 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 16 Oct 2013 18:41:06 -0700 Subject: [PATCH 3/6] Allow port selection in addIntf() and moveIntf() --- examples/mobility.py | 77 ++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/examples/mobility.py b/examples/mobility.py index f59b7ae..c565947 100755 --- a/examples/mobility.py +++ b/examples/mobility.py @@ -12,20 +12,32 @@ 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, Controller +from mininet.node import OVSSwitch from mininet.topo import LinearTopo -from mininet.cli import CLI -from mininet.util import dumpNetConnections -from mininet.log import output -from time import sleep +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" + @classmethod + def setup( cls ): + "Call our parent method and determine OVS version" + OVSSwitch.setup() + info = quietRun( 'ovs-vsctl --version' ) + cls.OVSVersion = float( findall( '\d+\.\d+', info )[ 0 ] ) + def delIntf( self, intf ): "Remove (and detach) an interface" port = self.ports[ intf ] @@ -33,13 +45,33 @@ class MobilitySwitch( OVSSwitch ): del self.intfs[ port ] del self.nameToIntf[ intf.name ] - def addIntf( self, intf, rename=True, **kwargs ): + 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 MobilitySwitch.OVSVersion >= 1.10: + self.cmd( 'ovs-vsctl add-port', self, intf, + '-- set Interface', intf, + 'ofport_request=%s' % port ) + else: + self.cmd( 'ovs-vsctl add-port', self, intf ) + 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', ofport, + 'but we wanted', self.ports[ intf ], '\n' ) + def renameIntf( self, intf, newname='' ): "Rename an interface (to its canonical name)" intf.ifconfig( 'down' ) @@ -51,19 +83,12 @@ class MobilitySwitch( OVSSwitch ): self.nameToIntf[ intf.name ] = intf intf.ifconfig( 'up' ) - def validatePort( self, intf ): - "Validate intf's OF port number" - ofport = int( intf.cmd( 'ovs-vsctl get Interface', intf, - 'ofport' ) ) - assert ofport == self.ports[ intf ] - - def moveIntf( self, intf, switch, rename=True ): + 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, rename=True ) + switch.addIntf( intf, port=port, rename=True ) switch.attach( intf ) - switch.validatePort( intf ) def printConnections( switches ): @@ -79,10 +104,10 @@ def printConnections( switches ): output( '\n' ) -def moveHost( host, oldSwitch, newSwitch ): +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 ) + oldSwitch.moveIntf( sintf, newSwitch, port=newPort ) return hintf, sintf @@ -90,16 +115,21 @@ def mobilityTest(): "A simple test of mobility" print '* Simple mobility test' net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch ) - net.start() print '* Starting network:' + net.start() printConnections( net.switches ) + if MobilitySwitch.OVSVersion < 1.10: + print '* WARNING: port selection may not work in OVS', + print MobilitySwitch.OVSVersion + print '* Testing network' net.pingAll() print '* Identifying switch interface for h1' - h1, last = net.get( 'h1', 's1' ) + h1, old = net.get( 'h1', 's1' ) for s in 2, 3, 1: - next = net[ 's%d' % s ] - print '* Moving', h1, 'from', last, 'to', next - hintf, sintf = moveHost( h1, last, next ) + 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: @@ -108,9 +138,8 @@ def mobilityTest(): printConnections( net.switches ) print '* Testing connectivity:' net.pingAll() - last = next + old = new net.stop() if __name__ == '__main__': mobilityTest() - From 60b0c7a9148e2b6008168af0c0e5395954772c75 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 16 Oct 2013 19:13:03 -0700 Subject: [PATCH 4/6] Fix version check. --- examples/mobility.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/mobility.py b/examples/mobility.py index c565947..29ddf9c 100755 --- a/examples/mobility.py +++ b/examples/mobility.py @@ -27,6 +27,7 @@ from mininet.log import output, warn from random import randint from re import findall +from distutils.version import StrictVersion class MobilitySwitch( OVSSwitch ): "Switch that can reattach and rename interfaces" @@ -36,7 +37,15 @@ class MobilitySwitch( OVSSwitch ): "Call our parent method and determine OVS version" OVSSwitch.setup() info = quietRun( 'ovs-vsctl --version' ) - cls.OVSVersion = float( findall( '\d+\.\d+', info )[ 0 ] ) + cls.OVSVersion = findall( '\d+\.\d+', info )[ 0 ] + if cls.isOldOVS(): + warn( 'WARNING: port selection may not work ' + ' with OVS ', cls.OVSVersion ) + + @classmethod + def isOldOVS( cls ): + return ( StrictVersion( cls.OVSVersion ) < + StrictVersion( '1.10' ) ) def delIntf( self, intf ): "Remove (and detach) an interface" @@ -56,12 +65,12 @@ class MobilitySwitch( OVSSwitch ): "Attach an interface and set its port" port = self.ports[ intf ] if port: - if MobilitySwitch.OVSVersion >= 1.10: + 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 ) - else: - self.cmd( 'ovs-vsctl add-port', self, intf ) self.validatePort( intf ) def validatePort( self, intf ): @@ -69,8 +78,8 @@ class MobilitySwitch( OVSSwitch ): ofport = int( self.cmd( 'ovs-vsctl get Interface', intf, 'ofport' ) ) if ofport != self.ports[ intf ]: - warn( 'WARNING: ofport for', intf, 'is', ofport, - 'but we wanted', self.ports[ intf ], '\n' ) + warn( 'WARNING: ofport for', intf, 'is actually', ofport, + '\n' ) def renameIntf( self, intf, newname='' ): "Rename an interface (to its canonical name)" @@ -118,9 +127,6 @@ def mobilityTest(): print '* Starting network:' net.start() printConnections( net.switches ) - if MobilitySwitch.OVSVersion < 1.10: - print '* WARNING: port selection may not work in OVS', - print MobilitySwitch.OVSVersion print '* Testing network' net.pingAll() print '* Identifying switch interface for h1' From 13bdd914dcf58dfd6565415884d03b6d8a69b6d5 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 16 Oct 2013 19:17:17 -0700 Subject: [PATCH 5/6] Pass rename correctly in moveSwitch() --- examples/mobility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mobility.py b/examples/mobility.py index 29ddf9c..18abae2 100755 --- a/examples/mobility.py +++ b/examples/mobility.py @@ -96,7 +96,7 @@ class MobilitySwitch( OVSSwitch ): "Move one of our interfaces to another switch" self.detach( intf ) self.delIntf( intf ) - switch.addIntf( intf, port=port, rename=True ) + switch.addIntf( intf, port=port, rename=rename ) switch.attach( intf ) From 02bf34aa961921ac49faddf8f23ebd3f48300dd2 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Mon, 22 Sep 2014 12:27:49 -0700 Subject: [PATCH 6/6] Remove setup/isOldOVS which have been merged into OVSSwitch --- examples/mobility.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/examples/mobility.py b/examples/mobility.py index 18abae2..0e3e557 100755 --- a/examples/mobility.py +++ b/examples/mobility.py @@ -27,26 +27,11 @@ from mininet.log import output, warn from random import randint from re import findall -from distutils.version import StrictVersion + class MobilitySwitch( OVSSwitch ): "Switch that can reattach and rename interfaces" - @classmethod - def setup( cls ): - "Call our parent method and determine OVS version" - OVSSwitch.setup() - info = quietRun( 'ovs-vsctl --version' ) - cls.OVSVersion = findall( '\d+\.\d+', info )[ 0 ] - if cls.isOldOVS(): - warn( 'WARNING: port selection may not work ' - ' with OVS ', cls.OVSVersion ) - - @classmethod - def isOldOVS( cls ): - return ( StrictVersion( cls.OVSVersion ) < - StrictVersion( '1.10' ) ) - def delIntf( self, intf ): "Remove (and detach) an interface" port = self.ports[ intf ]