Fix codecheck and MininetWithControlNet.
This commit is contained in:
@@ -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
|
||||
@@ -68,7 +70,7 @@ HOSTS = { 'proc': Host,
|
||||
CONTROLLERDEF = 'ref'
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'none': lambda name: None }
|
||||
|
||||
@@ -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' )
|
||||
|
||||
@@ -241,7 +243,7 @@ class MininetRunner( object ):
|
||||
start = time.time()
|
||||
|
||||
topo = buildTopo( self.options.topo )
|
||||
switch = customNode( SWITCHES, self.options.switch )
|
||||
switch = customNode( SWITCHES, self.options.switch )
|
||||
host = customNode( HOSTS, self.options.host )
|
||||
controller = customNode( CONTROLLERS, self.options.controller )
|
||||
intf = customNode( INTFS, self.options.intf )
|
||||
@@ -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,13 +260,13 @@ class MininetRunner( object ):
|
||||
listenPort = None
|
||||
if not self.options.nolistenport:
|
||||
listenPort = self.options.listenport
|
||||
mn = Mininet( topo=topo,
|
||||
switch=switch, host=host, controller=controller,
|
||||
intf=intf,
|
||||
ipBase=ipBase,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, listenPort=listenPort )
|
||||
mn = Net( topo=topo,
|
||||
switch=switch, host=host, controller=controller,
|
||||
intf=intf,
|
||||
ipBase=ipBase,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, listenPort=listenPort )
|
||||
|
||||
if self.options.pre:
|
||||
CLI( mn, script=self.options.pre )
|
||||
|
||||
+4
-12
@@ -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.]"
|
||||
|
||||
+5
-2
@@ -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,8 +27,9 @@ 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):
|
||||
sleep( 1 )
|
||||
# It's a shame that this is what pylint prefers
|
||||
for _ in range( 5 ):
|
||||
sleep( 1 )
|
||||
print quietRun( cmd ).strip()
|
||||
h1.cmd( 'kill %1')
|
||||
h2.cmd( 'kill %1')
|
||||
|
||||
+7
-18
@@ -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
|
||||
|
||||
@@ -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
@@ -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."
|
||||
|
||||
+133
-91
@@ -41,30 +41,35 @@ 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
|
||||
self.ip, self.prefixLen = ipstr.split( '/' )
|
||||
return self.ifconfig( ipstr, 'up' )
|
||||
# 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.
|
||||
macstr: MAC address as string"""
|
||||
self.mac = macstr
|
||||
return ( self.ifconfig( 'down' ) +
|
||||
return ( self.ifconfig( 'down' ) +
|
||||
self.ifconfig( 'hw', 'ether', macstr ) +
|
||||
self.ifconfig( 'up' ) )
|
||||
|
||||
@@ -78,13 +83,13 @@ 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 )
|
||||
self.mac = macs[ 0 ] if macs else None
|
||||
return self.mac
|
||||
|
||||
|
||||
def IP( self ):
|
||||
"Return IP address"
|
||||
return self.ip
|
||||
@@ -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()
|
||||
|
||||
@@ -124,8 +129,8 @@ class Intf( object ):
|
||||
results[ name ] = result
|
||||
return result
|
||||
|
||||
def config( self, mac=None, ip=None, ifconfig=None,
|
||||
defaultRoute=None, up=True, **params):
|
||||
def config( self, mac=None, ip=None, ifconfig=None,
|
||||
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,106 +243,58 @@ class TCIntf( Intf ):
|
||||
|
||||
result = Intf.config( self, **params)
|
||||
|
||||
# disable GRO
|
||||
# Disable GRO
|
||||
if disable_gro:
|
||||
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 ):
|
||||
return
|
||||
|
||||
if bw and ( bw < 0 or bw > 1000 ):
|
||||
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
|
||||
return
|
||||
|
||||
if delay and delay < 0:
|
||||
error( 'Negative delay', delay, '\n' )
|
||||
return
|
||||
# Clear existing configuration
|
||||
cmds = [ '%s qdisc del dev %s root' ]
|
||||
|
||||
if loss and ( loss < 0 or loss > 100 ):
|
||||
error( 'Bad loss percentage', loss, '%%\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
|
||||
|
||||
# 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 [] ) +
|
||||
( [ '%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
|
||||
|
||||
|
||||
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 ):
|
||||
|
||||
+22
-18
@@ -104,7 +104,7 @@ class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
|
||||
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
|
||||
controller=Controller, link=Link, intf=None,
|
||||
controller=Controller, link=Link, intf=None,
|
||||
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||
inNamespace=False,
|
||||
autoSetMacs=False, autoStaticArp=False, listenPort=None ):
|
||||
@@ -176,7 +176,7 @@ class Mininet( object ):
|
||||
switch: custom switch constructor (optional)
|
||||
returns: added switch
|
||||
side effect: increments listenPort ivar ."""
|
||||
defaults = { 'listenPort': self.listenPort,
|
||||
defaults = { 'listenPort': self.listenPort,
|
||||
'inNamespace': self.inNamespace }
|
||||
defaults.update( params )
|
||||
if not switch:
|
||||
@@ -229,7 +229,7 @@ class Mininet( object ):
|
||||
ipBaseNum=ipBaseNum,
|
||||
prefixLen=prefixLen ) }
|
||||
if self.autoSetMacs:
|
||||
defaults[ 'mac'] = macColonHex( nodeId )
|
||||
defaults[ 'mac'] = macColonHex( nodeId )
|
||||
defaults.update( ni.params )
|
||||
node = addMethod( name, cls=ni.cls, **defaults )
|
||||
self.idToNode[ nodeId ] = node
|
||||
@@ -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()
|
||||
@@ -533,7 +532,7 @@ class Mininet( object ):
|
||||
return result
|
||||
|
||||
inited = False
|
||||
|
||||
|
||||
@classmethod
|
||||
def init( cls ):
|
||||
"Initialize Mininet"
|
||||
@@ -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' )
|
||||
|
||||
|
||||
|
||||
+68
-52
@@ -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 ):
|
||||
@@ -129,7 +113,7 @@ class Node( object ):
|
||||
error( "%s: shell is already running" )
|
||||
return
|
||||
# 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'
|
||||
if self.inNamespace:
|
||||
opts += 'n'
|
||||
@@ -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 ):
|
||||
@@ -307,7 +295,7 @@ class Node( object ):
|
||||
self.ports[ intf ] = port
|
||||
self.nameToIntf[ intf.name ] = intf
|
||||
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:
|
||||
debug( 'moving', intf, 'into namespace for', self.name, '\n' )
|
||||
moveIntf( intf.name, 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.
|
||||
@@ -430,8 +418,8 @@ class Node( object ):
|
||||
results[ name ] = result
|
||||
return result
|
||||
|
||||
def config( self, mac=None, ip=None, ifconfig=None,
|
||||
defaultRoute=None, **params):
|
||||
def config( self, mac=None, ip=None, ifconfig=None,
|
||||
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 ]
|
||||
@@ -544,7 +551,7 @@ class CPULimitedHost( Host ):
|
||||
return pstr, qstr, period, quota
|
||||
|
||||
# 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"
|
||||
# units the way link bandwidth is specified.
|
||||
# We should use MIPS or SPECINT or something instead.
|
||||
@@ -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 )
|
||||
@@ -684,7 +691,7 @@ class UserSwitch( Switch ):
|
||||
if self.inNamespace:
|
||||
intfs = intfs[ :-1 ]
|
||||
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
|
||||
' punix:/tmp/' + self.name + ' -d ' + self.dpid +
|
||||
' punix:/tmp/' + self.name + ' -d ' + self.dpid +
|
||||
' --no-slicing ' +
|
||||
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
|
||||
@@ -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)')
|
||||
@@ -741,7 +748,7 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
controller = controllers[ 0 ]
|
||||
self.cmd( 'ovs-openflowd ' + self.dp +
|
||||
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --datapath-id=' + self.dpid +
|
||||
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||
self.execed = False
|
||||
@@ -766,26 +773,34 @@ class OVSSwitch( Switch ):
|
||||
# dpid, which is a 64-bit numerical value used by
|
||||
# the openflow protocol.
|
||||
self.dp = name
|
||||
|
||||
@staticmethod
|
||||
def setup():
|
||||
if self.inNamespace:
|
||||
error( "OVSSwitch currently only works"
|
||||
" in the root namespace.\n" )
|
||||
exit( 1 )
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Make sure Open vSwitch is installed and working"
|
||||
pathCheck( 'ovs-vsctl',
|
||||
pathCheck( 'ovs-vsctl',
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
|
||||
if exitcode:
|
||||
error( out + err +
|
||||
error( out + err +
|
||||
'ovs-vsctl exited with code %d\n' % exitcode +
|
||||
'*** Error connecting to ovs-db with ovs-vsctl\n'
|
||||
'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 ):
|
||||
|
||||
+3
-3
@@ -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.'''
|
||||
@@ -116,7 +116,7 @@ class Topo(object):
|
||||
per-node/link classes and parameters
|
||||
per-topo classes
|
||||
per-network classes"""
|
||||
|
||||
|
||||
def __init__(self, node=None, switch=None, link=None ):
|
||||
"""Create Topo object.
|
||||
node: default node/host class (optional)
|
||||
@@ -364,7 +364,7 @@ class Topo(object):
|
||||
# BL: may wish to rethink this or just use dicts..
|
||||
return self.node_info[ dpid ]
|
||||
|
||||
|
||||
|
||||
class SingleSwitchTopo(Topo):
|
||||
'''Single switch connected to k hosts.'''
|
||||
|
||||
|
||||
+14
-10
@@ -47,10 +47,11 @@ def oldQuietRun( *cmd ):
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
# This is a bit complicated, but it enables us to
|
||||
# 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
|
||||
cmd: string or list of command and args
|
||||
stderr: STDOUT to merge stderr with stdout
|
||||
@@ -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:
|
||||
@@ -91,7 +95,7 @@ def errRun( *cmd, **kwargs ):
|
||||
err += data
|
||||
returncode = popen.poll()
|
||||
if returncode is not None:
|
||||
break
|
||||
break
|
||||
return out, err, returncode
|
||||
|
||||
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
|
||||
|
||||
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,14 +208,14 @@ 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"""
|
||||
w = ( ip >> 24 ) & 0xff
|
||||
w = 10 if w == 0 else w
|
||||
x = ( ip >> 16 ) & 0xff
|
||||
y = ( ip >> 8 ) & 0xff
|
||||
x = ( ip >> 16 ) & 0xff
|
||||
y = ( ip >> 8 ) & 0xff
|
||||
z = ip & 0xff
|
||||
return "%i.%i.%i.%i" % ( w, x, y, z )
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user