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.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
NOX, RemoteController, DefaultController,
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
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import custom, customConstructor
from mininet.util import buildTopo
@@ -40,24 +42,29 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo }
'tree': TreeTopo,
'torus': TorusTopo }
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,
'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'
@@ -261,12 +268,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:
+42 -167
View File
@@ -1,197 +1,72 @@
#!/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.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 )
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/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:', privateDirs, '\n' )
directories = []
for directory in privateDirs:
directories.append( directory[ 0 ]
if isinstance( directory, tuple )
else directory )
info( 'Private Directories:', directories, '\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')
+2 -2
View File
@@ -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 )
net = Mininet( topo=topo, switch=Switch, controller=Controller, waitConnected=True )
net.start()
print "*** testing basic connectivity"
for n in lengths:
+15 -1
View File
@@ -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
@@ -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" )
+55 -14
View File
@@ -90,12 +90,13 @@ import os
import re
import select
import signal
import copy
from time import sleep
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.log import info, error, debug, output, warn
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
@@ -108,11 +109,11 @@ 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,
listenPort=None ):
listenPort=None, waitConnected=False ):
"""Create Mininet object.
topo: Topo (topology) object or None
switch: default Switch class
@@ -148,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 = []
@@ -163,6 +165,37 @@ class Mininet( object ):
if topo and 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 ):
"""Add host.
name: name of host to add
@@ -213,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 isinstance( name, Controller ):
controller_new = name
# Pylint thinks controller is a str()
# pylint: disable=E1103
@@ -222,7 +255,7 @@ class Mininet( object ):
else:
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
@@ -324,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 isinstance( cls, Controller ):
self.addController( cls )
else:
self.addController( 'c%d' % i, cls )
info( '*** Adding hosts:\n' )
for hostName in topo.hosts():
@@ -401,9 +438,16 @@ 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"
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 +463,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 ):
@@ -616,7 +655,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=None ):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses opposite hosts
l4Type: string, one of [ TCP, UDP ]
@@ -639,6 +678,8 @@ class Mininet( object ):
bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if format:
iperfArgs += '-f %s ' %format
server.sendCmd( iperfArgs + '-s', printPid=True )
servout = ''
while server.lastPid is None:
@@ -646,7 +687,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 )
+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
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
@@ -735,6 +740,33 @@ class CPULimitedHost( Host ):
mountCgroups()
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:
#
@@ -834,6 +866,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
@@ -846,7 +880,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 ) +
@@ -1253,13 +1287,19 @@ class Controller( Node ):
return '<%s %s: %s:%s pid=%s> ' % (
self.__class__.__name__, self.name,
self.IP(), self.port, self.pid )
@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."
@@ -1314,3 +1354,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 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 )
+11 -6
View File
@@ -55,6 +55,9 @@ class testOptionsTopoCommon( object ):
"""
self.assertGreaterEqual( float(measured),
float(expected) * tolerance_frac )
self.assertLessEqual( float( measured ),
float(expected) + (1-tolerance_frac)
* float( expected ) )
def testCPULimits( self ):
"Verify topology creation with CPU limits set for both schedulers."
@@ -68,19 +71,20 @@ class testOptionsTopoCommon( object ):
mn.start()
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop()
for cpu in results:
self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE )
for pct in results:
#divide cpu by 100 to convert from percentage to fraction
self.assertWithinTolerance( pct/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 )
@@ -91,7 +95,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
@@ -102,9 +106,10 @@ 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)
def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate."
LOSS_PERCENT = 99
+1 -1
View File
@@ -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 )
+13 -6
View File
@@ -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.
+34
View File
@@ -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 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);
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 +152,16 @@ 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 */
+6
View File
@@ -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',
}