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"
+26 -33
View File
@@ -1,4 +1,4 @@
'''Logging functions for Mininet.''' "Logging functions for Mininet."
import logging import logging
from logging import Logger from logging import Logger
@@ -11,31 +11,26 @@ LEVELS = {'debug': logging.DEBUG,
'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 Since StreamHandler automatically adds newlines, define a mod to more
easily support interactive mode when we want it, or errors-only logging for easily support interactive mode when we want it, or errors-only logging
running unit tests. for running unit tests."""
'''
def emit( self, record ): def emit( self, record ):
''' """Emit a record.
Emit a record.
If a formatter is specified, it is used to format the 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 The record is then written to the stream with a trailing newline
[ N.B. this may be removed depending on feedback ]. If exception [ N.B. this may be removed depending on feedback ]. If exception
information is present, it is formatted using information is present, it is formatted using
traceback.print_exception and appended to the stream. traceback.printException 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'
@@ -54,14 +49,13 @@ class StreamHandlerNoNewline(logging.StreamHandler):
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_ ): def __init__( mcs, name, bases, dict_ ):
super( Singleton, mcs ).__init__( name, bases, dict_ ) super( Singleton, mcs ).__init__( name, bases, dict_ )
@@ -74,16 +68,17 @@ class Singleton(type):
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
@@ -91,8 +86,8 @@ class MininetLogger(Logger, object):
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 ):
@@ -102,22 +97,20 @@ class MininetLogger(Logger, object):
# 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):
'''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 levelName: level name from LEVELS"""
''' level = LOGLEVELDEFAULT
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' )
+214 -241
View File
@@ -1,8 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
"""Mininet: A simple networking testbed for OpenFlow! """Mininet: A simple networking testbed for OpenFlow!
author: Bob Lantz ( rlantz@cs.stanford.edu )
@author Bob Lantz (rlantz@cs.stanford.edu) author: Brandon Heller ( brandonh@stanford.edu )
@author Brandon Heller (brandonh@stanford.edu)
Mininet creates scalable OpenFlow test networks by using Mininet creates scalable OpenFlow test networks by using
process-based virtualization and network namespaces. process-based virtualization and network namespaces.
@@ -42,9 +41,7 @@ reside in the host nodes that the switches are connected to.
Naming: Naming:
Host nodes are named h1-hN Host nodes are named h1-hN
Switch nodes are named s0-sN Switch nodes are named s0-sN
Interfaces are named {nodename}-eth0 .. {nodename}-ethN, Interfaces are named { nodename }-eth0 .. { nodename }-ethN,"""
"""
import os import os
import re import re
import signal import signal
@@ -55,12 +52,11 @@ from time import sleep
from mininet.log import lg from mininet.log import lg
from mininet.node import KernelSwitch, OVSKernelSwitch from mininet.node import KernelSwitch, OVSKernelSwitch
from mininet.util import quietRun, fixLimits from mininet.util import quietRun, fixLimits
from mininet.util import make_veth_pair, move_intf, retry, MOVEINTF_DELAY from mininet.util import makeIntfPair, moveIntf
from mininet.xterm import cleanUpScreens, makeXterms from mininet.xterm import cleanUpScreens, makeXterms
DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ] DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ]
def init(): def init():
"Initialize Mininet." "Initialize Mininet."
if os.getuid() != 0: if os.getuid() != 0:
@@ -74,28 +70,25 @@ def init():
raise Exception( "Could not find netns; see INSTALL" ) raise Exception( "Could not find netns; see INSTALL" )
fixLimits() fixLimits()
class Mininet( object ): class Mininet( object ):
'''Network emulation with hosts spawned in network namespaces.''' "Network emulation with hosts spawned in network namespaces."
def __init__( self, topo, switch, host, controller, cparams, def __init__( self, topo, switch, host, controller, cparams,
build=True, xterms=False, cleanup=False, build=True, xterms=False, cleanup=False,
in_namespace = False, inNamespace=False,
auto_set_macs = False, auto_static_arp = False): autoSetMacs=False, autoStaticArp=False ):
'''Create Mininet object. """Create Mininet object.
topo: Topo object
@param topo Topo object switch: Switch class
@param switch Switch class host: Host class
@param host Host class controller: Controller class
@param controller Controller class cparams: ControllerParams object
@param cparams ControllerParams object now: build now?
@param now build now? xterms: if build now, spawn xterms?
@param xterms if build now, spawn xterms? cleanup: if build now, cleanup before creating?
@param cleanup if build now, cleanup before creating? inNamespace: spawn switches and controller in net namespaces?
@param in_namespace spawn switches and controller in net namespaces? autoSetMacs: set MAC addrs to DPIDs?
@param auto_set_macs set MAC addrs to DPIDs? autoStaticArp: set all-pairs static MAC addrs?"""
@param auto_static_arp set all-pairs static MAC addrs?
'''
self.topo = topo self.topo = topo
self.switch = switch self.switch = switch
self.host = host self.host = host
@@ -104,79 +97,71 @@ class Mininet(object):
self.nodes = {} # dpid to Node{ Host, Switch } objects self.nodes = {} # dpid to Node{ Host, Switch } objects
self.controllers = {} # controller name to Controller objects self.controllers = {} # controller name to Controller objects
self.dps = 0 # number of created kernel datapaths self.dps = 0 # number of created kernel datapaths
self.in_namespace = in_namespace self.inNamespace = inNamespace
self.xterms = xterms self.xterms = xterms
self.cleanup = cleanup self.cleanup = cleanup
self.auto_set_macs = auto_set_macs self.autoSetMacs = autoSetMacs
self.auto_static_arp = auto_static_arp self.autoStaticArp = autoStaticArp
self.terms = [] # list of spawned xterm processes self.terms = [] # list of spawned xterm processes
if build: if build:
self.build() self.build()
def _add_host(self, dpid): def _addHost( self, dpid ):
'''Add host. """Add host.
dpid: DPID of host to add"""
@param dpid DPID of host to add
'''
host = self.host( 'h_' + self.topo.name( dpid ) ) host = self.host( 'h_' + self.topo.name( dpid ) )
# for now, assume one interface per host. # for now, assume one interface per host.
host.intfs.append( 'h_' + self.topo.name( dpid ) + '-eth0' ) host.intfs.append( 'h_' + self.topo.name( dpid ) + '-eth0' )
self.nodes[ dpid ] = host self.nodes[ dpid ] = host
#lg.info( '%s ' % host.name ) #lg.info( '%s ' % host.name )
def _add_switch(self, dpid): def _addSwitch( self, dpid ):
'''Add switch. """Add switch.
dpid: DPID of switch to add"""
@param dpid DPID of switch to add
'''
sw = None sw = None
sw_dpid = None swDpid = None
if self.auto_set_macs: if self.autoSetMacs:
sw_dpid = dpid swDpid = dpid
if self.switch is KernelSwitch or self.switch is OVSKernelSwitch: if self.switch is KernelSwitch or self.switch is OVSKernelSwitch:
sw = self.switch( 's_' + self.topo.name( dpid ), dp = self.dps, sw = self.switch( 's_' + self.topo.name( dpid ), dp = self.dps,
dpid = sw_dpid) dpid = swDpid )
self.dps += 1 self.dps += 1
else: else:
sw = self.switch( 's_' + self.topo.name( dpid ) ) sw = self.switch( 's_' + self.topo.name( dpid ) )
self.nodes[ dpid ] = sw self.nodes[ dpid ] = sw
def _add_link(self, src, dst): def _addLink( self, src, dst ):
'''Add link. """Add link.
src: source DPID
@param src source DPID dst: destination DPID"""
@param dst destination DPID srcPort, dstPort = self.topo.port( src, dst )
''' srcNode = self.nodes[ src ]
src_port, dst_port = self.topo.port(src, dst) dstNode = self.nodes[ dst ]
src_node = self.nodes[src] srcIntf = srcNode.intfName( srcPort )
dst_node = self.nodes[dst] dstIntf = dstNode.intfName( dstPort )
src_intf = src_node.intfName(src_port) makeIntfPair( srcIntf, dstIntf )
dst_intf = dst_node.intfName(dst_port) srcNode.intfs.append( srcIntf )
make_veth_pair(src_intf, dst_intf) dstNode.intfs.append( dstIntf )
src_node.intfs.append(src_intf) srcNode.ports[ srcPort ] = srcIntf
dst_node.intfs.append(dst_intf) dstNode.ports[ dstPort ] = dstIntf
src_node.ports[src_port] = src_intf
dst_node.ports[dst_port] = dst_intf
#lg.info( '\n' ) #lg.info( '\n' )
#lg.info('added intf %s to src node %x\n' % (src_intf, src)) #lg.info( 'added intf %s to src node %x\n' % ( srcIntf, src ) )
#lg.info('added intf %s to dst node %x\n' % (dst_intf, dst)) #lg.info( 'added intf %s to dst node %x\n' % ( dstIntf, dst ) )
if src_node.inNamespace: if srcNode.inNamespace:
#lg.info( 'moving src w/inNamespace set\n' ) #lg.info( 'moving src w/inNamespace set\n' )
retry(3, MOVEINTF_DELAY, move_intf, src_intf, src_node) moveIntf( srcIntf, srcNode )
if dst_node.inNamespace: if dstNode.inNamespace:
#lg.info( 'moving dst w/inNamespace set\n' ) #lg.info( 'moving dst w/inNamespace set\n' )
retry(3, MOVEINTF_DELAY, move_intf, dst_intf, dst_node) moveIntf( dstIntf, dstNode )
src_node.connection[src_intf] = (dst_node, dst_intf) srcNode.connection[ srcIntf ] = ( dstNode, dstIntf )
dst_node.connection[dst_intf] = (src_node, src_intf) dstNode.connection[ dstIntf ] = ( srcNode, srcIntf )
def _add_controller(self, controller): def _addController( self, controller ):
'''Add controller. """Add controller.
controller: Controller class"""
@param controller Controller class controller = self.controller( 'c0', self.inNamespace )
'''
controller = self.controller('c0', self.in_namespace)
if controller: # allow controller-less setups if controller: # allow controller-less setups
self.controllers[ 'c0' ] = controller self.controllers[ 'c0' ] = controller
@@ -202,24 +187,23 @@ class Mininet(object):
# network ( since real networks may need one! ) # network ( since real networks may need one! )
def _configureControlNetwork( self ): def _configureControlNetwork( self ):
'''Configure control network.''' "Configure control network."
self._configureRoutedControlNetwork() self._configureRoutedControlNetwork()
def _configureRoutedControlNetwork( self ): def _configureRoutedControlNetwork( self ):
'''Configure a routed control network on controller and switches. """Configure a routed control network on controller and switches.
For use with the user datapath only right now. For use with the user datapath only right now.
TODO( brandonh ) test this code!
"""
@todo(brandonh) Test this code!
'''
# params were: controller, switches, ips # params were: controller, switches, ips
controller = self.controllers[ 'c0' ] controller = self.controllers[ 'c0' ]
lg.info( '%s <-> ' % controller.name ) lg.info( '%s <-> ' % controller.name )
for switch_dpid in self.topo.switches(): for switchDpid in self.topo.switches():
switch = self.nodes[switch_dpid] switch = self.nodes[ switchDpid ]
lg.info( '%s ' % switch.name ) lg.info( '%s ' % switch.name )
sip = self.topo.ip(switch_dpid)#ips.next() sip = self.topo.ip( switchDpid )#ips.next()
sintf = switch.intfs[ 0 ] sintf = switch.intfs[ 0 ]
node, cintf = switch.connection[ sintf ] node, cintf = switch.connection[ sintf ]
if node != controller: if node != controller:
@@ -228,8 +212,8 @@ class Mininet(object):
switch.name ) switch.name )
exit( 1 ) exit( 1 )
controller.setIP( cintf, self.cparams.ip, '/' + controller.setIP( cintf, self.cparams.ip, '/' +
self.cparams.subnet_size) self.cparams.subnetSize )
switch.setIP(sintf, sip, '/' + self.cparams.subnet_size) switch.setIP( sintf, sip, '/' + self.cparams.subnetSize )
controller.setHostRoute( sip, cintf ) controller.setHostRoute( sip, cintf )
switch.setHostRoute( self.cparams.ip, sintf ) switch.setHostRoute( self.cparams.ip, sintf )
lg.info( '\n' ) lg.info( '\n' )
@@ -237,24 +221,25 @@ class Mininet(object):
while not controller.intfIsUp( controller.intfs[ 0 ] ): while not controller.intfIsUp( controller.intfs[ 0 ] ):
lg.info( '*** Waiting for %s to come up\n', controller.intfs[ 0 ] ) lg.info( '*** Waiting for %s to come up\n', controller.intfs[ 0 ] )
sleep( 1 ) sleep( 1 )
for switch_dpid in self.topo.switches(): for switchDpid in self.topo.switches():
switch = self.nodes[switch_dpid] switch = self.nodes[ switchDpid ]
while not switch.intfIsUp( switch.intfs[ 0 ] ): while not switch.intfIsUp( switch.intfs[ 0 ] ):
lg.info('*** Waiting for %s to come up\n' % switch.intfs[0]) lg.info( '*** Waiting for %s to come up\n' %
switch.intfs[ 0 ] )
sleep( 1 ) sleep( 1 )
if self.ping( hosts=[ switch, controller ] ) != 0: if self.ping( hosts=[ switch, controller ] ) != 0:
lg.error( '*** Error: control network test failed\n' ) lg.error( '*** Error: control network test failed\n' )
exit( 1 ) exit( 1 )
lg.info( '\n' ) lg.info( '\n' )
def _config_hosts(self): def _configHosts( self ):
'''Configure a set of hosts.''' "Configure a set of hosts."
# params were: hosts, ips # params were: hosts, ips
for host_dpid in self.topo.hosts(): for hostDpid in self.topo.hosts():
host = self.nodes[host_dpid] host = self.nodes[ hostDpid ]
hintf = host.intfs[ 0 ] hintf = host.intfs[ 0 ]
host.setIP(hintf, self.topo.ip(host_dpid), host.setIP( hintf, self.topo.ip( hostDpid ),
'/' + str(self.cparams.subnet_size)) '/' + str( self.cparams.subnetSize ) )
host.setDefaultRoute( hintf ) host.setDefaultRoute( hintf )
# You're low priority, dude! # You're low priority, dude!
quietRun( 'renice +18 -p ' + repr( host.pid ) ) quietRun( 'renice +18 -p ' + repr( host.pid ) )
@@ -262,111 +247,108 @@ class Mininet(object):
lg.info( '\n' ) lg.info( '\n' )
def build( self ): def build( self ):
'''Build mininet. """Build mininet.
At the end of this function, everything should be connected
At the end of this function, everything should be connected and up. and up."""
'''
if self.cleanup: if self.cleanup:
pass # cleanup pass # cleanup
# validate topo? # validate topo?
lg.info( '*** Adding controller\n' ) lg.info( '*** Adding controller\n' )
self._add_controller(self.controller) self._addController( self.controller )
lg.info( '*** Creating network\n' ) lg.info( '*** Creating network\n' )
lg.info( '*** Adding hosts:\n' ) lg.info( '*** Adding hosts:\n' )
for host in sorted( self.topo.hosts() ): for host in sorted( self.topo.hosts() ):
self._add_host(host) self._addHost( host )
lg.info( '0x%x ' % host ) lg.info( '0x%x ' % host )
lg.info( '\n*** Adding switches:\n' ) lg.info( '\n*** Adding switches:\n' )
for switch in sorted( self.topo.switches() ): for switch in sorted( self.topo.switches() ):
self._add_switch(switch) self._addSwitch( switch )
lg.info( '0x%x ' % switch ) lg.info( '0x%x ' % switch )
lg.info( '\n*** Adding edges:\n' ) lg.info( '\n*** Adding edges:\n' )
for src, dst in sorted( self.topo.edges() ): for src, dst in sorted( self.topo.edges() ):
self._add_link(src, dst) self._addLink( src, dst )
lg.info( '(0x%x, 0x%x) ' % ( src, dst ) ) lg.info( '(0x%x, 0x%x) ' % ( src, dst ) )
lg.info( '\n' ) lg.info( '\n' )
if self.in_namespace: if self.inNamespace:
lg.info( '*** Configuring control network\n' ) lg.info( '*** Configuring control network\n' )
self._configureControlNetwork() self._configureControlNetwork()
lg.info( '*** Configuring hosts\n' ) lg.info( '*** Configuring hosts\n' )
self._config_hosts() self._configHosts()
if self.xterms: if self.xterms:
self.start_xterms() self.startXterms()
if self.auto_set_macs: if self.autoSetMacs:
self.set_macs() self.setMacs()
if self.auto_static_arp: if self.autoStaticArp:
self.static_arp() self.staticArp()
def switch_nodes(self): def switchNodes( self ):
'''Return switch nodes.''' "Return switch nodes."
return [ self.nodes[ dpid ] for dpid in self.topo.switches() ] return [ self.nodes[ dpid ] for dpid in self.topo.switches() ]
def host_nodes(self): def hostNodes( self ):
'''Return host nodes.''' "Return host nodes."
return [ self.nodes[ dpid ] for dpid in self.topo.hosts() ] return [ self.nodes[ dpid ] for dpid in self.topo.hosts() ]
def start_xterms(self): def startXterms( self ):
'''Start an xterm for each node in the topo.''' "Start an xterm for each node in the topo."
lg.info( "*** Running xterms on %s\n" % os.environ[ 'DISPLAY' ] ) lg.info( "*** Running xterms on %s\n" % os.environ[ 'DISPLAY' ] )
cleanUpScreens() cleanUpScreens()
self.terms += makeXterms( self.controllers.values(), 'controller' ) self.terms += makeXterms( self.controllers.values(), 'controller' )
self.terms += makeXterms(self.switch_nodes(), 'switch') self.terms += makeXterms( self.switchNodes(), 'switch' )
self.terms += makeXterms(self.host_nodes(), 'host') self.terms += makeXterms( self.hostNodes(), 'host' )
def stop_xterms(self): def stopXterms( self ):
'''Kill each xterm.''' "Kill each xterm."
# Kill xterms # Kill xterms
for term in self.terms: for term in self.terms:
os.kill( term.pid, signal.SIGKILL ) os.kill( term.pid, signal.SIGKILL )
cleanUpScreens() cleanUpScreens()
def set_macs(self): def setMacs( self ):
'''Set MAC addrs to correspond to datapath IDs on hosts. """Set MAC addrs to correspond to datapath IDs on hosts.
Assume that the host only has one interface."""
Assume that the host only has one interface.
'''
for dpid in self.topo.hosts(): for dpid in self.topo.hosts():
host_node = self.nodes[dpid] hostNode = self.nodes[ dpid ]
host_node.setMAC(host_node.intfs[0], dpid) hostNode.setMAC( hostNode.intfs[ 0 ], dpid )
def static_arp(self): def staticArp( self ):
'''Add all-pairs ARP entries to remove the need to handle broadcast.''' "Add all-pairs ARP entries to remove the need to handle broadcast."
for src in self.topo.hosts(): for src in self.topo.hosts():
src_node = self.nodes[src] srcNode = self.nodes[ src ]
for dst in self.topo.hosts(): for dst in self.topo.hosts():
if src != dst: if src != dst:
src_node.setARP(dst, dst) srcNode.setARP( dst, dst )
def start( self ): def start( self ):
'''Start controller and switches\n''' "Start controller and switches\n"
lg.info( '*** Starting controller\n' ) lg.info( '*** Starting controller\n' )
for cnode in self.controllers.values(): for cnode in self.controllers.values():
cnode.start() cnode.start()
lg.info( '*** Starting %s switches\n' % len( self.topo.switches() ) ) lg.info( '*** Starting %s switches\n' % len( self.topo.switches() ) )
for switch_dpid in self.topo.switches(): for switchDpid in self.topo.switches():
switch = self.nodes[switch_dpid] switch = self.nodes[ switchDpid ]
#lg.info( 'switch = %s' % switch ) #lg.info( 'switch = %s' % switch )
lg.info('0x%x ' % switch_dpid) lg.info( '0x%x ' % switchDpid )
switch.start( self.controllers ) switch.start( self.controllers )
lg.info( '\n' ) lg.info( '\n' )
def stop( self ): def stop( self ):
'''Stop the controller(s), switches and hosts\n''' "Stop the controller(s), switches and hosts\n"
if self.terms: if self.terms:
lg.info( '*** Stopping %i terms\n' % len( self.terms ) ) lg.info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stop_xterms() self.stopXterms()
lg.info( '*** Stopping %i hosts\n' % len( self.topo.hosts() ) ) lg.info( '*** Stopping %i hosts\n' % len( self.topo.hosts() ) )
for host_dpid in self.topo.hosts(): for hostDpid in self.topo.hosts():
host = self.nodes[host_dpid] host = self.nodes[ hostDpid ]
lg.info( '%s ' % host.name ) lg.info( '%s ' % host.name )
host.terminate() host.terminate()
lg.info( '\n' ) lg.info( '\n' )
lg.info( '*** Stopping %i switches\n' % len( self.topo.switches() ) ) lg.info( '*** Stopping %i switches\n' % len( self.topo.switches() ) )
for switch_dpid in self.topo.switches(): for switchDpid in self.topo.switches():
switch = self.nodes[switch_dpid] switch = self.nodes[ switchDpid ]
lg.info( '%s' % switch.name ) lg.info( '%s' % switch.name )
switch.stop() switch.stop()
lg.info( '\n' ) lg.info( '\n' )
@@ -376,7 +358,7 @@ class Mininet(object):
lg.info( '*** Test complete\n' ) lg.info( '*** Test complete\n' )
def run( self, test, **params ): def run( self, test, **params ):
'''Perform a complete start/test/stop cycle.''' "Perform a complete start/test/stop cycle."
self.start() self.start()
lg.info( '*** Running test\n' ) lg.info( '*** Running test\n' )
result = getattr( self, test )( **params ) result = getattr( self, test )( **params )
@@ -384,8 +366,8 @@ class Mininet(object):
return result return result
@staticmethod @staticmethod
def _parse_ping(pingOutput): def _parsePing( pingOutput ):
'''Parse ping output and return packets sent, received.''' "Parse ping output and return packets sent, received."
r = r'(\d+) packets transmitted, (\d+) received' r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput ) m = re.search( r, pingOutput )
if m == None: if m == None:
@@ -396,11 +378,9 @@ class Mininet(object):
return sent, received return sent, received
def ping( self, hosts=None ): def ping( self, hosts=None ):
'''Ping between all specified hosts. """Ping between all specified hosts.
hosts: list of host DPIDs
@param hosts list of host DPIDs returns: ploss packet loss percentage"""
@return ploss packet loss percentage
'''
#self.start() #self.start()
# check if running - only then, start? # check if running - only then, start?
packets = 0 packets = 0
@@ -409,14 +389,14 @@ class Mininet(object):
if not hosts: if not hosts:
hosts = self.topo.hosts() hosts = self.topo.hosts()
lg.info( '*** Ping: testing ping reachability\n' ) lg.info( '*** Ping: testing ping reachability\n' )
for node_dpid in hosts: for nodeDpid in hosts:
node = self.nodes[node_dpid] node = self.nodes[ nodeDpid ]
lg.info( '%s -> ' % node.name ) lg.info( '%s -> ' % node.name )
for dest_dpid in hosts: for destDpid in hosts:
dest = self.nodes[dest_dpid] dest = self.nodes[ destDpid ]
if node != dest: if node != dest:
result = node.cmd( 'ping -c1 ' + dest.IP() ) result = node.cmd( 'ping -c1 ' + dest.IP() )
sent, received = self._parse_ping(result) sent, received = self._parsePing( result )
packets += sent packets += sent
if received > sent: if received > sent:
lg.error( '*** Error: received too many packets' ) lg.error( '*** Error: received too many packets' )
@@ -431,29 +411,23 @@ class Mininet(object):
( ploss, lost, packets ) ) ( ploss, lost, packets ) )
return ploss return ploss
def ping_all(self): def pingAll( self ):
'''Ping between all hosts. """Ping between all hosts.
returns: ploss packet loss percentage"""
@return ploss packet loss percentage
'''
return self.ping() return self.ping()
def ping_pair(self): def pingPair( self ):
'''Ping between first two hosts, useful for testing. """Ping between first two hosts, useful for testing.
returns: ploss packet loss percentage"""
@return ploss packet loss percentage hostsSorted = sorted( self.topo.hosts() )
''' hosts = [ hostsSorted[ 0 ], hostsSorted[ 1 ] ]
hosts_sorted = sorted(self.topo.hosts())
hosts = [hosts_sorted[0], hosts_sorted[1]]
return self.ping( hosts=hosts ) return self.ping( hosts=hosts )
@staticmethod @staticmethod
def _parseIperf( iperfOutput ): def _parseIperf( iperfOutput ):
'''Parse iperf output and return bandwidth. """Parse iperf output and return bandwidth.
iperfOutput: string
@param iperfOutput string returns: result string"""
@return result string
'''
r = r'([\d\.]+ \w+/sec)' r = r'([\d\.]+ \w+/sec)'
m = re.search( r, iperfOutput ) m = re.search( r, iperfOutput )
if m: if m:
@@ -461,54 +435,52 @@ class Mininet(object):
else: else:
raise Exception( 'could not parse iperf output' ) raise Exception( 'could not parse iperf output' )
def iperf(self, hosts = None, l4_type = 'TCP', udp_bw = '10M', def iperf( self, hosts=None, l4Type='TCP', udpBw='10M',
verbose=False ): verbose=False ):
'''Run iperf between two hosts. """Run iperf between two hosts.
hosts: list of host DPIDs; if None, uses opposite hosts
@param hosts list of host DPIDs; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ]
@param l4_type string, one of [TCP, UDP] verbose: verbose printing
@param verbose verbose printing returns: results two-element array of server and client speeds"""
@return results two-element array of server and client speeds
'''
if not hosts: if not hosts:
hosts_sorted = sorted(self.topo.hosts()) hostsSorted = sorted( self.topo.hosts() )
hosts = [hosts_sorted[0], hosts_sorted[-1]] hosts = [ hostsSorted[ 0 ], hostsSorted[ -1 ] ]
else: else:
assert len( hosts ) == 2 assert len( hosts ) == 2
host0 = self.nodes[ hosts[ 0 ] ] host0 = self.nodes[ hosts[ 0 ] ]
host1 = self.nodes[ hosts[ 1 ] ] host1 = self.nodes[ hosts[ 1 ] ]
lg.info('*** Iperf: testing ' + l4_type + ' bandwidth between ') lg.info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
lg.info( "%s and %s\n" % ( host0.name, host1.name ) ) lg.info( "%s and %s\n" % ( host0.name, host1.name ) )
host0.cmd( 'killall -9 iperf' ) host0.cmd( 'killall -9 iperf' )
iperf_args = 'iperf ' iperfArgs = 'iperf '
bw_args = '' bwArgs = ''
if l4_type == 'UDP': if l4Type == 'UDP':
iperf_args += '-u ' iperfArgs += '-u '
bw_args = '-b ' + udp_bw + ' ' bwArgs = '-b ' + udpBw + ' '
elif l4_type != 'TCP': elif l4Type != 'TCP':
raise Exception('Unexpected l4 type: %s' % l4_type) raise Exception( 'Unexpected l4 type: %s' % l4Type )
server = host0.cmd(iperf_args + '-s &') server = host0.cmd( iperfArgs + '-s &' )
if verbose: if verbose:
lg.info( '%s\n' % server ) lg.info( '%s\n' % server )
client = host1.cmd(iperf_args + '-t 5 -c ' + host0.IP() + ' ' + client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' +
bw_args) bwArgs )
if verbose: if verbose:
lg.info( '%s\n' % client ) lg.info( '%s\n' % client )
server = host0.cmd( 'killall -9 iperf' ) server = host0.cmd( 'killall -9 iperf' )
if verbose: if verbose:
lg.info( '%s\n' % server ) lg.info( '%s\n' % server )
result = [ self._parseIperf( server ), self._parseIperf( client ) ] result = [ self._parseIperf( server ), self._parseIperf( client ) ]
if l4_type == 'UDP': if l4Type == 'UDP':
result.insert(0, udp_bw) result.insert( 0, udpBw )
lg.info( '*** Results: %s\n' % result ) lg.info( '*** Results: %s\n' % result )
return result return result
def iperf_udp(self, udp_bw = '10M'): def iperfUdp( self, udpBw='10M' ):
'''Run iperf UDP test.''' "Run iperf UDP test."
return self.iperf(l4_type = 'UDP', udp_bw = udp_bw) return self.iperf( l4Type='UDP', udpBw=udpBw )
def interact( self ): def interact( self ):
'''Start network and run our simple CLI.''' "Start network and run our simple CLI."
self.start() self.start()
result = MininetCLI( self ) result = MininetCLI( self )
self.stop() self.stop()
@@ -516,7 +488,7 @@ class Mininet(object):
class MininetCLI( object ): class MininetCLI( object ):
'''Simple command-line interface to talk to nodes.''' "Simple command-line interface to talk to nodes."
cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \ cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \
'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump' ] 'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump' ]
@@ -536,34 +508,34 @@ class MininetCLI(object):
# Commands # Commands
def help( self, args ): def help( self, args ):
'''Semi-useful help for CLI.''' "Semi-useful help for CLI."
help_str = 'Available commands are:' + str(self.cmds) + '\n' + \ helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n' +
'You may also send a command to a node using:\n' + \ 'You may also send a command to a node using:\n' +
' <node> command {args}\n' + \ ' <node> command {args}\n' +
'For example:\n' + \ 'For example:\n' +
' mininet> h0 ifconfig\n' + \ ' mininet> h0 ifconfig\n' +
'\n' + \ '\n' +
'The interpreter automatically substitutes IP ' + \ 'The interpreter automatically substitutes IP ' +
'addresses\n' + \ 'addresses\n' +
'for node names, so commands like\n' + \ 'for node names, so commands like\n' +
' mininet> h0 ping -c1 h1\n' + \ ' mininet> h0 ping -c1 h1\n' +
'should work.\n' + \ 'should work.\n' +
'\n\n' + \ '\n\n' +
'Interactive commands are not really supported yet,\n' + \ 'Interactive commands are not really supported yet,\n' +
'so please limit commands to ones that do not\n' + \ 'so please limit commands to ones that do not\n' +
'require user interaction and will terminate\n' + \ 'require user interaction and will terminate\n' +
'after a reasonable amount of time.\n' 'after a reasonable amount of time.\n' )
print(help_str) print( helpStr )
def nodes( self, args ): def nodes( self, args ):
'''List all nodes.''' "List all nodes."
lg.info('available nodes are: \n%s\n', nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
' '.join([node.name for node in sorted(self.nodelist)])) lg.info( 'available nodes are: \n%s\n' % nodes )
def net( self, args ): def net( self, args ):
'''List network connections.''' "List network connections."
for switch_dpid in self.mn.topo.switches(): for switchDpid in self.mn.topo.switches():
switch = self.mn.nodes[switch_dpid] switch = self.mn.nodes[ switchDpid ]
lg.info( '%s <->', switch.name ) lg.info( '%s <->', switch.name )
for intf in switch.intfs: for intf in switch.intfs:
node = switch.connection[ intf ] node = switch.connection[ intf ]
@@ -571,33 +543,33 @@ class MininetCLI(object):
lg.info( '\n' ) lg.info( '\n' )
def sh( self, args ): def sh( self, args ):
'''Run an external shell command''' "Run an external shell command"
call( [ 'sh', '-c' ] + args ) call( [ 'sh', '-c' ] + args )
def ping_all(self, args): def pingAll( self, args ):
'''Ping between all hosts.''' "Ping between all hosts."
self.mn.ping_all() self.mn.pingAll()
def ping_pair(self, args): def pingPair( self, args ):
'''Ping between first two hosts, useful for testing.''' "Ping between first two hosts, useful for testing."
self.mn.ping_pair() self.mn.pingPair()
def iperf( self, args ): def iperf( self, args ):
'''Simple iperf TCP test between two hosts.''' "Simple iperf TCP test between two hosts."
self.mn.iperf() self.mn.iperf()
def iperf_udp(self, args): def iperfUdp( self, args ):
'''Simple iperf UDP test between two hosts.''' "Simple iperf UDP test between two hosts."
udp_bw = args[0] if len(args) else '10M' udpBw = args[ 0 ] if len( args ) else '10M'
self.mn.iperf_udp(udp_bw) self.mn.iperfUdp( udpBw )
def intfs( self, args ): def intfs( self, args ):
'''List interfaces.''' "List interfaces."
for node in self.mn.nodes.values(): for node in self.mn.nodes.values():
lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) ) lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) )
def dump( self, args ): def dump( self, args ):
'''Dump node info.''' "Dump node info."
for node in self.mn.nodes.values(): for node in self.mn.nodes.values():
lg.info( '%s\n' % node ) lg.info( '%s\n' % node )
@@ -605,16 +577,16 @@ class MininetCLI(object):
# pylint: enable-msg=W0613 # pylint: enable-msg=W0613
def run( self ): def run( self ):
'''Read and execute commands.''' "Read and execute commands."
lg.warn( '*** Starting CLI:\n' ) lg.warn( '*** Starting CLI:\n' )
while True: while True:
lg.warn( 'mininet> ' ) lg.warn( 'mininet> ' )
input_line = sys.stdin.readline() inputLine = sys.stdin.readline()
if input_line == '': if inputLine == '':
break break
if input_line[-1] == '\n': if inputLine[ -1 ] == '\n':
input_line = input_line[:-1] inputLine = inputLine[ :-1 ]
cmd = input_line.split(' ') cmd = inputLine.split( ' ' )
first = cmd[ 0 ] first = cmd[ 0 ]
rest = cmd[ 1: ] rest = cmd[ 1: ]
if first in self.cmds and hasattr( self, first ): if first in self.cmds and hasattr( self, first ):
@@ -622,7 +594,8 @@ class MininetCLI(object):
elif first in self.nodemap and rest != []: elif first in self.nodemap and rest != []:
node = self.nodemap[ first ] node = self.nodemap[ first ]
# Substitute IP addresses for node names in command # Substitute IP addresses for node names in command
rest = [self.nodemap[arg].IP() if arg in self.nodemap else arg rest = [ self.nodemap[ arg ].IP()
if arg in self.nodemap else arg
for arg in rest ] for arg in rest ]
rest = ' '.join( rest ) rest = ' '.join( rest )
# Interactive commands don't work yet, and # Interactive commands don't work yet, and
+126 -172
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
@@ -14,21 +14,21 @@ 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()
@@ -49,49 +49,43 @@ class Node(object):
# replace with Port object, eventually # replace with Port object, eventually
def fdToNode( self, f ): def fdToNode( self, f ):
'''Insert docstring. """Insert docstring.
f: unknown
@param f unknown returns: bool unknown"""
@return bool unknown
'''
node = self.outToNode.get( f ) node = self.outToNode.get( f )
return node or self.inToNode.get( f ) return node or self.inToNode.get( f )
def cleanup( self ): def cleanup( self ):
'''Help python collect its garbage.''' "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"""
@param fileno_max unknown return os.read( self.stdout.fileno(), filenoMax )
'''
return os.read(self.stdout.fileno(), fileno_max)
def write( self, data ): def write( self, data ):
'''Write data to node. """Write data to node.
data: string"""
@param data string
'''
os.write( self.stdin.fileno(), data ) os.write( self.stdin.fileno(), data )
def terminate( self ): def terminate( self ):
'''Send kill signal to Node and cleanup after it.''' "Send kill signal to Node and cleanup after it."
os.kill( self.pid, signal.SIGKILL ) 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 = '&'
@@ -104,7 +98,7 @@ class Node(object):
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 )
@@ -115,16 +109,14 @@ class Node(object):
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:
@@ -139,18 +131,14 @@ class Node(object):
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 ) #lg.info( '*** %s : %s', self.name, cmd )
result = self.cmd( cmd ) result = self.cmd( cmd )
#lg.info( '%s\n', result ) #lg.info( '%s\n', result )
@@ -158,72 +146,62 @@ class Node(object):
# 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 )
'''
mac_str = macColonHex(mac)
result = self.cmd( [ 'ifconfig', intf, 'down' ] ) result = self.cmd( [ 'ifconfig', intf, 'down' ] )
result += self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str]) result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
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 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' ] ) result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
self.ips[ intf ] = ip 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
@param ip IP address as dotted decimal intf: string, interface name"""
@param intf string, interface name
'''
return self.cmd( 'route add -host ' + ip + ' dev ' + intf ) return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
def setDefaultRoute( self, intf ): def setDefaultRoute( self, intf ):
'''Set the default route to go through intf. """Set the default route to go through intf.
intf: string, interface name"""
@param intf string, interface name
'''
self.cmd( 'ip route flush' ) self.cmd( 'ip route flush' )
return self.cmd( 'route add default ' + intf ) return self.cmd( 'route add default ' + intf )
def IP( self ): def IP( self ):
'''Return IP address of first interface''' "Return IP address of first interface"
if len( self.intfs ) > 0: if len( self.intfs ) > 0:
return self.ips.get( self.intfs[ 0 ], None ) return self.ips.get( self.intfs[ 0 ], None )
def intfIsUp( self ): def intfIsUp( self ):
'''Check if one of our interfaces is up.''' "Check if one of our interfaces is up."
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] ) return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
# Other methods # Other methods
@@ -237,19 +215,17 @@ class Node(object):
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:
@@ -257,7 +233,7 @@ class Switch(Node):
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:
@@ -265,25 +241,18 @@ class Switch(Node):
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 ): def __init__( self, name ):
'''Init. """Init.
name: name for the switch"""
@param name
'''
Switch.__init__( self, name, inNamespace=False ) Switch.__init__( self, name, inNamespace=False )
def start( self, controllers ): def start( self, controllers ):
'''Start OpenFlow reference user datapath. """Start OpenFlow reference user datapath.
Log to /tmp/sN-{ ofd,ofp }.log. Log to /tmp/sN-{ ofd,ofp }.log.
controllers: dict of controller names to objects"""
@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' ]
@@ -298,30 +267,26 @@ class UserSwitch(Switch):
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 ): def __init__( self, name, dp=None, dpid=None ):
'''Init. """Init.
name:
@param name dp: netlink id ( 0, 1, 2, ... )
@param dp netlink id (0, 1, 2, ...) dpid: datapath ID as unsigned int; random value if None"""
@param dpid datapath ID as unsigned int; random value if None
'''
Switch.__init__( self, name, inNamespace=False ) 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;
@@ -330,14 +295,15 @@ class KernelSwitch(Switch):
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() + ':' +
@@ -346,7 +312,7 @@ class KernelSwitch(Switch):
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
@@ -359,24 +325,20 @@ class KernelSwitch(Switch):
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 ): def __init__( self, name, dp=None, dpid=None ):
'''Init. """Init.
name:
@param name dp: netlink id ( 0, 1, 2, ... )
@param dp netlink id (0, 1, 2, ...) dpid: datapath ID as unsigned int; random value if None"""
@param dpid datapath ID as unsigned int; random value if None
'''
Switch.__init__( self, name, inNamespace=False ) 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;
@@ -385,8 +347,8 @@ class OVSKernelSwitch(Switch):
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'
@@ -401,7 +363,7 @@ class OVSKernelSwitch(Switch):
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
@@ -414,24 +376,22 @@ class OVSKernelSwitch(Switch):
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 )
@@ -440,69 +400,63 @@ class Controller(Node):
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']
if not nox_core_dir:
raise Exception( 'please set NOX_CORE_DIR env var\n' ) raise Exception( 'please set NOX_CORE_DIR env var\n' )
Controller.__init__( self, name, Controller.__init__( self, name,
controller = nox_core_dir + '/nox_core', controller=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \ cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \
' '.join(nox_args), ' '.join( noxArgs ),
cdir = nox_core_dir, **kwargs) cdir = noxCoreDir, **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
@param name name to give controller ipAddress: the IP address where the remote controller is
@param ip_address the IP address where the remote controller is
listening listening
@param port the port where the remote controller is listening port: the port where the remote controller is listening"""
''' Controller.__init__( self, name, ipAddress=ipAddress, port=port )
Controller.__init__(self, name, ip_address = ip_address, port = port)
def start( self ): def start( self ):
'''Overridden to do nothing.''' "Overridden to do nothing."
return return
def stop( self ): def stop( self ):
'''Overridden to do nothing.''' "Overridden to do nothing."
return return
+58 -123
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,28 +8,19 @@ from subprocess import call, check_call, Popen, PIPE, STDOUT
from mininet.log import lg from mininet.log import lg
def run( cmd ): def run( cmd ):
'''Simple interface to subprocess.call() """Simple interface to subprocess.call()
cmd: list of command params"""
@param cmd list of command params
'''
return call( cmd.split( ' ' ) ) return call( cmd.split( ' ' ) )
def checkRun( cmd ): def checkRun( cmd ):
'''Simple interface to subprocess.check_call() """Simple interface to subprocess.check_call()
cmd: list of command params"""
@param cmd list of command params
'''
check_call( cmd.split( ' ' ) ) check_call( cmd.split( ' ' ) )
def quietRun( cmd ): def quietRun( cmd ):
'''Run a command, routing stderr to stdout, and return the output. """Run a command, routing stderr to stdout, and return the output.
cmd: list of command params"""
@param cmd list of command params
'''
if isinstance( cmd, str ): if isinstance( cmd, str ):
cmd = cmd.split( ' ' ) cmd = cmd.split( ' ' )
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT ) popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
@@ -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,14 +54,11 @@ 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. """Make a veth pair connecting intf1 and intf2.
intf1: string, interface
@param intf1 string, interface intf2: string, interface
@param intf2 string, interface returns: success boolean"""
@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 )
@@ -114,100 +66,83 @@ def makeIntfPair(intf1, intf2):
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 ) cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
quietRun( cmd ) quietRun( cmd )
links = node.cmd( 'ip link show' ) 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
@param val input as unsigned int bytes: number of bytes to convert
@param bytes number of bytes to convert returns: chStr colon-hex string"""
@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 ): def macColonHex( mac ):
'''Generate MAC colon-hex string from unsigned int. """Generate MAC colon-hex string from unsigned int.
mac: MAC address as unsigned int
@param mac MAC address as unsigned int returns: macStr MAC colon-hex string"""
@return mac_str MAC colon-hex string
'''
return _colonHex( mac, 6 ) return _colonHex( mac, 6 )
def ipStr( ip ): def ipStr( ip ):
'''Generate IP address string """Generate IP address string
returns: ip addr string"""
@return ip addr string
'''
hi = ( ip & 0xff0000 ) >> 16 hi = ( ip & 0xff0000 ) >> 16
mid = ( ip & 0xff00 ) >> 8 mid = ( ip & 0xff00 ) >> 8
lo = ip & 0xff lo = ip & 0xff
Executable → Regular
+11 -18
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
"""XTerm creation and cleanup.
"""
XTerm creation and cleanup.
Utility functions to run an xterm ( connected via screen( 1 ) ) on each host. 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 ).
@@ -8,17 +9,13 @@ 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
@param node Node object title: base title
@param title base title returns: process created"""
@return process created
'''
title += ': ' + node.name title += ': ' + node.name
if not node.inNamespace: if not node.inNamespace:
title += ' (root)' title += ' (root)'
@@ -30,9 +27,8 @@ def makeXterm(node, title):
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:
@@ -40,12 +36,9 @@ def cleanUpScreens():
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
@param nodes list of Node objects title: base title for each
@param title base title for each returns: list of created xterm processes"""
@return list of created xterm processes
'''
return [ makeXterm( node, title ) for node in nodes ] 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: