Fix codecheck and MininetWithControlNet.
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user