Merge branch 'master' of git://github.com/mininet/mininet into devel/startup

This commit is contained in:
cody burkard
2014-07-19 02:09:03 -07:00
10 changed files with 203 additions and 37 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:
+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:
+53 -12
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
@@ -109,11 +110,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
@@ -149,6 +150,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 = []
@@ -166,6 +168,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
@@ -216,7 +249,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
@@ -327,6 +360,10 @@ 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 ):
# Allow Controller objects because nobody understands currying
if isinstance( cls, Controller ):
self.addController( cls )
else:
self.addController( 'c%d' % i, cls ) self.addController( 'c%d' % i, cls )
info( '*** Adding hosts:\n' ) info( '*** Adding hosts:\n' )
@@ -408,9 +445,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()
@@ -426,11 +470,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 ):
@@ -623,7 +662,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 ]
@@ -646,6 +685,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:
@@ -653,7 +694,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 )
+15 -2
View File
@@ -1277,13 +1277,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."
@@ -1338,3 +1344,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 )
+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',
} }