Also tweaked cmd/cmdPrint to make them more beautiful and functional, and a few other minor changes, such as using a new idiom for verbose. These changes support reinstating the examples.
151 lines
4.9 KiB
Python
151 lines
4.9 KiB
Python
"Utility functions for Mininet."
|
|
|
|
from time import sleep
|
|
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
|
import select
|
|
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
|
|
|
from mininet.log import lg
|
|
|
|
def run( cmd ):
|
|
"""Simple interface to subprocess.call()
|
|
cmd: list of command params"""
|
|
return call( cmd.split( ' ' ) )
|
|
|
|
def checkRun( cmd ):
|
|
"""Simple interface to subprocess.check_call()
|
|
cmd: list of command params"""
|
|
return check_call( cmd.split( ' ' ) )
|
|
|
|
def quietRun( cmd ):
|
|
"""Run a command, routing stderr to stdout, and return the output.
|
|
cmd: list of command params"""
|
|
if isinstance( cmd, str ):
|
|
cmd = cmd.split( ' ' )
|
|
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
|
|
# We can't use Popen.communicate() because it uses
|
|
# select(), which can't handle
|
|
# high file descriptor numbers! poll() can, however.
|
|
output = ''
|
|
readable = select.poll()
|
|
readable.register( popen.stdout )
|
|
while True:
|
|
while readable.poll():
|
|
data = popen.stdout.read( 1024 )
|
|
if len( data ) == 0:
|
|
break
|
|
output += data
|
|
popen.poll()
|
|
if popen.returncode != None:
|
|
break
|
|
return output
|
|
|
|
# Interface management
|
|
#
|
|
# Interfaces are managed as strings which are simply the
|
|
# interface names, of the form 'nodeN-ethM'.
|
|
#
|
|
# To connect nodes, we create a pair of veth interfaces, and then place them
|
|
# in the pair of nodes that we want to communicate. We then update the node's
|
|
# list of interfaces and connectivity map.
|
|
#
|
|
# For the kernel datapath, switch interfaces
|
|
# live in the root namespace and thus do not have to be
|
|
# explicitly moved.
|
|
|
|
def makeIntfPair( intf1, intf2 ):
|
|
"""Make a veth pair connecting intf1 and intf2.
|
|
intf1: string, interface
|
|
intf2: string, interface
|
|
returns: success boolean"""
|
|
# Delete any old interfaces with the same names
|
|
quietRun( 'ip link del ' + intf1 )
|
|
quietRun( 'ip link del ' + intf2 )
|
|
# Create new pair
|
|
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
|
|
return quietRun( cmd )
|
|
|
|
def retry( retries, delaySecs, fn, *args, **keywords ):
|
|
"""Try something several times before giving up.
|
|
n: number of times to retry
|
|
delaySecs: wait this long between tries
|
|
fn: function to call
|
|
args: args to apply to function call"""
|
|
tries = 0
|
|
while not fn( *args, **keywords ) and tries < retries:
|
|
sleep( delaySecs )
|
|
tries += 1
|
|
if tries >= retries:
|
|
lg.error( "*** gave up after %i retries\n" % tries )
|
|
exit( 1 )
|
|
|
|
def moveIntfNoRetry( intf, node, printError=False ):
|
|
"""Move interface to node, without retrying.
|
|
intf: string, interface
|
|
node: Node object
|
|
printError: if true, print error"""
|
|
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
|
|
quietRun( cmd )
|
|
links = node.cmd( 'ip link show' )
|
|
if not intf in links:
|
|
if printError:
|
|
lg.error( '*** Error: moveIntf: ' + intf +
|
|
' not successfully moved to ' + node.name + '\n' )
|
|
return False
|
|
return True
|
|
|
|
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
|
|
"""Move interface to node, retrying on failure.
|
|
intf: string, interface
|
|
node: Node object
|
|
printError: if true, print error"""
|
|
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
|
|
|
|
def createLink( node1, port1, node2, port2 ):
|
|
"""Create a link between nodes, making an interface for each.
|
|
node1: Node object
|
|
port1: node1 port number
|
|
node2: Node object
|
|
port2: node2 port number
|
|
returns: intf1 name, intf2 name"""
|
|
intf1 = node1.intfName( port1 )
|
|
intf2 = node2.intfName( port2 )
|
|
makeIntfPair( intf1, intf2 )
|
|
node1.addIntf( intf1, port1 )
|
|
node2.addIntf( intf2, port2 )
|
|
node1.connect( intf1, node2, intf2 )
|
|
node2.connect( intf2, node1, intf1 )
|
|
return intf1, intf2
|
|
|
|
def fixLimits():
|
|
"Fix ridiculously small resource limits."
|
|
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
|
|
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
|
|
|
|
def _colonHex( val, bytes ):
|
|
"""Generate colon-hex string.
|
|
val: input as unsigned int
|
|
bytes: number of bytes to convert
|
|
returns: chStr colon-hex string"""
|
|
pieces = []
|
|
for i in range( bytes - 1, -1, -1 ):
|
|
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
|
|
pieces.append( '%02x' % piece )
|
|
chStr = ':'.join( pieces )
|
|
return chStr
|
|
|
|
def macColonHex( mac ):
|
|
"""Generate MAC colon-hex string from unsigned int.
|
|
mac: MAC address as unsigned int
|
|
returns: macStr MAC colon-hex string"""
|
|
return _colonHex( mac, 6 )
|
|
|
|
def ipStr( ip ):
|
|
"""Generate IP address string
|
|
ip: unsigned int of form x << 16 | y << 8 | z
|
|
returns: ip address string 10.x.y.z """
|
|
hi = ( ip & 0xff0000 ) >> 16
|
|
mid = ( ip & 0xff00 ) >> 8
|
|
lo = ip & 0xff
|
|
return "10.%i.%i.%i" % ( hi, mid, lo )
|