Changed to support new cmd() interface.
It seems that it is more convenient to be able to call cmd( 'foo', 'bar', 1) for example. However, this may make it slightly less efficient, so we will see how it works.
This commit is contained in:
+65
-53
@@ -60,14 +60,16 @@ class Node( object ):
|
|||||||
outToNode = {} # mapping of output fds to nodes
|
outToNode = {} # mapping of output fds to nodes
|
||||||
|
|
||||||
def __init__( self, name, inNamespace=True,
|
def __init__( self, name, inNamespace=True,
|
||||||
defaultMAC=None, defaultIP=None ):
|
defaultMAC=None, defaultIP=None, **kwargs ):
|
||||||
"""name: name of node
|
"""name: name of node
|
||||||
inNamespace: in network namespace?
|
inNamespace: in network namespace?
|
||||||
defaultMAC: default MAC address for intf 0
|
defaultMAC: default MAC address for intf 0
|
||||||
defaultIP: default IP address for intf 0"""
|
defaultIP: default IP address for intf 0"""
|
||||||
self.name = name
|
self.name = name
|
||||||
opts = '-cdp'
|
|
||||||
self.inNamespace = inNamespace
|
self.inNamespace = inNamespace
|
||||||
|
self.defaultIP = defaultIP
|
||||||
|
self.defaultMAC = defaultMAC
|
||||||
|
opts = '-cdp'
|
||||||
if self.inNamespace:
|
if self.inNamespace:
|
||||||
opts += 'n'
|
opts += 'n'
|
||||||
cmd = [ 'mnexec', opts, 'bash', '-m' ]
|
cmd = [ 'mnexec', opts, 'bash', '-m' ]
|
||||||
@@ -75,6 +77,7 @@ class Node( object ):
|
|||||||
close_fds=False )
|
close_fds=False )
|
||||||
self.stdin = self.shell.stdin
|
self.stdin = self.shell.stdin
|
||||||
self.stdout = self.shell.stdout
|
self.stdout = self.shell.stdout
|
||||||
|
self.pid = self.shell.pid
|
||||||
self.pollOut = select.poll()
|
self.pollOut = select.poll()
|
||||||
self.pollOut.register( self.stdout )
|
self.pollOut.register( self.stdout )
|
||||||
# Maintain mapping between file descriptors and nodes
|
# Maintain mapping between file descriptors and nodes
|
||||||
@@ -82,15 +85,12 @@ class Node( object ):
|
|||||||
# using select.poll()
|
# using select.poll()
|
||||||
self.outToNode[ self.stdout.fileno() ] = self
|
self.outToNode[ self.stdout.fileno() ] = self
|
||||||
self.inToNode[ self.stdin.fileno() ] = self
|
self.inToNode[ self.stdin.fileno() ] = self
|
||||||
self.pid = self.shell.pid
|
|
||||||
self.intfs = {} # dict of port numbers to interface names
|
self.intfs = {} # dict of port numbers to interface names
|
||||||
self.ports = {} # dict of interface names to port numbers
|
self.ports = {} # dict of interface names to port numbers
|
||||||
# replace with Port objects, eventually ?
|
# 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.execed = False
|
self.execed = False
|
||||||
self.defaultIP = defaultIP
|
|
||||||
self.defaultMAC = defaultMAC
|
|
||||||
self.lastCmd = None
|
self.lastCmd = None
|
||||||
self.lastPid = None
|
self.lastPid = None
|
||||||
self.readbuf = ''
|
self.readbuf = ''
|
||||||
@@ -155,11 +155,16 @@ class Node( object ):
|
|||||||
if len( self.readbuf ) == 0:
|
if len( self.readbuf ) == 0:
|
||||||
self.pollOut.poll( timeoutms )
|
self.pollOut.poll( timeoutms )
|
||||||
|
|
||||||
def sendCmd( self, cmd, printPid=True ):
|
def sendCmd( self, *args, **kwargs ):
|
||||||
"""Send a command, followed by a command to echo a sentinel,
|
"""Send a command, followed by a command to echo a sentinel,
|
||||||
and return without waiting for the command to complete."""
|
and return without waiting for the command to complete.
|
||||||
|
args: command and arguments, or string
|
||||||
|
printPid: print command's PID?"""
|
||||||
assert not self.waiting
|
assert not self.waiting
|
||||||
if isinstance( cmd, list ):
|
printPid = kwargs.get( 'printPid', True )
|
||||||
|
if len( args ) > 0:
|
||||||
|
cmd = args
|
||||||
|
if not isinstance( cmd, str ):
|
||||||
cmd = ' '.join( cmd )
|
cmd = ' '.join( cmd )
|
||||||
if not re.search( r'\w', cmd ):
|
if not re.search( r'\w', cmd ):
|
||||||
# Replace empty commands with something harmless
|
# Replace empty commands with something harmless
|
||||||
@@ -221,18 +226,19 @@ class Node( object ):
|
|||||||
log( data )
|
log( data )
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def cmd( self, cmd, verbose=False ):
|
def cmd( self, *args, **kwargs ):
|
||||||
"""Send a command, wait for output, and return it.
|
"""Send a command, wait for output, and return it.
|
||||||
cmd: string"""
|
cmd: string"""
|
||||||
|
verbose = kwargs.get( 'verbose', False )
|
||||||
log = info if verbose else debug
|
log = info if verbose else debug
|
||||||
log( '*** %s : %s\n' % ( self.name, cmd ) )
|
log( '*** %s : %s\n' % ( self.name, args ) )
|
||||||
self.sendCmd( cmd )
|
self.sendCmd( *args, **kwargs )
|
||||||
return self.waitOutput( verbose )
|
return self.waitOutput( verbose )
|
||||||
|
|
||||||
def cmdPrint( self, cmd ):
|
def cmdPrint( self, *args):
|
||||||
"""Call cmd and printing its output
|
"""Call cmd and printing its output
|
||||||
cmd: string"""
|
cmd: string"""
|
||||||
return self.cmd( cmd, verbose=True )
|
return self.cmd( *args, **{ 'verbose': True } )
|
||||||
|
|
||||||
# Interface management, configuration, and routing
|
# Interface management, configuration, and routing
|
||||||
|
|
||||||
@@ -252,10 +258,12 @@ class Node( object ):
|
|||||||
return max( self.ports.values() ) + 1
|
return max( self.ports.values() ) + 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def addIntf( self, intf, port ):
|
def addIntf( self, intf, port=None ):
|
||||||
"""Add an interface.
|
"""Add an interface.
|
||||||
intf: interface name (nodeN-ethM)
|
intf: interface name (e.g. nodeN-ethM)
|
||||||
port: port number (typically OpenFlow port number)"""
|
port: port number (optional, typically OpenFlow port number)"""
|
||||||
|
if port is None:
|
||||||
|
port = self.newPort()
|
||||||
self.intfs[ port ] = intf
|
self.intfs[ port ] = intf
|
||||||
self.ports[ intf ] = port
|
self.ports[ intf ] = port
|
||||||
#info( '\n' )
|
#info( '\n' )
|
||||||
@@ -293,7 +301,7 @@ class Node( object ):
|
|||||||
if port1 is None:
|
if port1 is None:
|
||||||
port1 = node1.newPort()
|
port1 = node1.newPort()
|
||||||
if port2 is None:
|
if port2 is None:
|
||||||
port2 = node2.newPort()
|
port2 = node2.newPort()
|
||||||
intf1 = node1.intfName( port1 )
|
intf1 = node1.intfName( port1 )
|
||||||
intf2 = node2.intfName( port2 )
|
intf2 = node2.intfName( port2 )
|
||||||
makeIntfPair( intf1, intf2 )
|
makeIntfPair( intf1, intf2 )
|
||||||
@@ -319,16 +327,16 @@ class Node( object ):
|
|||||||
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"""
|
||||||
result = self.cmd( [ 'ifconfig', intf, 'down' ] )
|
result = self.cmd( 'ifconfig', intf, 'down' )
|
||||||
result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
|
result += self.cmd( 'ifconfig', intf, 'hw', 'ether', mac )
|
||||||
result += self.cmd( [ 'ifconfig', intf, 'up' ] )
|
result += self.cmd( 'ifconfig', intf, 'up' )
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def setARP( self, ip, mac ):
|
def setARP( self, ip, mac ):
|
||||||
"""Add an ARP entry.
|
"""Add an ARP entry.
|
||||||
ip: IP address as string
|
ip: IP address as string
|
||||||
mac: MAC address as string"""
|
mac: MAC address as string"""
|
||||||
result = self.cmd( [ 'arp', '-s', ip, mac ] )
|
result = self.cmd( 'arp', '-s', ip, mac )
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def setIP( self, intf, ip, prefixLen=8 ):
|
def setIP( self, intf, ip, prefixLen=8 ):
|
||||||
@@ -337,7 +345,7 @@ class Node( object ):
|
|||||||
ip: IP address as a string
|
ip: IP address as a string
|
||||||
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
|
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
|
||||||
ipSub = '%s/%d' % ( ip, prefixLen )
|
ipSub = '%s/%d' % ( ip, prefixLen )
|
||||||
result = self.cmd( [ 'ifconfig', intf, ipSub, 'up' ] )
|
result = self.cmd( 'ifconfig', intf, ipSub, 'up' )
|
||||||
self.ips[ intf ] = ip
|
self.ips[ intf ] = ip
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -391,11 +399,16 @@ class Switch( Node ):
|
|||||||
"""A Switch is a Node that is running (or has execed?)
|
"""A Switch is a Node that is running (or has execed?)
|
||||||
an OpenFlow switch."""
|
an OpenFlow switch."""
|
||||||
|
|
||||||
def sendCmd( self, cmd, printPid=False):
|
def __init__( self, name, opts='', **kwargs):
|
||||||
|
Node.__init__( self, name, **kwargs )
|
||||||
|
self.opts = opts
|
||||||
|
|
||||||
|
def sendCmd( self, *cmd, **kwargs ):
|
||||||
"""Send command to Node.
|
"""Send command to Node.
|
||||||
cmd: string"""
|
cmd: string"""
|
||||||
|
kwargs.setdefault( 'printPid', False )
|
||||||
if not self.execed:
|
if not self.execed:
|
||||||
return Node.sendCmd( self, cmd, printPid )
|
return Node.sendCmd( self, *cmd, **kwargs )
|
||||||
else:
|
else:
|
||||||
error( '*** Error: %s has execed and cannot accept commands' %
|
error( '*** Error: %s has execed and cannot accept commands' %
|
||||||
self.name )
|
self.name )
|
||||||
@@ -433,10 +446,10 @@ 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 +
|
' punix:/tmp/' + self.name +
|
||||||
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||||
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
|
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
|
||||||
' tcp:' + controller.IP() + ' --fail=closed' +
|
' tcp:' + controller.IP() + ' --fail=closed ' + self.opts +
|
||||||
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||||
|
|
||||||
def stop( self ):
|
def stop( self ):
|
||||||
@@ -448,14 +461,16 @@ class UserSwitch( Switch ):
|
|||||||
class KernelSwitch( Switch ):
|
class KernelSwitch( Switch ):
|
||||||
"""Kernel-space switch.
|
"""Kernel-space switch.
|
||||||
Currently only works in root namespace."""
|
Currently only works in root namespace."""
|
||||||
|
|
||||||
def __init__( self, name, dp=None, **kwargs ):
|
def __init__( self, name, dp=None, **kwargs ):
|
||||||
"""Init.
|
"""Init.
|
||||||
name: name for switch
|
name: name for switch
|
||||||
dp: netlink id (0, 1, 2, ...)
|
dp: netlink id (0, 1, 2, ...)
|
||||||
defaultMAC: default MAC as string; random value if None"""
|
defaultMAC: default MAC as string; random value if None"""
|
||||||
Switch.__init__( self, name, **kwargs )
|
Switch.__init__( self, name, **kwargs )
|
||||||
self.dp = dp
|
print kwargs, "opts=", self.opts
|
||||||
|
self.dp = 'nl:%i' % dp
|
||||||
|
self.intf = 'of%i' % dp
|
||||||
if self.inNamespace:
|
if self.inNamespace:
|
||||||
error( "KernelSwitch currently only works"
|
error( "KernelSwitch currently only works"
|
||||||
" in the root namespace." )
|
" in the root namespace." )
|
||||||
@@ -472,28 +487,26 @@ class KernelSwitch( Switch ):
|
|||||||
quietRun( 'ifconfig lo up' )
|
quietRun( 'ifconfig lo up' )
|
||||||
# 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 ' + self.dp )
|
||||||
self.cmd( 'dpctl adddp nl:%i' % self.dp )
|
self.cmd( 'dpctl adddp ' + self.dp )
|
||||||
if self.defaultMAC:
|
if self.defaultMAC:
|
||||||
intf = 'of%i' % self.dp
|
self.cmd( 'ifconfig', self.intf, 'hw', 'ether', self.defaultMAC )
|
||||||
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMAC ] )
|
|
||||||
if len( self.intfs ) != max( self.intfs ) + 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.intfs )
|
'supported: %s' % self.intfs )
|
||||||
intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
|
intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
|
||||||
self.cmd( 'dpctl addif nl:' + str( self.dp ) + ' ' +
|
self.cmd( 'dpctl', 'addif', self.dp, ' '.join( intfs ) )
|
||||||
' '.join( intfs ) )
|
|
||||||
# Run protocol daemon
|
# Run protocol daemon
|
||||||
controller = controllers[ 0 ]
|
controller = controllers[ 0 ]
|
||||||
self.cmd( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
|
self.cmdPrint( 'ofprotocol ' + self.dp +
|
||||||
controller.IP() + ':' +
|
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
|
||||||
str( controller.port ) +
|
' --fail=closed ' + self.opts +
|
||||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||||
self.execed = False
|
self.execed = False
|
||||||
|
|
||||||
def stop( self ):
|
def stop( self ):
|
||||||
"Terminate kernel datapath."
|
"Terminate kernel datapath."
|
||||||
quietRun( 'dpctl deldp nl:%i' % self.dp )
|
quietRun( 'dpctl deldp ' + self.dp )
|
||||||
self.cmd( 'kill %ofprotocol' )
|
self.cmd( 'kill %ofprotocol' )
|
||||||
self.deleteIntfs()
|
self.deleteIntfs()
|
||||||
|
|
||||||
@@ -508,7 +521,8 @@ class OVSKernelSwitch( Switch ):
|
|||||||
dp: netlink id (0, 1, 2, ...)
|
dp: netlink id (0, 1, 2, ...)
|
||||||
defaultMAC: default MAC as unsigned int; random value if None"""
|
defaultMAC: default MAC as unsigned int; random value if None"""
|
||||||
Switch.__init__( self, name, **kwargs )
|
Switch.__init__( self, name, **kwargs )
|
||||||
self.dp = dp
|
self.dp = 'dp%i' % dp
|
||||||
|
self.intf = self.dp
|
||||||
if self.inNamespace:
|
if self.inNamespace:
|
||||||
error( "OVSKernelSwitch currently only works"
|
error( "OVSKernelSwitch currently only works"
|
||||||
" in the root namespace." )
|
" in the root namespace." )
|
||||||
@@ -525,29 +539,27 @@ class OVSKernelSwitch( Switch ):
|
|||||||
quietRun( 'ifconfig lo up' )
|
quietRun( 'ifconfig lo up' )
|
||||||
# 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 ' + self.dp )
|
||||||
self.cmd( 'ovs-dpctl add-dp dp%i' % self.dp )
|
self.cmd( 'ovs-dpctl add-dp ' + self.dp )
|
||||||
if self.defaultMAC:
|
if self.defaultMAC:
|
||||||
intf = 'dp%i' % self.dp
|
|
||||||
mac = self.defaultMAC
|
mac = self.defaultMAC
|
||||||
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
|
self.cmd( 'ifconfig', self.intf, 'hw', 'ether', mac )
|
||||||
|
|
||||||
if len( self.intfs ) != max( self.intfs ) + 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.intfs )
|
'supported: %s' % self.intfs )
|
||||||
intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
|
intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
|
||||||
self.cmd( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
|
self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
|
||||||
' '.join( intfs ) )
|
|
||||||
# Run protocol daemon
|
# Run protocol daemon
|
||||||
controller = controllers[ 0 ]
|
controller = controllers[ 0 ]
|
||||||
self.cmd( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
|
self.cmd( 'ovs-openflowd ' + self.dp +
|
||||||
controller.IP() + ':' +
|
' tcp:%s:%i' % ( controller.IP(), controller.port ) +
|
||||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
' --fail=closed ' + self.opts +
|
||||||
|
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||||
self.execed = False
|
self.execed = False
|
||||||
|
|
||||||
def stop( self ):
|
def stop( self ):
|
||||||
"Terminate kernel datapath."
|
"Terminate kernel datapath."
|
||||||
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
|
quietRun( 'ovs-dpctl del-dp ' + self.dp )
|
||||||
self.cmd( 'kill %ovs-openflowd' )
|
self.cmd( 'kill %ovs-openflowd' )
|
||||||
self.deleteIntfs()
|
self.deleteIntfs()
|
||||||
|
|
||||||
@@ -573,7 +585,7 @@ class Controller( Node ):
|
|||||||
if self.cdir is not None:
|
if self.cdir is not None:
|
||||||
self.cmd( 'cd ' + self.cdir )
|
self.cmd( 'cd ' + self.cdir )
|
||||||
self.cmd( self.controller + ' ' + self.cargs +
|
self.cmd( self.controller + ' ' + self.cargs +
|
||||||
' 1> ' + cout + ' 2> ' + cout + ' &' )
|
' 1>' + cout + ' 2>' + cout + '&' )
|
||||||
self.execed = False
|
self.execed = False
|
||||||
|
|
||||||
def stop( self ):
|
def stop( self ):
|
||||||
@@ -619,7 +631,7 @@ class NOX( Controller ):
|
|||||||
controller=noxCoreDir + '/nox_core',
|
controller=noxCoreDir + '/nox_core',
|
||||||
cargs='--libdir=/usr/local/lib -v -i ptcp: ' +
|
cargs='--libdir=/usr/local/lib -v -i ptcp: ' +
|
||||||
' '.join( noxArgs ),
|
' '.join( noxArgs ),
|
||||||
cdir = noxCoreDir, **kwargs )
|
cdir=noxCoreDir, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
class RemoteController( Controller ):
|
class RemoteController( Controller ):
|
||||||
|
|||||||
Reference in New Issue
Block a user