First crack at restoring mininet python style, assisted by handy

'unpep8' script, which does most of the work.

- topo.py is still in pep8
- not all examples work, but this is due to other issues
This commit is contained in:
Bob Lantz
2010-02-05 02:33:34 -08:00
parent bebe9dbed2
commit 80a8fa62d5
9 changed files with 849 additions and 999 deletions
+3 -1
View File
@@ -8,10 +8,12 @@ TEST = mininet/test/*.py
BIN = bin/mn bin/mnclean BIN = bin/mn bin/mnclean
PYSRC = $(MININET) $(TEST) $(BIN) PYSRC = $(MININET) $(TEST) $(BIN)
P8IGN = E251,E201,E302
codecheck: $(PYSRC) codecheck: $(PYSRC)
pyflakes $(PYSRC) pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC) pylint --rcfile=.pylint $(PYSRC)
pep8 --ignore=E251 $(PYSRC) pep8 --ignore=$(P8IGN) $(PYSRC)
test: $(MININET) $(TEST) test: $(MININET) $(TEST)
mininet/test/test_nets.py mininet/test/test_nets.py
+1 -1
View File
@@ -130,7 +130,7 @@ class MininetRunner(object):
'''Setup and validate environment.''' '''Setup and validate environment.'''
# set logging verbosity # set logging verbosity
lg.set_loglevel(self.options.verbosity) lg.setLogLevel(self.options.verbosity)
# validate environment setup # validate environment setup
init() init()
+1 -1
View File
@@ -1 +1 @@
'''Docstring to silence pylint; ignores --ignore option for __init__.py''' "Docstring to silence pylint; ignores --ignore option for __init__.py"
+62 -69
View File
@@ -1,131 +1,124 @@
'''Logging functions for Mininet.''' "Logging functions for Mininet."
import logging import logging
from logging import Logger from logging import Logger
import types import types
LEVELS = {'debug': logging.DEBUG, LEVELS = { 'debug': logging.DEBUG,
'info': logging.INFO, 'info': logging.INFO,
'warning': logging.WARNING, 'warning': logging.WARNING,
'error': logging.ERROR, 'error': logging.ERROR,
'critical': logging.CRITICAL} 'critical': logging.CRITICAL }
# change this to logging.INFO to get printouts when running unit tests # change this to logging.INFO to get printouts when running unit tests
LOG_LEVEL_DEFAULT = logging.WARNING LOGLEVELDEFAULT = logging.WARNING
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' #default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
LOG_MSG_FORMAT = '%(message)s' LOGMSGFORMAT = '%(message)s'
# Modified from python2.5/__init__.py # Modified from python2.5/__init__.py
class StreamHandlerNoNewline(logging.StreamHandler): class StreamHandlerNoNewline( logging.StreamHandler ):
'''StreamHandler that doesn't print newlines by default. """StreamHandler that doesn't print newlines by default.
Since StreamHandler automatically adds newlines, define a mod to more
easily support interactive mode when we want it, or errors-only logging
for running unit tests."""
Since StreamHandler automatically adds newlines, define a mod to more def emit( self, record ):
easily support interactive mode when we want it, or errors-only logging for """Emit a record.
running unit tests. If a formatter is specified, it is used to format the record.
''' The record is then written to the stream with a trailing newline
[ N.B. this may be removed depending on feedback ]. If exception
def emit(self, record): information is present, it is formatted using
''' traceback.printException and appended to the stream."""
Emit a record.
If a formatter is specified, it is used to format the record.
The record is then written to the stream with a trailing newline
[N.B. this may be removed depending on feedback]. If exception
information is present, it is formatted using
traceback.print_exception and appended to the stream.
'''
try: try:
msg = self.format(record) msg = self.format( record )
fs = '%s' # was '%s\n' fs = '%s' # was '%s\n'
if not hasattr(types, 'UnicodeType'): #if no unicode support... if not hasattr( types, 'UnicodeType' ): #if no unicode support...
self.stream.write(fs % msg) self.stream.write( fs % msg )
else: else:
try: try:
self.stream.write(fs % msg) self.stream.write( fs % msg )
except UnicodeError: except UnicodeError:
self.stream.write(fs % msg.encode('UTF-8')) self.stream.write( fs % msg.encode( 'UTF-8' ) )
self.flush() self.flush()
except (KeyboardInterrupt, SystemExit): except ( KeyboardInterrupt, SystemExit ):
raise raise
except: except:
self.handleError(record) self.handleError( record )
class Singleton(type): class Singleton( type ):
'''Singleton pattern from Wikipedia """Singleton pattern from Wikipedia
See http://en.wikipedia.org/wiki/SingletonPattern#Python
See http://en.wikipedia.org/wiki/Singleton_pattern#Python Intended to be used as a __metaclass_ param, as shown for the class
below.
Intended to be used as a __metaclass_ param, as shown for the class below. Changed cls first args to mcs to satisfy pylint."""
Changed cls first args to mcs to satsify pylint. def __init__( mcs, name, bases, dict_ ):
''' super( Singleton, mcs ).__init__( name, bases, dict_ )
def __init__(mcs, name, bases, dict_):
super(Singleton, mcs).__init__(name, bases, dict_)
mcs.instance = None mcs.instance = None
def __call__(mcs, *args, **kw): def __call__( mcs, *args, **kw ):
if mcs.instance is None: if mcs.instance is None:
mcs.instance = super(Singleton, mcs).__call__(*args, **kw) mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
return mcs.instance return mcs.instance
class MininetLogger(Logger, object): class MininetLogger( Logger, object ):
'''Mininet-specific logger """Mininet-specific logger
Enable each mininet .py file to with one import:
Enable each mininet .py file to with one import:
from mininet.log import lg from mininet.log import lg
...get a default logger that doesn't require one newline per logging call. ...get a default logger that doesn't require one newline per logging
call.
Inherit from object to ensure that we have at least one new-style base Inherit from object to ensure that we have at least one new-style base
class, and can then use the __metaclass__ directive, to prevent this error: class, and can then use the __metaclass__ directive, to prevent this
error:
TypeError: Error when calling the metaclass bases TypeError: Error when calling the metaclass bases
a new-style class can't have only classic bases a new-style class can't have only classic bases
If Python2.5/logging/__init__.py defined Filterer as a new-style class, If Python2.5/logging/__init__.py defined Filterer as a new-style class,
via Filterer(object): rather than Filterer, we wouldn't need this. via Filterer( object ): rather than Filterer, we wouldn't need this.
Use singleton pattern to ensure only one logger is ever created."""
Use singleton pattern to ensure only one logger is ever created.
'''
__metaclass__ = Singleton __metaclass__ = Singleton
def __init__(self): def __init__( self ):
Logger.__init__(self, "mininet") Logger.__init__( self, "mininet" )
# create console handler # create console handler
ch = StreamHandlerNoNewline() ch = StreamHandlerNoNewline()
# create formatter # create formatter
formatter = logging.Formatter(LOG_MSG_FORMAT) formatter = logging.Formatter( LOGMSGFORMAT )
# add formatter to ch # add formatter to ch
ch.setFormatter(formatter) ch.setFormatter( formatter )
# add ch to lg # add ch to lg
self.addHandler(ch) self.addHandler( ch )
self.set_loglevel() self.setLogLevel()
def set_loglevel(self, levelname = None): def setLogLevel( self, levelname=None ):
'''Setup loglevel. """Setup loglevel.
Convenience function to support lowercase names.
Convenience function to support lowercase names. levelName: level name from LEVELS"""
level = LOGLEVELDEFAULT
@param level_name level name from LEVELS
'''
level = LOG_LEVEL_DEFAULT
if levelname != None: if levelname != None:
if levelname not in LEVELS: if levelname not in LEVELS:
raise Exception('unknown loglevel seen in set_loglevel') raise Exception( 'unknown loglevel seen in set_loglevel' )
else: else:
level = LEVELS.get(levelname, level) level = LEVELS.get( levelname, level )
self.setLevel(level) self.setLevel( level )
self.handlers[0].setLevel(level) self.handlers[ 0 ].setLevel( level )
lg = MininetLogger() lg = MininetLogger()
+385 -412
View File
File diff suppressed because it is too large Load Diff
+276 -322
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
'''Node objects for Mininet.''' "Node objects for Mininet."
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
import os import os
@@ -13,31 +13,31 @@ from mininet.log import lg
from mininet.util import quietRun, macColonHex, ipStr from mininet.util import quietRun, macColonHex, ipStr
class Node(object): class Node( object ):
'''A virtual network node is simply a shell in a network namespace. """A virtual network node is simply a shell in a network namespace.
We communicate with it using pipes.''' We communicate with it using pipes."""
inToNode = {} inToNode = {}
outToNode = {} outToNode = {}
def __init__(self, name, inNamespace = True): def __init__( self, name, inNamespace=True ):
self.name = name self.name = name
closeFds = False # speed vs. memory use closeFds = False # speed vs. memory use
# xpg_echo is needed so we can echo our sentinel in sendCmd # xpgEcho is needed so we can echo our sentinel in sendCmd
cmd = ['/bin/bash', '-O', 'xpg_echo'] cmd = [ '/bin/bash', '-O', 'xpg_echo' ]
self.inNamespace = inNamespace self.inNamespace = inNamespace
if self.inNamespace: if self.inNamespace:
cmd = ['netns'] + cmd cmd = [ 'netns' ] + cmd
self.shell = Popen(cmd, stdin = PIPE, stdout = PIPE, stderr = STDOUT, self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
close_fds = closeFds) closeFds=closeFds )
self.stdin = self.shell.stdin self.stdin = self.shell.stdin
self.stdout = self.shell.stdout self.stdout = self.shell.stdout
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
# This could be useful for monitoring multiple nodes # This could be useful for monitoring multiple nodes
# 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.pid = self.shell.pid
self.intfCount = 0 self.intfCount = 0
self.intfs = [] # list of interface names, as strings self.intfs = [] # list of interface names, as strings
@@ -48,461 +48,415 @@ class Node(object):
self.ports = {} # dict of ints to interface strings self.ports = {} # dict of ints to interface strings
# replace with Port object, eventually # replace with Port object, eventually
def fdToNode(self, f): def fdToNode( self, f ):
'''Insert docstring. """Insert docstring.
f: unknown
returns: bool unknown"""
node = self.outToNode.get( f )
return node or self.inToNode.get( f )
@param f unknown def cleanup( self ):
@return bool unknown "Help python collect its garbage."
'''
node = self.outToNode.get(f)
return node or self.inToNode.get(f)
def cleanup(self):
'''Help python collect its garbage.'''
self.shell = None self.shell = None
# Subshell I/O, commands and control # Subshell I/O, commands and control
def read(self, fileno_max): def read( self, filenoMax ):
'''Insert docstring. """Insert docstring.
filenoMax: unknown"""
return os.read( self.stdout.fileno(), filenoMax )
@param fileno_max unknown def write( self, data ):
''' """Write data to node.
return os.read(self.stdout.fileno(), fileno_max) data: string"""
os.write( self.stdin.fileno(), data )
def write(self, data): def terminate( self ):
'''Write data to node. "Send kill signal to Node and cleanup after it."
os.kill( self.pid, signal.SIGKILL )
@param data string
'''
os.write(self.stdin.fileno(), data)
def terminate(self):
'''Send kill signal to Node and cleanup after it.'''
os.kill(self.pid, signal.SIGKILL)
self.cleanup() self.cleanup()
def stop(self): def stop( self ):
'''Stop node.''' "Stop node."
self.terminate() self.terminate()
def waitReadable(self): def waitReadable( self ):
'''Poll on node.''' "Poll on node."
self.pollOut.poll() self.pollOut.poll()
def sendCmd(self, cmd): def sendCmd( self, cmd ):
'''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."""
assert not self.waiting assert not self.waiting
if cmd[-1] == '&': if cmd[ -1 ] == '&':
separator = '&' separator = '&'
cmd = cmd[:-1] cmd = cmd[ :-1 ]
else: else:
separator = ';' separator = ';'
if isinstance(cmd, list): if isinstance( cmd, list ):
cmd = ' '.join(cmd) cmd = ' '.join( cmd )
self.write(cmd + separator + ' echo -n "\\0177" \n') self.write( cmd + separator + ' echo -n "\\0177" \n' )
self.waiting = True self.waiting = True
def monitor(self): def monitor( self ):
'''Monitor the output of a command, returning (done, data).''' "Monitor the output of a command, returning (done, data)."
assert self.waiting assert self.waiting
self.waitReadable() self.waitReadable()
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 ):
self.waiting = False self.waiting = False
return True, data[:-1] return True, data[ :-1 ]
else: else:
return False, data return False, data
def sendInt(self): def sendInt( self ):
'''Send ^C, hopefully interrupting a running subprocess.''' "Send ^C, hopefully interrupting a running subprocess."
self.write(chr(3)) self.write( chr( 3 ) )
def waitOutput(self): def waitOutput( self ):
'''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 appearing in the output stream. Wait for the sentinel and return
the output stream. Wait for the sentinel and return the output, the output, including trailing newline."""
including trailing newline.
'''
assert self.waiting assert self.waiting
output = '' output = ''
while True: while True:
self.waitReadable() self.waitReadable()
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 ]
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 ):
'''Send a command, wait for output, and return it. """Send a command, wait for output, and return it.
cmd: string"""
@param cmd string self.sendCmd( cmd )
'''
self.sendCmd(cmd)
return self.waitOutput() return self.waitOutput()
def cmdPrint(self, cmd): def cmdPrint( self, cmd ):
'''Call cmd and printing its output """Call cmd and printing its output
cmd: string"""
@param cmd string #lg.info( '*** %s : %s', self.name, cmd )
''' result = self.cmd( cmd )
#lg.info('*** %s : %s', self.name, cmd) #lg.info( '%s\n', result )
result = self.cmd(cmd)
#lg.info('%s\n', result)
return result return result
# Interface management, configuration, and routing # Interface management, configuration, and routing
def intfName(self, n): def intfName( self, n ):
'''Construct a canonical interface name node-intf for interface N.''' "Construct a canonical interface name node-intf 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] self.intfs += [ intfName ]
return intfName return intfName
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 unsigned int"""
@param mac MAC address as unsigned int macStr = macColonHex( mac )
''' result = self.cmd( [ 'ifconfig', intf, 'down' ] )
mac_str = macColonHex(mac) result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
result = self.cmd(['ifconfig', intf, 'down']) result += self.cmd( [ 'ifconfig', intf, 'up' ] )
result += self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
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 unsigned int
@param ip IP address as unsigned int mac: MAC address as unsigned int"""
@param mac MAC address as unsigned int ip = ipStr( ip )
''' mac = macColonHex( mac )
ip_str = ipStr(ip) result = self.cmd( [ 'arp', '-s', ip, mac ] )
mac_str = macColonHex(mac)
result = self.cmd(['arp', '-s', ip_str, mac_str])
return result return result
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
@param intf string, interface name ip: IP address as a string
@param ip IP address as a string bits:"""
@param bits result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
''' self.ips[ intf ] = ip
result = self.cmd(['ifconfig', intf, ip + bits, 'up'])
self.ips[intf] = ip
return result return result
def setHostRoute(self, ip, intf): def setHostRoute( self, ip, intf ):
'''Add route to host. """Add route to host.
ip: IP address as dotted decimal
intf: string, interface name"""
return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
@param ip IP address as dotted decimal def setDefaultRoute( self, intf ):
@param intf string, interface name """Set the default route to go through intf.
''' intf: string, interface name"""
return self.cmd('route add -host ' + ip + ' dev ' + intf) self.cmd( 'ip route flush' )
return self.cmd( 'route add default ' + intf )
def setDefaultRoute(self, intf): def IP( self ):
'''Set the default route to go through intf. "Return IP address of first interface"
if len( self.intfs ) > 0:
return self.ips.get( self.intfs[ 0 ], None )
@param intf string, interface name def intfIsUp( self ):
''' "Check if one of our interfaces is up."
self.cmd('ip route flush') return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
return self.cmd('route add default ' + intf)
def IP(self):
'''Return IP address of first interface'''
if len(self.intfs) > 0:
return self.ips.get(self.intfs[0], None)
def intfIsUp(self):
'''Check if one of our interfaces is up.'''
return 'UP' in self.cmd('ifconfig ' + self.intfs[0])
# Other methods # Other methods
def __str__(self): def __str__( self ):
result = self.name + ':' result = self.name + ':'
if self.IP(): if self.IP():
result += ' IP=' + self.IP() result += ' IP=' + self.IP()
result += ' intfs=' + ','.join(self.intfs) result += ' intfs=' + ','.join( self.intfs )
result += ' waiting=' + repr(self.waiting) result += ' waiting=' + repr( self.waiting )
return result return result
class Host(Node): class Host( Node ):
'''A host is simply a Node.''' "A host is simply a Node."
pass pass
class Switch(Node): 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): def sendCmd( self, cmd ):
'''Send command to Node. """Send command to Node.
cmd: string"""
@param cmd string
'''
if not self.execed: if not self.execed:
return Node.sendCmd(self, cmd) return Node.sendCmd( self, cmd )
else: else:
lg.error('*** Error: %s has execed and cannot accept commands' % lg.error( '*** Error: %s has execed and cannot accept commands' %
self.name) self.name )
def monitor(self): def monitor( self ):
'''Monitor node.''' "Monitor node."
if not self.execed: if not self.execed:
return Node.monitor(self) return Node.monitor( self )
else: else:
return True, '' return True, ''
class UserSwitch(Switch): class UserSwitch( Switch ):
'''User-space switch. """User-space switch.
Currently only works in the root namespace."""
Currently only works in the root namespace. def __init__( self, name ):
''' """Init.
name: name for the switch"""
Switch.__init__( self, name, inNamespace=False )
def __init__(self, name): def start( self, controllers ):
'''Init. """Start OpenFlow reference user datapath.
Log to /tmp/sN-{ ofd,ofp }.log.
@param name controllers: dict of controller names to objects"""
'''
Switch.__init__(self, name, inNamespace = False)
def start(self, controllers):
'''Start OpenFlow reference user datapath.
Log to /tmp/sN-{ofd,ofp}.log.
@param controllers dict of controller names to objects
'''
if 'c0' not in controllers: if 'c0' not in controllers:
raise Exception('User datapath start() requires controller c0') raise Exception( 'User datapath start() requires controller c0' )
controller = controllers['c0'] controller = controllers[ 'c0' ]
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 = self.intfs
self.cmdPrint('ofdatapath -i ' + ','.join(intfs) + ' punix:/tmp/' + self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) + ' punix:/tmp/' +
self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &') self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
self.cmdPrint('ofprotocol unix:/tmp/' + self.name + ' tcp:' + self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name + ' tcp:' +
controller.IP() + ' --fail=closed 1> ' + ofplog + ' 2>' + controller.IP() + ' --fail=closed 1> ' + ofplog + ' 2>' +
ofplog + ' &') ofplog + ' &' )
def stop(self): def stop( self ):
'''Stop OpenFlow reference user datapath.''' "Stop OpenFlow reference user datapath."
self.cmd('kill %ofdatapath') self.cmd( 'kill %ofdatapath' )
self.cmd('kill %ofprotocol') self.cmd( 'kill %ofprotocol' )
class KernelSwitch(Switch): class KernelSwitch( Switch ):
'''Kernel-space switch. """Kernel-space switch.
Currently only works in the root namespace."""
Currently only works in the root namespace. def __init__( self, name, dp=None, dpid=None ):
''' """Init.
name:
def __init__(self, name, dp = None, dpid = None): dp: netlink id ( 0, 1, 2, ... )
'''Init. dpid: datapath ID as unsigned int; random value if None"""
Switch.__init__( self, name, inNamespace=False )
@param name
@param dp netlink id (0, 1, 2, ...)
@param dpid datapath ID as unsigned int; random value if None
'''
Switch.__init__(self, name, inNamespace = False)
self.dp = dp self.dp = dp
self.dpid = dpid self.dpid = dpid
def start(self, controllers): def start( self, controllers ):
'''Start up reference kernel datapath.''' "Start up reference kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log' ofplog = '/tmp/' + self.name + '-ofp.log'
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 nl:%i' % self.dp )
self.cmdPrint('dpctl adddp nl:%i' % self.dp) self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
if self.dpid: if self.dpid:
intf = 'of%i' % self.dp intf = 'of%i' % self.dp
mac_str = macColonHex(self.dpid) macStr = macColonHex( self.dpid )
self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str]) self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
if len(self.ports) != max(self.ports.keys()) + 1: if len( self.ports ) != max( self.ports.keys() ) + 1:
raise Exception('only contiguous, zero-indexed port ranges' raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports) 'supported: %s' % self.ports )
intfs = [self.ports[port] for port in self.ports.keys()] intfs = [ self.ports[ port ] for port in self.ports.keys() ]
self.cmdPrint('dpctl addif nl:' + str(self.dp) + ' ' + ' '.join(intfs)) self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon # Run protocol daemon
self.cmdPrint('ofprotocol nl:' + str(self.dp) + ' tcp:' + self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
controllers['c0'].IP() + ':' + controllers[ 'c0' ].IP() + ':' +
str(controllers['c0'].port) + str( controllers[ 'c0' ].port ) +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &') ' --fail=closed 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 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 to remove 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:
quietRun('ip link del ' + intf) quietRun( 'ip link del ' + intf )
lg.info('.') lg.info( '.' )
class OVSKernelSwitch(Switch): class OVSKernelSwitch( Switch ):
'''Open VSwitch kernel-space switch. """Open VSwitch kernel-space switch.
Currently only works in the root namespace."""
Currently only works in the root namespace. def __init__( self, name, dp=None, dpid=None ):
''' """Init.
name:
def __init__(self, name, dp = None, dpid = None): dp: netlink id ( 0, 1, 2, ... )
'''Init. dpid: datapath ID as unsigned int; random value if None"""
Switch.__init__( self, name, inNamespace=False )
@param name
@param dp netlink id (0, 1, 2, ...)
@param dpid datapath ID as unsigned int; random value if None
'''
Switch.__init__(self, name, inNamespace = False)
self.dp = dp self.dp = dp
self.dpid = dpid self.dpid = dpid
def start(self, controllers): def start( self, controllers ):
'''Start up kernel datapath.''' "Start up kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log' ofplog = '/tmp/' + self.name + '-ofp.log'
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 dp%i' % self.dp )
self.cmdPrint('ovs-dpctl add-dp dp%i' % self.dp) self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp )
if self.dpid: if self.dpid:
intf = 'dp' % self.dp intf = 'dp' % self.dp
mac_str = macColonHex(self.dpid) macStr = macColonHex( self.dpid )
self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str]) self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
if len(self.ports) != max(self.ports.keys()) + 1: if len( self.ports ) != max( self.ports.keys() ) + 1:
raise Exception('only contiguous, zero-indexed port ranges' raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports) 'supported: %s' % self.ports )
intfs = [self.ports[port] for port in self.ports.keys()] intfs = [ self.ports[ port ] for port in self.ports.keys() ]
self.cmdPrint('ovs-dpctl add-if dp' + str(self.dp) + ' ' + self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
' '.join(intfs)) ' '.join( intfs ) )
# Run protocol daemon # Run protocol daemon
self.cmdPrint('ovs-openflowd dp' + str(self.dp) + ' tcp:' + self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
controllers['c0'].IP() + ':' + controllers[ 'c0' ].IP() + ':' +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &') ' --fail=closed 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 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 to remove 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:
quietRun('ip link del ' + intf) quietRun( 'ip link del ' + intf )
lg.info('.') lg.info( '.' )
class Controller(Node): class Controller( Node ):
'''A Controller is a Node that is running (or has execed) an """A Controller is a Node that is running ( or has execed ) an
OpenFlow controller.''' OpenFlow controller."""
def __init__(self, name, inNamespace = False, controller = 'controller', def __init__( self, name, inNamespace=False, controller='controller',
cargs = '-v ptcp:', cdir = None, ip_address="127.0.0.1", cargs='-v ptcp:', cdir=None, ipAddress="127.0.0.1",
port = 6633): port=6633 ):
self.controller = controller self.controller = controller
self.cargs = cargs self.cargs = cargs
self.cdir = cdir self.cdir = cdir
self.ip_address = ip_address self.ipAddress = ipAddress
self.port = port self.port = port
Node.__init__(self, name, inNamespace = inNamespace) Node.__init__( self, name, inNamespace=inNamespace )
def start(self): def start( self ):
'''Start <controller> <args> on controller. """Start <controller> <args> on controller.
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.cmdPrint( 'cd ' + self.cdir )
self.cmdPrint(self.controller + ' ' + self.cargs + self.cmdPrint( self.controller + ' ' + self.cargs +
' 1> ' + cout + ' 2> ' + cout + ' &') ' 1> ' + cout + ' 2> ' + cout + ' &' )
self.execed = False self.execed = False
def stop(self): def stop( self ):
'''Stop controller.''' "Stop controller."
self.cmd('kill %' + self.controller) self.cmd( 'kill %' + self.controller )
self.terminate() self.terminate()
def IP(self): def IP( self ):
'''Return IP address of the Controller''' "Return IP address of the Controller"
return self.ip_address return self.ipAddress
class ControllerParams(object): class ControllerParams( object ):
'''Container for controller IP parameters.''' "Container for controller IP parameters."
def __init__(self, ip, subnet_size): def __init__( self, ip, subnetSize ):
'''Init. """Init.
ip: integer, controller IP
@param ip integer, controller IP subnetSize: integer, ex 8 for slash-8, covering 17M"""
@param subnet_size integer, ex 8 for slash-8, covering 17M
'''
self.ip = ip self.ip = ip
self.subnet_size = subnet_size self.subnetSize = subnetSize
class NOX(Controller): class NOX( Controller ):
'''Controller to run a NOX application.''' "Controller to run a NOX application."
def __init__(self, name, inNamespace = False, nox_args = None, **kwargs): def __init__( self, name, inNamespace=False, noxArgs=None, **kwargs ):
'''Init. """Init.
name: name to give controller
@param name name to give controller noxArgs: list of args, or single arg, to pass to NOX"""
@param nox_args list of args, or single arg, to pass to NOX if type( noxArgs ) != list:
''' noxArgs = [ noxArgs ]
if type(nox_args) != list: if not noxArgs:
nox_args = [nox_args] noxArgs = [ 'packetdump' ]
if not nox_args: noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
nox_args = ['packetdump'] if not noxCoreDir:
nox_core_dir = os.environ['NOX_CORE_DIR'] raise Exception( 'please set NOX_CORE_DIR env var\n' )
if not nox_core_dir: Controller.__init__( self, name,
raise Exception('please set NOX_CORE_DIR env var\n') controller=noxCoreDir + '/nox_core',
Controller.__init__(self, name, cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \
controller = nox_core_dir + '/nox_core', ' '.join( noxArgs ),
cargs = '--libdir=/usr/local/lib -v -i ptcp: ' + \ cdir = noxCoreDir, **kwargs )
' '.join(nox_args),
cdir = nox_core_dir, **kwargs)
class RemoteController(Controller): class RemoteController( Controller ):
'''Controller running outside of Mininet's control.''' "Controller running outside of Mininet's control."
def __init__(self, name, inNamespace = False, ip_address = '127.0.0.1', def __init__( self, name, inNamespace=False, ipAddress='127.0.0.1',
port = 6633): port=6633 ):
'''Init. """Init.
name: name to give controller
ipAddress: the IP address where the remote controller is
listening
port: the port where the remote controller is listening"""
Controller.__init__( self, name, ipAddress=ipAddress, port=port )
@param name name to give controller def start( self ):
@param ip_address the IP address where the remote controller is "Overridden to do nothing."
listening
@param port the port where the remote controller is listening
'''
Controller.__init__(self, name, ip_address = ip_address, port = port)
def start(self):
'''Overridden to do nothing.'''
return return
def stop(self): def stop( self ):
'''Overridden to do nothing.''' "Overridden to do nothing."
return return
+89 -154
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
'''Utility functions for Mininet.''' "Utility functions for Mininet."
from time import sleep from time import sleep
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
@@ -8,41 +8,32 @@ from subprocess import call, check_call, Popen, PIPE, STDOUT
from mininet.log import lg from mininet.log import lg
def run( cmd ):
"""Simple interface to subprocess.call()
cmd: list of command params"""
return call( cmd.split( ' ' ) )
def run(cmd): def checkRun( cmd ):
'''Simple interface to subprocess.call() """Simple interface to subprocess.check_call()
cmd: list of command params"""
check_call( cmd.split( ' ' ) )
@param cmd list of command params def quietRun( cmd ):
''' """Run a command, routing stderr to stdout, and return the output.
return call(cmd.split(' ')) cmd: list of command params"""
if isinstance( cmd, str ):
cmd = cmd.split( ' ' )
def checkRun(cmd): popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
'''Simple interface to subprocess.check_call()
@param cmd list of command params
'''
check_call(cmd.split(' '))
def quietRun(cmd):
'''Run a command, routing stderr to stdout, and return the output.
@param 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 # We can't use Popen.communicate() because it uses
# select(), which can't handle # select(), which can't handle
# high file descriptor numbers! poll() can, however. # high file descriptor numbers! poll() can, however.
output = '' output = ''
readable = select.poll() readable = select.poll()
readable.register(popen.stdout) readable.register( popen.stdout )
while True: while True:
while readable.poll(): while readable.poll():
data = popen.stdout.read(1024) data = popen.stdout.read( 1024 )
if len(data) == 0: if len( data ) == 0:
break break
output += data output += data
popen.poll() popen.poll()
@@ -50,42 +41,6 @@ def quietRun(cmd):
break break
return output return output
def make_veth_pair(intf1, intf2):
'''Create a veth pair connecting intf1 and intf2.
@param intf1 string, interface name
@param intf2 string, interface name
'''
# 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
#lg.info('running command: %s\n' % cmd)
return checkRun(cmd)
def move_intf(intf, node):
'''Move interface to node.
@param intf string interface name
@param node Node object
@return success boolean, did operation complete?
'''
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
#lg.info('running command: %s\n' % cmd)
quietRun(cmd)
#lg.info(' output: %s\n' % output)
links = node.cmd('ip link show')
if not intf in links:
lg.error('*** Error: move_intf: %s not successfully moved to %s:\n' %
(intf, node.name))
return False
return True
# Interface management # Interface management
# #
# Interfaces are managed as strings which are simply the # Interfaces are managed as strings which are simply the
@@ -99,116 +54,96 @@ def move_intf(intf, node):
# live in the root namespace and thus do not have to be # live in the root namespace and thus do not have to be
# explicitly moved. # explicitly moved.
def makeIntfPair( intf1, intf2 ):
def makeIntfPair(intf1, intf2): """Make a veth pair connecting intf1 and intf2.
'''Make a veth pair. intf1: string, interface
intf2: string, interface
@param intf1 string, interface returns: success boolean"""
@param intf2 string, interface
@return success boolean
'''
# Delete any old interfaces with the same names # Delete any old interfaces with the same names
quietRun('ip link del ' + intf1) quietRun( 'ip link del ' + intf1 )
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 checkRun( 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 moveIntf(intf, node, print_error = False): def moveIntfNoRetry( intf, node, printError=False ):
'''Move interface to node. """Move interface to node, without retrying.
intf: string, interface
@param intf string, interface node: Node object
@param node Node object printError: if true, print error"""
@param print_error if true, print error cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
''' quietRun( cmd )
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid) links = node.cmd( 'ip link show' )
quietRun(cmd)
links = node.cmd('ip link show')
if not intf in links: if not intf in links:
if print_error: if printError:
lg.error('*** Error: moveIntf: % not successfully moved to %s:\n' % lg.error( '*** Error: moveIntf: ' + intf +
(intf, node.name)) ' not successfully moved to ' + node.name + '\n' )
return False return False
return True 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, moveIntf, intf, node, printError )
def retry(n, retry_delay, fn, *args, **keywords): def createLink( node1, node2, retries=10, delaySecs=0.001 ):
'''Try something N times before giving up. """Create a link between nodes, making an interface for each.
node1: Node object
@param n number of times to retry node2: Node object"""
@param retry_delay seconds wait this long between tries
@param fn function to call
@param args args to apply to function call
'''
tries = 0
while not fn(*args, **keywords) and tries < n:
sleep(retry_delay)
tries += 1
if tries >= n:
lg.error("*** gave up after %i retries\n" % tries)
exit(1)
# delay between interface move checks in seconds
MOVEINTF_DELAY = 0.0001
CREATE_LINK_RETRIES = 10
def createLink(node1, node2):
'''Create a link between nodes, making an interface for each.
@param node1 Node object
@param node2 Node object
'''
intf1 = node1.newIntf() intf1 = node1.newIntf()
intf2 = node2.newIntf() intf2 = node2.newIntf()
makeIntfPair(intf1, intf2) makeIntfPair( intf1, intf2 )
if node1.inNamespace: if node1.inNamespace:
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf1, node1) retry( retries, delaySecs, moveIntf, intf1, node1 )
if node2.inNamespace: if node2.inNamespace:
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf2, node2) retry( retries, delaySecs, moveIntf, intf2, node2 )
node1.connection[intf1] = (node2, intf2) node1.connection[ intf1 ] = ( node2, intf2 )
node2.connection[intf2] = (node1, intf1) node2.connection[ intf2 ] = ( node1, intf1 )
return intf1, intf2 return intf1, intf2
def fixLimits(): def fixLimits():
'''Fix ridiculously small resource limits.''' "Fix ridiculously small resource limits."
setrlimit(RLIMIT_NPROC, (4096, 8192)) setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit(RLIMIT_NOFILE, (16384, 32768)) setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
def _colonHex( val, bytes ):
def _colonHex(val, bytes): """Generate colon-hex string.
'''Generate colon-hex string. val: input as unsigned int
bytes: number of bytes to convert
@param val input as unsigned int returns: chStr colon-hex string"""
@param bytes number of bytes to convert
@return ch_str colon-hex string
'''
pieces = [] pieces = []
for i in range(bytes - 1, -1, -1): for i in range( bytes - 1, -1, -1 ):
pieces.append('%02x' % (((0xff << (i * 8)) & val) >> (i * 8))) piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
ch_str = ':'.join(pieces) pieces.append( '%02x' % piece )
return ch_str 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 macColonHex(mac): def ipStr( ip ):
'''Generate MAC colon-hex string from unsigned int. """Generate IP address string
returns: ip addr string"""
@param mac MAC address as unsigned int hi = ( ip & 0xff0000 ) >> 16
@return mac_str MAC colon-hex string mid = ( ip & 0xff00 ) >> 8
'''
return _colonHex(mac, 6)
def ipStr(ip):
'''Generate IP address string
@return ip addr string
'''
hi = (ip & 0xff0000) >> 16
mid = (ip & 0xff00) >> 8
lo = ip & 0xff lo = ip & 0xff
return "10.%i.%i.%i" % (hi, mid, lo) return "10.%i.%i.%i" % ( hi, mid, lo )
Executable → Regular
+24 -31
View File
@@ -1,51 +1,44 @@
#!/usr/bin/env python #!/usr/bin/env python
"""XTerm creation and cleanup.
Utility functions to run an xterm (connected via screen(1)) on each host. """
XTerm creation and cleanup.
Utility functions to run an xterm ( connected via screen( 1 ) ) on each host.
Requires xterm(1) and GNU screen(1). Requires xterm( 1 ) and GNU screen( 1 ).
""" """
import re import re
from subprocess import Popen from subprocess import Popen
from mininet.util import quietRun from mininet.util import quietRun
def makeXterm( node, title ):
def makeXterm(node, title): """Run screen on a node, and hook up an xterm.
'''Run screen on a node, and hook up an xterm. node: Node object
title: base title
@param node Node object returns: process created"""
@param title base title
@return process created
'''
title += ': ' + node.name title += ': ' + node.name
if not node.inNamespace: if not node.inNamespace:
title += ' (root)' title += ' (root)'
cmd = ['xterm', '-title', title, '-e'] cmd = [ 'xterm', '-title', title, '-e' ]
if not node.execed: if not node.execed:
node.cmdPrint('screen -dmS ' + node.name) node.cmdPrint( 'screen -dmS ' + node.name )
cmd += ['screen', '-D', '-RR', '-S', node.name] cmd += [ 'screen', '-D', '-RR', '-S', node.name ]
else: else:
cmd += ['sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log'] cmd += [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
return Popen(cmd) return Popen( cmd )
def cleanUpScreens(): def cleanUpScreens():
'''Remove moldy old screen sessions.''' "Remove moldy old screen sessions."
r = r'(\d+.[hsc]\d+)' r = r'(\d+.[hsc]\d+)'
output = quietRun('screen -ls').split('\n') output = quietRun( 'screen -ls' ).split( '\n' )
for line in output: for line in output:
m = re.search(r, line) m = re.search( r, line )
if m: if m:
quietRun('screen -S ' + m.group(1) + ' -X kill') quietRun( 'screen -S ' + m.group( 1 ) + ' -X kill' )
def makeXterms( nodes, title ):
def makeXterms(nodes, title): """Create XTerms.
'''Create XTerms. nodes: list of Node objects
title: base title for each
@param nodes list of Node objects returns: list of created xterm processes"""
@param title base title for each return [ makeXterm( node, title ) for node in nodes ]
@return list of created xterm processes
'''
return [makeXterm(node, title) for node in nodes]
+8 -8
View File
@@ -1,16 +1,16 @@
#!/usr/bin/python #!/usr/bin/python
""" """
unpep8:
Translate from PEP8 Python style to Mininet (i.e. Arista-like) Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Python style: Python style
- Reinstates CapWords for methods and instance variables. usage: unpep8 < old.py > new.py
- Gets rid of triple single quotes.
- Eliminates triple quotes on single lines. - Reinstates CapWords for methods and instance variables
- Inserts extra spaces to improve readability. - Gets rid of triple single quotes
- Fixes Doxygen (or doxypy) ugliness. - Eliminates triple quotes on single lines
- Inserts extra spaces to improve readability
- Fixes Doxygen (or doxypy) ugliness
Does the following translations: Does the following translations: