Fix codecheck and MininetWithControlNet.

This commit is contained in:
Bob Lantz
2012-03-10 20:44:34 -08:00
parent 1d814c606d
commit 14ff3ad3d0
11 changed files with 292 additions and 239 deletions
+18 -15
View File
@@ -19,7 +19,7 @@ import time
from mininet.clean import cleanup
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, warn
from mininet.net import Mininet
from mininet.net import Mininet, MininetWithControlNet
from mininet.node import Host, CPULimitedHost, Controller, OVSController, NOX
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
from mininet.link import Intf, TCIntf
@@ -32,12 +32,13 @@ def customNode( constructors, argStr ):
"Return custom Node constructor based on argStr"
cname, newargs, kwargs = splitArgs( argStr )
constructor = constructors.get( cname, None )
#if args:
# raise Exception( "please specify keyword arguments for " + cname )
if not constructor:
raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, constructors.keys() ) )
def custom( name, *args, **params ):
def customized( name, *args, **params ):
"Customized Node constructor"
params.update( kwargs )
if not newargs:
return constructor( name, *args, **params )
@@ -45,7 +46,8 @@ def customNode( constructors, argStr ):
warn( 'warning: %s replacing %s with %s\n',
constructor, args, newargs )
return constructor( name, *newargs, **params )
return custom
return customized
# built in topologies, created only when run
@@ -94,8 +96,7 @@ def splitArgs( argstr ):
params = split[ 1: ]
# Convert int and float args; removes the need for function
# to be flexible with input arg formats.
args = [ s for s in params if '=' not in s ]
args = map( makeNumeric, args )
args = [ makeNumeric( s ) for s in params if '=' not in s ]
kwargs = {}
for s in [ p for p in params if '=' in p ]:
key, val = s.split( '=' )
@@ -122,7 +123,8 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
if not helpStr:
helpStr = '|'.join( sorted( choicesDict.keys() ) ) + '[,param=value...]'
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
@@ -157,11 +159,11 @@ class MininetRunner( object ):
def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options."
custom = {}
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, custom, custom )
for name in custom:
self.setCustom( name, custom[ name ] )
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
@@ -171,8 +173,8 @@ class MininetRunner( object ):
if '--custom' in sys.argv:
index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1:
custom = sys.argv[ index + 1 ]
self.parseCustomFile( custom )
filename = sys.argv[ index + 1 ]
self.parseCustomFile( filename )
else:
raise Exception( 'Custom file name not found' )
@@ -250,6 +252,7 @@ class MininetRunner( object ):
self.validate( self.options )
inNamespace = self.options.innamespace
Net = MininetWithControlNet if inNamespace else Mininet
ipBase = self.options.ipbase
xterms = self.options.xterms
mac = self.options.mac
@@ -257,7 +260,7 @@ class MininetRunner( object ):
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
mn = Mininet( topo=topo,
mn = Net( topo=topo,
switch=switch, host=host, controller=controller,
intf=intf,
ipBase=ipBase,
+4 -12
View File
@@ -107,7 +107,7 @@ class Console( Frame ):
self.text.insert( 'end', text )
self.text.mark_set( 'insert', 'end' )
self.text.see( 'insert' )
outputHook = lambda x,y: True # make pylint happy
outputHook = lambda x, y: True # make pylint happier
if self.outputHook:
outputHook = self.outputHook
outputHook( self, text )
@@ -132,27 +132,22 @@ class Console( Frame ):
self.sendCmd( cmd )
# Callback ignores event
# pylint: disable-msg=W0613
def handleInt( self, event=None ):
def handleInt( self, _event=None ):
"Handle control-c."
self.node.sendInt()
# pylint: enable-msg=W0613
def sendCmd( self, cmd ):
"Send a command to our node."
if not self.node.waiting:
self.node.sendCmd( cmd )
# Callback ignores fds
# pylint: disable-msg=W0613
def handleReadable( self, fds, timeoutms=None ):
def handleReadable( self, _fds, timeoutms=None ):
"Handle file readable event."
data = self.node.monitor( timeoutms )
self.append( data )
if not self.node.waiting:
# Print prompt
self.append( self.prompt )
# pylint: enable-msg=W0613
def waiting( self ):
"Are we waiting for output?"
@@ -321,9 +316,7 @@ class ConsoleApp( Frame ):
self.pack( expand=True, fill='both' )
# Update callback doesn't use console arg
# pylint: disable-msg=W0613
def updateGraph( self, console, output ):
def updateGraph( self, _console, output ):
"Update our graph."
m = re.search( r'(\d+) Mbits/sec', output )
if not m:
@@ -334,7 +327,6 @@ class ConsoleApp( Frame ):
self.graph.addBar( self.bw )
self.bw = 0
self.updates = 0
# pylint: enable-msg=W0613
def setOutputHook( self, fn=None, consoles=None ):
"Register fn as output hook [on specific consoles.]"
+4 -1
View File
@@ -13,10 +13,12 @@ from mininet.log import setLogLevel
from time import sleep
def testLinkLimit( net, bw ):
"Run bandwidth limit test"
print '*** Testing network %.2f Mbps bandwidth limit' % bw
net.iperf( )
def testCpuLimit( net, cpu ):
"run CPU limit test"
pct = cpu * 100
print '*** Testing CPU %.0f%% bandwidth limit' % pct
h1, h2 = net.hosts
@@ -25,7 +27,8 @@ def testCpuLimit( net, cpu ):
pid1 = h1.cmd( 'echo $!' ).strip()
pid2 = h2.cmd( 'echo $!' ).strip()
cmd = 'ps -p %s,%s -o pid,%%cpu,args' % ( pid1, pid2 )
for i in range( 0, 5):
# It's a shame that this is what pylint prefers
for _ in range( 5 ):
sleep( 1 )
print quietRun( cmd ).strip()
h1.cmd( 'kill %1')
+7 -18
View File
@@ -299,14 +299,11 @@ class MiniEdit( Frame ):
# Delete from view
self.canvas.delete( item )
# Callback ignores event
# pylint: disable-msg=W0613
def deleteSelection( self, event ):
def deleteSelection( self, _event ):
"Delete the selected item."
if self.selection is not None:
self.deleteItem( self.selection )
self.selectItem( None )
# pylint: enable-msg=W0613
def nodeIcon( self, node, name ):
"Create a new node icon."
@@ -350,14 +347,11 @@ class MiniEdit( Frame ):
c = self.canvas
c.coords( self.link, self.linkx, self.linky, x, y )
# Callback ignores event
# pylint: disable-msg=W0613
def releaseLink( self, event ):
def releaseLink( self, _event ):
"Give up on the current link."
if self.link is not None:
self.canvas.delete( self.link )
self.linkWidget = self.linkItem = self.link = None
# pylint: enable-msg=W0613
# Generic node handlers
@@ -385,12 +379,9 @@ class MiniEdit( Frame ):
"Select node on entry."
self.selectNode( event )
# Callback ignores event
# pylint: disable-msg=W0613
def leaveNode( self, event ):
def leaveNode( self, _event ):
"Restore old selection on exit."
self.selectItem( self.lastSelection )
# pylint: enable-msg=W0613
def clickNode( self, event ):
"Node click handler."
@@ -454,23 +445,21 @@ class MiniEdit( Frame ):
# Link bindings
# Selection still needs a bit of work overall
# Callbacks ignore event
# pylint: disable-msg=W0613
def select( event, link=self.link ):
def select( _event, link=self.link ):
"Select item on mouse entry."
self.selectItem( link )
def highlight( event, link=self.link ):
def highlight( _event, link=self.link ):
"Highlight item on mouse entry."
# self.selectItem( link )
self.canvas.itemconfig( link, fill='green' )
def unhighlight( event, link=self.link ):
def unhighlight( _event, link=self.link ):
"Unhighlight item on mouse exit."
self.canvas.itemconfig( link, fill='blue' )
# self.selectItem( None )
# pylint: disable-msg=W0613
self.canvas.tag_bind( self.link, '<Enter>', highlight )
self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
@@ -602,7 +591,7 @@ class MiniEdit( Frame ):
cleanUpScreens()
self.net = None
def xterm( self, ignore=None ):
def xterm( self, _=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or
self.net is None or
+1 -1
View File
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' )
intfs = map( str, [ sintf1, sintf2 ] )
intfs = [ str( i ) for i in sintf1, sintf2 ]
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
+9 -9
View File
@@ -77,7 +77,7 @@ class CLI( Cmd ):
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=W0613,R0201
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
@@ -104,12 +104,12 @@ class CLI( Cmd ):
if line is '':
output( self.helpStr )
def do_nodes( self, line ):
def do_nodes( self, _line ):
"List all nodes."
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
output( 'available nodes are: \n%s\n' % nodes )
def do_net( self, line ):
def do_net( self, _line ):
"List network connections."
for switch in self.mn.switches:
output( switch.name, '<->' )
@@ -143,11 +143,11 @@ class CLI( Cmd ):
# pylint: enable-msg=W0703
def do_pingall( self, line ):
def do_pingall( self, _line ):
"Ping between all hosts."
self.mn.pingAll()
def do_pingpair( self, line ):
def do_pingpair( self, _line ):
"Ping between first two hosts, useful for testing."
self.mn.pingPair()
@@ -191,13 +191,13 @@ class CLI( Cmd ):
error( 'invalid number of args: iperfudp bw src dst\n' +
'bw examples: 10M\n' )
def do_intfs( self, line ):
def do_intfs( self, _line ):
"List interfaces."
for node in self.nodelist:
output( '%s: %s\n' %
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
def do_dump( self, line ):
def do_dump( self, _line ):
"Dump node info."
for node in self.nodelist:
output( '%s\n' % node )
@@ -229,7 +229,7 @@ class CLI( Cmd ):
"Spawn gnome-terminal(s) for the given node(s)."
self.do_xterm( line, term='gterm' )
def do_exit( self, line ):
def do_exit( self, _line ):
"Exit"
return 'exited by user command'
@@ -311,7 +311,7 @@ class CLI( Cmd ):
else:
error( '*** Unknown command: %s\n' % first )
# pylint: enable-msg=W0613,R0201
# pylint: enable-msg=R0201
def waitForNode( self, node ):
"Wait for a node to finish, and print its output."
+124 -82
View File
@@ -41,24 +41,29 @@ class Intf( object ):
self.node = node
self.name = name
self.link = link
self.mac, self.ip = None, None
self.mac, self.ip, self.prefixLen = None, None, None
# Add to node (and move ourselves if necessary )
node.addIntf( self )
self.config( **kwargs )
def cmd( self, *args, **kwargs ):
"Run a command in our owning node"
return self.node.cmd( *args, **kwargs )
def ifconfig( self, *args ):
"Configure ourselves using ifconfig"
return self.cmd( 'ifconfig', self.name, *args )
def setIP( self, ipstr ):
def setIP( self, ipstr, prefixLen=None ):
"""Set our IP address"""
# This is a sign that we should perhaps rethink our prefix
# mechanism
# mechanism and/or the way we specify IP addresses
if '/' in ipstr:
self.ip, self.prefixLen = ipstr.split( '/' )
return self.ifconfig( ipstr, 'up' )
else:
self.ip, self.prefixLen = ipstr, prefixLen
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
def setMAC( self, macstr ):
"""Set the MAC address for an interface.
@@ -78,7 +83,7 @@ class Intf( object ):
self.ip = ips[ 0 ] if ips else None
return self.ip
def updateMAC( self, intf ):
def updateMAC( self ):
"Return updated MAC address based on ifconfig"
ifconfig = self.ifconfig()
macs = self._macMatchRegex.findall( ifconfig )
@@ -93,9 +98,9 @@ class Intf( object ):
"Return MAC address"
return self.mac
def isUp( self, set=False ):
def isUp( self, setUp=False ):
"Return whether interface is up"
if set:
if setUp:
self.ifconfig( 'up' )
return "UP" in self.ifconfig()
@@ -125,7 +130,7 @@ class Intf( object ):
return result
def config( self, mac=None, ip=None, ifconfig=None,
defaultRoute=None, up=True, **params):
up=True, **_params ):
"""Configure Node according to (optional) parameters:
mac: MAC address
ip: IP address
@@ -153,7 +158,83 @@ class Intf( object ):
class TCIntf( Intf ):
"Interface customized by tc (traffic control) utility"
"""Interface customized by tc (traffic control) utility
Allows specification of bandwidth limits (various methods)
as well as delay, loss and max queue length"""
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
elif bw is not None:
# BL: this seems a bit brittle...
if ( speedup > 0 and
self.node.name[0:2] == 'sw' ):
bw = speedup
if use_hfsc:
cmds = [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
'class add dev %s parent 1:0 classid 1:1 hfsc sc '
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
latency_us = 10 * 1500 * 8 / bw
cmds = ['%s qdisc add dev %s root handle 1: tbf ' +
'rate %fMbit burst 15000 latency %fus' %
(bw, latency_us) ]
else:
cmds = [ '%s qdisc add dev %s root handle 1:0 htb default 1',
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 1:1 '
# ECN or RED
if enable_ecn:
cmds = [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'min 20000 max 25000 avpkt 1000 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1 ecn' % bw ]
parent = ' parent 10: '
elif enable_red:
cmds = [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'min 20000 max 25000 avpkt 1000 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1' % bw ]
parent = ' parent 10: '
return cmds, parent
@staticmethod
def delayCmds( parent, delay=None, loss=None,
max_queue_size=None ):
"Internal method: return tc commands for delay and loss"
cmds = []
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
elif loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# Delay/loss/max queue size
netemargs = '%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'loss %d ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
if netemargs:
cmds = [ '%s qdisc add dev %s ' + parent + ' netem ' +
netemargs ]
return cmds
def tc( self, cmd, tc='tc' ):
"Execute tc command for our interface"
c = cmd % (tc, self) # Add in tc command and our name
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
def config( self, bw=None, delay=None, loss=None, disable_gro=True,
speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False,
@@ -162,96 +243,46 @@ class TCIntf( Intf ):
result = Intf.config( self, **params)
# disable GRO
# Disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
# Optimization: return if nothing else to configure
# Question: what happens if we want to reset things?
if ( bw is None and not delay and not loss
and max_queue_size is None ):
return
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
return
# Clear existing configuration
cmds = [ '%s qdisc del dev %s root' ]
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
return
# Bandwidth limits via various methods
bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
use_hfsc=use_hfsc, use_tbf=use_tbf,
enable_ecn=enable_ecn,
enable_red=enable_red )
cmds += bwcmds
if loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
return
# Delay/loss/max_queue_size using netem
cmds += self.delayCmds( delay=delay, loss=loss,
max_queue_size=max_queue_size,
parent=parent )
# Ugly but functional
# Ugly but functional: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ] if enable_red else [] ) )
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
cmds = [ '%s qdisc del dev %s root' ]
tc = 'tc' # was getCmd( 'tc' )
# Bandwidth control algorithms
if bw is None:
parent = ' root '
else:
parent = ' parent 1:1 '
# BL: hmm... this seems a bit brittle
if speedup > 0 and self.node.name[0:2] == 'sw':
bw = speedup
if use_hfsc:
cmds += [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
'%s class add dev %s parent 1:0 classid 1:1 hfsc sc ' +
'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
latency_us = 10 * 1500 * 8 / bw
cmds += ['%s qdisc add dev %s root handle 1: tbf ' +
'rate %fMbit burst 15000 latency %fus' % (bw, latency_us) ]
else:
cmds += [ '%s qdisc add dev %s root handle 1:0 htb default 1',
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 1:1 '
# ECN or RED
if enable_ecn:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 '+
'min 20000 max 25000 avpkt 1000 '+
'burst 20 '+
'bandwidth %fmbit probability 1 ecn' % bw ]
parent = ' parent 10: '
elif enable_red:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 '+
'min 20000 max 25000 avpkt 1000 '+
'burst 20 '+
'bandwidth %fmbit probability 1' % bw ]
parent = ' parent 10: '
# Delay/loss/max queue size
netemargs = '%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'loss %d ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None else '' )
if netemargs:
cmds += [ '%s qdisc add dev %s ' + parent + ' netem ' +
netemargs ]
# Execute all the commands in the container
# Execute all the commands in our node
debug("at map stage w/cmds: %s\n" % cmds)
def doConfigPort(s):
c = s % (tc, self)
debug(" *** executing command: %s\n" % c)
return self.cmd(c)
tcoutputs = [ doConfigPort(cmd) for cmd in cmds ]
tcoutputs = [ self.tc(cmd) for cmd in cmds ]
debug( "cmds:", cmds, '\n' )
debug( "outputs:", tcoutputs, '\n' )
result[ 'tcoutputs'] = tcoutputs
return result
@@ -260,8 +291,10 @@ class Link( object ):
"""A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc.."""
def __init__( self, node1, node2, port1=None, port2=None, intfName1=None, intfName2=None,
intf=Intf, cls1=None, cls2=None, params1={}, params2={} ):
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
@@ -284,13 +317,21 @@ class Link( object ):
intfName1 = self.intfName( node1, port1 )
if not intfName2:
intfName2 = self.intfName( node2, port2 )
self.makeIntfPair( intfName1, intfName2 )
if not cls1:
cls1 = intf
if not cls2:
cls2 = intf
if not params1:
params1 = {}
if not params2:
params2 = {}
intf1 = cls1( name=intfName1, node=node1, link=self, **params1 )
intf2 = cls2( name=intfName2, node=node2, link=self, **params2 )
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
@@ -304,7 +345,8 @@ class Link( object ):
"""Create pair of interfaces
intf1: name of interface 1
intf2: name of interface 2
(override this class method [and possibly delete()] to change link type)"""
(override this class method [and possibly delete()]
to change link type)"""
makeIntfPair( intf1, intf2 )
def delete( self ):
+18 -14
View File
@@ -275,17 +275,16 @@ class Mininet( object ):
info( '\n' )
def configureControlNetwork( self ):
error( "configureControlNetwork: override in subclass, or use"
"MininetWithControlNet class" )
"Control net config hook: override in subclass"
raise Exception( 'configureControlNetwork: '
'should be overriden in subclass', self )
def build( self ):
"Build mininet."
if self.topo:
self.buildFromTopo( self.topo )
if self.inNamespace:
info( '*** Configuring control network\n' )
if ( self.inNamespace ):
self.configureControlNetwork()
info( '*** Configuring hosts\n' )
self.configHosts()
@@ -541,7 +540,8 @@ class Mininet( object ):
return
if os.getuid() != 0:
# Note: this script must be run as root
# Perhaps we should do so automatically!
# Probably we should only sudo when we need
# to as per Big Switch's patch
print "*** Mininet must run as root."
exit( 1 )
fixLimits()
@@ -570,7 +570,11 @@ class MininetWithControlNet( Mininet ):
network (since real networks may need one!)
5. Basically nobody ever used this code, so it has been moved
into its own class."""
into its own class.
6. Ultimately we may wish to extend this to allow us to create a
control network which every node's control interface is
attached to."""
def configureControlNetwork( self ):
"Configure control network."
@@ -589,27 +593,27 @@ class MininetWithControlNet( Mininet ):
snum = ipParse( ip )
for switch in self.switches:
info( ' ' + switch.name )
sintf, cintf = self.link( switch, controller )
link = self.link( switch, controller, port1=0 )
sintf, cintf = link.intf1, link.intf2
switch.controlIntf = sintf
snum += 1
while snum & 0xff in [ 0, 255 ]:
snum += 1
sip = ipStr( snum )
controller.setIP( cintf, cip, prefixLen )
switch.setIP( sintf, sip, prefixLen )
cintf.setIP( cip, prefixLen )
sintf.setIP( sip, prefixLen )
controller.setHostRoute( sip, cintf )
switch.setHostRoute( cip, sintf )
info( '\n' )
info( '*** Testing control network\n' )
while not controller.intfIsUp( cintf ):
while not cintf.isUp():
info( '*** Waiting for', cintf, 'to come up\n' )
sleep( 1 )
for switch in self.switches:
while not switch.intfIsUp( sintf ):
while not sintf.isUp():
info( '*** Waiting for', sintf, 'to come up\n' )
sleep( 1 )
if self.ping( hosts=[ switch, controller ] ) != 0:
error( '*** Error: control network test failed\n' )
exit( 1 )
info( '\n' )
+59 -43
View File
@@ -81,8 +81,14 @@ class Node( object ):
# replace with Port objects, eventually ?
self.nameToIntf = {} # dict of interface names to Intfs
# Make pylint happy
( self.shell, self.execed, self.pid, self.stdin, self.stdout,
self.lastPid, self.lastCmd, self.pollOut ) = (
None, None, None, None, None, None, None, None )
self.waiting = False
self.readbuf = ''
# Start command interpreter shell
self.shell = None
self.startShell()
# File descriptor to node mapping support
@@ -99,28 +105,6 @@ class Node( object ):
node = cls.outToNode.get( fd )
return node or cls.inToNode.get( fd )
# Automatic class setup support
isSetup = False;
@classmethod
def checkSetup( cls ):
"Make sure our class and superclasses are set up"
while cls and not getattr( cls, 'isSetup', True ):
cls.setup()
cls.isSetup = True
# Make pylint happy
cls = getattr( type( cls ), '__base__', None )
@classmethod
def setup( cls ):
"Make sure our class dependencies are available"
pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
def cleanup( self ):
"Help python collect its garbage."
self.shell = None
# Command support via shell process in namespace
def startShell( self ):
@@ -153,19 +137,23 @@ class Node( object ):
self.readbuf = ''
self.waiting = False
def read( self, bytes=1024 ):
def cleanup( self ):
"Help python collect its garbage."
self.shell = None
def read( self, maxbytes=1024 ):
"""Buffered read from node, non-blocking.
bytes: maximum number of bytes to return"""
maxbytes: maximum number of bytes to return"""
count = len( self.readbuf )
if count < bytes:
data = os.read( self.stdout.fileno(), bytes - count )
if count < maxbytes:
data = os.read( self.stdout.fileno(), maxbytes - count )
self.readbuf += data
if bytes >= len( self.readbuf ):
if maxbytes >= len( self.readbuf ):
result = self.readbuf
self.readbuf = ''
else:
result = self.readbuf[ :bytes ]
self.readbuf = self.readbuf[ bytes: ]
result = self.readbuf[ :maxbytes ]
self.readbuf = self.readbuf[ maxbytes: ]
return result
def readline( self ):
@@ -363,7 +351,7 @@ class Node( object ):
"""Add route to host.
ip: IP address as dotted decimal
intf: string, interface name"""
return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
return self.cmd( 'route add -host', ip, 'dev', intf )
def setDefaultRoute( self, intf=None ):
"""Set the default route to go through intf.
@@ -431,7 +419,7 @@ class Node( object ):
return result
def config( self, mac=None, ip=None, ifconfig=None,
defaultRoute=None, **params):
defaultRoute=None, **_params ):
"""Configure Node according to (optional) parameters:
mac: MAC address for default interface
ip: IP address for default interface
@@ -440,7 +428,7 @@ class Node( object ):
the parent class's config(**params)"""
# If we were overriding this method, we would call
# the superclass config method here as follows:
# r = Parent.config( **params )
# r = Parent.config( **_params )
r = {}
self.setParam( r, 'setMAC', mac=mac )
self.setParam( r, 'setIP', ip=ip )
@@ -473,6 +461,24 @@ class Node( object ):
return '%s: IP=%s intfs=%s pid=%s' % (
self.name, self.IP(), ','.join( self.intfNames() ), self.pid )
# Automatic class setup support
isSetup = False
@classmethod
def checkSetup( cls ):
"Make sure our class and superclasses are set up"
while cls and not getattr( cls, 'isSetup', True ):
cls.setup()
cls.isSetup = True
# Make pylint happy
cls = getattr( type( cls ), '__base__', None )
@classmethod
def setup( cls ):
"Make sure our class dependencies are available"
pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
class Host( Node ):
"A host is simply a Node"
@@ -484,7 +490,7 @@ class CPULimitedHost( Host ):
"CPU limited host"
def __init__( self, *args, **kwargs ):
Node.__init__( self, *args, **kwargs )
Host.__init__( self, *args, **kwargs )
# Create a cgroup and move shell into it
self.cgroup = 'cpu,cpuacct:/' + self.name
errFail( 'cgcreate -g ' + self.cgroup )
@@ -510,6 +516,7 @@ class CPULimitedHost( Host ):
return nvalue
def cgroupGet( self, param, resource='cpu' ):
"Return value of cgroup parameter"
cmd = 'cgget -r %s.%s /%s' % (
resource, param, self.name )
return quietRun( cmd ).split()[ -1 ]
@@ -578,7 +585,7 @@ class CPULimitedHost( Host ):
self.chrt( prio=20 )
info( '(%s %d/%dus) ' % ( sched, quota, period ) )
def config( self, cpu=None, sched=None, **params ):
def config( self, cpu=None, **params ):
"""cpu: desired overall system CPU fraction
params: parameters for Node.config()"""
r = Node.config( self, **params )
@@ -665,8 +672,8 @@ class UserSwitch( Switch ):
pathCheck( 'ofdatapath', 'ofprotocol',
moduleName='the OpenFlow reference user switch (openflow.org)' )
@staticmethod
def setup():
@classmethod
def setup( cls ):
"Ensure any dependencies are loaded; if not, try to load them."
if not os.path.exists( '/dev/net/tun' ):
moduleDeps( add=TUN )
@@ -716,8 +723,8 @@ class OVSLegacyKernelSwitch( Switch ):
" in the root namespace.\n" )
exit( 1 )
@staticmethod
def setup():
@classmethod
def setup( cls ):
"Ensure any dependencies are loaded; if not, try to load them."
pathCheck( 'ovs-dpctl', 'ovs-openflowd',
moduleName='Open vSwitch (openvswitch.org)')
@@ -766,9 +773,13 @@ class OVSSwitch( Switch ):
# dpid, which is a 64-bit numerical value used by
# the openflow protocol.
self.dp = name
if self.inNamespace:
error( "OVSSwitch currently only works"
" in the root namespace.\n" )
exit( 1 )
@staticmethod
def setup():
@classmethod
def setup( cls ):
"Make sure Open vSwitch is installed and working"
pathCheck( 'ovs-vsctl',
moduleName='Open vSwitch (openvswitch.org)')
@@ -781,11 +792,15 @@ class OVSSwitch( Switch ):
'Make sure that Open vSwitch is installed, '
'that ovsdb-server is running, and that\n'
'"ovs-vsctl show" works correctly.\n'
'You may wish to try "service openvswitch-switch start".\n' )
'You may wish to try '
'"service openvswitch-switch start".\n' )
exit( 1 )
def start( self, controllers ):
"Start up a new OVS OpenFlow switch using ovs-vsctl"
if self.inNamespace:
raise Exception(
'OVS kernel switch does not work in a namespace' )
# Annoyingly, --if-exists option seems not to work
self.cmd( 'ovs-vsctl del-br ', self.dp )
self.cmd( 'ovs-vsctl add-br', self.dp )
@@ -800,7 +815,8 @@ class OVSSwitch( Switch ):
self.cmd( 'ovs-vsctl add-port', self.dp, intf )
self.cmd( 'ifconfig', intf, 'up' )
# Add controllers
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port ) for c in controllers ] )
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
for c in controllers ] )
self.cmd( 'ovs-vsctl set-controller', self.dp, clist )
def stop( self ):
+1 -1
View File
@@ -16,7 +16,7 @@ setup for testing, and can even be emulated with the Mininet package.
# from networkx.classes.graph import Graph
from networkx import Graph
from util import netParse, ipStr
from mininet.util import netParse, ipStr
class NodeID(object):
'''Topo node identifier.'''
+10 -6
View File
@@ -47,6 +47,7 @@ def oldQuietRun( *cmd ):
break
return out
# This is a bit complicated, but it enables us to
# monitor commount output as it is happening
@@ -80,7 +81,10 @@ def errRun( *cmd, **kwargs ):
poller.register( popen.stderr, POLLIN )
while True:
readable = poller.poll()
# Tell pylint to ignore unused variable event
# pylint: disable-msg=W0612
for fd, event in readable:
# pylint: enable-msg=W0612
f = fdtofile[ fd ]
data = f.read( 1024 )
if echo:
@@ -186,13 +190,13 @@ def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
# IP and Mac address formatting and parsing
def _colonHex( val, bytes ):
def _colonHex( val, bytecount ):
"""Generate colon-hex string.
val: input as unsigned int
bytes: number of bytes to convert
bytescount: number of bytes to convert
returns: chStr colon-hex string"""
pieces = []
for i in range( bytes - 1, -1, -1 ):
for i in range( bytecount - 1, -1, -1 ):
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
pieces.append( '%02x' % piece )
chStr = ':'.join( pieces )
@@ -204,7 +208,7 @@ def macColonHex( mac ):
returns: macStr MAC colon-hex string"""
return _colonHex( mac, 6 )
def ipStr( ip, defaultNet=10 ):
def ipStr( ip ):
"""Generate IP address string from an unsigned int.
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
@@ -270,6 +274,7 @@ def fixLimits():
def natural( text ):
"To sort sanely/alphabetically: sorted( l, key=natural )"
def num( s ):
"Convert text segment to int if necessary"
return int( s ) if s.isdigit() else text
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
@@ -286,8 +291,7 @@ def numCores():
def custom( cls, **params ):
"Returns customized constructor for class cls."
def customized( *args, **kwargs):
"Customized constructor"
kwargs.update( params )
return cls( *args, **kwargs )
return customized