Moved link creation into Node.addIntf() and fixed util:CreateLink().

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.
This commit is contained in:
Bob Lantz
2010-03-03 13:52:50 -08:00
parent 04fa18dc61
commit efc9a01cf1
4 changed files with 138 additions and 128 deletions
+41 -27
View File
@@ -6,35 +6,49 @@ This is more complicated than using the higher-level classes,
but it exposes the configuration details and allows customization. but it exposes the configuration details and allows customization.
""" """
from mininet.mininet import init, Node, createLink import logging
from mininet.net import init
from mininet.node import Node
from mininet.util import createLink
from mininet.log import info
# print out info() messages, including cmdPrint
logging.LOGLEVELDEFAULT = logging.INFO
def scratchNet( cname='controller', cargs='ptcp:'): def scratchNet( cname='controller', cargs='ptcp:'):
# Create Network # Create Network
controller = Node( 'c0', inNamespace=False ) print "*** creating Nodes"
switch = Node( 's0', inNamespace=False ) controller = Node( 'c0', inNamespace=False )
h0 = Node( 'h0' ) switch = Node( 's0', inNamespace=False )
h1 = Node( 'h1' ) h0 = Node( 'h0' )
createLink( h0, switch ) h1 = Node( 'h1' )
createLink( h1, switch ) print "*** creating links"
# Configure hosts createLink( node1=h0, port1=0, node2=switch, port2=0 )
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', '/24' ) createLink( node1=h1, port1=0, node2=switch, port2=1 )
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', '/24' ) # Configure hosts
# Start network using kernel datapath print "*** configuring hosts"
controller.cmdPrint( cname + ' ' + cargs + '&' ) h0.setIP( h0.intfs[ 0 ], '192.168.123.1', '/24' )
switch.cmdPrint( 'dpctl deldp nl:0' ) h1.setIP( h1.intfs[ 0 ], '192.168.123.2', '/24' )
switch.cmdPrint( 'dpctl adddp nl:0' ) print h0
for intf in switch.intfs: print h1
# Start network using kernel datapath
controller.cmdPrint( cname + ' ' + cargs + '&' )
switch.cmdPrint( 'dpctl deldp nl:0' )
switch.cmdPrint( 'dpctl adddp nl:0' )
for intf in switch.intfs.values():
switch.cmdPrint( 'dpctl addif nl:0 ' + intf ) switch.cmdPrint( 'dpctl addif nl:0 ' + intf )
switch.cmdPrint( 'ofprotocol nl:0 tcp:localhost &') switch.cmdPrint( 'ofprotocol nl:0 tcp:localhost &')
# Run test # Run test
h0.cmdPrint( 'ping -c1 ' + h1.IP() ) print h0.cmd( 'ping -c1 ' + h1.IP() )
# Stop network # Stop network
controller.cmdPrint( 'kill %' + cname) controller.cmdPrint( 'kill %' + cname)
switch.cmdPrint( 'dpctl deldp nl:0' ) switch.cmdPrint( 'dpctl deldp nl:0' )
switch.cmdPrint( 'kill %ofprotocol' ) switch.cmdPrint( 'kill %ofprotocol' )
switch.stop() switch.stop()
controller.stop() controller.stop()
if __name__ == '__main__': if __name__ == '__main__':
init() info( '*** Scratch network demo\n' )
scratchNet() init()
scratchNet()
+11 -40
View File
@@ -81,10 +81,10 @@ import signal
from time import sleep from time import sleep
from mininet.cli import CLI from mininet.cli import CLI
from mininet.log import info, error from mininet.log import info, error, debug
from mininet.node import KernelSwitch, OVSKernelSwitch from mininet.node import KernelSwitch, OVSKernelSwitch
from mininet.util import quietRun, fixLimits from mininet.util import quietRun, fixLimits
from mininet.util import makeIntfPair, moveIntf, macColonHex from mininet.util import createLink, macColonHex
from mininet.xterm import cleanUpScreens, makeXterms from mininet.xterm import cleanUpScreens, makeXterms
DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ] DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ]
@@ -150,8 +150,6 @@ class Mininet( object ):
defaultIp: default IP address for intf 0 defaultIp: default IP address for intf 0
returns: added host""" returns: added host"""
host = self.host( name ) host = self.host( name )
# for now, assume one interface per host.
host.intfs.append( name + '-eth0' )
self.hosts.append( host ) self.hosts.append( host )
self.nameToNode[ name ] = host self.nameToNode[ name ] = host
# May wish to add this to actual object # May wish to add this to actual object
@@ -175,31 +173,6 @@ class Mininet( object ):
self.nameToNode[ name ] = sw self.nameToNode[ name ] = sw
return sw return sw
def addLink( self, src, srcPort, dst, dstPort ):
"""Add link.
src: source Node
srcPort: source port
dst: destination Node
dstPort: destination port"""
srcIntf = src.intfName( srcPort )
dstIntf = dst.intfName( dstPort )
makeIntfPair( srcIntf, dstIntf )
src.intfs.append( srcIntf )
dst.intfs.append( dstIntf )
src.ports[ srcPort ] = srcIntf
dst.ports[ dstPort ] = dstIntf
#info( '\n' )
#info( 'added intf %s to src node %x\n' % ( srcIntf, src ) )
#info( 'added intf %s to dst node %x\n' % ( dstIntf, dst ) )
if src.inNamespace:
#info( 'moving src w/inNamespace set\n' )
moveIntf( srcIntf, src )
if dst.inNamespace:
#info( 'moving dst w/inNamespace set\n' )
moveIntf( dstIntf, dst )
src.connection[ srcIntf ] = ( dst, dstIntf )
dst.connection[ dstIntf ] = ( src, srcIntf )
def addController( self, controller ): def addController( self, controller ):
"""Add controller. """Add controller.
controller: Controller class""" controller: Controller class"""
@@ -315,7 +288,7 @@ class Mininet( object ):
for srcId, dstId in sorted( topo.edges() ): for srcId, dstId in sorted( topo.edges() ):
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ] src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
srcPort, dstPort = topo.port( srcId, dstId ) srcPort, dstPort = topo.port( srcId, dstId )
self.addLink( src, srcPort, dst, dstPort ) createLink( src, srcPort, dst, dstPort )
info( '(%s, %s) ' % ( src.name, dst.name ) ) info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' ) info( '\n' )
@@ -368,7 +341,7 @@ class Mininet( object ):
controller.start() controller.start()
info( '*** Starting %s switches\n' % len( self.switches ) ) info( '*** Starting %s switches\n' % len( self.switches ) )
for switch in self.switches: for switch in self.switches:
info( switch.name ) info( switch.name + ' ')
switch.start( self.controllers ) switch.start( self.controllers )
info( '\n' ) info( '\n' )
@@ -474,13 +447,14 @@ class Mininet( object ):
l4Type: string, one of [ TCP, UDP ] l4Type: string, one of [ TCP, UDP ]
verbose: verbose printing verbose: verbose printing
returns: results two-element array of server and client speeds""" returns: results two-element array of server and client speeds"""
log = info if verbose else debug
if not hosts: if not hosts:
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ] hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else: else:
assert len( hosts ) == 2 assert len( hosts ) == 2
host0, host1 = hosts host0, host1 = hosts
info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' ) log( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
info( "%s and %s\n" % ( host0.name, host1.name ) ) log( "%s and %s\n" % ( host0.name, host1.name ) )
host0.cmd( 'killall -9 iperf' ) host0.cmd( 'killall -9 iperf' )
iperfArgs = 'iperf ' iperfArgs = 'iperf '
bwArgs = '' bwArgs = ''
@@ -490,19 +464,16 @@ class Mininet( object ):
elif l4Type != 'TCP': elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type ) raise Exception( 'Unexpected l4 type: %s' % l4Type )
server = host0.cmd( iperfArgs + '-s &' ) server = host0.cmd( iperfArgs + '-s &' )
if verbose: log( '%s\n' % server )
info( '%s\n' % server )
client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' + client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' +
bwArgs ) bwArgs )
if verbose: log( '%s\n' % client )
info( '%s\n' % client )
server = host0.cmd( 'killall -9 iperf' ) server = host0.cmd( 'killall -9 iperf' )
if verbose: log( '%s\n' % server )
info( '%s\n' % server )
result = [ self._parseIperf( server ), self._parseIperf( client ) ] result = [ self._parseIperf( server ), self._parseIperf( client ) ]
if l4Type == 'UDP': if l4Type == 'UDP':
result.insert( 0, udpBw ) result.insert( 0, udpBw )
info( '*** Results: %s\n' % result ) log( '*** Results: %s\n' % result )
return result return result
def iperfUdp( self, udpBw='10M' ): def iperfUdp( self, udpBw='10M' ):
+71 -48
View File
@@ -40,8 +40,8 @@ import os
import signal import signal
import select import select
from mininet.log import info, error from mininet.log import info, error, debug
from mininet.util import quietRun from mininet.util import quietRun, moveIntf
class Node( object ): class Node( object ):
@@ -72,13 +72,13 @@ class Node( object ):
self.inToNode[ self.stdin.fileno() ] = self self.inToNode[ self.stdin.fileno() ] = self
self.pid = self.shell.pid self.pid = self.shell.pid
self.intfCount = 0 self.intfCount = 0
self.intfs = [] # list of interface names, as strings self.intfs = {} # dict of port numbers to interface names
self.ports = {} # dict of interface names to port numbers
# replace with Port objects, eventually ?
self.ips = {} # dict of interfaces to ip addresses as strings self.ips = {} # dict of interfaces to ip addresses as strings
self.connection = {} # remote node connected to each interface self.connection = {} # remote node connected to each interface
self.waiting = False self.waiting = False
self.execed = False self.execed = False
self.ports = {} # dict of ints to interface strings
# replace with Port object, eventually
@classmethod @classmethod
def fdToNode( cls, fd ): def fdToNode( cls, fd ):
@@ -145,11 +145,13 @@ class Node( object ):
"Send ^C, hopefully interrupting an interactive subprocess." "Send ^C, hopefully interrupting an interactive subprocess."
self.write( chr( 3 ) ) self.write( chr( 3 ) )
def waitOutput( self ): def waitOutput( self, verbose=False ):
"""Wait for a command to complete. """Wait for a command to complete.
Completion is signaled by a sentinel character, ASCII(127) Completion is signaled by a sentinel character, ASCII(127)
appearing in the output stream. Wait for the sentinel and return appearing in the output stream. Wait for the sentinel and return
the output, including trailing newline.""" the output, including trailing newline.
verbose: print output interactively"""
log = info if verbose else debug
assert self.waiting assert self.waiting
output = '' output = ''
while True: while True:
@@ -157,38 +159,60 @@ class Node( object ):
data = self.read( 1024 ) data = self.read( 1024 )
if len( data ) > 0 and data[ -1 ] == chr( 0177 ): if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
output += data[ :-1 ] output += data[ :-1 ]
log( output )
break break
else: else:
output += data output += data
self.waiting = False self.waiting = False
return output return output
def cmd( self, cmd ): def cmd( self, cmd, verbose=False ):
"""Send a command, wait for output, and return it. """Send a command, wait for output, and return it.
cmd: string""" cmd: string"""
log = info if verbose else debug
log( '*** %s : %s', self.name, cmd )
self.sendCmd( cmd ) self.sendCmd( cmd )
return self.waitOutput() return self.waitOutput( verbose )
def cmdPrint( self, cmd ): def cmdPrint( self, cmd ):
"""Call cmd and printing its output """Call cmd and printing its output
cmd: string""" cmd: string"""
#info( '*** %s : %s', self.name, cmd ) return self.cmd( cmd, verbose=True )
result = self.cmd( cmd )
#info( '%s\n', result )
return result
# Interface management, configuration, and routing # Interface management, configuration, and routing
# BL notes: This might be a bit redundant or over-complicated.
# However, it does allow a bit of specialization, including
# changing the canonical interface names. It's also tricky since
# the real interfaces are created as veth pairs, so we can't
# make a single interface at a time.
def intfName( self, n ): def intfName( self, n ):
"Construct a canonical interface name node-intf for interface N." "Construct a canonical interface name node-ethN for interface n."
return self.name + '-eth' + repr( n ) return self.name + '-eth' + repr( n )
def newIntf( self ): def newIntf( self ):
"Reserve and return a new interface name." "Reserve and return a new interface name."
intfName = self.intfName( self.intfCount ) intfName = self.intfName( self.intfCount )
self.intfCount += 1 self.intfCount += 1
self.intfs += [ intfName ]
return intfName return intfName
def addIntf( self, intf, port ):
"""Add an interface.
intf: interface name (nodeN-ethM)
port: port number (typically OpenFlow port number)"""
self.intfs[ port ] = intf
self.ports[ intf ] = port
#info( '\n' )
#info( 'added intf %s to node %x\n' % ( srcIntf, src ) )
if self.inNamespace:
#info( 'moving w/inNamespace set\n' )
moveIntf( intf, self )
def connect( self, intf, dstNode, dstIntf ):
"Register connection of intf to dstIntf on dstNode."
self.connection[ intf ] = ( dstNode, dstIntf )
def setMAC( self, intf, mac ): def setMAC( self, intf, mac ):
"""Set the MAC address for an interface. """Set the MAC address for an interface.
mac: MAC address as string""" mac: MAC address as string"""
@@ -206,9 +230,9 @@ class Node( object ):
def setIP( self, intf, ip, bits ): def setIP( self, intf, ip, bits ):
"""Set the IP address for an interface. """Set the IP address for an interface.
intf: string, interface name intf: interface name
ip: IP address as a string ip: IP address as a string
bits:""" bits: prefix length of form /24"""
result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] ) result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
self.ips[ intf ] = ip self.ips[ intf ] = ip
return result return result
@@ -226,20 +250,19 @@ class Node( object ):
return self.cmd( 'route add default ' + intf ) return self.cmd( 'route add default ' + intf )
def IP( self ): def IP( self ):
"Return IP address of first interface" "Return IP address of interface 0"
if len( self.intfs ) > 0: return self.ips.get( self.intfs.get( 0 , None ), None )
return self.ips.get( self.intfs[ 0 ], None )
def intfIsUp( self ): def intfIsUp( self, port ):
"Check if one of our interfaces is up." """Check if interface for a given port number is up.
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] ) port: port number"""
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ port ] )
# Other methods # Other methods
def __str__( self ): def __str__( self ):
result = self.name + ':' result = self.name + ':'
if self.IP(): result += ' IP=' + repr( self.IP() )
result += ' IP=' + self.IP() result += ' intfs=' + ','.join( sorted( self.intfs.values() ) )
result += ' intfs=' + ','.join( self.intfs )
result += ' waiting=' + repr( self.waiting ) result += ' waiting=' + repr( self.waiting )
return result return result
@@ -282,17 +305,17 @@ class UserSwitch( Switch ):
def start( self, controllers ): def start( self, controllers ):
"""Start OpenFlow reference user datapath. """Start OpenFlow reference user datapath.
Log to /tmp/sN-{ofd,ofp}.log. Log to /tmp/sN-{ofd,ofp}.log.
controllers: dict of controller names to objects""" controllers: list of controller objects"""
controller = controllers[ 0 ] controller = controllers[ 0 ]
ofdlog = '/tmp/' + self.name + '-ofd.log' ofdlog = '/tmp/' + self.name + '-ofd.log'
ofplog = '/tmp/' + self.name + '-ofp.log' ofplog = '/tmp/' + self.name + '-ofp.log'
self.cmd( 'ifconfig lo up' ) self.cmd( 'ifconfig lo up' )
intfs = self.intfs intfs = sorted( self.intfs.values() )
self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) + self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
' punix:/tmp/' + self.name + ' punix:/tmp/' + self.name +
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' ) ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name + self.cmd( 'ofprotocol unix:/tmp/' + self.name +
' tcp:' + controller.IP() + ' --fail=closed' + ' tcp:' + controller.IP() + ' --fail=closed' +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' ) ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
@@ -322,20 +345,20 @@ class KernelSwitch( Switch ):
# Delete local datapath if it exists; # Delete local datapath if it exists;
# then create a new one monitoring the given interfaces # then create a new one monitoring the given interfaces
quietRun( 'dpctl deldp nl:%i' % self.dp ) quietRun( 'dpctl deldp nl:%i' % self.dp )
self.cmdPrint( 'dpctl adddp nl:%i' % self.dp ) self.cmd( 'dpctl adddp nl:%i' % self.dp )
if self.defaultMac: if self.defaultMac:
intf = 'of%i' % self.dp intf = 'of%i' % self.dp
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] ) self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] )
if len( self.ports ) != max( self.ports.keys() ) + 1: if len( self.intfs ) != max( self.intfs ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges' raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports ) 'supported: %s' % self.intfs )
intfs = [ self.ports[ port ] for port in self.ports.keys() ] intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' + self.cmd( 'dpctl addif nl:' + str( self.dp ) + ' ' +
' '.join( intfs ) ) ' '.join( intfs ) )
# Run protocol daemon # Run protocol daemon
controller = controllers[ 0 ] controller = controllers[ 0 ]
self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' + self.cmd( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
controller.IP() + ':' + controller.IP() + ':' +
str( controller.port ) + str( controller.port ) +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' ) ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
@@ -345,11 +368,11 @@ class KernelSwitch( Switch ):
"Terminate kernel datapath." "Terminate kernel datapath."
quietRun( 'dpctl deldp nl:%i' % self.dp ) quietRun( 'dpctl deldp nl:%i' % self.dp )
# In theory the interfaces should go away after we shut down. # In theory the interfaces should go away after we shut down.
# However, this takes time, so we're better off to remove them # However, this takes time, so we're better off removing them
# explicitly so that we won't get errors if we run before they # explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow. # have been removed by the kernel. Unfortunately this is very slow.
self.cmd( 'kill %ofprotocol' ) self.cmd( 'kill %ofprotocol' )
for intf in self.intfs: for intf in self.intfs.values():
quietRun( 'ip link del ' + intf ) quietRun( 'ip link del ' + intf )
info( '.' ) info( '.' )
@@ -374,21 +397,21 @@ class OVSKernelSwitch( Switch ):
# Delete local datapath if it exists; # Delete local datapath if it exists;
# then create a new one monitoring the given interfaces # then create a new one monitoring the given interfaces
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp ) quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp ) self.cmd( 'ovs-dpctl add-dp dp%i' % self.dp )
if self.defaultMac: if self.defaultMac:
intf = 'dp' % self.dp intf = 'dp' % self.dp
mac = self.defaultMac mac = self.defaultMac
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] ) self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
if len( self.ports ) != max( self.ports.keys() ) + 1: if len( self.intfs ) != max( self.intfs ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges' raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports ) 'supported: %s' % self.intfs )
intfs = [ self.ports[ port ] for port in self.ports.keys() ] intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' + self.cmd( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
' '.join( intfs ) ) ' '.join( intfs ) )
# Run protocol daemon # Run protocol daemon
controller = controllers[ 0 ] controller = controllers[ 0 ]
self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' + self.cmd( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
controller.IP() + ':' + controller.IP() + ':' +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' ) ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False self.execed = False
@@ -397,11 +420,11 @@ class OVSKernelSwitch( Switch ):
"Terminate kernel datapath." "Terminate kernel datapath."
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp ) quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
# In theory the interfaces should go away after we shut down. # In theory the interfaces should go away after we shut down.
# However, this takes time, so we're better off to remove them # However, this takes time, so we're better off removing them
# explicitly so that we won't get errors if we run before they # explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow. # have been removed by the kernel. Unfortunately this is very slow.
self.cmd( 'kill %ovs-openflowd' ) self.cmd( 'kill %ovs-openflowd' )
for intf in self.intfs: for intf in self.intfs.values():
quietRun( 'ip link del ' + intf ) quietRun( 'ip link del ' + intf )
info( '.' ) info( '.' )
@@ -425,8 +448,8 @@ class Controller( Node ):
Log to /tmp/cN.log""" Log to /tmp/cN.log"""
cout = '/tmp/' + self.name + '.log' cout = '/tmp/' + self.name + '.log'
if self.cdir is not None: if self.cdir is not None:
self.cmdPrint( 'cd ' + self.cdir ) self.cmd( 'cd ' + self.cdir )
self.cmdPrint( self.controller + ' ' + self.cargs + self.cmd( self.controller + ' ' + self.cargs +
' 1> ' + cout + ' 2> ' + cout + ' &' ) ' 1> ' + cout + ' 2> ' + cout + ' &' )
self.execed = False self.execed = False
+15 -13
View File
@@ -15,7 +15,7 @@ def run( cmd ):
def checkRun( cmd ): def checkRun( cmd ):
"""Simple interface to subprocess.check_call() """Simple interface to subprocess.check_call()
cmd: list of command params""" cmd: list of command params"""
check_call( cmd.split( ' ' ) ) return check_call( cmd.split( ' ' ) )
def quietRun( cmd ): def quietRun( cmd ):
"""Run a command, routing stderr to stdout, and return the output. """Run a command, routing stderr to stdout, and return the output.
@@ -63,7 +63,7 @@ def makeIntfPair( intf1, intf2 ):
quietRun( 'ip link del ' + intf2 ) quietRun( 'ip link del ' + intf2 )
# Create new pair # Create new pair
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
return checkRun( cmd ) return quietRun( cmd )
def retry( retries, delaySecs, fn, *args, **keywords ): def retry( retries, delaySecs, fn, *args, **keywords ):
"""Try something several times before giving up. """Try something several times before giving up.
@@ -101,19 +101,20 @@ def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
printError: if true, print error""" printError: if true, print error"""
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError ) retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
def createLink( node1, node2, retries=10, delaySecs=0.001 ): def createLink( node1, port1, node2, port2 ):
"""Create a link between nodes, making an interface for each. """Create a link between nodes, making an interface for each.
node1: Node object node1: Node object
node2: Node object""" port1: node1 port number
intf1 = node1.newIntf() node2: Node object
intf2 = node2.newIntf() port2: node2 port number
returns: intf1 name, intf2 name"""
intf1 = node1.intfName( port1 )
intf2 = node2.intfName( port2 )
makeIntfPair( intf1, intf2 ) makeIntfPair( intf1, intf2 )
if node1.inNamespace: node1.addIntf( intf1, port1 )
retry( retries, delaySecs, moveIntf, intf1, node1 ) node2.addIntf( intf2, port2 )
if node2.inNamespace: node1.connect( intf1, node2, intf2 )
retry( retries, delaySecs, moveIntf, intf2, node2 ) node2.connect( intf2, node1, intf1 )
node1.connection[ intf1 ] = ( node2, intf2 )
node2.connection[ intf2 ] = ( node1, intf1 )
return intf1, intf2 return intf1, intf2
def fixLimits(): def fixLimits():
@@ -141,7 +142,8 @@ def macColonHex( mac ):
def ipStr( ip ): def ipStr( ip ):
"""Generate IP address string """Generate IP address string
returns: ip addr string""" ip: unsigned int of form x << 16 | y << 8 | z
returns: ip address string 10.x.y.z """
hi = ( ip & 0xff0000 ) >> 16 hi = ( ip & 0xff0000 ) >> 16
mid = ( ip & 0xff00 ) >> 8 mid = ( ip & 0xff00 ) >> 8
lo = ip & 0xff lo = ip & 0xff