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
PYSRC = $(MININET) $(TEST) $(BIN)
P8IGN = E251,E201,E302
codecheck: $(PYSRC)
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
pep8 --ignore=E251 $(PYSRC)
pep8 --ignore=$(P8IGN) $(PYSRC)
test: $(MININET) $(TEST)
mininet/test/test_nets.py
+1 -1
View File
@@ -130,7 +130,7 @@ class MininetRunner(object):
'''Setup and validate environment.'''
# set logging verbosity
lg.set_loglevel(self.options.verbosity)
lg.setLogLevel(self.options.verbosity)
# validate environment setup
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
from logging import Logger
import types
LEVELS = {'debug': logging.DEBUG,
LEVELS = { 'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL}
'critical': logging.CRITICAL }
# 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'
LOG_MSG_FORMAT = '%(message)s'
LOGMSGFORMAT = '%(message)s'
# Modified from python2.5/__init__.py
class StreamHandlerNoNewline(logging.StreamHandler):
'''StreamHandler that doesn't print newlines by default.
class StreamHandlerNoNewline( logging.StreamHandler ):
"""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
easily support interactive mode when we want it, or errors-only logging for
running unit tests.
'''
def emit(self, record):
'''
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.
'''
def emit( self, record ):
"""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.printException and appended to the stream."""
try:
msg = self.format(record)
msg = self.format( record )
fs = '%s' # was '%s\n'
if not hasattr(types, 'UnicodeType'): #if no unicode support...
self.stream.write(fs % msg)
if not hasattr( types, 'UnicodeType' ): #if no unicode support...
self.stream.write( fs % msg )
else:
try:
self.stream.write(fs % msg)
self.stream.write( fs % msg )
except UnicodeError:
self.stream.write(fs % msg.encode('UTF-8'))
self.stream.write( fs % msg.encode( 'UTF-8' ) )
self.flush()
except (KeyboardInterrupt, SystemExit):
except ( KeyboardInterrupt, SystemExit ):
raise
except:
self.handleError(record)
self.handleError( record )
class Singleton(type):
'''Singleton pattern from Wikipedia
class Singleton( type ):
"""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
def __call__(mcs, *args, **kw):
def __call__( mcs, *args, **kw ):
if mcs.instance is None:
mcs.instance = super(Singleton, mcs).__call__(*args, **kw)
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
return mcs.instance
class MininetLogger(Logger, object):
'''Mininet-specific logger
Enable each mininet .py file to with one import:
class MininetLogger( Logger, object ):
"""Mininet-specific logger
Enable each mininet .py file to with one import:
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
class, and can then use the __metaclass__ directive, to prevent this error:
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:
TypeError: Error when calling the metaclass bases
TypeError: Error when calling the metaclass bases
a new-style class can't have only classic bases
If Python2.5/logging/__init__.py defined Filterer as a new-style class,
via Filterer(object): rather than Filterer, we wouldn't need this.
If Python2.5/logging/__init__.py defined Filterer as a new-style class,
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
def __init__(self):
def __init__( self ):
Logger.__init__(self, "mininet")
Logger.__init__( self, "mininet" )
# create console handler
ch = StreamHandlerNoNewline()
# create formatter
formatter = logging.Formatter(LOG_MSG_FORMAT)
formatter = logging.Formatter( LOGMSGFORMAT )
# add formatter to ch
ch.setFormatter(formatter)
ch.setFormatter( formatter )
# add ch to lg
self.addHandler(ch)
self.addHandler( ch )
self.set_loglevel()
self.setLogLevel()
def set_loglevel(self, levelname = None):
'''Setup loglevel.
def setLogLevel( self, levelname=None ):
"""Setup loglevel.
Convenience function to support lowercase names.
Convenience function to support lowercase names.
@param level_name level name from LEVELS
'''
level = LOG_LEVEL_DEFAULT
levelName: level name from LEVELS"""
level = LOGLEVELDEFAULT
if levelname != None:
if levelname not in LEVELS:
raise Exception('unknown loglevel seen in set_loglevel')
raise Exception( 'unknown loglevel seen in set_loglevel' )
else:
level = LEVELS.get(levelname, level)
level = LEVELS.get( levelname, level )
self.setLevel(level)
self.handlers[0].setLevel(level)
self.setLevel( level )
self.handlers[ 0 ].setLevel( level )
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
'''Node objects for Mininet.'''
"Node objects for Mininet."
from subprocess import Popen, PIPE, STDOUT
import os
@@ -13,31 +13,31 @@ from mininet.log import lg
from mininet.util import quietRun, macColonHex, ipStr
class Node(object):
'''A virtual network node is simply a shell in a network namespace.
We communicate with it using pipes.'''
class Node( object ):
"""A virtual network node is simply a shell in a network namespace.
We communicate with it using pipes."""
inToNode = {}
outToNode = {}
def __init__(self, name, inNamespace = True):
def __init__( self, name, inNamespace=True ):
self.name = name
closeFds = False # speed vs. memory use
# xpg_echo is needed so we can echo our sentinel in sendCmd
cmd = ['/bin/bash', '-O', 'xpg_echo']
# xpgEcho is needed so we can echo our sentinel in sendCmd
cmd = [ '/bin/bash', '-O', 'xpg_echo' ]
self.inNamespace = inNamespace
if self.inNamespace:
cmd = ['netns'] + cmd
self.shell = Popen(cmd, stdin = PIPE, stdout = PIPE, stderr = STDOUT,
close_fds = closeFds)
cmd = [ 'netns' ] + cmd
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
closeFds=closeFds )
self.stdin = self.shell.stdin
self.stdout = self.shell.stdout
self.pollOut = select.poll()
self.pollOut.register(self.stdout)
self.pollOut.register( self.stdout )
# Maintain mapping between file descriptors and nodes
# This could be useful for monitoring multiple nodes
# using select.poll()
self.outToNode[self.stdout.fileno()] = self
self.inToNode[self.stdin.fileno()] = self
self.outToNode[ self.stdout.fileno() ] = self
self.inToNode[ self.stdin.fileno() ] = self
self.pid = self.shell.pid
self.intfCount = 0
self.intfs = [] # list of interface names, as strings
@@ -48,461 +48,415 @@ class Node(object):
self.ports = {} # dict of ints to interface strings
# replace with Port object, eventually
def fdToNode(self, f):
'''Insert docstring.
def fdToNode( self, f ):
"""Insert docstring.
f: unknown
returns: bool unknown"""
node = self.outToNode.get( f )
return node or self.inToNode.get( f )
@param f unknown
@return bool unknown
'''
node = self.outToNode.get(f)
return node or self.inToNode.get(f)
def cleanup(self):
'''Help python collect its garbage.'''
def cleanup( self ):
"Help python collect its garbage."
self.shell = None
# Subshell I/O, commands and control
def read(self, fileno_max):
'''Insert docstring.
def read( self, filenoMax ):
"""Insert docstring.
filenoMax: unknown"""
return os.read( self.stdout.fileno(), filenoMax )
@param fileno_max unknown
'''
return os.read(self.stdout.fileno(), fileno_max)
def write( self, data ):
"""Write data to node.
data: string"""
os.write( self.stdin.fileno(), data )
def write(self, data):
'''Write data to node.
@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)
def terminate( self ):
"Send kill signal to Node and cleanup after it."
os.kill( self.pid, signal.SIGKILL )
self.cleanup()
def stop(self):
'''Stop node.'''
def stop( self ):
"Stop node."
self.terminate()
def waitReadable(self):
'''Poll on node.'''
def waitReadable( self ):
"Poll on node."
self.pollOut.poll()
def sendCmd(self, cmd):
'''Send a command, followed by a command to echo a sentinel,
and return without waiting for the command to complete.'''
def sendCmd( self, cmd ):
"""Send a command, followed by a command to echo a sentinel,
and return without waiting for the command to complete."""
assert not self.waiting
if cmd[-1] == '&':
if cmd[ -1 ] == '&':
separator = '&'
cmd = cmd[:-1]
cmd = cmd[ :-1 ]
else:
separator = ';'
if isinstance(cmd, list):
cmd = ' '.join(cmd)
self.write(cmd + separator + ' echo -n "\\0177" \n')
if isinstance( cmd, list ):
cmd = ' '.join( cmd )
self.write( cmd + separator + ' echo -n "\\0177" \n' )
self.waiting = True
def monitor(self):
'''Monitor the output of a command, returning (done, data).'''
def monitor( self ):
"Monitor the output of a command, returning (done, data)."
assert self.waiting
self.waitReadable()
data = self.read(1024)
if len(data) > 0 and data[-1] == chr(0177):
data = self.read( 1024 )
if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
self.waiting = False
return True, data[:-1]
return True, data[ :-1 ]
else:
return False, data
def sendInt(self):
'''Send ^C, hopefully interrupting a running subprocess.'''
self.write(chr(3))
def sendInt( self ):
"Send ^C, hopefully interrupting a running subprocess."
self.write( chr( 3 ) )
def waitOutput(self):
'''Wait for a command to complete.
Completion is signaled by a sentinel character, ASCII(127) appearing in
the output stream. Wait for the sentinel and return the output,
including trailing newline.
'''
def waitOutput( self ):
"""Wait for a command to complete.
Completion is signaled by a sentinel character, ASCII( 127 )
appearing in the output stream. Wait for the sentinel and return
the output, including trailing newline."""
assert self.waiting
output = ''
while True:
self.waitReadable()
data = self.read(1024)
if len(data) > 0 and data[-1] == chr(0177):
output += data[:-1]
data = self.read( 1024 )
if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
output += data[ :-1 ]
break
else:
output += data
self.waiting = False
return output
def cmd(self, cmd):
'''Send a command, wait for output, and return it.
@param cmd string
'''
self.sendCmd(cmd)
def cmd( self, cmd ):
"""Send a command, wait for output, and return it.
cmd: string"""
self.sendCmd( cmd )
return self.waitOutput()
def cmdPrint(self, cmd):
'''Call cmd and printing its output
@param cmd string
'''
#lg.info('*** %s : %s', self.name, cmd)
result = self.cmd(cmd)
#lg.info('%s\n', result)
def cmdPrint( self, cmd ):
"""Call cmd and printing its output
cmd: string"""
#lg.info( '*** %s : %s', self.name, cmd )
result = self.cmd( cmd )
#lg.info( '%s\n', result )
return result
# Interface management, configuration, and routing
def intfName(self, n):
'''Construct a canonical interface name node-intf for interface N.'''
return self.name + '-eth' + repr(n)
def intfName( self, n ):
"Construct a canonical interface name node-intf for interface N."
return self.name + '-eth' + repr( n )
def newIntf(self):
'''Reserve and return a new interface name.'''
intfName = self.intfName(self.intfCount)
def newIntf( self ):
"Reserve and return a new interface name."
intfName = self.intfName( self.intfCount )
self.intfCount += 1
self.intfs += [intfName]
self.intfs += [ intfName ]
return intfName
def setMAC(self, intf, mac):
'''Set the MAC address for an interface.
@param mac MAC address as unsigned int
'''
mac_str = macColonHex(mac)
result = self.cmd(['ifconfig', intf, 'down'])
result += self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
result += self.cmd(['ifconfig', intf, 'up'])
def setMAC( self, intf, mac ):
"""Set the MAC address for an interface.
mac: MAC address as unsigned int"""
macStr = macColonHex( mac )
result = self.cmd( [ 'ifconfig', intf, 'down' ] )
result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
result += self.cmd( [ 'ifconfig', intf, 'up' ] )
return result
def setARP(self, ip, mac):
'''Add an ARP entry.
@param ip IP address as unsigned int
@param mac MAC address as unsigned int
'''
ip_str = ipStr(ip)
mac_str = macColonHex(mac)
result = self.cmd(['arp', '-s', ip_str, mac_str])
def setARP( self, ip, mac ):
"""Add an ARP entry.
ip: IP address as unsigned int
mac: MAC address as unsigned int"""
ip = ipStr( ip )
mac = macColonHex( mac )
result = self.cmd( [ 'arp', '-s', ip, mac ] )
return result
def setIP(self, intf, ip, bits):
'''Set the IP address for an interface.
@param intf string, interface name
@param ip IP address as a string
@param bits
'''
result = self.cmd(['ifconfig', intf, ip + bits, 'up'])
self.ips[intf] = ip
def setIP( self, intf, ip, bits ):
"""Set the IP address for an interface.
intf: string, interface name
ip: IP address as a string
bits:"""
result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
self.ips[ intf ] = ip
return result
def setHostRoute(self, ip, intf):
'''Add route to host.
def setHostRoute( self, ip, intf ):
"""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
@param intf string, interface name
'''
return self.cmd('route add -host ' + ip + ' dev ' + intf)
def setDefaultRoute( self, intf ):
"""Set the default route to go through intf.
intf: string, interface name"""
self.cmd( 'ip route flush' )
return self.cmd( 'route add default ' + intf )
def setDefaultRoute(self, intf):
'''Set the default route to go through intf.
def IP( self ):
"Return IP address of first interface"
if len( self.intfs ) > 0:
return self.ips.get( self.intfs[ 0 ], None )
@param intf string, interface name
'''
self.cmd('ip route flush')
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])
def intfIsUp( self ):
"Check if one of our interfaces is up."
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
# Other methods
def __str__(self):
def __str__( self ):
result = self.name + ':'
if self.IP():
result += ' IP=' + self.IP()
result += ' intfs=' + ','.join(self.intfs)
result += ' waiting=' + repr(self.waiting)
result += ' intfs=' + ','.join( self.intfs )
result += ' waiting=' + repr( self.waiting )
return result
class Host(Node):
'''A host is simply a Node.'''
class Host( Node ):
"A host is simply a Node."
pass
class Switch(Node):
'''A Switch is a Node that is running (or has execed)
an OpenFlow switch.'''
class Switch( Node ):
"""A Switch is a Node that is running ( or has execed )
an OpenFlow switch."""
def sendCmd(self, cmd):
'''Send command to Node.
@param cmd string
'''
def sendCmd( self, cmd ):
"""Send command to Node.
cmd: string"""
if not self.execed:
return Node.sendCmd(self, cmd)
return Node.sendCmd( self, cmd )
else:
lg.error('*** Error: %s has execed and cannot accept commands' %
self.name)
lg.error( '*** Error: %s has execed and cannot accept commands' %
self.name )
def monitor(self):
'''Monitor node.'''
def monitor( self ):
"Monitor node."
if not self.execed:
return Node.monitor(self)
return Node.monitor( self )
else:
return True, ''
class UserSwitch(Switch):
'''User-space switch.
class UserSwitch( 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):
'''Init.
@param name
'''
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
'''
def start( self, controllers ):
"""Start OpenFlow reference user datapath.
Log to /tmp/sN-{ ofd,ofp }.log.
controllers: dict of controller names to objects"""
if 'c0' not in controllers:
raise Exception('User datapath start() requires controller c0')
controller = controllers['c0']
raise Exception( 'User datapath start() requires controller c0' )
controller = controllers[ 'c0' ]
ofdlog = '/tmp/' + self.name + '-ofd.log'
ofplog = '/tmp/' + self.name + '-ofp.log'
self.cmd('ifconfig lo up')
self.cmd( 'ifconfig lo up' )
intfs = self.intfs
self.cmdPrint('ofdatapath -i ' + ','.join(intfs) + ' punix:/tmp/' +
self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &')
self.cmdPrint('ofprotocol unix:/tmp/' + self.name + ' tcp:' +
self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) + ' punix:/tmp/' +
self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name + ' tcp:' +
controller.IP() + ' --fail=closed 1> ' + ofplog + ' 2>' +
ofplog + ' &')
ofplog + ' &' )
def stop(self):
'''Stop OpenFlow reference user datapath.'''
self.cmd('kill %ofdatapath')
self.cmd('kill %ofprotocol')
def stop( self ):
"Stop OpenFlow reference user datapath."
self.cmd( 'kill %ofdatapath' )
self.cmd( 'kill %ofprotocol' )
class KernelSwitch(Switch):
'''Kernel-space switch.
class KernelSwitch( 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.
@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)
def __init__( self, name, dp=None, dpid=None ):
"""Init.
name:
dp: netlink id ( 0, 1, 2, ... )
dpid: datapath ID as unsigned int; random value if None"""
Switch.__init__( self, name, inNamespace=False )
self.dp = dp
self.dpid = dpid
def start(self, controllers):
'''Start up reference kernel datapath.'''
def start( self, controllers ):
"Start up reference kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log'
quietRun('ifconfig lo up')
quietRun( 'ifconfig lo up' )
# Delete local datapath if it exists;
# then create a new one monitoring the given interfaces
quietRun('dpctl deldp nl:%i' % self.dp)
self.cmdPrint('dpctl adddp nl:%i' % self.dp)
quietRun( 'dpctl deldp nl:%i' % self.dp )
self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
if self.dpid:
intf = 'of%i' % self.dp
mac_str = macColonHex(self.dpid)
self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
macStr = macColonHex( self.dpid )
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
if len(self.ports) != max(self.ports.keys()) + 1:
raise Exception('only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports)
intfs = [self.ports[port] for port in self.ports.keys()]
self.cmdPrint('dpctl addif nl:' + str(self.dp) + ' ' + ' '.join(intfs))
if len( self.ports ) != max( self.ports.keys() ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports )
intfs = [ self.ports[ port ] for port in self.ports.keys() ]
self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon
self.cmdPrint('ofprotocol nl:' + str(self.dp) + ' tcp:' +
controllers['c0'].IP() + ':' +
str(controllers['c0'].port) +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
controllers[ 'c0' ].IP() + ':' +
str( controllers[ 'c0' ].port ) +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False
def stop(self):
'''Terminate kernel datapath.'''
quietRun('dpctl deldp nl:%i' % self.dp)
def stop( self ):
"Terminate kernel datapath."
quietRun( 'dpctl deldp nl:%i' % self.dp )
# In theory the interfaces should go away after we shut down.
# 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
# have been removed by the kernel. Unfortunately this is very slow.
self.cmd('kill %ofprotocol')
self.cmd( 'kill %ofprotocol' )
for intf in self.intfs:
quietRun('ip link del ' + intf)
lg.info('.')
quietRun( 'ip link del ' + intf )
lg.info( '.' )
class OVSKernelSwitch(Switch):
'''Open VSwitch kernel-space switch.
class OVSKernelSwitch( 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.
@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)
def __init__( self, name, dp=None, dpid=None ):
"""Init.
name:
dp: netlink id ( 0, 1, 2, ... )
dpid: datapath ID as unsigned int; random value if None"""
Switch.__init__( self, name, inNamespace=False )
self.dp = dp
self.dpid = dpid
def start(self, controllers):
'''Start up kernel datapath.'''
def start( self, controllers ):
"Start up kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log'
quietRun('ifconfig lo up')
quietRun( 'ifconfig lo up' )
# Delete local datapath if it exists;
# then create a new one monitoring the given interfaces
quietRun('ovs-dpctl del-dp dp%i' % self.dp)
self.cmdPrint('ovs-dpctl add-dp dp%i' % self.dp)
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp )
if self.dpid:
intf = 'dp' % self.dp
mac_str = macColonHex(self.dpid)
self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
macStr = macColonHex( self.dpid )
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
if len(self.ports) != max(self.ports.keys()) + 1:
raise Exception('only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports)
intfs = [self.ports[port] for port in self.ports.keys()]
self.cmdPrint('ovs-dpctl add-if dp' + str(self.dp) + ' ' +
' '.join(intfs))
if len( self.ports ) != max( self.ports.keys() ) + 1:
raise Exception( 'only contiguous, zero-indexed port ranges'
'supported: %s' % self.ports )
intfs = [ self.ports[ port ] for port in self.ports.keys() ]
self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon
self.cmdPrint('ovs-openflowd dp' + str(self.dp) + ' tcp:' +
controllers['c0'].IP() + ':' +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
controllers[ 'c0' ].IP() + ':' +
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False
def stop(self):
'''Terminate kernel datapath.'''
quietRun('ovs-dpctl del-dp dp%i' % self.dp)
def stop( self ):
"Terminate kernel datapath."
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
# In theory the interfaces should go away after we shut down.
# 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
# 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:
quietRun('ip link del ' + intf)
lg.info('.')
quietRun( 'ip link del ' + intf )
lg.info( '.' )
class Controller(Node):
'''A Controller is a Node that is running (or has execed) an
OpenFlow controller.'''
class Controller( Node ):
"""A Controller is a Node that is running ( or has execed ) an
OpenFlow controller."""
def __init__(self, name, inNamespace = False, controller = 'controller',
cargs = '-v ptcp:', cdir = None, ip_address="127.0.0.1",
port = 6633):
def __init__( self, name, inNamespace=False, controller='controller',
cargs='-v ptcp:', cdir=None, ipAddress="127.0.0.1",
port=6633 ):
self.controller = controller
self.cargs = cargs
self.cdir = cdir
self.ip_address = ip_address
self.ipAddress = ipAddress
self.port = port
Node.__init__(self, name, inNamespace = inNamespace)
Node.__init__( self, name, inNamespace=inNamespace )
def start(self):
'''Start <controller> <args> on controller.
Log to /tmp/cN.log
'''
def start( self ):
"""Start <controller> <args> on controller.
Log to /tmp/cN.log"""
cout = '/tmp/' + self.name + '.log'
if self.cdir is not None:
self.cmdPrint('cd ' + self.cdir)
self.cmdPrint(self.controller + ' ' + self.cargs +
' 1> ' + cout + ' 2> ' + cout + ' &')
self.cmdPrint( 'cd ' + self.cdir )
self.cmdPrint( self.controller + ' ' + self.cargs +
' 1> ' + cout + ' 2> ' + cout + ' &' )
self.execed = False
def stop(self):
'''Stop controller.'''
self.cmd('kill %' + self.controller)
def stop( self ):
"Stop controller."
self.cmd( 'kill %' + self.controller )
self.terminate()
def IP(self):
'''Return IP address of the Controller'''
return self.ip_address
def IP( self ):
"Return IP address of the Controller"
return self.ipAddress
class ControllerParams(object):
'''Container for controller IP parameters.'''
class ControllerParams( object ):
"Container for controller IP parameters."
def __init__(self, ip, subnet_size):
'''Init.
@param ip integer, controller IP
@param subnet_size integer, ex 8 for slash-8, covering 17M
'''
def __init__( self, ip, subnetSize ):
"""Init.
ip: integer, controller IP
subnetSize: integer, ex 8 for slash-8, covering 17M"""
self.ip = ip
self.subnet_size = subnet_size
self.subnetSize = subnetSize
class NOX(Controller):
'''Controller to run a NOX application.'''
class NOX( Controller ):
"Controller to run a NOX application."
def __init__(self, name, inNamespace = False, nox_args = None, **kwargs):
'''Init.
@param name name to give controller
@param nox_args list of args, or single arg, to pass to NOX
'''
if type(nox_args) != list:
nox_args = [nox_args]
if not nox_args:
nox_args = ['packetdump']
nox_core_dir = os.environ['NOX_CORE_DIR']
if not nox_core_dir:
raise Exception('please set NOX_CORE_DIR env var\n')
Controller.__init__(self, name,
controller = nox_core_dir + '/nox_core',
cargs = '--libdir=/usr/local/lib -v -i ptcp: ' + \
' '.join(nox_args),
cdir = nox_core_dir, **kwargs)
def __init__( self, name, inNamespace=False, noxArgs=None, **kwargs ):
"""Init.
name: name to give controller
noxArgs: list of args, or single arg, to pass to NOX"""
if type( noxArgs ) != list:
noxArgs = [ noxArgs ]
if not noxArgs:
noxArgs = [ 'packetdump' ]
noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
if not noxCoreDir:
raise Exception( 'please set NOX_CORE_DIR env var\n' )
Controller.__init__( self, name,
controller=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \
' '.join( noxArgs ),
cdir = noxCoreDir, **kwargs )
class RemoteController(Controller):
'''Controller running outside of Mininet's control.'''
class RemoteController( Controller ):
"Controller running outside of Mininet's control."
def __init__(self, name, inNamespace = False, ip_address = '127.0.0.1',
port = 6633):
'''Init.
def __init__( self, name, inNamespace=False, ipAddress='127.0.0.1',
port=6633 ):
"""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
@param ip_address the IP address where the remote controller is
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.'''
def start( self ):
"Overridden to do nothing."
return
def stop(self):
'''Overridden to do nothing.'''
def stop( self ):
"Overridden to do nothing."
return
+89 -154
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
'''Utility functions for Mininet.'''
"Utility functions for Mininet."
from time import sleep
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
def run( cmd ):
"""Simple interface to subprocess.call()
cmd: list of command params"""
return call( cmd.split( ' ' ) )
def run(cmd):
'''Simple interface to subprocess.call()
def checkRun( cmd ):
"""Simple interface to subprocess.check_call()
cmd: list of command params"""
check_call( cmd.split( ' ' ) )
@param cmd list of command params
'''
return call(cmd.split(' '))
def checkRun(cmd):
'''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)
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)
readable.register( popen.stdout )
while True:
while readable.poll():
data = popen.stdout.read(1024)
if len(data) == 0:
data = popen.stdout.read( 1024 )
if len( data ) == 0:
break
output += data
popen.poll()
@@ -50,42 +41,6 @@ def quietRun(cmd):
break
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
#
# 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
# explicitly moved.
def makeIntfPair(intf1, intf2):
'''Make a veth pair.
@param intf1 string, interface
@param intf2 string, interface
@return success boolean
'''
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)
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 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):
'''Move interface to node.
@param intf string, interface
@param node Node object
@param print_error if true, print error
'''
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
quietRun(cmd)
links = node.cmd('ip link show')
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 print_error:
lg.error('*** Error: moveIntf: % not successfully moved to %s:\n' %
(intf, node.name))
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, moveIntf, intf, node, printError )
def retry(n, retry_delay, fn, *args, **keywords):
'''Try something N times before giving up.
@param n number of times to retry
@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
'''
def createLink( node1, node2, retries=10, delaySecs=0.001 ):
"""Create a link between nodes, making an interface for each.
node1: Node object
node2: Node object"""
intf1 = node1.newIntf()
intf2 = node2.newIntf()
makeIntfPair(intf1, intf2)
makeIntfPair( intf1, intf2 )
if node1.inNamespace:
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf1, node1)
retry( retries, delaySecs, moveIntf, intf1, node1 )
if node2.inNamespace:
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf2, node2)
node1.connection[intf1] = (node2, intf2)
node2.connection[intf2] = (node1, intf1)
retry( retries, delaySecs, moveIntf, intf2, node2 )
node1.connection[ intf1 ] = ( node2, intf2 )
node2.connection[ intf2 ] = ( node1, intf1 )
return intf1, intf2
def fixLimits():
'''Fix ridiculously small resource limits.'''
setrlimit(RLIMIT_NPROC, (4096, 8192))
setrlimit(RLIMIT_NOFILE, (16384, 32768))
"Fix ridiculously small resource limits."
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
def _colonHex(val, bytes):
'''Generate colon-hex string.
@param val input as unsigned int
@param bytes number of bytes to convert
@return ch_str colon-hex string
'''
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):
pieces.append('%02x' % (((0xff << (i * 8)) & val) >> (i * 8)))
ch_str = ':'.join(pieces)
return ch_str
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 macColonHex(mac):
'''Generate MAC colon-hex string from unsigned int.
@param mac MAC address as unsigned int
@return mac_str MAC colon-hex string
'''
return _colonHex(mac, 6)
def ipStr(ip):
'''Generate IP address string
@return ip addr string
'''
hi = (ip & 0xff0000) >> 16
mid = (ip & 0xff00) >> 8
def ipStr( ip ):
"""Generate IP address string
returns: ip addr string"""
hi = ( ip & 0xff0000 ) >> 16
mid = ( ip & 0xff00 ) >> 8
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
"""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
from subprocess import Popen
from mininet.util import quietRun
def makeXterm(node, title):
'''Run screen on a node, and hook up an xterm.
@param node Node object
@param title base title
@return process created
'''
def makeXterm( node, title ):
"""Run screen on a node, and hook up an xterm.
node: Node object
title: base title
returns: process created"""
title += ': ' + node.name
if not node.inNamespace:
title += ' (root)'
cmd = ['xterm', '-title', title, '-e']
cmd = [ 'xterm', '-title', title, '-e' ]
if not node.execed:
node.cmdPrint('screen -dmS ' + node.name)
cmd += ['screen', '-D', '-RR', '-S', node.name]
node.cmdPrint( 'screen -dmS ' + node.name )
cmd += [ 'screen', '-D', '-RR', '-S', node.name ]
else:
cmd += ['sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log']
return Popen(cmd)
cmd += [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
return Popen( cmd )
def cleanUpScreens():
'''Remove moldy old screen sessions.'''
"Remove moldy old screen sessions."
r = r'(\d+.[hsc]\d+)'
output = quietRun('screen -ls').split('\n')
output = quietRun( 'screen -ls' ).split( '\n' )
for line in output:
m = re.search(r, line)
m = re.search( r, line )
if m:
quietRun('screen -S ' + m.group(1) + ' -X kill')
quietRun( 'screen -S ' + m.group( 1 ) + ' -X kill' )
def makeXterms(nodes, title):
'''Create XTerms.
@param nodes list of Node objects
@param title base title for each
@return list of created xterm processes
'''
return [makeXterm(node, title) for node in nodes]
def makeXterms( nodes, title ):
"""Create XTerms.
nodes: list of Node objects
title: base title for each
returns: list of created xterm processes"""
return [ makeXterm( node, title ) for node in nodes ]
+8 -8
View File
@@ -1,16 +1,16 @@
#!/usr/bin/python
"""
unpep8:
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Python style:
Python style
- Reinstates CapWords for methods and instance variables.
- Gets rid of triple single quotes.
- Eliminates triple quotes on single lines.
- Inserts extra spaces to improve readability.
- Fixes Doxygen (or doxypy) ugliness.
usage: unpep8 < old.py > new.py
- Reinstates CapWords for methods and instance variables
- Gets rid of triple single quotes
- Eliminates triple quotes on single lines
- Inserts extra spaces to improve readability
- Fixes Doxygen (or doxypy) ugliness
Does the following translations: