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
+26 -23
View File
@@ -19,7 +19,7 @@ import time
from mininet.clean import cleanup from mininet.clean import cleanup
from mininet.cli import CLI from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, warn 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 Host, CPULimitedHost, Controller, OVSController, NOX
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
from mininet.link import Intf, TCIntf from mininet.link import Intf, TCIntf
@@ -32,12 +32,13 @@ def customNode( constructors, argStr ):
"Return custom Node constructor based on argStr" "Return custom Node constructor based on argStr"
cname, newargs, kwargs = splitArgs( argStr ) cname, newargs, kwargs = splitArgs( argStr )
constructor = constructors.get( cname, None ) constructor = constructors.get( cname, None )
#if args:
# raise Exception( "please specify keyword arguments for " + cname )
if not constructor: if not constructor:
raise Exception( "error: %s is unknown - please specify one of %s" % raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, constructors.keys() ) ) ( cname, constructors.keys() ) )
def custom( name, *args, **params ):
def customized( name, *args, **params ):
"Customized Node constructor"
params.update( kwargs ) params.update( kwargs )
if not newargs: if not newargs:
return constructor( name, *args, **params ) return constructor( name, *args, **params )
@@ -45,7 +46,8 @@ def customNode( constructors, argStr ):
warn( 'warning: %s replacing %s with %s\n', warn( 'warning: %s replacing %s with %s\n',
constructor, args, newargs ) constructor, args, newargs )
return constructor( name, *newargs, **params ) return constructor( name, *newargs, **params )
return custom
return customized
# built in topologies, created only when run # built in topologies, created only when run
@@ -68,7 +70,7 @@ HOSTS = { 'proc': Host,
CONTROLLERDEF = 'ref' CONTROLLERDEF = 'ref'
CONTROLLERS = { 'ref': Controller, CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController, 'ovsc': OVSController,
'nox': NOX, 'nox': NOX,
'remote': RemoteController, 'remote': RemoteController,
'none': lambda name: None } 'none': lambda name: None }
@@ -94,8 +96,7 @@ def splitArgs( argstr ):
params = split[ 1: ] params = split[ 1: ]
# Convert int and float args; removes the need for function # Convert int and float args; removes the need for function
# to be flexible with input arg formats. # to be flexible with input arg formats.
args = [ s for s in params if '=' not in s ] args = [ makeNumeric( s ) for s in params if '=' not in s ]
args = map( makeNumeric, args )
kwargs = {} kwargs = {}
for s in [ p for p in params if '=' in p ]: for s in [ p for p in params if '=' in p ]:
key, val = s.split( '=' ) 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' % raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) ) ( default, name ) )
if not helpStr: if not helpStr:
helpStr = '|'.join( sorted( choicesDict.keys() ) ) + '[,param=value...]' helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name, opts.add_option( '--' + name,
type='string', type='string',
default = default, default = default,
@@ -157,11 +159,11 @@ class MininetRunner( object ):
def parseCustomFile( self, fileName ): def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options." "Parse custom file and add params before parsing cmd-line options."
custom = {} customs = {}
if os.path.isfile( fileName ): if os.path.isfile( fileName ):
execfile( fileName, custom, custom ) execfile( fileName, customs, customs )
for name in custom: for name, val in customs.iteritems():
self.setCustom( name, custom[ name ] ) self.setCustom( name, val )
else: else:
raise Exception( 'could not find custom file: %s' % fileName ) raise Exception( 'could not find custom file: %s' % fileName )
@@ -171,8 +173,8 @@ class MininetRunner( object ):
if '--custom' in sys.argv: if '--custom' in sys.argv:
index = sys.argv.index( '--custom' ) index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1: if len( sys.argv ) > index + 1:
custom = sys.argv[ index + 1 ] filename = sys.argv[ index + 1 ]
self.parseCustomFile( custom ) self.parseCustomFile( filename )
else: else:
raise Exception( 'Custom file name not found' ) raise Exception( 'Custom file name not found' )
@@ -241,7 +243,7 @@ class MininetRunner( object ):
start = time.time() start = time.time()
topo = buildTopo( self.options.topo ) topo = buildTopo( self.options.topo )
switch = customNode( SWITCHES, self.options.switch ) switch = customNode( SWITCHES, self.options.switch )
host = customNode( HOSTS, self.options.host ) host = customNode( HOSTS, self.options.host )
controller = customNode( CONTROLLERS, self.options.controller ) controller = customNode( CONTROLLERS, self.options.controller )
intf = customNode( INTFS, self.options.intf ) intf = customNode( INTFS, self.options.intf )
@@ -250,6 +252,7 @@ class MininetRunner( object ):
self.validate( self.options ) self.validate( self.options )
inNamespace = self.options.innamespace inNamespace = self.options.innamespace
Net = MininetWithControlNet if inNamespace else Mininet
ipBase = self.options.ipbase ipBase = self.options.ipbase
xterms = self.options.xterms xterms = self.options.xterms
mac = self.options.mac mac = self.options.mac
@@ -257,13 +260,13 @@ class MininetRunner( object ):
listenPort = None listenPort = None
if not self.options.nolistenport: if not self.options.nolistenport:
listenPort = self.options.listenport listenPort = self.options.listenport
mn = Mininet( topo=topo, mn = Net( topo=topo,
switch=switch, host=host, controller=controller, switch=switch, host=host, controller=controller,
intf=intf, intf=intf,
ipBase=ipBase, ipBase=ipBase,
inNamespace=inNamespace, inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac, xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, listenPort=listenPort ) autoStaticArp=arp, listenPort=listenPort )
if self.options.pre: if self.options.pre:
CLI( mn, script=self.options.pre ) CLI( mn, script=self.options.pre )
+4 -12
View File
@@ -107,7 +107,7 @@ class Console( Frame ):
self.text.insert( 'end', text ) self.text.insert( 'end', text )
self.text.mark_set( 'insert', 'end' ) self.text.mark_set( 'insert', 'end' )
self.text.see( 'insert' ) self.text.see( 'insert' )
outputHook = lambda x,y: True # make pylint happy outputHook = lambda x, y: True # make pylint happier
if self.outputHook: if self.outputHook:
outputHook = self.outputHook outputHook = self.outputHook
outputHook( self, text ) outputHook( self, text )
@@ -132,27 +132,22 @@ class Console( Frame ):
self.sendCmd( cmd ) self.sendCmd( cmd )
# Callback ignores event # Callback ignores event
# pylint: disable-msg=W0613 def handleInt( self, _event=None ):
def handleInt( self, event=None ):
"Handle control-c." "Handle control-c."
self.node.sendInt() self.node.sendInt()
# pylint: enable-msg=W0613
def sendCmd( self, cmd ): def sendCmd( self, cmd ):
"Send a command to our node." "Send a command to our node."
if not self.node.waiting: if not self.node.waiting:
self.node.sendCmd( cmd ) self.node.sendCmd( cmd )
# Callback ignores fds def handleReadable( self, _fds, timeoutms=None ):
# pylint: disable-msg=W0613
def handleReadable( self, fds, timeoutms=None ):
"Handle file readable event." "Handle file readable event."
data = self.node.monitor( timeoutms ) data = self.node.monitor( timeoutms )
self.append( data ) self.append( data )
if not self.node.waiting: if not self.node.waiting:
# Print prompt # Print prompt
self.append( self.prompt ) self.append( self.prompt )
# pylint: enable-msg=W0613
def waiting( self ): def waiting( self ):
"Are we waiting for output?" "Are we waiting for output?"
@@ -321,9 +316,7 @@ class ConsoleApp( Frame ):
self.pack( expand=True, fill='both' ) self.pack( expand=True, fill='both' )
# Update callback doesn't use console arg def updateGraph( self, _console, output ):
# pylint: disable-msg=W0613
def updateGraph( self, console, output ):
"Update our graph." "Update our graph."
m = re.search( r'(\d+) Mbits/sec', output ) m = re.search( r'(\d+) Mbits/sec', output )
if not m: if not m:
@@ -334,7 +327,6 @@ class ConsoleApp( Frame ):
self.graph.addBar( self.bw ) self.graph.addBar( self.bw )
self.bw = 0 self.bw = 0
self.updates = 0 self.updates = 0
# pylint: enable-msg=W0613
def setOutputHook( self, fn=None, consoles=None ): def setOutputHook( self, fn=None, consoles=None ):
"Register fn as output hook [on specific consoles.]" "Register fn as output hook [on specific consoles.]"
+5 -2
View File
@@ -13,10 +13,12 @@ from mininet.log import setLogLevel
from time import sleep from time import sleep
def testLinkLimit( net, bw ): def testLinkLimit( net, bw ):
"Run bandwidth limit test"
print '*** Testing network %.2f Mbps bandwidth limit' % bw print '*** Testing network %.2f Mbps bandwidth limit' % bw
net.iperf( ) net.iperf( )
def testCpuLimit( net, cpu ): def testCpuLimit( net, cpu ):
"run CPU limit test"
pct = cpu * 100 pct = cpu * 100
print '*** Testing CPU %.0f%% bandwidth limit' % pct print '*** Testing CPU %.0f%% bandwidth limit' % pct
h1, h2 = net.hosts h1, h2 = net.hosts
@@ -25,8 +27,9 @@ def testCpuLimit( net, cpu ):
pid1 = h1.cmd( 'echo $!' ).strip() pid1 = h1.cmd( 'echo $!' ).strip()
pid2 = h2.cmd( 'echo $!' ).strip() pid2 = h2.cmd( 'echo $!' ).strip()
cmd = 'ps -p %s,%s -o pid,%%cpu,args' % ( pid1, pid2 ) 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
sleep( 1 ) for _ in range( 5 ):
sleep( 1 )
print quietRun( cmd ).strip() print quietRun( cmd ).strip()
h1.cmd( 'kill %1') h1.cmd( 'kill %1')
h2.cmd( 'kill %1') h2.cmd( 'kill %1')
+7 -18
View File
@@ -299,14 +299,11 @@ class MiniEdit( Frame ):
# Delete from view # Delete from view
self.canvas.delete( item ) self.canvas.delete( item )
# Callback ignores event def deleteSelection( self, _event ):
# pylint: disable-msg=W0613
def deleteSelection( self, event ):
"Delete the selected item." "Delete the selected item."
if self.selection is not None: if self.selection is not None:
self.deleteItem( self.selection ) self.deleteItem( self.selection )
self.selectItem( None ) self.selectItem( None )
# pylint: enable-msg=W0613
def nodeIcon( self, node, name ): def nodeIcon( self, node, name ):
"Create a new node icon." "Create a new node icon."
@@ -350,14 +347,11 @@ class MiniEdit( Frame ):
c = self.canvas c = self.canvas
c.coords( self.link, self.linkx, self.linky, x, y ) c.coords( self.link, self.linkx, self.linky, x, y )
# Callback ignores event def releaseLink( self, _event ):
# pylint: disable-msg=W0613
def releaseLink( self, event ):
"Give up on the current link." "Give up on the current link."
if self.link is not None: if self.link is not None:
self.canvas.delete( self.link ) self.canvas.delete( self.link )
self.linkWidget = self.linkItem = self.link = None self.linkWidget = self.linkItem = self.link = None
# pylint: enable-msg=W0613
# Generic node handlers # Generic node handlers
@@ -385,12 +379,9 @@ class MiniEdit( Frame ):
"Select node on entry." "Select node on entry."
self.selectNode( event ) self.selectNode( event )
# Callback ignores event def leaveNode( self, _event ):
# pylint: disable-msg=W0613
def leaveNode( self, event ):
"Restore old selection on exit." "Restore old selection on exit."
self.selectItem( self.lastSelection ) self.selectItem( self.lastSelection )
# pylint: enable-msg=W0613
def clickNode( self, event ): def clickNode( self, event ):
"Node click handler." "Node click handler."
@@ -454,23 +445,21 @@ class MiniEdit( Frame ):
# Link bindings # Link bindings
# Selection still needs a bit of work overall # Selection still needs a bit of work overall
# Callbacks ignore event # Callbacks ignore event
# pylint: disable-msg=W0613
def select( event, link=self.link ): def select( _event, link=self.link ):
"Select item on mouse entry." "Select item on mouse entry."
self.selectItem( link ) self.selectItem( link )
def highlight( event, link=self.link ): def highlight( _event, link=self.link ):
"Highlight item on mouse entry." "Highlight item on mouse entry."
# self.selectItem( link ) # self.selectItem( link )
self.canvas.itemconfig( link, fill='green' ) self.canvas.itemconfig( link, fill='green' )
def unhighlight( event, link=self.link ): def unhighlight( _event, link=self.link ):
"Unhighlight item on mouse exit." "Unhighlight item on mouse exit."
self.canvas.itemconfig( link, fill='blue' ) self.canvas.itemconfig( link, fill='blue' )
# self.selectItem( None ) # self.selectItem( None )
# pylint: disable-msg=W0613
self.canvas.tag_bind( self.link, '<Enter>', highlight ) self.canvas.tag_bind( self.link, '<Enter>', highlight )
self.canvas.tag_bind( self.link, '<Leave>', unhighlight ) self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select ) self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
@@ -602,7 +591,7 @@ class MiniEdit( Frame ):
cleanUpScreens() cleanUpScreens()
self.net = None self.net = None
def xterm( self, ignore=None ): def xterm( self, _=None ):
"Make an xterm when a button is pressed." "Make an xterm when a button is pressed."
if ( self.selection is None or if ( self.selection is None or
self.net 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' ) info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' ) controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' ) 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( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' ) 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 # Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function # "method could be a function" warning, since each CLI function
# must have the same interface # must have the same interface
# pylint: disable-msg=W0613,R0201 # pylint: disable-msg=R0201
helpStr = ( helpStr = (
'You may also send a command to a node using:\n' 'You may also send a command to a node using:\n'
@@ -104,12 +104,12 @@ class CLI( Cmd ):
if line is '': if line is '':
output( self.helpStr ) output( self.helpStr )
def do_nodes( self, line ): def do_nodes( self, _line ):
"List all nodes." "List all nodes."
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] ) nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
output( 'available nodes are: \n%s\n' % nodes ) output( 'available nodes are: \n%s\n' % nodes )
def do_net( self, line ): def do_net( self, _line ):
"List network connections." "List network connections."
for switch in self.mn.switches: for switch in self.mn.switches:
output( switch.name, '<->' ) output( switch.name, '<->' )
@@ -143,11 +143,11 @@ class CLI( Cmd ):
# pylint: enable-msg=W0703 # pylint: enable-msg=W0703
def do_pingall( self, line ): def do_pingall( self, _line ):
"Ping between all hosts." "Ping between all hosts."
self.mn.pingAll() self.mn.pingAll()
def do_pingpair( self, line ): def do_pingpair( self, _line ):
"Ping between first two hosts, useful for testing." "Ping between first two hosts, useful for testing."
self.mn.pingPair() self.mn.pingPair()
@@ -191,13 +191,13 @@ class CLI( Cmd ):
error( 'invalid number of args: iperfudp bw src dst\n' + error( 'invalid number of args: iperfudp bw src dst\n' +
'bw examples: 10M\n' ) 'bw examples: 10M\n' )
def do_intfs( self, line ): def do_intfs( self, _line ):
"List interfaces." "List interfaces."
for node in self.nodelist: for node in self.nodelist:
output( '%s: %s\n' % output( '%s: %s\n' %
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) ) ( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
def do_dump( self, line ): def do_dump( self, _line ):
"Dump node info." "Dump node info."
for node in self.nodelist: for node in self.nodelist:
output( '%s\n' % node ) output( '%s\n' % node )
@@ -229,7 +229,7 @@ class CLI( Cmd ):
"Spawn gnome-terminal(s) for the given node(s)." "Spawn gnome-terminal(s) for the given node(s)."
self.do_xterm( line, term='gterm' ) self.do_xterm( line, term='gterm' )
def do_exit( self, line ): def do_exit( self, _line ):
"Exit" "Exit"
return 'exited by user command' return 'exited by user command'
@@ -311,7 +311,7 @@ class CLI( Cmd ):
else: else:
error( '*** Unknown command: %s\n' % first ) error( '*** Unknown command: %s\n' % first )
# pylint: enable-msg=W0613,R0201 # pylint: enable-msg=R0201
def waitForNode( self, node ): def waitForNode( self, node ):
"Wait for a node to finish, and print its output." "Wait for a node to finish, and print its output."
+133 -91
View File
@@ -41,30 +41,35 @@ class Intf( object ):
self.node = node self.node = node
self.name = name self.name = name
self.link = link 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 ) # Add to node (and move ourselves if necessary )
node.addIntf( self ) node.addIntf( self )
self.config( **kwargs ) self.config( **kwargs )
def cmd( self, *args, **kwargs ): def cmd( self, *args, **kwargs ):
"Run a command in our owning node"
return self.node.cmd( *args, **kwargs ) return self.node.cmd( *args, **kwargs )
def ifconfig( self, *args ): def ifconfig( self, *args ):
"Configure ourselves using ifconfig" "Configure ourselves using ifconfig"
return self.cmd( 'ifconfig', self.name, *args ) return self.cmd( 'ifconfig', self.name, *args )
def setIP( self, ipstr ): def setIP( self, ipstr, prefixLen=None ):
"""Set our IP address""" """Set our IP address"""
# This is a sign that we should perhaps rethink our prefix # This is a sign that we should perhaps rethink our prefix
# mechanism # mechanism and/or the way we specify IP addresses
self.ip, self.prefixLen = ipstr.split( '/' ) if '/' in ipstr:
return self.ifconfig( ipstr, 'up' ) 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 ): def setMAC( self, macstr ):
"""Set the MAC address for an interface. """Set the MAC address for an interface.
macstr: MAC address as string""" macstr: MAC address as string"""
self.mac = macstr self.mac = macstr
return ( self.ifconfig( 'down' ) + return ( self.ifconfig( 'down' ) +
self.ifconfig( 'hw', 'ether', macstr ) + self.ifconfig( 'hw', 'ether', macstr ) +
self.ifconfig( 'up' ) ) self.ifconfig( 'up' ) )
@@ -78,13 +83,13 @@ class Intf( object ):
self.ip = ips[ 0 ] if ips else None self.ip = ips[ 0 ] if ips else None
return self.ip return self.ip
def updateMAC( self, intf ): def updateMAC( self ):
"Return updated MAC address based on ifconfig" "Return updated MAC address based on ifconfig"
ifconfig = self.ifconfig() ifconfig = self.ifconfig()
macs = self._macMatchRegex.findall( ifconfig ) macs = self._macMatchRegex.findall( ifconfig )
self.mac = macs[ 0 ] if macs else None self.mac = macs[ 0 ] if macs else None
return self.mac return self.mac
def IP( self ): def IP( self ):
"Return IP address" "Return IP address"
return self.ip return self.ip
@@ -93,9 +98,9 @@ class Intf( object ):
"Return MAC address" "Return MAC address"
return self.mac return self.mac
def isUp( self, set=False ): def isUp( self, setUp=False ):
"Return whether interface is up" "Return whether interface is up"
if set: if setUp:
self.ifconfig( 'up' ) self.ifconfig( 'up' )
return "UP" in self.ifconfig() return "UP" in self.ifconfig()
@@ -124,8 +129,8 @@ class Intf( object ):
results[ name ] = result results[ name ] = result
return result return result
def config( self, mac=None, ip=None, ifconfig=None, def config( self, mac=None, ip=None, ifconfig=None,
defaultRoute=None, up=True, **params): up=True, **_params ):
"""Configure Node according to (optional) parameters: """Configure Node according to (optional) parameters:
mac: MAC address mac: MAC address
ip: IP address ip: IP address
@@ -153,7 +158,83 @@ class Intf( object ):
class TCIntf( Intf ): 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, def config( self, bw=None, delay=None, loss=None, disable_gro=True,
speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False, speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False,
@@ -162,106 +243,58 @@ class TCIntf( Intf ):
result = Intf.config( self, **params) result = Intf.config( self, **params)
# disable GRO # Disable GRO
if disable_gro: if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self ) self.cmd( 'ethtool -K %s gro off' % self )
if ( bw is None and not delay and not loss # 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 ): and max_queue_size is None ):
return return
if bw and ( bw < 0 or bw > 1000 ): # Clear existing configuration
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' ) cmds = [ '%s qdisc del dev %s root' ]
return
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
return
if loss and ( loss < 0 or loss > 100 ): # Bandwidth limits via various methods
error( 'Bad loss percentage', loss, '%%\n' ) bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
return use_hfsc=use_hfsc, use_tbf=use_tbf,
enable_ecn=enable_ecn,
enable_red=enable_red )
cmds += bwcmds
# Ugly but functional # 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: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) + stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) + ( [ '%s delay' % delay ] if delay is not None else [] ) +
( ['%d%% loss' % loss ] if loss 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 ) + ') ' ) info( '(' + ' '.join( stuff ) + ') ' )
cmds = [ '%s qdisc del dev %s root' ] # Execute all the commands in our node
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
debug("at map stage w/cmds: %s\n" % cmds) debug("at map stage w/cmds: %s\n" % cmds)
tcoutputs = [ self.tc(cmd) for cmd in 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 ]
debug( "cmds:", cmds, '\n' ) debug( "cmds:", cmds, '\n' )
debug( "outputs:", tcoutputs, '\n' ) debug( "outputs:", tcoutputs, '\n' )
result[ 'tcoutputs'] = tcoutputs result[ 'tcoutputs'] = tcoutputs
return result return result
class Link( object ): class Link( object ):
"""A basic link is just a veth pair. """A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc..""" Other types of links could be tunnels, link emulators, etc.."""
def __init__( self, node1, node2, port1=None, port2=None, intfName1=None, intfName2=None, def __init__( self, node1, node2, port1=None, port2=None,
intf=Intf, cls1=None, cls2=None, params1={}, params2={} ): intfName1=None, intfName2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None ):
"""Create veth link to another node, making two new interfaces. """Create veth link to another node, making two new interfaces.
node1: first node node1: first node
node2: second node node2: second node
@@ -284,13 +317,21 @@ class Link( object ):
intfName1 = self.intfName( node1, port1 ) intfName1 = self.intfName( node1, port1 )
if not intfName2: if not intfName2:
intfName2 = self.intfName( node2, port2 ) intfName2 = self.intfName( node2, port2 )
self.makeIntfPair( intfName1, intfName2 ) self.makeIntfPair( intfName1, intfName2 )
if not cls1: if not cls1:
cls1 = intf cls1 = intf
if not cls2: if not cls2:
cls2 = intf cls2 = intf
if not params1:
params1 = {}
if not params2:
params2 = {}
intf1 = cls1( name=intfName1, node=node1, link=self, **params1 ) intf1 = cls1( name=intfName1, node=node1, link=self, **params1 )
intf2 = cls2( name=intfName2, node=node2, link=self, **params2 ) intf2 = cls2( name=intfName2, node=node2, link=self, **params2 )
# All we are is dust in the wind, and our two interfaces # All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2 self.intf1, self.intf2 = intf1, intf2
@@ -304,7 +345,8 @@ class Link( object ):
"""Create pair of interfaces """Create pair of interfaces
intf1: name of interface 1 intf1: name of interface 1
intf2: name of interface 2 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 ) makeIntfPair( intf1, intf2 )
def delete( self ): def delete( self ):
+22 -18
View File
@@ -104,7 +104,7 @@ 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=None, controller=Controller, link=Link, intf=None,
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, listenPort=None ): autoSetMacs=False, autoStaticArp=False, listenPort=None ):
@@ -176,7 +176,7 @@ class Mininet( object ):
switch: custom switch constructor (optional) switch: custom switch constructor (optional)
returns: added switch returns: added switch
side effect: increments listenPort ivar .""" side effect: increments listenPort ivar ."""
defaults = { 'listenPort': self.listenPort, defaults = { 'listenPort': self.listenPort,
'inNamespace': self.inNamespace } 'inNamespace': self.inNamespace }
defaults.update( params ) defaults.update( params )
if not switch: if not switch:
@@ -229,7 +229,7 @@ class Mininet( object ):
ipBaseNum=ipBaseNum, ipBaseNum=ipBaseNum,
prefixLen=prefixLen ) } prefixLen=prefixLen ) }
if self.autoSetMacs: if self.autoSetMacs:
defaults[ 'mac'] = macColonHex( nodeId ) defaults[ 'mac'] = macColonHex( nodeId )
defaults.update( ni.params ) defaults.update( ni.params )
node = addMethod( name, cls=ni.cls, **defaults ) node = addMethod( name, cls=ni.cls, **defaults )
self.idToNode[ nodeId ] = node self.idToNode[ nodeId ] = node
@@ -275,17 +275,16 @@ class Mininet( object ):
info( '\n' ) info( '\n' )
def configureControlNetwork( self ): def configureControlNetwork( self ):
error( "configureControlNetwork: override in subclass, or use" "Control net config hook: override in subclass"
"MininetWithControlNet class" ) raise Exception( 'configureControlNetwork: '
'should be overriden in subclass', self )
def build( self ): def build( self ):
"Build mininet." "Build mininet."
if self.topo: if self.topo:
self.buildFromTopo( self.topo ) self.buildFromTopo( self.topo )
if self.inNamespace: if ( self.inNamespace ):
info( '*** Configuring control network\n' )
self.configureControlNetwork() self.configureControlNetwork()
info( '*** Configuring hosts\n' ) info( '*** Configuring hosts\n' )
self.configHosts() self.configHosts()
@@ -533,7 +532,7 @@ class Mininet( object ):
return result return result
inited = False inited = False
@classmethod @classmethod
def init( cls ): def init( cls ):
"Initialize Mininet" "Initialize Mininet"
@@ -541,7 +540,8 @@ class Mininet( object ):
return return
if os.getuid() != 0: if os.getuid() != 0:
# Note: this script must be run as root # 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." print "*** Mininet must run as root."
exit( 1 ) exit( 1 )
fixLimits() fixLimits()
@@ -570,7 +570,11 @@ class MininetWithControlNet( Mininet ):
network (since real networks may need one!) network (since real networks may need one!)
5. Basically nobody ever used this code, so it has been moved 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 ): def configureControlNetwork( self ):
"Configure control network." "Configure control network."
@@ -589,27 +593,27 @@ class MininetWithControlNet( Mininet ):
snum = ipParse( ip ) snum = ipParse( ip )
for switch in self.switches: for switch in self.switches:
info( ' ' + switch.name ) 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 snum += 1
while snum & 0xff in [ 0, 255 ]: while snum & 0xff in [ 0, 255 ]:
snum += 1 snum += 1
sip = ipStr( snum ) sip = ipStr( snum )
controller.setIP( cintf, cip, prefixLen ) cintf.setIP( cip, prefixLen )
switch.setIP( sintf, sip, prefixLen ) sintf.setIP( sip, prefixLen )
controller.setHostRoute( sip, cintf ) controller.setHostRoute( sip, cintf )
switch.setHostRoute( cip, sintf ) switch.setHostRoute( cip, sintf )
info( '\n' ) info( '\n' )
info( '*** Testing control network\n' ) info( '*** Testing control network\n' )
while not controller.intfIsUp( cintf ): while not cintf.isUp():
info( '*** Waiting for', cintf, 'to come up\n' ) info( '*** Waiting for', cintf, 'to come up\n' )
sleep( 1 ) sleep( 1 )
for switch in self.switches: for switch in self.switches:
while not switch.intfIsUp( sintf ): while not sintf.isUp():
info( '*** Waiting for', sintf, 'to come up\n' ) info( '*** Waiting for', sintf, 'to come up\n' )
sleep( 1 ) sleep( 1 )
if self.ping( hosts=[ switch, controller ] ) != 0: if self.ping( hosts=[ switch, controller ] ) != 0:
error( '*** Error: control network test failed\n' ) error( '*** Error: control network test failed\n' )
exit( 1 ) exit( 1 )
info( '\n' ) info( '\n' )
+68 -52
View File
@@ -81,8 +81,14 @@ class Node( object ):
# replace with Port objects, eventually ? # replace with Port objects, eventually ?
self.nameToIntf = {} # dict of interface names to Intfs 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 # Start command interpreter shell
self.shell = None
self.startShell() self.startShell()
# File descriptor to node mapping support # File descriptor to node mapping support
@@ -99,28 +105,6 @@ class Node( object ):
node = cls.outToNode.get( fd ) node = cls.outToNode.get( fd )
return node or cls.inToNode.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 # Command support via shell process in namespace
def startShell( self ): def startShell( self ):
@@ -129,7 +113,7 @@ class Node( object ):
error( "%s: shell is already running" ) error( "%s: shell is already running" )
return return
# mnexec: (c)lose descriptors, (d)etach from tty, # mnexec: (c)lose descriptors, (d)etach from tty,
# (p)rint pid, and run in (n)amespace # (p)rint pid, and run in (n)amespace
opts = '-cdp' opts = '-cdp'
if self.inNamespace: if self.inNamespace:
opts += 'n' opts += 'n'
@@ -153,19 +137,23 @@ class Node( object ):
self.readbuf = '' self.readbuf = ''
self.waiting = False 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. """Buffered read from node, non-blocking.
bytes: maximum number of bytes to return""" maxbytes: maximum number of bytes to return"""
count = len( self.readbuf ) count = len( self.readbuf )
if count < bytes: if count < maxbytes:
data = os.read( self.stdout.fileno(), bytes - count ) data = os.read( self.stdout.fileno(), maxbytes - count )
self.readbuf += data self.readbuf += data
if bytes >= len( self.readbuf ): if maxbytes >= len( self.readbuf ):
result = self.readbuf result = self.readbuf
self.readbuf = '' self.readbuf = ''
else: else:
result = self.readbuf[ :bytes ] result = self.readbuf[ :maxbytes ]
self.readbuf = self.readbuf[ bytes: ] self.readbuf = self.readbuf[ maxbytes: ]
return result return result
def readline( self ): def readline( self ):
@@ -307,7 +295,7 @@ class Node( object ):
self.ports[ intf ] = port self.ports[ intf ] = port
self.nameToIntf[ intf.name ] = intf self.nameToIntf[ intf.name ] = intf
debug( '\n' ) debug( '\n' )
debug( 'added intf %s:%d to node %s\n' % ( intf,port, self.name ) ) debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
if self.inNamespace: if self.inNamespace:
debug( 'moving', intf, 'into namespace for', self.name, '\n' ) debug( 'moving', intf, 'into namespace for', self.name, '\n' )
moveIntf( intf.name, self ) moveIntf( intf.name, self )
@@ -363,7 +351,7 @@ class Node( object ):
"""Add route to host. """Add route to host.
ip: IP address as dotted decimal ip: IP address as dotted decimal
intf: string, interface name""" 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 ): def setDefaultRoute( self, intf=None ):
"""Set the default route to go through intf. """Set the default route to go through intf.
@@ -430,8 +418,8 @@ class Node( object ):
results[ name ] = result results[ name ] = result
return result return result
def config( self, mac=None, ip=None, ifconfig=None, def config( self, mac=None, ip=None, ifconfig=None,
defaultRoute=None, **params): defaultRoute=None, **_params ):
"""Configure Node according to (optional) parameters: """Configure Node according to (optional) parameters:
mac: MAC address for default interface mac: MAC address for default interface
ip: IP address for default interface ip: IP address for default interface
@@ -440,7 +428,7 @@ class Node( object ):
the parent class's config(**params)""" the parent class's config(**params)"""
# If we were overriding this method, we would call # If we were overriding this method, we would call
# the superclass config method here as follows: # the superclass config method here as follows:
# r = Parent.config( **params ) # r = Parent.config( **_params )
r = {} r = {}
self.setParam( r, 'setMAC', mac=mac ) self.setParam( r, 'setMAC', mac=mac )
self.setParam( r, 'setIP', ip=ip ) self.setParam( r, 'setIP', ip=ip )
@@ -473,6 +461,24 @@ class Node( object ):
return '%s: IP=%s intfs=%s pid=%s' % ( return '%s: IP=%s intfs=%s pid=%s' % (
self.name, self.IP(), ','.join( self.intfNames() ), self.pid ) 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 ): class Host( Node ):
"A host is simply a Node" "A host is simply a Node"
@@ -484,7 +490,7 @@ class CPULimitedHost( Host ):
"CPU limited host" "CPU limited host"
def __init__( self, *args, **kwargs ): def __init__( self, *args, **kwargs ):
Node.__init__( self, *args, **kwargs ) Host.__init__( self, *args, **kwargs )
# Create a cgroup and move shell into it # Create a cgroup and move shell into it
self.cgroup = 'cpu,cpuacct:/' + self.name self.cgroup = 'cpu,cpuacct:/' + self.name
errFail( 'cgcreate -g ' + self.cgroup ) errFail( 'cgcreate -g ' + self.cgroup )
@@ -510,6 +516,7 @@ class CPULimitedHost( Host ):
return nvalue return nvalue
def cgroupGet( self, param, resource='cpu' ): def cgroupGet( self, param, resource='cpu' ):
"Return value of cgroup parameter"
cmd = 'cgget -r %s.%s /%s' % ( cmd = 'cgget -r %s.%s /%s' % (
resource, param, self.name ) resource, param, self.name )
return quietRun( cmd ).split()[ -1 ] return quietRun( cmd ).split()[ -1 ]
@@ -544,7 +551,7 @@ class CPULimitedHost( Host ):
return pstr, qstr, period, quota return pstr, qstr, period, quota
# BL comment: # BL comment:
# This may not be the right API, # This may not be the right API,
# since it doesn't specify CPU bandwidth in "absolute" # since it doesn't specify CPU bandwidth in "absolute"
# units the way link bandwidth is specified. # units the way link bandwidth is specified.
# We should use MIPS or SPECINT or something instead. # We should use MIPS or SPECINT or something instead.
@@ -578,7 +585,7 @@ class CPULimitedHost( Host ):
self.chrt( prio=20 ) self.chrt( prio=20 )
info( '(%s %d/%dus) ' % ( sched, quota, period ) ) 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 """cpu: desired overall system CPU fraction
params: parameters for Node.config()""" params: parameters for Node.config()"""
r = Node.config( self, **params ) r = Node.config( self, **params )
@@ -665,8 +672,8 @@ class UserSwitch( Switch ):
pathCheck( 'ofdatapath', 'ofprotocol', pathCheck( 'ofdatapath', 'ofprotocol',
moduleName='the OpenFlow reference user switch (openflow.org)' ) moduleName='the OpenFlow reference user switch (openflow.org)' )
@staticmethod @classmethod
def setup(): def setup( cls ):
"Ensure any dependencies are loaded; if not, try to load them." "Ensure any dependencies are loaded; if not, try to load them."
if not os.path.exists( '/dev/net/tun' ): if not os.path.exists( '/dev/net/tun' ):
moduleDeps( add=TUN ) moduleDeps( add=TUN )
@@ -684,7 +691,7 @@ class UserSwitch( Switch ):
if self.inNamespace: if self.inNamespace:
intfs = intfs[ :-1 ] intfs = intfs[ :-1 ]
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) + self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
' punix:/tmp/' + self.name + ' -d ' + self.dpid + ' punix:/tmp/' + self.name + ' -d ' + self.dpid +
' --no-slicing ' + ' --no-slicing ' +
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' ) ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
self.cmd( 'ofprotocol unix:/tmp/' + self.name + self.cmd( 'ofprotocol unix:/tmp/' + self.name +
@@ -716,8 +723,8 @@ class OVSLegacyKernelSwitch( Switch ):
" in the root namespace.\n" ) " in the root namespace.\n" )
exit( 1 ) exit( 1 )
@staticmethod @classmethod
def setup(): def setup( cls ):
"Ensure any dependencies are loaded; if not, try to load them." "Ensure any dependencies are loaded; if not, try to load them."
pathCheck( 'ovs-dpctl', 'ovs-openflowd', pathCheck( 'ovs-dpctl', 'ovs-openflowd',
moduleName='Open vSwitch (openvswitch.org)') moduleName='Open vSwitch (openvswitch.org)')
@@ -741,7 +748,7 @@ class OVSLegacyKernelSwitch( Switch ):
controller = controllers[ 0 ] controller = controllers[ 0 ]
self.cmd( 'ovs-openflowd ' + self.dp + self.cmd( 'ovs-openflowd ' + self.dp +
' tcp:%s:%d' % ( controller.IP(), controller.port ) + ' tcp:%s:%d' % ( controller.IP(), controller.port ) +
' --fail=secure ' + self.opts + ' --fail=secure ' + self.opts +
' --datapath-id=' + self.dpid + ' --datapath-id=' + self.dpid +
' 1>' + ofplog + ' 2>' + ofplog + '&' ) ' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False self.execed = False
@@ -766,26 +773,34 @@ class OVSSwitch( Switch ):
# dpid, which is a 64-bit numerical value used by # dpid, which is a 64-bit numerical value used by
# the openflow protocol. # the openflow protocol.
self.dp = name self.dp = name
if self.inNamespace:
@staticmethod error( "OVSSwitch currently only works"
def setup(): " in the root namespace.\n" )
exit( 1 )
@classmethod
def setup( cls ):
"Make sure Open vSwitch is installed and working" "Make sure Open vSwitch is installed and working"
pathCheck( 'ovs-vsctl', pathCheck( 'ovs-vsctl',
moduleName='Open vSwitch (openvswitch.org)') moduleName='Open vSwitch (openvswitch.org)')
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD ) moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' ) out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
if exitcode: if exitcode:
error( out + err + error( out + err +
'ovs-vsctl exited with code %d\n' % exitcode + 'ovs-vsctl exited with code %d\n' % exitcode +
'*** Error connecting to ovs-db with ovs-vsctl\n' '*** Error connecting to ovs-db with ovs-vsctl\n'
'Make sure that Open vSwitch is installed, ' 'Make sure that Open vSwitch is installed, '
'that ovsdb-server is running, and that\n' 'that ovsdb-server is running, and that\n'
'"ovs-vsctl show" works correctly.\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 ) exit( 1 )
def start( self, controllers ): def start( self, controllers ):
"Start up a new OVS OpenFlow switch using ovs-vsctl" "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 # Annoyingly, --if-exists option seems not to work
self.cmd( 'ovs-vsctl del-br ', self.dp ) self.cmd( 'ovs-vsctl del-br ', self.dp )
self.cmd( 'ovs-vsctl add-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( 'ovs-vsctl add-port', self.dp, intf )
self.cmd( 'ifconfig', intf, 'up' ) self.cmd( 'ifconfig', intf, 'up' )
# Add controllers # 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 ) self.cmd( 'ovs-vsctl set-controller', self.dp, clist )
def stop( self ): def stop( self ):
+3 -3
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.classes.graph import Graph
from networkx import Graph from networkx import Graph
from util import netParse, ipStr from mininet.util import netParse, ipStr
class NodeID(object): class NodeID(object):
'''Topo node identifier.''' '''Topo node identifier.'''
@@ -116,7 +116,7 @@ class Topo(object):
per-node/link classes and parameters per-node/link classes and parameters
per-topo classes per-topo classes
per-network classes""" per-network classes"""
def __init__(self, node=None, switch=None, link=None ): def __init__(self, node=None, switch=None, link=None ):
"""Create Topo object. """Create Topo object.
node: default node/host class (optional) node: default node/host class (optional)
@@ -364,7 +364,7 @@ class Topo(object):
# BL: may wish to rethink this or just use dicts.. # BL: may wish to rethink this or just use dicts..
return self.node_info[ dpid ] return self.node_info[ dpid ]
class SingleSwitchTopo(Topo): class SingleSwitchTopo(Topo):
'''Single switch connected to k hosts.''' '''Single switch connected to k hosts.'''
+14 -10
View File
@@ -47,10 +47,11 @@ def oldQuietRun( *cmd ):
break break
return out return out
# This is a bit complicated, but it enables us to # This is a bit complicated, but it enables us to
# monitor commount output as it is happening # monitor commount output as it is happening
def errRun( *cmd, **kwargs ): def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code """Run a command and return stdout, stderr and return code
cmd: string or list of command and args cmd: string or list of command and args
stderr: STDOUT to merge stderr with stdout stderr: STDOUT to merge stderr with stdout
@@ -80,7 +81,10 @@ def errRun( *cmd, **kwargs ):
poller.register( popen.stderr, POLLIN ) poller.register( popen.stderr, POLLIN )
while True: while True:
readable = poller.poll() readable = poller.poll()
# Tell pylint to ignore unused variable event
# pylint: disable-msg=W0612
for fd, event in readable: for fd, event in readable:
# pylint: enable-msg=W0612
f = fdtofile[ fd ] f = fdtofile[ fd ]
data = f.read( 1024 ) data = f.read( 1024 )
if echo: if echo:
@@ -91,7 +95,7 @@ def errRun( *cmd, **kwargs ):
err += data err += data
returncode = popen.poll() returncode = popen.poll()
if returncode is not None: if returncode is not None:
break break
return out, err, returncode return out, err, returncode
def errFail( *cmd, **kwargs ): def errFail( *cmd, **kwargs ):
@@ -186,13 +190,13 @@ def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
# IP and Mac address formatting and parsing # IP and Mac address formatting and parsing
def _colonHex( val, bytes ): def _colonHex( val, bytecount ):
"""Generate colon-hex string. """Generate colon-hex string.
val: input as unsigned int val: input as unsigned int
bytes: number of bytes to convert bytescount: number of bytes to convert
returns: chStr colon-hex string""" returns: chStr colon-hex string"""
pieces = [] pieces = []
for i in range( bytes - 1, -1, -1 ): for i in range( bytecount - 1, -1, -1 ):
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 ) piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
pieces.append( '%02x' % piece ) pieces.append( '%02x' % piece )
chStr = ':'.join( pieces ) chStr = ':'.join( pieces )
@@ -204,14 +208,14 @@ def macColonHex( mac ):
returns: macStr MAC colon-hex string""" returns: macStr MAC colon-hex string"""
return _colonHex( mac, 6 ) return _colonHex( mac, 6 )
def ipStr( ip, defaultNet=10 ): def ipStr( ip ):
"""Generate IP address string from an unsigned int. """Generate IP address string from an unsigned int.
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z 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""" returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
w = ( ip >> 24 ) & 0xff w = ( ip >> 24 ) & 0xff
w = 10 if w == 0 else w w = 10 if w == 0 else w
x = ( ip >> 16 ) & 0xff x = ( ip >> 16 ) & 0xff
y = ( ip >> 8 ) & 0xff y = ( ip >> 8 ) & 0xff
z = ip & 0xff z = ip & 0xff
return "%i.%i.%i.%i" % ( w, x, y, z ) return "%i.%i.%i.%i" % ( w, x, y, z )
@@ -270,6 +274,7 @@ def fixLimits():
def natural( text ): def natural( text ):
"To sort sanely/alphabetically: sorted( l, key=natural )" "To sort sanely/alphabetically: sorted( l, key=natural )"
def num( s ): def num( s ):
"Convert text segment to int if necessary"
return int( s ) if s.isdigit() else text return int( s ) if s.isdigit() else text
return [ num( s ) for s in re.split( r'(\d+)', text ) ] return [ num( s ) for s in re.split( r'(\d+)', text ) ]
@@ -286,8 +291,7 @@ def numCores():
def custom( cls, **params ): def custom( cls, **params ):
"Returns customized constructor for class cls." "Returns customized constructor for class cls."
def customized( *args, **kwargs): def customized( *args, **kwargs):
"Customized constructor"
kwargs.update( params ) kwargs.update( params )
return cls( *args, **kwargs ) return cls( *args, **kwargs )
return customized return customized