Merge branch 'master' into devel/pty

This commit is contained in:
Brian O'Connor
2014-07-31 18:23:19 -07:00
13 changed files with 306 additions and 207 deletions
+15 -6
View File
@@ -25,11 +25,13 @@ from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, error from mininet.log import lg, LEVELS, info, debug, error
from mininet.net import Mininet, MininetWithControlNet, VERSION from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
NOX, RemoteController, UserSwitch, OVSKernelSwitch, NOX, RemoteController, DefaultController,
UserSwitch, OVSSwitch,
OVSLegacyKernelSwitch, IVSSwitch ) OVSLegacyKernelSwitch, IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo 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 custom, customConstructor
from mininet.util import buildTopo from mininet.util import buildTopo
@@ -40,24 +42,29 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo, 'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo, 'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo, 'single': SingleSwitchTopo,
'tree': TreeTopo } 'tree': TreeTopo,
'torus': TorusTopo }
SWITCHDEF = 'ovsk' SWITCHDEF = 'ovsk'
SWITCHES = { 'user': UserSwitch, SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch, 'ovs': OVSSwitch,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ovsl': OVSLegacyKernelSwitch, 'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch } 'ivs': IVSSwitch,
'lxbr': LinuxBridge }
HOSTDEF = 'proc' HOSTDEF = 'proc'
HOSTS = { 'proc': Host, HOSTS = { 'proc': Host,
'rt': custom( CPULimitedHost, sched='rt' ), 'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) } 'cfs': custom( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'ovsc' CONTROLLERDEF = 'default'
CONTROLLERS = { 'ref': Controller, CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController, 'ovsc': OVSController,
'nox': NOX, 'nox': NOX,
'remote': RemoteController, 'remote': RemoteController,
'default': DefaultController,
'none': lambda name: None } 'none': lambda name: None }
LINKDEF = 'default' LINKDEF = 'default'
@@ -261,12 +268,14 @@ class MininetRunner( object ):
if test == 'none': if test == 'none':
pass pass
elif test == 'all': elif test == 'all':
mn.waitConnected()
mn.start() mn.start()
mn.ping() mn.ping()
mn.iperf() mn.iperf()
elif test == 'cli': elif test == 'cli':
CLI( mn ) CLI( mn )
elif test != 'build': elif test != 'build':
mn.waitConnected()
getattr( mn, test )() getattr( mn, test )()
if self.options.post: if self.options.post:
+42 -167
View File
@@ -1,197 +1,72 @@
#!/usr/bin/python #!/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 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 from mininet.net import Mininet
from mininet.node import Host from mininet.node import Host, HostWithPrivateDirs
from mininet.cli import CLI from mininet.cli import CLI
from mininet.util import errFail, quietRun, errRun
from mininet.topo import SingleSwitchTopo from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info, debug from mininet.log import setLogLevel, info, debug
from os.path import realpath
from functools import partial 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 # Sample usage
def testHostWithPrivateDirs(): def testHostWithPrivateDirs():
"Test bind mounts" "Test bind mounts"
topo = SingleSwitchTopo( 10 ) topo = SingleSwitchTopo( 10 )
remounts = findRemounts( fstypes=[ 'nfs' ] ) privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
privateDirs = [ '/var/log', '/var/run' ] ( '/var/run', '/tmp/%(name)s/var/run' ),
host = partial( HostWithPrivateDirs, remounts=remounts, '/var/mn' ]
privateDirs=privateDirs, unmount=False ) host = partial( HostWithPrivateDirs,
privateDirs=privateDirs )
net = Mininet( topo=topo, host=host ) net = Mininet( topo=topo, host=host )
net.start() net.start()
info( 'Private Directories:', privateDirs, '\n' ) directories = []
for directory in privateDirs:
directories.append( directory[ 0 ]
if isinstance( directory, tuple )
else directory )
info( 'Private Directories:', directories, '\n' )
CLI( net ) CLI( net )
net.stop() net.stop()
# We do this all at once to save a bit of time
info( 'Unmounting host bind mounts...\n' )
unmountAll()
if __name__ == '__main__': if __name__ == '__main__':
unmountAll()
setLogLevel( 'info' ) setLogLevel( 'info' )
testHostWithPrivateDirs() testHostWithPrivateDirs()
info( 'Done.\n') info( 'Done.\n')
+2 -2
View File
@@ -24,7 +24,7 @@ of switches, this example demonstrates:
""" """
from mininet.net import Mininet 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.topo import Topo
from mininet.log import lg from mininet.log import lg
from mininet.util import irange from mininet.util import irange
@@ -76,7 +76,7 @@ def linearBandwidthTest( lengths ):
print "*** testing", datapath, "datapath" print "*** testing", datapath, "datapath"
Switch = switches[ datapath ] Switch = switches[ datapath ]
results[ datapath ] = [] results[ datapath ] = []
net = Mininet( topo=topo, switch=Switch ) net = Mininet( topo=topo, switch=Switch, controller=Controller, waitConnected=True )
net.start() net.start()
print "*** testing basic connectivity" print "*** testing basic connectivity"
for n in lengths: for n in lengths:
+15 -1
View File
@@ -10,7 +10,7 @@ It may also get rid of 'false positives', but hopefully
nothing irreplaceable! nothing irreplaceable!
""" """
from subprocess import Popen, PIPE from subprocess import Popen, PIPE, check_output as co
import time import time
from mininet.log import info from mininet.log import info
@@ -69,4 +69,18 @@ def cleanup():
if link: if link:
sh( "ip link del " + 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" ) info( "*** Cleanup complete.\n" )
+55 -14
View File
@@ -90,12 +90,13 @@ import os
import re import re
import select import select
import signal import signal
import copy
from time import sleep from time import sleep
from itertools import chain, groupby from itertools import chain, groupby
from mininet.cli import CLI 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.node import Host, OVSKernelSwitch, DefaultController, Controller
from mininet.link import Link, Intf from mininet.link import Link, Intf
from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import quietRun, fixLimits, numCores, ensureRoot
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
@@ -108,11 +109,11 @@ class Mininet( object ):
"Network emulation with hosts spawned in network namespaces." "Network emulation with hosts spawned in network namespaces."
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host, 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', build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False, inNamespace=False,
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None ): listenPort=None, waitConnected=False ):
"""Create Mininet object. """Create Mininet object.
topo: Topo (topology) object or None topo: Topo (topology) object or None
switch: default Switch class switch: default Switch class
@@ -148,6 +149,7 @@ class Mininet( object ):
self.numCores = numCores() self.numCores = numCores()
self.nextCore = 0 # next core for pinning hosts to CPUs self.nextCore = 0 # next core for pinning hosts to CPUs
self.listenPort = listenPort self.listenPort = listenPort
self.waitConn = waitConnected
self.hosts = [] self.hosts = []
self.switches = [] self.switches = []
@@ -163,6 +165,37 @@ class Mininet( object ):
if topo and build: if topo and build:
self.build() self.build()
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 = list( self.switches )
while True:
for switch in tuple( remaining ):
if switch.connected():
info( '%s ' % switch )
remaining.remove( switch )
if not remaining:
info( '\n' )
return True
if time > timeout and timeout is not None:
break
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 ): def addHost( self, name, cls=None, **params ):
"""Add host. """Add host.
name: name of host to add name: name of host to add
@@ -213,7 +246,7 @@ class Mininet( object ):
if not controller: if not controller:
controller = self.controller controller = self.controller
# Construct new controller if one is not given # Construct new controller if one is not given
if isinstance(name, Controller): if isinstance( name, Controller ):
controller_new = name controller_new = name
# Pylint thinks controller is a str() # Pylint thinks controller is a str()
# pylint: disable=E1103 # pylint: disable=E1103
@@ -222,7 +255,7 @@ class Mininet( object ):
else: else:
controller_new = controller( name, **params ) controller_new = controller( name, **params )
# Add new controller to net # 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.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new self.nameToNode[ name ] = controller_new
return controller_new return controller_new
@@ -324,7 +357,11 @@ class Mininet( object ):
if type( classes ) is not list: if type( classes ) is not list:
classes = [ classes ] classes = [ classes ]
for i, cls in enumerate( classes ): for i, cls in enumerate( classes ):
self.addController( 'c%d' % i, cls ) # Allow Controller objects because nobody understands currying
if isinstance( cls, Controller ):
self.addController( cls )
else:
self.addController( 'c%d' % i, cls )
info( '*** Adding hosts:\n' ) info( '*** Adding hosts:\n' )
for hostName in topo.hosts(): for hostName in topo.hosts():
@@ -401,9 +438,16 @@ class Mininet( object ):
info( switch.name + ' ') info( switch.name + ' ')
switch.start( self.controllers ) switch.start( self.controllers )
info( '\n' ) info( '\n' )
if self.waitConn:
self.waitConnected()
def stop( self ): def stop( self ):
"Stop the controller(s), switches and hosts" "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: if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) ) info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms() self.stopXterms()
@@ -419,11 +463,6 @@ class Mininet( object ):
for host in self.hosts: for host in self.hosts:
info( host.name + ' ' ) info( host.name + ' ' )
host.terminate() 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' ) info( '\n*** Done\n' )
def run( self, test, *args, **kwargs ): def run( self, test, *args, **kwargs ):
@@ -616,7 +655,7 @@ class Mininet( object ):
# XXX This should be cleaned up # 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=None ):
"""Run iperf between two hosts. """Run iperf between two hosts.
hosts: list of hosts; if None, uses opposite hosts hosts: list of hosts; if None, uses opposite hosts
l4Type: string, one of [ TCP, UDP ] l4Type: string, one of [ TCP, UDP ]
@@ -639,6 +678,8 @@ class Mininet( object ):
bwArgs = '-b ' + udpBw + ' ' bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP': elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type ) raise Exception( 'Unexpected l4 type: %s' % l4Type )
if format:
iperfArgs += '-f %s ' %format
server.sendCmd( iperfArgs + '-s', printPid=True ) server.sendCmd( iperfArgs + '-s', printPid=True )
servout = '' servout = ''
while server.lastPid is None: while server.lastPid is None:
@@ -646,7 +687,7 @@ class Mininet( object ):
if l4Type == 'TCP': if l4Type == 'TCP':
while 'Connected' not in client.cmd( while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()): '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) sleep(.5)
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' + cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs ) bwArgs )
+50 -3
View File
@@ -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 CPULimitedHost: a virtual host whose CPU bandwidth is limited by
RT or CFS bandwidth limiting. 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. Switch: superclass for switch nodes.
UserSwitch: a switch using the user-space switch from the OpenFlow UserSwitch: a switch using the user-space switch from the OpenFlow
@@ -735,6 +740,33 @@ class CPULimitedHost( Host ):
mountCgroups() mountCgroups()
cls.inited = True cls.inited = True
class HostWithPrivateDirs( Host ):
"Host with 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 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 )
# Some important things to note: # Some important things to note:
# #
@@ -834,6 +866,8 @@ class UserSwitch( Switch ):
'(openflow.org)' ) '(openflow.org)' )
if self.listenPort: if self.listenPort:
self.opts += ' --listen=ptcp:%i ' % self.listenPort self.opts += ' --listen=ptcp:%i ' % self.listenPort
else:
self.opts += ' --listen=punix:/tmp/%s.listen' % self.name
self.dpopts = dpopts self.dpopts = dpopts
@classmethod @classmethod
@@ -846,7 +880,7 @@ class UserSwitch( Switch ):
"Run dpctl command" "Run dpctl command"
listenAddr = None listenAddr = None
if not self.listenPort: if not self.listenPort:
listenAddr = 'unix:/tmp/' + self.name listenAddr = 'unix:/tmp/%s.listen' % self.name
else: else:
listenAddr = 'tcp:127.0.0.1:%i' % self.listenPort listenAddr = 'tcp:127.0.0.1:%i' % self.listenPort
return self.cmd( 'dpctl ' + ' '.join( args ) + return self.cmd( 'dpctl ' + ' '.join( args ) +
@@ -1253,13 +1287,19 @@ class Controller( Node ):
return '<%s %s: %s:%s pid=%s> ' % ( return '<%s %s: %s:%s pid=%s> ' % (
self.__class__.__name__, self.name, self.__class__.__name__, self.name,
self.IP(), self.port, self.pid ) self.IP(), self.port, self.pid )
@classmethod
def isAvailable( self ):
return quietRun( 'which controller' )
class OVSController( Controller ): class OVSController( Controller ):
"Open vSwitch controller" "Open vSwitch controller"
def __init__( self, name, command='ovs-controller', **kwargs ): def __init__( self, name, command='ovs-controller', **kwargs ):
if quietRun( 'which test-controller' ):
command = 'test-controller'
Controller.__init__( self, name, command=command, **kwargs ) Controller.__init__( self, name, command=command, **kwargs )
@classmethod
def isAvailable( self ):
return quietRun( 'which ovs-controller' ) or quietRun( 'which test-controller' )
class NOX( Controller ): class NOX( Controller ):
"Controller to run a NOX application." "Controller to run a NOX application."
@@ -1314,3 +1354,10 @@ class RemoteController( Controller ):
if 'Connected' not in listening: if 'Connected' not in listening:
warn( "Unable to contact the remote controller" warn( "Unable to contact the remote controller"
" at %s:%d\n" % ( self.ip, self.port ) ) " at %s:%d\n" % ( self.ip, self.port ) )
def DefaultController( name, order=[ Controller, OVSController ], **kwargs ):
"find any controller that is available and run it"
for controller in order:
if controller.isAvailable():
return controller( name, **kwargs )
+51
View File
@@ -0,0 +1,51 @@
"""
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 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 )
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 )
+10 -5
View File
@@ -55,6 +55,9 @@ class testOptionsTopoCommon( object ):
""" """
self.assertGreaterEqual( float(measured), self.assertGreaterEqual( float(measured),
float(expected) * tolerance_frac ) float(expected) * tolerance_frac )
self.assertLessEqual( float( measured ),
float(expected) + (1-tolerance_frac)
* float( expected ) )
def testCPULimits( self ): def testCPULimits( self ):
"Verify topology creation with CPU limits set for both schedulers." "Verify topology creation with CPU limits set for both schedulers."
@@ -68,19 +71,20 @@ class testOptionsTopoCommon( object ):
mn.start() mn.start()
results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop() mn.stop()
for cpu in results: for pct in results:
self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE ) #divide cpu by 100 to convert from percentage to fraction
self.assertWithinTolerance( pct/100, CPU_FRACTION, CPU_TOLERANCE )
def testLinkBandwidth( self ): def testLinkBandwidth( self ):
"Verify that link bandwidths are accurate within a bound." "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 BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first; # Verify ability to create limited-link topo first;
lopts = { 'bw': BW, 'use_htb': True } lopts = { 'bw': BW, 'use_htb': True }
# Also verify correctness of limit limitng within a bound. # Also verify correctness of limit limitng within a bound.
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass ) 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: for bw_str in bw_strs:
bw = float( bw_str.split(' ')[0] ) bw = float( bw_str.split(' ')[0] )
self.assertWithinTolerance( bw, BW, BW_TOLERANCE ) self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
@@ -91,7 +95,7 @@ class testOptionsTopoCommon( object ):
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True } lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), 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 ) ping_delays = mn.run( mn.pingFull )
test_outputs = ping_delays[0] test_outputs = ping_delays[0]
# Ignore unused variables below # Ignore unused variables below
@@ -105,6 +109,7 @@ class testOptionsTopoCommon( object ):
self.assertWithinTolerance( rttval, DELAY_MS * 4.0, self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
DELAY_TOLERANCE) DELAY_TOLERANCE)
def testLinkLoss( self ): def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate." "Verify that we see packet drops with a high configured loss rate."
LOSS_PERCENT = 99 LOSS_PERCENT = 99
+1 -1
View File
@@ -66,7 +66,7 @@ class testLinearCommon( object ):
def testLinear5( self ): def testLinear5( self ):
"Ping test on a 5-switch topology" "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 ) dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 ) self.assertEqual( dropped, 0 )
+13 -6
View File
@@ -48,18 +48,25 @@ class MultiGraph( object ):
class Topo(object): class Topo(object):
"Data center network representation for structured multi-trees." "Data center network representation for structured multi-trees."
def __init__(self, hopts=None, sopts=None, lopts=None): def __init__(self, *args, **params):
"""Topo object: """Topo object.
Optional named parameters:
hinfo: default host options hinfo: default host options
sopts: default switch options sopts: default switch options
lopts: default link options""" lopts: default link options
calls build()"""
self.g = MultiGraph() self.g = MultiGraph()
self.node_info = {} self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = {} if hopts is None else hopts self.hopts = params.pop( 'hopts', {} )
self.sopts = {} if sopts is None else sopts self.sopts = params.pop( 'sopts', {} )
self.lopts = {} if lopts is None else lopts self.lopts = params.pop( 'lopts', {} )
self.ports = {} # ports[src][dst] is port on src that connects to dst 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): def addNode(self, name, **opts):
"""Add Node to graph. """Add Node to graph.
+34
View File
@@ -34,3 +34,37 @@ def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks." "Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout ) topo = TreeTopo( depth, fanout )
return Mininet( topo, **kwargs ) 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 2.1' )
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 )
+11 -1
View File
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
fflush(stdout); fflush(stdout);
break; break;
case 'a': case 'a':
/* Attach to pid's network namespace */ /* Attach to pid's network namespace and mount namespace*/
pid = atoi(optarg); pid = atoi(optarg);
sprintf(path, "/proc/%d/ns/net", pid ); sprintf(path, "/proc/%d/ns/net", pid );
nsid = open(path, O_RDONLY); nsid = open(path, O_RDONLY);
@@ -152,6 +152,16 @@ int main(int argc, char *argv[])
perror("setns"); perror("setns");
return 1; 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; break;
case 'g': case 'g':
/* Attach to cgroup */ /* Attach to cgroup */
+6
View File
@@ -89,6 +89,12 @@ isoURLs = {
'trusty64server': 'trusty64server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/' 'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04-server-amd64.iso', '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',
} }