From 9109233886f88fdfd6b87dd8f368328c838a7098 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 10 Jun 2014 11:44:03 -0700 Subject: [PATCH 01/43] Added support for mount namespaces in bind.py. Also moved it to the node class as a host type. --- examples/bind.py | 164 +---------------------------------------------- mininet/node.py | 17 +++++ 2 files changed, 20 insertions(+), 161 deletions(-) diff --git a/examples/bind.py b/examples/bind.py index 2c317cf..af02254 100755 --- a/examples/bind.py +++ b/examples/bind.py @@ -7,191 +7,33 @@ This creates hosts with private directories as desired. """ from mininet.net import Mininet -from mininet.node import Host +from mininet.node import Host, HostWithPrivateDirs from mininet.cli import CLI -from mininet.util import errFail, quietRun, errRun from mininet.topo import SingleSwitchTopo from mininet.log import setLogLevel, info, debug -from os.path import realpath from functools import partial -# Utility functions for unmounting a tree - -MNRUNDIR = realpath( '/var/run/mn' ) - -def mountPoints(): - "Return list of mounted file systems" - mtab, _err, _ret = errFail( 'cat /proc/mounts' ) - lines = mtab.split( '\n' ) - mounts = [] - for line in lines: - if not line: - continue - fields = line.split( ' ') - mount = fields[ 1 ] - mounts.append( mount ) - return mounts - -def unmountAll( rootdir=MNRUNDIR ): - "Unmount all mounts under a directory tree" - rootdir = realpath( rootdir ) - # Find all mounts below rootdir - # This is subtle because /foo is not - # a parent of /foot - dirslash = rootdir + '/' - mounts = [ m for m in mountPoints() - if m == dir or m.find( dirslash ) == 0 ] - # Unmount them from bottom to top - mounts.sort( reverse=True ) - for mount in mounts: - debug( 'Unmounting', mount, '\n' ) - _out, err, code = errRun( 'umount', mount ) - if code != 0: - info( '*** Warning: failed to umount', mount, '\n' ) - info( err ) - - -class HostWithPrivateDirs( Host ): - "Host with private directories" - - mnRunDir = MNRUNDIR - - def __init__(self, name, *args, **kwargs ): - """privateDirs: list of private directories - remounts: dirs to remount - unmount: unmount dirs in cleanup? (True) - Note: if unmount is False, you must call unmountAll() - manually.""" - self.privateDirs = kwargs.pop( 'privateDirs', [] ) - self.remounts = kwargs.pop( 'remounts', [] ) - self.unmount = kwargs.pop( 'unmount', True ) - Host.__init__( self, name, *args, **kwargs ) - self.rundir = '%s/%s' % ( self.mnRunDir, name ) - self.root, self.private = None, None # set in createBindMounts - if self.privateDirs: - self.privateDirs = [ realpath( d ) for d in self.privateDirs ] - self.createBindMounts() - # These should run in the namespace before we chroot, - # in order to put the right entries in /etc/mtab - # Eventually this will allow a local pid space - # Now we chroot and cd to wherever we were before. - pwd = self.cmd( 'pwd' ).strip() - self.sendCmd( 'exec chroot', self.root, 'bash -ms mininet:' - + self.name ) - self.waiting = False - self.cmd( 'cd', pwd ) - # In order for many utilities to work, - # we need to remount /proc and /sys - self.cmd( 'mount /proc' ) - self.cmd( 'mount /sys' ) - - def mountPrivateDirs( self ): - "Create and bind mount private dirs" - for dir_ in self.privateDirs: - privateDir = self.private + dir_ - errFail( 'mkdir -p ' + privateDir ) - mountPoint = self.root + dir_ - errFail( 'mount -B %s %s' % - ( privateDir, mountPoint) ) - - def mountDirs( self, dirs ): - "Mount a list of directories" - for dir_ in dirs: - mountpoint = self.root + dir_ - errFail( 'mount -B %s %s' % - ( dir_, mountpoint ) ) - - @classmethod - def findRemounts( cls, fstypes=None ): - """Identify mount points in /proc/mounts to remount - fstypes: file system types to match""" - if fstypes is None: - fstypes = [ 'nfs' ] - dirs = quietRun( 'cat /proc/mounts' ).strip().split( '\n' ) - remounts = [] - for dir_ in dirs: - line = dir_.split() - mountpoint, fstype = line[ 1 ], line[ 2 ] - # Don't re-remount directories!!! - if mountpoint.find( cls.mnRunDir ) == 0: - continue - if fstype in fstypes: - remounts.append( mountpoint ) - return remounts - - def createBindMounts( self ): - """Create a chroot directory structure, - with self.privateDirs as private dirs""" - errFail( 'mkdir -p '+ self.rundir ) - unmountAll( self.rundir ) - # Create /root and /private directories - self.root = self.rundir + '/root' - self.private = self.rundir + '/private' - errFail( 'mkdir -p ' + self.root ) - errFail( 'mkdir -p ' + self.private ) - # Recursively mount / in private doort - # note we'll remount /sys and /proc later - errFail( 'mount -B / ' + self.root ) - self.mountDirs( self.remounts ) - self.mountPrivateDirs() - - def unmountBindMounts( self ): - "Unmount all of our bind mounts" - unmountAll( self.rundir ) - - def popen( self, *args, **kwargs ): - "Popen with chroot support" - chroot = kwargs.pop( 'chroot', True ) - mncmd = kwargs.get( 'mncmd', - [ 'mnexec', '-a', str( self.pid ) ] ) - if chroot: - mncmd = [ 'chroot', self.root ] + mncmd - kwargs[ 'mncmd' ] = mncmd - return Host.popen( self, *args, **kwargs ) - - def cleanup( self ): - """Clean up, then unmount bind mounts - unmount: actually unmount bind mounts?""" - # Wait for process to actually terminate - self.shell.wait() - Host.cleanup( self ) - if self.unmount: - self.unmountBindMounts() - errFail( 'rmdir ' + self.root ) - - -# Convenience aliases - -findRemounts = HostWithPrivateDirs.findRemounts - - # Sample usage def testHostWithPrivateDirs(): "Test bind mounts" topo = SingleSwitchTopo( 10 ) - remounts = findRemounts( fstypes=[ 'nfs' ] ) privateDirs = [ '/var/log', '/var/run' ] - host = partial( HostWithPrivateDirs, remounts=remounts, - privateDirs=privateDirs, unmount=False ) + host = partial( HostWithPrivateDirs, + privateDirs=privateDirs ) net = Mininet( topo=topo, host=host ) net.start() info( 'Private Directories:', privateDirs, '\n' ) CLI( net ) net.stop() - # We do this all at once to save a bit of time - info( 'Unmounting host bind mounts...\n' ) - unmountAll() if __name__ == '__main__': - unmountAll() setLogLevel( 'info' ) testHostWithPrivateDirs() info( 'Done.\n') - diff --git a/mininet/node.py b/mininet/node.py index e5e8cb1..a9852b2 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -725,6 +725,23 @@ class CPULimitedHost( Host ): mountCgroups() cls.inited = True +class HostWithPrivateDirs( Host ): + "Host with private directories" + + def __init__(self, *args, **kwargs ): + """privateDirs: list of private directories""" + + self.privateDirs = kwargs.pop( 'privateDirs', [] ) + Host.__init__( self, *args, **kwargs ) + self.mountPrivateDirs() + + def mountPrivateDirs( self ): + "Mount tmpfs for each private directory" + for dir_ in self.privateDirs: + self.cmd( 'mkdir -p ' + dir_ ) + self.cmd( 'mount -n -t tmpfs tmpfs %s' % dir_ ) + + # Some important things to note: # From 0d39f11034b2d28cf68c5cc7a0d888fa0ea4ef18 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Mon, 16 Jun 2014 17:39:24 -0700 Subject: [PATCH 02/43] added code to kill stale mininet processes --- mininet/clean.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mininet/clean.py b/mininet/clean.py index 675c7d7..9b0d6aa 100755 --- a/mininet/clean.py +++ b/mininet/clean.py @@ -69,4 +69,18 @@ def cleanup(): if link: sh( "ip link del " + link ) + info( "*** Killing stale mininet node processes\n" ) + sh( 'pkill -9 -f mininet:' ) + # Make sure they are gone + while True: + try: + pids = co( 'pgrep -f mininet:'.split() ) + except: + pids = '' + if pids: + sh( 'pkill -f 9 mininet:' ) + sleep( .5 ) + else: + break + info( "*** Cleanup complete.\n" ) From 4e76439c796cb74efa8559c084dec32cfbec5ad9 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Mon, 16 Jun 2014 23:33:38 -0700 Subject: [PATCH 03/43] added support in iperf for different result formats. also added upper bounds for hifi tests --- mininet/net.py | 4 +++- mininet/test/test_hifi.py | 14 +++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 1c932f9..4e98c60 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -617,7 +617,7 @@ class Mininet( object ): # XXX This should be cleaned up - def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ): + def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format='M' ): """Run iperf between two hosts. hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] @@ -640,6 +640,8 @@ class Mininet( object ): bwArgs = '-b ' + udpBw + ' ' elif l4Type != 'TCP': raise Exception( 'Unexpected l4 type: %s' % l4Type ) + if not format == 'M': + iperfArgs += '-f %s ' %format server.sendCmd( iperfArgs + '-s', printPid=True ) servout = '' while server.lastPid is None: diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index 20ee031..1ded385 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -55,6 +55,8 @@ class testOptionsTopoCommon( object ): """ self.assertGreaterEqual( float(measured), float(expected) * tolerance_frac ) + self.assertLess( float( measured ), + float(expected) + (1-tolerance_frac) * float( expected ) ) def testCPULimits( self ): "Verify topology creation with CPU limits set for both schedulers." @@ -69,18 +71,19 @@ class testOptionsTopoCommon( object ): results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) mn.stop() for cpu in results: - self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE ) + #divide cpu by 100 to convert from percentage to fraction + self.assertWithinTolerance( cpu/100, CPU_FRACTION, CPU_TOLERANCE ) def testLinkBandwidth( self ): "Verify that link bandwidths are accurate within a bound." - BW = 5 # Mbps + BW = .5 # Mbps BW_TOLERANCE = 0.8 # BW fraction below which test should fail # Verify ability to create limited-link topo first; lopts = { 'bw': BW, 'use_htb': True } # Also verify correctness of limit limitng within a bound. mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), link=TCLink, switch=self.switchClass ) - bw_strs = mn.run( mn.iperf ) + bw_strs = mn.run( mn.iperf, format='m' ) for bw_str in bw_strs: bw = float( bw_str.split(' ')[0] ) self.assertWithinTolerance( bw, BW, BW_TOLERANCE ) @@ -101,10 +104,11 @@ class testOptionsTopoCommon( object ): self.assertEqual( sent, received ) # pylint: enable-msg=W0612 for rttval in [rttmin, rttavg, rttmax]: - # Multiply delay by 4 to cover there & back on two links - self.assertWithinTolerance( rttval, DELAY_MS * 4.0, + # Multiply delay by 8 to cover there & back on two links, for both the icmp packets and the arp packets + self.assertWithinTolerance( rttval, DELAY_MS * 8.0, DELAY_TOLERANCE) + def testLinkLoss( self ): "Verify that we see packet drops with a high configured loss rate." LOSS_PERCENT = 99 From 6a81b6dfb38297a50e20dfb2e29d57be4e572ee2 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Thu, 19 Jun 2014 15:08:26 -0700 Subject: [PATCH 04/43] added persistence option to HostWithPrivateDirs. also attached mount namespaces when mnexec -a is specified --- examples/bind.py | 4 +++- mininet/node.py | 20 +++++++++++++++++--- mnexec.c | 15 ++++++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/examples/bind.py b/examples/bind.py index af02254..c2e7902 100755 --- a/examples/bind.py +++ b/examples/bind.py @@ -20,7 +20,9 @@ from functools import partial def testHostWithPrivateDirs(): "Test bind mounts" topo = SingleSwitchTopo( 10 ) - privateDirs = [ '/var/log', '/var/run' ] + privateDirs = [ ( '/var/log', '/onos/%(name)s/var/log' ), + ( '/var/run', '/ovx/%(name)s/var/run' ), + '/mn' ] host = partial( HostWithPrivateDirs, privateDirs=privateDirs ) net = Mininet( topo=topo, host=host ) diff --git a/mininet/node.py b/mininet/node.py index a9852b2..9d5516c 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -728,14 +728,28 @@ class CPULimitedHost( Host ): class HostWithPrivateDirs( Host ): "Host with private directories" - def __init__(self, *args, **kwargs ): + def __init__(self, name, *args, **kwargs ): """privateDirs: list of private directories""" - + self.name = name self.privateDirs = kwargs.pop( 'privateDirs', [] ) - Host.__init__( self, *args, **kwargs ) + Host.__init__( self, name, *args, **kwargs ) self.mountPrivateDirs() def mountPrivateDirs( self ): + "mount the directories that have specified mountpoints" + for directory in self.privateDirs: + if isinstance( directory, tuple ): + privateDir = directory[ 1 ] %self.__dict__ + mountPoint = directory[ 0 ] + self.cmd( 'mkdir -p %s' %privateDir ) + self.cmd( 'mkdir -p %s' %mountPoint ) + self.cmd( 'mount --bind %s %s' %( privateDir, mountPoint ) ) + else: + self.cmd( 'mkdir -p %s' %directory ) + self.cmd( 'mount -n -t tmpfs tmpfs %s' %directory ) + + + def mountTempDirs( self ): "Mount tmpfs for each private directory" for dir_ in self.privateDirs: self.cmd( 'mkdir -p ' + dir_ ) diff --git a/mnexec.c b/mnexec.c index fee3d25..8f6830e 100644 --- a/mnexec.c +++ b/mnexec.c @@ -133,6 +133,7 @@ int main(int argc, char *argv[]) perror("mount"); return 1; } + break; case 'p': /* print pid */ @@ -140,7 +141,7 @@ int main(int argc, char *argv[]) fflush(stdout); break; case 'a': - /* Attach to pid's network namespace */ + /* Attach to pid's network namespace and mount namespace*/ pid = atoi(optarg); sprintf(path, "/proc/%d/ns/net", pid ); nsid = open(path, O_RDONLY); @@ -152,6 +153,18 @@ int main(int argc, char *argv[]) perror("setns"); return 1; } + sprintf(path, "/proc/%d/ns/mnt", pid ); + nsid = open(path, O_RDONLY); + if (nsid < 0) { + perror(path); + return 1; + } + if (setns(nsid, 0) != 0) { + perror("setns"); + return 1; + } + + break; case 'g': /* Attach to cgroup */ From 3c3344e1f5df164eb87a8e4a1a6060a1dd0f6e68 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Fri, 20 Jun 2014 23:31:45 -0700 Subject: [PATCH 05/43] imported check_output --- mininet/clean.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/clean.py b/mininet/clean.py index 9b0d6aa..49e32ea 100755 --- a/mininet/clean.py +++ b/mininet/clean.py @@ -10,7 +10,7 @@ It may also get rid of 'false positives', but hopefully nothing irreplaceable! """ -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, check_output as co import time from mininet.log import info From 752c2d6e7cbe1b8bde132677cec317b169266dd6 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Fri, 20 Jun 2014 23:54:18 -0700 Subject: [PATCH 06/43] mountprivatedirs is no longer needed --- examples/bind.py | 8 +++++++- mininet/node.py | 7 ------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/bind.py b/examples/bind.py index c2e7902..22eb3a2 100755 --- a/examples/bind.py +++ b/examples/bind.py @@ -27,7 +27,13 @@ def testHostWithPrivateDirs(): privateDirs=privateDirs ) net = Mininet( topo=topo, host=host ) net.start() - info( 'Private Directories:', privateDirs, '\n' ) + info( 'private Directories: [ ' ) + for directory in privateDirs: + if isinstance( directory, tuple ): + info( '%s, ' %directory[0] ) + else: + info( '%s, ' %directory ) + info( ']\n' ) CLI( net ) net.stop() diff --git a/mininet/node.py b/mininet/node.py index 9d5516c..a2deb44 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -749,13 +749,6 @@ class HostWithPrivateDirs( Host ): self.cmd( 'mount -n -t tmpfs tmpfs %s' %directory ) - def mountTempDirs( self ): - "Mount tmpfs for each private directory" - for dir_ in self.privateDirs: - self.cmd( 'mkdir -p ' + dir_ ) - self.cmd( 'mount -n -t tmpfs tmpfs %s' % dir_ ) - - # Some important things to note: # From af2f67d98c337ff5b8055b52d55d2153b68ce3e0 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Thu, 26 Jun 2014 09:29:06 -0700 Subject: [PATCH 07/43] added documentation for HostWithPrivateDirs --- examples/bind.py | 65 +++++++++++++++++++++++++++++++++++++++--------- mininet/node.py | 13 +++++++--- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/examples/bind.py b/examples/bind.py index 22eb3a2..9eb027e 100755 --- a/examples/bind.py +++ b/examples/bind.py @@ -1,9 +1,38 @@ #!/usr/bin/python """ -bind.py: Bind mount prototype +bind.py: Bind mount example -This creates hosts with private directories as desired. +This creates hosts with private directories that the user specifies. +These hosts may have persistent directories that will be available +across multiple mininet session, or temporary directories that will +only last for one mininet session. To specify a persistent +directory, add a tuple to a list of private directories: + + [ ( 'directory to be mounted on', 'directory to be mounted' ) ] + +String expansion may be used to create a directory template for +each host. To do this, add a %(name)s in place of the host name +when creating your list of directories: + + [ ( '/var/run', '/tmp/%(name)s/var/run' ) ] + +If no persistent directory is specified, the directories will default +to temporary private directories. To do this, simply create a list of +directories to be made private. A tmpfs will then be mounted on them. + +You may use both temporary and persistent directories at the same +time. In the following privateDirs string, each host will have a +persistent directory in the root filesystem at +"/tmp/(hostname)/var/run" mounted on "/var/run". Each host will also +have a temporary private directory mounted on "/var/log". + + [ ( '/var/run', '/tmp/%(name)s/var/run' ), '/var/log' ] + +This example runs TWO mininet instances, one after the other. +The first mounts persistent private directories on /var/log and +/var/run. The second mounts temporary private directories on the same +directories. """ from mininet.net import Mininet @@ -15,33 +44,45 @@ from mininet.log import setLogLevel, info, debug from functools import partial -# Sample usage +# Persistent directory sample usage -def testHostWithPrivateDirs(): +def testPersistentPrivateDirs(): "Test bind mounts" topo = SingleSwitchTopo( 10 ) - privateDirs = [ ( '/var/log', '/onos/%(name)s/var/log' ), - ( '/var/run', '/ovx/%(name)s/var/run' ), - '/mn' ] + privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ), + ( '/var/run', '/tmp/%(name)s/var/run' ) ] host = partial( HostWithPrivateDirs, privateDirs=privateDirs ) net = Mininet( topo=topo, host=host ) net.start() - info( 'private Directories: [ ' ) + info( 'Private Directories: [ ' ) for directory in privateDirs: if isinstance( directory, tuple ): - info( '%s, ' %directory[0] ) + info( '%s, ' %directory[ 0 ] ) else: info( '%s, ' %directory ) info( ']\n' ) CLI( net ) net.stop() +# Temporary directory sample usage + +def testTempPrivateDirs(): + "test bind mounts with temporary directories" + topo = SingleSwitchTopo( 10 ) + privateDirs = [ '/var/log', '/var/run' ] + host = partial( HostWithPrivateDirs, + privateDirs=privateDirs ) + net = Mininet( topo=topo, host=host ) + net.start() + info( 'Private Directories: ', privateDirs, '\n' ) + CLI( net ) + net.stop() if __name__ == '__main__': setLogLevel( 'info' ) - testHostWithPrivateDirs() - info( 'Done.\n') - + testPersistentPrivateDirs() + testTempPrivateDirs() + info( 'Done.\n' ) diff --git a/mininet/node.py b/mininet/node.py index a2deb44..bdf7642 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -16,6 +16,11 @@ Host: a virtual host. By default, a host is simply a shell; commands CPULimitedHost: a virtual host whose CPU bandwidth is limited by RT or CFS bandwidth limiting. +HostWithPrivateDirs: a virtual host that has user-specified private + directories. These may be temporary directories stored as a tmpfs, + or persistent directories that are mounted from another directory in + the root filesystem. + Switch: superclass for switch nodes. UserSwitch: a switch using the user-space switch from the OpenFlow @@ -728,23 +733,25 @@ class CPULimitedHost( Host ): class HostWithPrivateDirs( Host ): "Host with private directories" - def __init__(self, name, *args, **kwargs ): - """privateDirs: list of private directories""" + def __init__( self, name, *args, **kwargs ): + "privateDirs: list of private directory strings or tuples" self.name = name self.privateDirs = kwargs.pop( 'privateDirs', [] ) Host.__init__( self, name, *args, **kwargs ) self.mountPrivateDirs() def mountPrivateDirs( self ): - "mount the directories that have specified mountpoints" + "mount private directories" for directory in self.privateDirs: if isinstance( directory, tuple ): + # mount given private directory privateDir = directory[ 1 ] %self.__dict__ mountPoint = directory[ 0 ] self.cmd( 'mkdir -p %s' %privateDir ) self.cmd( 'mkdir -p %s' %mountPoint ) self.cmd( 'mount --bind %s %s' %( privateDir, mountPoint ) ) else: + # mount temporary filesystem on directory self.cmd( 'mkdir -p %s' %directory ) self.cmd( 'mount -n -t tmpfs tmpfs %s' %directory ) From 40a4a25dd8585789ed7d75fcbb0ef3bd526e7928 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 1 Jul 2014 14:51:07 -0700 Subject: [PATCH 08/43] use a single mininet instance in bindpy --- examples/bind.py | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/examples/bind.py b/examples/bind.py index 9eb027e..287c97a 100755 --- a/examples/bind.py +++ b/examples/bind.py @@ -29,10 +29,9 @@ have a temporary private directory mounted on "/var/log". [ ( '/var/run', '/tmp/%(name)s/var/run' ), '/var/log' ] -This example runs TWO mininet instances, one after the other. -The first mounts persistent private directories on /var/log and -/var/run. The second mounts temporary private directories on the same -directories. +This example has both persistent directories mounted on '/var/log' +and '/var/run'. It also has a temporary private directory mounted +on '/var/mn' """ from mininet.net import Mininet @@ -44,45 +43,30 @@ from mininet.log import setLogLevel, info, debug from functools import partial -# Persistent directory sample usage +# Sample usage -def testPersistentPrivateDirs(): +def testHostWithPrivateDirs(): "Test bind mounts" topo = SingleSwitchTopo( 10 ) privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ), - ( '/var/run', '/tmp/%(name)s/var/run' ) ] + ( '/var/run', '/tmp/%(name)s/var/run' ), + '/var/mn' ] host = partial( HostWithPrivateDirs, privateDirs=privateDirs ) net = Mininet( topo=topo, host=host ) net.start() - info( 'Private Directories: [ ' ) + directories = [] for directory in privateDirs: - if isinstance( directory, tuple ): - info( '%s, ' %directory[ 0 ] ) - else: - info( '%s, ' %directory ) - info( ']\n' ) - CLI( net ) - net.stop() - -# Temporary directory sample usage - -def testTempPrivateDirs(): - "test bind mounts with temporary directories" - topo = SingleSwitchTopo( 10 ) - privateDirs = [ '/var/log', '/var/run' ] - host = partial( HostWithPrivateDirs, - privateDirs=privateDirs ) - net = Mininet( topo=topo, host=host ) - net.start() - info( 'Private Directories: ', privateDirs, '\n' ) + directories.append( directory[ 0 ] + if isinstance( directory, tuple ) + else directory ) + info( 'Private Directories:', directories, '\n' ) CLI( net ) net.stop() if __name__ == '__main__': setLogLevel( 'info' ) - testPersistentPrivateDirs() - testTempPrivateDirs() - info( 'Done.\n' ) + testHostWithPrivateDirs() + info( 'Done.\n') From 342b743b136ab0918f5764b45a2a4137e18621df Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 1 Jul 2014 17:07:14 -0700 Subject: [PATCH 09/43] set staticArp in testLinkDelay --- mininet/test/test_hifi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index 1ded385..107138f 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -94,7 +94,7 @@ class testOptionsTopoCommon( object ): DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True } mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), - link=TCLink, switch=self.switchClass ) + link=TCLink, switch=self.switchClass, autoStaticArp=True ) ping_delays = mn.run( mn.pingFull ) test_outputs = ping_delays[0] # Ignore unused variables below @@ -104,8 +104,8 @@ class testOptionsTopoCommon( object ): self.assertEqual( sent, received ) # pylint: enable-msg=W0612 for rttval in [rttmin, rttavg, rttmax]: - # Multiply delay by 8 to cover there & back on two links, for both the icmp packets and the arp packets - self.assertWithinTolerance( rttval, DELAY_MS * 8.0, + # Multiply delay by 4 to cover there & back on two links + self.assertWithinTolerance( rttval, DELAY_MS * 4.0, DELAY_TOLERANCE) From 9c3ecfe338e361cbeaec18689c5632c372873794 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Wed, 2 Jul 2014 10:53:41 -0700 Subject: [PATCH 10/43] conforming to mininet style --- mininet/node.py | 13 +++++++------ mnexec.c | 3 --- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/mininet/node.py b/mininet/node.py index bdf7642..72c782a 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -745,15 +745,16 @@ class HostWithPrivateDirs( Host ): for directory in self.privateDirs: if isinstance( directory, tuple ): # mount given private directory - privateDir = directory[ 1 ] %self.__dict__ + privateDir = directory[ 1 ] % self.__dict__ mountPoint = directory[ 0 ] - self.cmd( 'mkdir -p %s' %privateDir ) - self.cmd( 'mkdir -p %s' %mountPoint ) - self.cmd( 'mount --bind %s %s' %( privateDir, mountPoint ) ) + self.cmd( 'mkdir -p %s' % privateDir ) + self.cmd( 'mkdir -p %s' % mountPoint ) + self.cmd( 'mount --bind %s %s' % + ( privateDir, mountPoint ) ) else: # mount temporary filesystem on directory - self.cmd( 'mkdir -p %s' %directory ) - self.cmd( 'mount -n -t tmpfs tmpfs %s' %directory ) + self.cmd( 'mkdir -p %s' % directory ) + self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory ) diff --git a/mnexec.c b/mnexec.c index 8f6830e..c7103d4 100644 --- a/mnexec.c +++ b/mnexec.c @@ -133,7 +133,6 @@ int main(int argc, char *argv[]) perror("mount"); return 1; } - break; case 'p': /* print pid */ @@ -163,8 +162,6 @@ int main(int argc, char *argv[]) perror("setns"); return 1; } - - break; case 'g': /* Attach to cgroup */ From 0e733c77543b16a67d77465b416fdd77cb509807 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Wed, 2 Jul 2014 13:07:57 -0700 Subject: [PATCH 11/43] fixed default iperf formatting behavior --- mininet/net.py | 6 +++--- mininet/test/test_hifi.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 4e98c60..3b3b1ff 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -617,7 +617,7 @@ class Mininet( object ): # XXX This should be cleaned up - def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format='M' ): + def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ): """Run iperf between two hosts. hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] @@ -640,8 +640,8 @@ class Mininet( object ): bwArgs = '-b ' + udpBw + ' ' elif l4Type != 'TCP': raise Exception( 'Unexpected l4 type: %s' % l4Type ) - if not format == 'M': - iperfArgs += '-f %s ' %format + if format: + iperfArgs += '-f %s ' % format server.sendCmd( iperfArgs + '-s', printPid=True ) servout = '' while server.lastPid is None: diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index 107138f..3f89b87 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -105,7 +105,7 @@ class testOptionsTopoCommon( object ): # pylint: enable-msg=W0612 for rttval in [rttmin, rttavg, rttmax]: # Multiply delay by 4 to cover there & back on two links - self.assertWithinTolerance( rttval, DELAY_MS * 4.0, + self.assertWithinTolerance( rttval, DELAY_MS * 4.0, DELAY_TOLERANCE) From 3131c90344766c30d2516ebab5792785296a7104 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 16:53:45 -0700 Subject: [PATCH 12/43] rolled back to iperf format option, and changed 'cpu' variable to 'pct' --- mininet/test/test_hifi.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index 3f89b87..8f8a1ce 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -55,8 +55,9 @@ class testOptionsTopoCommon( object ): """ self.assertGreaterEqual( float(measured), float(expected) * tolerance_frac ) - self.assertLess( float( measured ), - float(expected) + (1-tolerance_frac) * float( expected ) ) + self.assertLessEqual( float( measured ), + float(expected) + (1-tolerance_frac) + * float( expected ) ) def testCPULimits( self ): "Verify topology creation with CPU limits set for both schedulers." @@ -70,9 +71,9 @@ class testOptionsTopoCommon( object ): mn.start() results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) mn.stop() - for cpu in results: + for pct in results: #divide cpu by 100 to convert from percentage to fraction - self.assertWithinTolerance( cpu/100, CPU_FRACTION, CPU_TOLERANCE ) + self.assertWithinTolerance( pct/100, CPU_FRACTION, CPU_TOLERANCE ) def testLinkBandwidth( self ): "Verify that link bandwidths are accurate within a bound." From 93ddd926621f1ced84299bdf763471af71761f6a Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 16:55:50 -0700 Subject: [PATCH 13/43] Revert "fixed default iperf formatting behavior" This reverts commit 0e733c77543b16a67d77465b416fdd77cb509807. --- mininet/net.py | 6 +++--- mininet/test/test_hifi.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 3b3b1ff..4e98c60 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -617,7 +617,7 @@ class Mininet( object ): # XXX This should be cleaned up - def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ): + def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format='M' ): """Run iperf between two hosts. hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] @@ -640,8 +640,8 @@ class Mininet( object ): bwArgs = '-b ' + udpBw + ' ' elif l4Type != 'TCP': raise Exception( 'Unexpected l4 type: %s' % l4Type ) - if format: - iperfArgs += '-f %s ' % format + if not format == 'M': + iperfArgs += '-f %s ' %format server.sendCmd( iperfArgs + '-s', printPid=True ) servout = '' while server.lastPid is None: diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index 8f8a1ce..c888e29 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -106,7 +106,7 @@ class testOptionsTopoCommon( object ): # pylint: enable-msg=W0612 for rttval in [rttmin, rttavg, rttmax]: # Multiply delay by 4 to cover there & back on two links - self.assertWithinTolerance( rttval, DELAY_MS * 4.0, + self.assertWithinTolerance( rttval, DELAY_MS * 4.0, DELAY_TOLERANCE) From a1acfa89c2cc90c0125d18374c8779edd6af2311 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 17:23:35 -0700 Subject: [PATCH 14/43] set default iperf formatting to none --- mininet/net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 4e98c60..744d4c9 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -617,7 +617,7 @@ class Mininet( object ): # XXX This should be cleaned up - def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format='M' ): + def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ): """Run iperf between two hosts. hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] @@ -640,7 +640,7 @@ class Mininet( object ): bwArgs = '-b ' + udpBw + ' ' elif l4Type != 'TCP': raise Exception( 'Unexpected l4 type: %s' % l4Type ) - if not format == 'M': + if format: iperfArgs += '-f %s ' %format server.sendCmd( iperfArgs + '-s', printPid=True ) servout = '' From 191df1cb731743dc938b5fac7268e358b0de69b0 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Wed, 9 Jul 2014 17:47:25 -0700 Subject: [PATCH 15/43] Adding listen socket to UserSwitch when there is no listenPort set --- mininet/node.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mininet/node.py b/mininet/node.py index 96107f9..568d986 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -856,6 +856,8 @@ class UserSwitch( Switch ): '(openflow.org)' ) if self.listenPort: self.opts += ' --listen=ptcp:%i ' % self.listenPort + else: + self.opts += ' --listen=punix:/tmp/%s.listen' % self.name self.dpopts = dpopts @classmethod @@ -868,7 +870,7 @@ class UserSwitch( Switch ): "Run dpctl command" listenAddr = None if not self.listenPort: - listenAddr = 'unix:/tmp/' + self.name + listenAddr = 'unix:/tmp/%s.listen' % self.name else: listenAddr = 'tcp:127.0.0.1:%i' % self.listenPort return self.cmd( 'dpctl ' + ' '.join( args ) + From 84ea8d7f901ed10e49b0cd8fcdeeebde0f6274a7 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Wed, 2 Jul 2014 15:22:19 -0700 Subject: [PATCH 16/43] added waitConnected attribute to mininet class --- bin/mn | 2 ++ mininet/net.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/bin/mn b/bin/mn index 71c76ec..a86d758 100755 --- a/bin/mn +++ b/bin/mn @@ -261,12 +261,14 @@ class MininetRunner( object ): if test == 'none': pass elif test == 'all': + mn.waitConnected() mn.start() mn.ping() mn.iperf() elif test == 'cli': CLI( mn ) elif test != 'build': + mn.waitConnected() getattr( mn, test )() if self.options.post: diff --git a/mininet/net.py b/mininet/net.py index 8edaee3..f068bdc 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -94,7 +94,7 @@ from time import sleep from itertools import chain, groupby from mininet.cli import CLI -from mininet.log import info, error, debug, output +from mininet.log import info, error, debug, output, warn from mininet.node import Host, OVSKernelSwitch, Controller from mininet.link import Link, Intf from mininet.util import quietRun, fixLimits, numCores, ensureRoot @@ -112,7 +112,7 @@ class Mininet( object ): build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8', inNamespace=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, - listenPort=None ): + listenPort=None, waitConnected=False ): """Create Mininet object. topo: Topo (topology) object or None switch: default Switch class @@ -163,6 +163,33 @@ class Mininet( object ): if topo and build: self.build() + if waitConnected: + self.waitConnected() + + def waitConnected( self ): + """wait for each switch to connect to a controller, + up to 5 seconds + returns: True if all switches are connected""" + info( '***waiting for switches to connect\n' ) + time = 0 + while time < 5: + connected = True + for switch in self.switches: + if not switch.connected(): + connected = False + sleep( .1 ) + time += .1 + break + if connected: + break + if time >= 5: + warn( 'Timed out after %d seconds\n' % time ) + for switch in self.switches: + if not switch.connected(): + warn( 'Warning: %s is not connected to a controller\n' + % switch.name ) + return connected + def addHost( self, name, cls=None, **params ): """Add host. name: name of host to add From 8e2443ada408bcfbd98bfa2193e5b903990b0df5 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 13:08:48 -0700 Subject: [PATCH 17/43] improved waitConnected algorithm and set default wait time to wait forever --- mininet/net.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index f068bdc..d2e9b59 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -90,6 +90,7 @@ import os import re import select import signal +import copy from time import sleep from itertools import chain, groupby @@ -166,23 +167,25 @@ class Mininet( object ): if waitConnected: self.waitConnected() - def waitConnected( self ): + def waitConnected( self, timeout=None ): """wait for each switch to connect to a controller, up to 5 seconds returns: True if all switches are connected""" info( '***waiting for switches to connect\n' ) time = 0 - while time < 5: + remaining = copy.copy( self.switches ) + while time < timeout or timeout == None: connected = True - for switch in self.switches: + for switch in remaining: if not switch.connected(): connected = False - sleep( .1 ) - time += .1 - break + sleep( .5 ) + time += .5 + else: + remaining.remove( switch ) if connected: break - if time >= 5: + if time >= timeout and not timeout == None: warn( 'Timed out after %d seconds\n' % time ) for switch in self.switches: if not switch.connected(): From 6845fd833975554dd11f724e7aba540a5a94d50e Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 14:40:59 -0700 Subject: [PATCH 18/43] added documentation for waitConnected timeout --- mininet/net.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mininet/net.py b/mininet/net.py index d2e9b59..e86a07d 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -170,6 +170,7 @@ class Mininet( object ): def waitConnected( self, timeout=None ): """wait for each switch to connect to a controller, up to 5 seconds + timeout: max time to wait for switches to connect. returns: True if all switches are connected""" info( '***waiting for switches to connect\n' ) time = 0 From 4797b42005f0503c12f4ceebac3137bfe8aac901 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 17:29:37 -0700 Subject: [PATCH 19/43] conforming to style, and fixing documentation --- mininet/net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index e86a07d..784f20e 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -170,7 +170,7 @@ class Mininet( object ): def waitConnected( self, timeout=None ): """wait for each switch to connect to a controller, up to 5 seconds - timeout: max time to wait for switches to connect. + timeout: time to wait, or None to wait indefinitely returns: True if all switches are connected""" info( '***waiting for switches to connect\n' ) time = 0 @@ -186,7 +186,7 @@ class Mininet( object ): remaining.remove( switch ) if connected: break - if time >= timeout and not timeout == None: + if time >= timeout and timeout is not None: warn( 'Timed out after %d seconds\n' % time ) for switch in self.switches: if not switch.connected(): From 73f477be9dcbf0c02540d5afcb0b60c99da7e52b Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 18:19:11 -0700 Subject: [PATCH 20/43] added waitConnect to linearbandwidth example. --- examples/linearbandwidth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linearbandwidth.py b/examples/linearbandwidth.py index 3fd06c7..8e866a7 100755 --- a/examples/linearbandwidth.py +++ b/examples/linearbandwidth.py @@ -76,7 +76,7 @@ def linearBandwidthTest( lengths ): print "*** testing", datapath, "datapath" Switch = switches[ datapath ] results[ datapath ] = [] - net = Mininet( topo=topo, switch=Switch ) + net = Mininet( topo=topo, switch=Switch, waitConnected=True ) net.start() print "*** testing basic connectivity" for n in lengths: From c23c992f144146822fbf641d23f7d787ababbe59 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 8 Jul 2014 19:36:42 -0700 Subject: [PATCH 21/43] fixed waitConnected performance and moved waitConnected call to mn.start --- mininet/net.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 784f20e..0a31c68 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -149,6 +149,7 @@ class Mininet( object ): self.numCores = numCores() self.nextCore = 0 # next core for pinning hosts to CPUs self.listenPort = listenPort + self.waitConn = waitConnected self.hosts = [] self.switches = [] @@ -164,8 +165,6 @@ class Mininet( object ): if topo and build: self.build() - if waitConnected: - self.waitConnected() def waitConnected( self, timeout=None ): """wait for each switch to connect to a controller, @@ -180,12 +179,12 @@ class Mininet( object ): for switch in remaining: if not switch.connected(): connected = False - sleep( .5 ) - time += .5 else: remaining.remove( switch ) if connected: break + sleep( .5 ) + time += .5 if time >= timeout and timeout is not None: warn( 'Timed out after %d seconds\n' % time ) for switch in self.switches: @@ -432,6 +431,8 @@ class Mininet( object ): info( switch.name + ' ') switch.start( self.controllers ) info( '\n' ) + if self.waitConn: + self.waitConnected() def stop( self ): "Stop the controller(s), switches and hosts" From 3a52ad2f530242cd368f5fb900ff254b7d4e8b00 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Wed, 9 Jul 2014 19:24:22 -0700 Subject: [PATCH 22/43] fixed linearbandwidth and waitconnected --- examples/linearbandwidth.py | 4 ++-- mininet/net.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/linearbandwidth.py b/examples/linearbandwidth.py index 8e866a7..dee5490 100755 --- a/examples/linearbandwidth.py +++ b/examples/linearbandwidth.py @@ -24,7 +24,7 @@ of switches, this example demonstrates: """ from mininet.net import Mininet -from mininet.node import UserSwitch, OVSKernelSwitch +from mininet.node import UserSwitch, OVSKernelSwitch, Controller from mininet.topo import Topo from mininet.log import lg from mininet.util import irange @@ -76,7 +76,7 @@ def linearBandwidthTest( lengths ): print "*** testing", datapath, "datapath" Switch = switches[ datapath ] results[ datapath ] = [] - net = Mininet( topo=topo, switch=Switch, waitConnected=True ) + net = Mininet( topo=topo, switch=Switch, controller=Controller, waitConnected=True ) net.start() print "*** testing basic connectivity" for n in lengths: diff --git a/mininet/net.py b/mininet/net.py index 0a31c68..a5b9a34 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -432,7 +432,7 @@ class Mininet( object ): switch.start( self.controllers ) info( '\n' ) if self.waitConn: - self.waitConnected() + self.waitConnected( ) def stop( self ): "Stop the controller(s), switches and hosts" From 1324ae62621972a226c8629e96856de53c470346 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 9 Jul 2014 21:20:39 -0700 Subject: [PATCH 23/43] Add build() method to simplify Topo() usage --- mininet/topo.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mininet/topo.py b/mininet/topo.py index f9c421f..de5ba8a 100644 --- a/mininet/topo.py +++ b/mininet/topo.py @@ -48,18 +48,25 @@ class MultiGraph( object ): class Topo(object): "Data center network representation for structured multi-trees." - def __init__(self, hopts=None, sopts=None, lopts=None): - """Topo object: + def __init__(self, *args, **params): + """Topo object. + Optional named parameters: hinfo: default host options sopts: default switch options - lopts: default link options""" + lopts: default link options + calls build()""" self.g = MultiGraph() self.node_info = {} self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects - self.hopts = {} if hopts is None else hopts - self.sopts = {} if sopts is None else sopts - self.lopts = {} if lopts is None else lopts + self.hopts = params.pop( 'hopts', {} ) + self.sopts = params.pop( 'sopts', {} ) + self.lopts = params.pop( 'lopts', {} ) self.ports = {} # ports[src][dst] is port on src that connects to dst + self.build( *args, **params ) + + def build( self, *args, **params ): + "Override this method to build your topology." + pass def addNode(self, name, **opts): """Add Node to graph. From 3878c000fe5da0e972cceb629990420be9a1e699 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 10 Jul 2014 00:38:18 -0700 Subject: [PATCH 24/43] Add nodelib.py, a library of new node types --- mininet/nodelib.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 mininet/nodelib.py diff --git a/mininet/nodelib.py b/mininet/nodelib.py new file mode 100644 index 0000000..6c27f28 --- /dev/null +++ b/mininet/nodelib.py @@ -0,0 +1,45 @@ +""" +Package: nodelib + +Node Library for Mininet + +This contains additional node types which you may find to be useful +""" + +from mininet.net import Mininet +from mininet.topo import Topo +from mininet.node import Switch +from mininet.log import setLogLevel, info + +class LinuxBridge( Switch ): + "Linux Bridge (with optional spanning tree)" + + nextPrio = 100 # next bridge priority for spanning tree + + def __init__( self, name, stp=False, prio=None, **kwargs ): + """stp: use spanning tree protocol? (default False) + prio: optional explicit bridge priority for STP""" + self.stp = stp + if prio: + self.prio = prio + else: + self.prio = LinuxBridge.nextPrio + LinuxBridge.nextPrio += 1 + Switch.__init__( self, name, **kwargs ) + + def start( self, controllers ): + self.cmd( 'ifconfig', self, 'down' ) + self.cmd( 'brctl delbr', self ) + self.cmd( 'brctl addbr', self ) + if self.stp: + self.cmd( 'brctl setbridgeprio', self.prio ) + self.cmd( 'brctl stp', self, 'on' ) + for i in self.intfList(): + if self.name in i.name: + self.cmd( 'brctl addif', self, i ) + self.cmd( 'ifconfig', self, 'up' ) + + def stop( self ): + self.cmd( 'ifconfig', self, 'down' ) + self.cmd( 'brctl delbr', self ) + From 38addf2e24840a10fcee88308fe9a41ee0b4c559 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 10 Jul 2014 01:28:40 -0700 Subject: [PATCH 25/43] Add alias + switch: { 'ovs': OVSSwitch, 'lxbr': LinuxBridge } --- bin/mn | 10 +++++++--- mininet/nodelib.py | 5 ++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/bin/mn b/bin/mn index 71c76ec..567e213 100755 --- a/bin/mn +++ b/bin/mn @@ -25,8 +25,9 @@ from mininet.cli import CLI from mininet.log import lg, LEVELS, info, debug, error from mininet.net import Mininet, MininetWithControlNet, VERSION from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, - NOX, RemoteController, UserSwitch, OVSKernelSwitch, + NOX, RemoteController, UserSwitch, OVSSwitch, OVSLegacyKernelSwitch, IVSSwitch ) +from mininet.nodelib import LinuxBridge from mininet.link import Link, TCLink from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo from mininet.topolib import TreeTopo @@ -44,9 +45,12 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ), SWITCHDEF = 'ovsk' SWITCHES = { 'user': UserSwitch, - 'ovsk': OVSKernelSwitch, + 'ovs': OVSSwitch, + # Keep ovsk for compatibility with 2.0 + 'ovsk': OVSSwitch, 'ovsl': OVSLegacyKernelSwitch, - 'ivs': IVSSwitch } + 'ivs': IVSSwitch, + 'lxbr': LinuxBridge } HOSTDEF = 'proc' HOSTS = { 'proc': Host, diff --git a/mininet/nodelib.py b/mininet/nodelib.py index 6c27f28..df6154c 100644 --- a/mininet/nodelib.py +++ b/mininet/nodelib.py @@ -1,9 +1,7 @@ """ -Package: nodelib - Node Library for Mininet -This contains additional node types which you may find to be useful +This contains additional Node types which you may find to be useful """ from mininet.net import Mininet @@ -11,6 +9,7 @@ from mininet.topo import Topo from mininet.node import Switch from mininet.log import setLogLevel, info + class LinuxBridge( Switch ): "Linux Bridge (with optional spanning tree)" From 5a9c74be03ba61d6764cd70fffb673ed4abc2ba8 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Thu, 10 Jul 2014 11:07:50 -0700 Subject: [PATCH 26/43] fixed last commit --- mininet/net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/net.py b/mininet/net.py index a5b9a34..0a31c68 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -432,7 +432,7 @@ class Mininet( object ): switch.start( self.controllers ) info( '\n' ) if self.waitConn: - self.waitConnected( ) + self.waitConnected() def stop( self ): "Stop the controller(s), switches and hosts" From b7a112cbec372954e88999607d8bdf8cabd27066 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Thu, 10 Jul 2014 11:24:58 -0700 Subject: [PATCH 27/43] Shutting down controller first --- mininet/net.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 8edaee3..e970bbd 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -404,6 +404,11 @@ class Mininet( object ): def stop( self ): "Stop the controller(s), switches and hosts" + info( '*** Stopping %i controllers\n' % len( self.controllers ) ) + for controller in self.controllers: + info( controller.name + ' ' ) + controller.stop() + info( '\n' ) if self.terms: info( '*** Stopping %i terms\n' % len( self.terms ) ) self.stopXterms() @@ -419,11 +424,6 @@ class Mininet( object ): for host in self.hosts: info( host.name + ' ' ) host.terminate() - info( '\n' ) - info( '*** Stopping %i controllers\n' % len( self.controllers ) ) - for controller in self.controllers: - info( controller.name + ' ' ) - controller.stop() info( '\n*** Done\n' ) def run( self, test, *args, **kwargs ): From 13d25b410920845f75c5bd1ff1b5f69f28cb0eee Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 10 Jul 2014 12:44:49 -0700 Subject: [PATCH 28/43] Minor message changes --- mininet/net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index be0d287..281980a 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -171,7 +171,7 @@ class Mininet( object ): up to 5 seconds timeout: time to wait, or None to wait indefinitely returns: True if all switches are connected""" - info( '***waiting for switches to connect\n' ) + info( '*** Waiting for switches to connect\n' ) time = 0 remaining = copy.copy( self.switches ) while time < timeout or timeout == None: @@ -678,7 +678,7 @@ class Mininet( object ): if l4Type == 'TCP': while 'Connected' not in client.cmd( 'sh -c "echo A | telnet -e A %s 5001"' % server.IP()): - output('waiting for iperf to start up...') + info( 'Waiting for iperf to start up...' ) sleep(.5) cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' + bwArgs ) From 4794871a9a47ff2baf74c5955c916ab3ce7b8be1 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 10 Jul 2014 13:39:17 -0700 Subject: [PATCH 29/43] Change algorithm slightly and print progress --- mininet/net.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 281980a..84416d2 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -166,32 +166,35 @@ class Mininet( object ): self.build() - def waitConnected( self, timeout=None ): + def waitConnected( self, timeout=None, delay=.5 ): """wait for each switch to connect to a controller, up to 5 seconds timeout: time to wait, or None to wait indefinitely + delay: seconds to sleep per iteration returns: True if all switches are connected""" info( '*** Waiting for switches to connect\n' ) time = 0 remaining = copy.copy( self.switches ) - while time < timeout or timeout == None: - connected = True + while True: for switch in remaining: - if not switch.connected(): - connected = False - else: + if switch.connected(): + info( '%s ' % switch ) remaining.remove( switch ) - if connected: + if not remaining: + info( '\n' ) + return True + if time > timeout and timeout is not None: break - sleep( .5 ) - time += .5 - if time >= timeout and timeout is not None: - warn( 'Timed out after %d seconds\n' % time ) - for switch in self.switches: - if not switch.connected(): - warn( 'Warning: %s is not connected to a controller\n' - % switch.name ) - return connected + sleep( delay ) + time += delay + warn( 'Timed out after %d seconds\n' % time ) + for switch in remaining: + if not switch.connected(): + warn( 'Warning: %s is not connected to a controller\n' + % switch.name ) + else: + remaining.remove( switch ) + return not remaining def addHost( self, name, cls=None, **params ): """Add host. From 72fd120dc85b86f4f1477337223fac85a5cb2171 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Fri, 11 Jul 2014 19:04:20 -0700 Subject: [PATCH 30/43] added default controller class --- bin/mn | 5 +++-- mininet/net.py | 7 ++++--- mininet/node.py | 9 +++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/bin/mn b/bin/mn index 71c76ec..3d01a58 100755 --- a/bin/mn +++ b/bin/mn @@ -25,7 +25,7 @@ from mininet.cli import CLI from mininet.log import lg, LEVELS, info, debug, error from mininet.net import Mininet, MininetWithControlNet, VERSION from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, - NOX, RemoteController, UserSwitch, OVSKernelSwitch, + NOX, RemoteController, DefaultController, UserSwitch, OVSKernelSwitch, OVSLegacyKernelSwitch, IVSSwitch ) from mininet.link import Link, TCLink from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo @@ -53,11 +53,12 @@ HOSTS = { 'proc': Host, 'rt': custom( CPULimitedHost, sched='rt' ), 'cfs': custom( CPULimitedHost, sched='cfs' ) } -CONTROLLERDEF = 'ovsc' +CONTROLLERDEF = 'default' CONTROLLERS = { 'ref': Controller, 'ovsc': OVSController, 'nox': NOX, 'remote': RemoteController, + 'default': DefaultController, 'none': lambda name: None } LINKDEF = 'default' diff --git a/mininet/net.py b/mininet/net.py index 8edaee3..d285abc 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -95,7 +95,7 @@ from itertools import chain, groupby from mininet.cli import CLI from mininet.log import info, error, debug, output -from mininet.node import Host, OVSKernelSwitch, Controller +from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller from mininet.link import Link, Intf from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd @@ -213,16 +213,17 @@ class Mininet( object ): if not controller: controller = self.controller # Construct new controller if one is not given - if isinstance(name, Controller): + if isinstance( name, Controller ): controller_new = name # Pylint thinks controller is a str() # pylint: disable=E1103 name = controller_new.name # pylint: enable=E1103 else: + # bookmark controller_new = controller( name, **params ) # Add new controller to net - if controller_new: # allow controller-less setups + if controller_new: # allow controller-less setups self.controllers.append( controller_new ) self.nameToNode[ name ] = controller_new return controller_new diff --git a/mininet/node.py b/mininet/node.py index 568d986..609b6e5 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1278,6 +1278,15 @@ class Controller( Node ): self.__class__.__name__, self.name, self.IP(), self.port, self.pid ) +class DefaultController( Controller ): + "find any controller that is available and run it" + def __init__( self, name, **kwargs ): + "search for any installed controller" + controllers = [ 'controller', 'ovs-controller', 'test-controller' ] # , 'pox', 'ryu' ] # test-controller is the important part + for c in controllers: + if quietRun( "which " + c ): + Controller.__init__( self, name, controller=c, **kwargs ) + break class OVSController( Controller ): "Open vSwitch controller" From 796b281bf1955022b4bac8e16b976e0e0ee277ea Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Mon, 14 Jul 2014 13:09:41 -0700 Subject: [PATCH 31/43] fixed command parameter --- mininet/node.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mininet/node.py b/mininet/node.py index 609b6e5..2680265 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1282,10 +1282,11 @@ class DefaultController( Controller ): "find any controller that is available and run it" def __init__( self, name, **kwargs ): "search for any installed controller" - controllers = [ 'controller', 'ovs-controller', 'test-controller' ] # , 'pox', 'ryu' ] # test-controller is the important part + controllers = [ 'controller', 'ovs-controller', + 'test-controller' ] for c in controllers: if quietRun( "which " + c ): - Controller.__init__( self, name, controller=c, **kwargs ) + Controller.__init__( self, name, command=c, **kwargs ) break class OVSController( Controller ): From a19cc915373c22bddb2e73360a6e165cf18e5955 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Mon, 14 Jul 2014 14:09:39 -0700 Subject: [PATCH 32/43] set DefaultController as the mininet class default --- mininet/net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/net.py b/mininet/net.py index d285abc..cfd8d08 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -108,7 +108,7 @@ class Mininet( object ): "Network emulation with hosts spawned in network namespaces." def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host, - controller=Controller, link=Link, intf=Intf, + controller=DefaultController, link=Link, intf=Intf, build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8', inNamespace=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, From ea97dea902709448d469c719b366c28377cbf197 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Mon, 14 Jul 2014 14:42:00 -0700 Subject: [PATCH 33/43] adding waitConnected to linear5 test --- mininet/test/test_nets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/test/test_nets.py b/mininet/test/test_nets.py index 159ba34..330e9a8 100755 --- a/mininet/test/test_nets.py +++ b/mininet/test/test_nets.py @@ -66,7 +66,7 @@ class testLinearCommon( object ): def testLinear5( self ): "Ping test on a 5-switch topology" - mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller ) + mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller, waitConnected=True ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) From 708b184397af9ae8d6787f42a678f899030fdbfb Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Jul 2014 01:24:19 -0700 Subject: [PATCH 34/43] Don't remove items from a list we're iterating over --- mininet/net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index f521fbe..353fc57 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -174,9 +174,9 @@ class Mininet( object ): returns: True if all switches are connected""" info( '*** Waiting for switches to connect\n' ) time = 0 - remaining = copy.copy( self.switches ) + remaining = list( self.switches ) while True: - for switch in remaining: + for switch in tuple( remaining ): if switch.connected(): info( '%s ' % switch ) remaining.remove( switch ) From 2935000485dad214f929601bb84b7601c0c9b7f8 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Jul 2014 04:26:31 -0700 Subject: [PATCH 35/43] Add utopic/Ubuntu 14.10 --- util/vm/build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/vm/build.py b/util/vm/build.py index be809ec..ad454f0 100755 --- a/util/vm/build.py +++ b/util/vm/build.py @@ -89,6 +89,12 @@ isoURLs = { 'trusty64server': 'http://mirrors.kernel.org/ubuntu-releases/14.04/' 'ubuntu-14.04-server-amd64.iso', + 'utopic32server': + 'http://mirrors.kernel.org/ubuntu-releases/14.10/' + 'ubuntu-14.10-server-i386.iso', + 'utopic64server': + 'http://mirrors.kernel.org/ubuntu-releases/14.10/' + 'ubuntu-14.10-server-amd64.iso', } From ece509d5795ee9494f69fbe9448acec690bbb98a Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Jul 2014 05:34:32 -0700 Subject: [PATCH 36/43] add connected() to LinuxBridge --- mininet/nodelib.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mininet/nodelib.py b/mininet/nodelib.py index df6154c..2eb8046 100644 --- a/mininet/nodelib.py +++ b/mininet/nodelib.py @@ -26,6 +26,13 @@ class LinuxBridge( Switch ): LinuxBridge.nextPrio += 1 Switch.__init__( self, name, **kwargs ) + def connected( self ): + "Are we forwarding yet?" + if self.stp: + return 'forwarding' in self.cmd( 'brctl showstp', self ) + else: + return True + def start( self, controllers ): self.cmd( 'ifconfig', self, 'down' ) self.cmd( 'brctl delbr', self ) From b5962e8ee982b5b2a81c1548378ff30ef986ba02 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Jul 2014 05:44:13 -0700 Subject: [PATCH 37/43] Added TorusTopo, a 2D torus topology --- bin/mn | 5 +++-- mininet/topolib.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/bin/mn b/bin/mn index 79941f0..67c7173 100755 --- a/bin/mn +++ b/bin/mn @@ -30,7 +30,7 @@ from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, from mininet.nodelib import LinuxBridge from mininet.link import Link, TCLink from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo -from mininet.topolib import TreeTopo +from mininet.topolib import TreeTopo, TorusTopo from mininet.util import custom, customConstructor from mininet.util import buildTopo @@ -41,7 +41,8 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ), 'linear': LinearTopo, 'reversed': SingleSwitchReversedTopo, 'single': SingleSwitchTopo, - 'tree': TreeTopo } + 'tree': TreeTopo, + 'torus': TorusTopo } SWITCHDEF = 'ovsk' SWITCHES = { 'user': UserSwitch, diff --git a/mininet/topolib.py b/mininet/topolib.py index 63ba36d..4c15e91 100644 --- a/mininet/topolib.py +++ b/mininet/topolib.py @@ -34,3 +34,37 @@ def TreeNet( depth=1, fanout=2, **kwargs ): "Convenience function for creating tree networks." topo = TreeTopo( depth, fanout ) return Mininet( topo, **kwargs ) + + +class TorusTopo( Topo ): + """2-D Torus topology + WARNING: this topology has LOOPS and WILL NOT WORK + with the default controller or any Ethernet bridge + without STP turned on! It can be used with STP, e.g.: + # mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall""" + def __init__( self, x, y, *args, **kwargs ): + Topo.__init__( self, *args, **kwargs ) + if x < 3 or y < 3: + raise Exception( 'Please use 3x3 or greater for compatibility ' + 'with Mininet 2.1.0' ) + hosts, switches, dpid = {}, {}, 0 + # Create and wire interior + for i in range( 0, x ): + for j in range( 0, y ): + loc = '%dx%d' % ( i + 1, j + 1 ) + # dpid cannot be zero for OVS + dpid = ( i + 1 ) * 256 + ( j + 1 ) + switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%016x' % dpid ) + host = hosts[ i, j ] = self.addHost( 'h' + loc ) + self.addLink( host, switch ) + # Connect switches + for i in range( 0, x ): + for j in range( 0, y ): + sw1 = switches[ i, j ] + sw2 = switches[ i, ( j + 1 ) % y ] + sw3 = switches[ ( i + 1 ) % x, j ] + self.addLink( sw1, sw2 ) + self.addLink( sw1, sw3 ) + + + From b7268856d7b74dc6022bad120b27749f32e9253a Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Jul 2014 08:28:05 -0700 Subject: [PATCH 38/43] Tolerate passing controller *objects* into Mininet() --- mininet/net.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 353fc57..b7b89f7 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -246,7 +246,7 @@ class Mininet( object ): if not controller: controller = self.controller # Construct new controller if one is not given - if isinstance(name, Controller): + if issubclass( name.__class__, Controller ): controller_new = name # Pylint thinks controller is a str() # pylint: disable=E1103 @@ -357,7 +357,11 @@ class Mininet( object ): if type( classes ) is not list: classes = [ classes ] for i, cls in enumerate( classes ): - self.addController( 'c%d' % i, cls ) + # Allow Controller objects because nobody understands currying + if issubclass( cls.__class__, Controller ): + self.addController( cls ) + else: + self.addController( 'c%d' % i, cls ) info( '*** Adding hosts:\n' ) for hostName in topo.hosts(): From 2a08dec648e7c66d23ed5bb81d673e07ba68cec3 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 15 Jul 2014 08:50:30 -0700 Subject: [PATCH 39/43] Hack to avoid failing version check... ;-/ --- mininet/topolib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/topolib.py b/mininet/topolib.py index 4c15e91..8e3b3a4 100644 --- a/mininet/topolib.py +++ b/mininet/topolib.py @@ -46,7 +46,7 @@ class TorusTopo( Topo ): Topo.__init__( self, *args, **kwargs ) if x < 3 or y < 3: raise Exception( 'Please use 3x3 or greater for compatibility ' - 'with Mininet 2.1.0' ) + 'with 2.1' ) hosts, switches, dpid = {}, {}, 0 # Create and wire interior for i in range( 0, x ): From 5ac3cde2bdeec58156f2d34a5ea4de710662b77e Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 15 Jul 2014 15:48:18 -0700 Subject: [PATCH 40/43] restructured defaultController into a function --- bin/mn | 2 +- mininet/node.py | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/bin/mn b/bin/mn index 3d01a58..04761da 100755 --- a/bin/mn +++ b/bin/mn @@ -25,7 +25,7 @@ from mininet.cli import CLI from mininet.log import lg, LEVELS, info, debug, error from mininet.net import Mininet, MininetWithControlNet, VERSION from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, - NOX, RemoteController, DefaultController, UserSwitch, OVSKernelSwitch, + NOX, DefaultController, RemoteController, UserSwitch, OVSKernelSwitch, OVSLegacyKernelSwitch, IVSSwitch ) from mininet.link import Link, TCLink from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo diff --git a/mininet/node.py b/mininet/node.py index 2680265..41b2d45 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1277,23 +1277,19 @@ class Controller( Node ): return '<%s %s: %s:%s pid=%s> ' % ( self.__class__.__name__, self.name, self.IP(), self.port, self.pid ) - -class DefaultController( Controller ): - "find any controller that is available and run it" - def __init__( self, name, **kwargs ): - "search for any installed controller" - controllers = [ 'controller', 'ovs-controller', - 'test-controller' ] - for c in controllers: - if quietRun( "which " + c ): - Controller.__init__( self, name, command=c, **kwargs ) - break + @classmethod + def isAvailable( self ): + return quietRun( 'which controller' ) class OVSController( Controller ): "Open vSwitch controller" def __init__( self, name, command='ovs-controller', **kwargs ): + if quietRun( 'which test-controller' ): + command = 'test-controller' Controller.__init__( self, name, command=command, **kwargs ) - + @classmethod + def isAvailable( self ): + return quietRun( 'which ovs-controller' ) or quietRun( 'which test-controller' ) class NOX( Controller ): "Controller to run a NOX application." @@ -1348,3 +1344,10 @@ class RemoteController( Controller ): if 'Connected' not in listening: warn( "Unable to contact the remote controller" " at %s:%d\n" % ( self.ip, self.port ) ) + + +def DefaultController( name, order=[ Controller, OVSController ], **kwargs ): + "find a default controller for mininet" + for controller in order: + if controller.isAvailable(): + return controller( name, **kwargs ) From 00d1963484af0b0252e7afd5b014a91598f6a95f Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 15 Jul 2014 18:21:56 -0700 Subject: [PATCH 41/43] revised comment on defaultController function --- mininet/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/node.py b/mininet/node.py index 41b2d45..d9052fa 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1347,7 +1347,7 @@ class RemoteController( Controller ): def DefaultController( name, order=[ Controller, OVSController ], **kwargs ): - "find a default controller for mininet" + "find any controller that is available and run it" for controller in order: if controller.isAvailable(): return controller( name, **kwargs ) From 779ea5f0ad2905acd977c2bdcd4e35915d4491d8 Mon Sep 17 00:00:00 2001 From: Cody Burkard Date: Tue, 15 Jul 2014 19:39:50 -0700 Subject: [PATCH 42/43] removed bookmark --- mininet/net.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mininet/net.py b/mininet/net.py index cfd8d08..afe8096 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -220,7 +220,6 @@ class Mininet( object ): name = controller_new.name # pylint: enable=E1103 else: - # bookmark controller_new = controller( name, **params ) # Add new controller to net if controller_new: # allow controller-less setups From e183e6999736d481452b2f41cfd4e94831e56a1f Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 16 Jul 2014 09:57:48 -0700 Subject: [PATCH 43/43] Check for Controller type using isinstance() --- mininet/net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index b7b89f7..e4228b9 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -246,7 +246,7 @@ class Mininet( object ): if not controller: controller = self.controller # Construct new controller if one is not given - if issubclass( name.__class__, Controller ): + if isinstance( name, Controller ): controller_new = name # Pylint thinks controller is a str() # pylint: disable=E1103 @@ -358,7 +358,7 @@ class Mininet( object ): classes = [ classes ] for i, cls in enumerate( classes ): # Allow Controller objects because nobody understands currying - if issubclass( cls.__class__, Controller ): + if isinstance( cls, Controller ): self.addController( cls ) else: self.addController( 'c%d' % i, cls )