From 80a8fa62d523a2fc9e570bf1c374898e92394ca2 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Fri, 5 Feb 2010 02:33:34 -0800 Subject: [PATCH] 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 --- Makefile | 4 +- bin/mn | 2 +- mininet/__init__.py | 2 +- mininet/log.py | 131 ++++---- mininet/net.py | 797 +++++++++++++++++++++----------------------- mininet/node.py | 598 +++++++++++++++------------------ mininet/util.py | 243 +++++--------- mininet/xterm.py | 55 ++- util/unpep8 | 16 +- 9 files changed, 849 insertions(+), 999 deletions(-) mode change 100755 => 100644 mininet/xterm.py diff --git a/Makefile b/Makefile index 184c532..58e7c55 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/bin/mn b/bin/mn index b0d2ec4..7f22f5c 100755 --- a/bin/mn +++ b/bin/mn @@ -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() diff --git a/mininet/__init__.py b/mininet/__init__.py index 802dc75..c15ea6a 100644 --- a/mininet/__init__.py +++ b/mininet/__init__.py @@ -1 +1 @@ -'''Docstring to silence pylint; ignores --ignore option for __init__.py''' +"Docstring to silence pylint; ignores --ignore option for __init__.py" diff --git a/mininet/log.py b/mininet/log.py index 0c79479..cec1ce5 100644 --- a/mininet/log.py +++ b/mininet/log.py @@ -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() diff --git a/mininet/net.py b/mininet/net.py index 91c7be5..8f2addc 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -1,8 +1,7 @@ #!/usr/bin/python """Mininet: A simple networking testbed for OpenFlow! - -@author Bob Lantz (rlantz@cs.stanford.edu) -@author Brandon Heller (brandonh@stanford.edu) +author: Bob Lantz ( rlantz@cs.stanford.edu ) +author: Brandon Heller ( brandonh@stanford.edu ) Mininet creates scalable OpenFlow test networks by using process-based virtualization and network namespaces. @@ -12,9 +11,9 @@ namespaces. This allows a complete OpenFlow network to be simulated on top of a single Linux kernel. Each host has: - A virtual console (pipes to a shell) - A virtual interfaces (half of a veth pair) - A parent shell (and possibly some child processes) in a namespace +A virtual console ( pipes to a shell ) +A virtual interfaces ( half of a veth pair ) +A parent shell ( and possibly some child processes ) in a namespace Hosts have a network interface which is configured via ifconfig/ip link/etc. @@ -25,26 +24,24 @@ from the OpenFlow reference implementation. In kernel datapath mode, the controller and switches are simply processes in the root namespace. -Kernel OpenFlow datapaths are instantiated using dpctl(8), and are +Kernel OpenFlow datapaths are instantiated using dpctl( 8 ), and are attached to the one side of a veth pair; the other side resides in the host namespace. In this mode, switch processes can simply connect to the controller via the loopback interface. In user datapath mode, the controller and switches are full-service nodes that live in their own network namespaces and have management -interfaces and IP addresses on a control network (e.g. 10.0.123.1, -currently routed although it could be bridged.) +interfaces and IP addresses on a control network ( e.g. 10.0.123.1, +currently routed although it could be bridged. ) In addition to a management interface, user mode switches also have several switch interfaces, halves of veth pairs whose other halves reside in the host nodes that the switches are connected to. Naming: - Host nodes are named h1-hN - Switch nodes are named s0-sN - Interfaces are named {nodename}-eth0 .. {nodename}-ethN, - -""" +Host nodes are named h1-hN +Switch nodes are named s0-sN +Interfaces are named { nodename }-eth0 .. { nodename }-ethN,""" import os import re import signal @@ -55,11 +52,10 @@ from time import sleep from mininet.log import lg from mininet.node import KernelSwitch, OVSKernelSwitch 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 -DATAPATHS = ['kernel'] #['user', 'kernel'] - +DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ] def init(): "Initialize Mininet." @@ -67,118 +63,107 @@ def init(): # Note: this script must be run as root # Perhaps we should do so automatically! print "*** Mininet must run as root." - exit(1) + exit( 1 ) # If which produces no output, then netns is not in the path. # May want to loosen this to handle netns in the current dir. - if not quietRun(['which', 'netns']): - raise Exception("Could not find netns; see INSTALL") + if not quietRun( [ 'which', 'netns' ] ): + raise Exception( "Could not find netns; see INSTALL" ) fixLimits() +class Mininet( object ): + "Network emulation with hosts spawned in network namespaces." -class Mininet(object): - '''Network emulation with hosts spawned in network namespaces.''' - - def __init__(self, topo, switch, host, controller, cparams, - build = True, xterms = False, cleanup = False, - in_namespace = False, - auto_set_macs = False, auto_static_arp = False): - '''Create Mininet object. - - @param topo Topo object - @param switch Switch class - @param host Host class - @param controller Controller class - @param cparams ControllerParams object - @param now build now? - @param xterms if build now, spawn xterms? - @param cleanup if build now, cleanup before creating? - @param in_namespace spawn switches and controller in net namespaces? - @param auto_set_macs set MAC addrs to DPIDs? - @param auto_static_arp set all-pairs static MAC addrs? - ''' + def __init__( self, topo, switch, host, controller, cparams, + build=True, xterms=False, cleanup=False, + inNamespace=False, + autoSetMacs=False, autoStaticArp=False ): + """Create Mininet object. + topo: Topo object + switch: Switch class + host: Host class + controller: Controller class + cparams: ControllerParams object + now: build now? + xterms: if build now, spawn xterms? + cleanup: if build now, cleanup before creating? + inNamespace: spawn switches and controller in net namespaces? + autoSetMacs: set MAC addrs to DPIDs? + autoStaticArp: set all-pairs static MAC addrs?""" self.topo = topo self.switch = switch self.host = host self.controller = controller self.cparams = cparams - self.nodes = {} # dpid to Node{Host, Switch} objects + self.nodes = {} # dpid to Node{ Host, Switch } objects self.controllers = {} # controller name to Controller objects self.dps = 0 # number of created kernel datapaths - self.in_namespace = in_namespace + self.inNamespace = inNamespace self.xterms = xterms self.cleanup = cleanup - self.auto_set_macs = auto_set_macs - self.auto_static_arp = auto_static_arp + self.autoSetMacs = autoSetMacs + self.autoStaticArp = autoStaticArp self.terms = [] # list of spawned xterm processes if build: self.build() - def _add_host(self, dpid): - '''Add host. - - @param dpid DPID of host to add - ''' - host = self.host('h_' + self.topo.name(dpid)) + def _addHost( self, dpid ): + """Add host. + dpid: DPID of host to add""" + host = self.host( 'h_' + self.topo.name( dpid ) ) # for now, assume one interface per host. - host.intfs.append('h_' + self.topo.name(dpid) + '-eth0') - self.nodes[dpid] = host - #lg.info('%s ' % host.name) + host.intfs.append( 'h_' + self.topo.name( dpid ) + '-eth0' ) + self.nodes[ dpid ] = host + #lg.info( '%s ' % host.name ) - def _add_switch(self, dpid): - '''Add switch. - - @param dpid DPID of switch to add - ''' + def _addSwitch( self, dpid ): + """Add switch. + dpid: DPID of switch to add""" sw = None - sw_dpid = None - if self.auto_set_macs: - sw_dpid = dpid + swDpid = None + if self.autoSetMacs: + swDpid = dpid if self.switch is KernelSwitch or self.switch is OVSKernelSwitch: - sw = self.switch('s_' + self.topo.name(dpid), dp = self.dps, - dpid = sw_dpid) + sw = self.switch( 's_' + self.topo.name( dpid ), dp = self.dps, + dpid = swDpid ) self.dps += 1 else: - sw = self.switch('s_' + self.topo.name(dpid)) - self.nodes[dpid] = sw + sw = self.switch( 's_' + self.topo.name( dpid ) ) + self.nodes[ dpid ] = sw - def _add_link(self, src, dst): - '''Add link. + def _addLink( self, src, dst ): + """Add link. + src: source DPID + dst: destination DPID""" + srcPort, dstPort = self.topo.port( src, dst ) + srcNode = self.nodes[ src ] + dstNode = self.nodes[ dst ] + srcIntf = srcNode.intfName( srcPort ) + dstIntf = dstNode.intfName( dstPort ) + makeIntfPair( srcIntf, dstIntf ) + srcNode.intfs.append( srcIntf ) + dstNode.intfs.append( dstIntf ) + srcNode.ports[ srcPort ] = srcIntf + dstNode.ports[ dstPort ] = dstIntf + #lg.info( '\n' ) + #lg.info( 'added intf %s to src node %x\n' % ( srcIntf, src ) ) + #lg.info( 'added intf %s to dst node %x\n' % ( dstIntf, dst ) ) + if srcNode.inNamespace: + #lg.info( 'moving src w/inNamespace set\n' ) + moveIntf( srcIntf, srcNode ) + if dstNode.inNamespace: + #lg.info( 'moving dst w/inNamespace set\n' ) + moveIntf( dstIntf, dstNode ) + srcNode.connection[ srcIntf ] = ( dstNode, dstIntf ) + dstNode.connection[ dstIntf ] = ( srcNode, srcIntf ) - @param src source DPID - @param dst destination DPID - ''' - src_port, dst_port = self.topo.port(src, dst) - src_node = self.nodes[src] - dst_node = self.nodes[dst] - src_intf = src_node.intfName(src_port) - dst_intf = dst_node.intfName(dst_port) - make_veth_pair(src_intf, dst_intf) - src_node.intfs.append(src_intf) - dst_node.intfs.append(dst_intf) - src_node.ports[src_port] = src_intf - dst_node.ports[dst_port] = dst_intf - #lg.info('\n') - #lg.info('added intf %s to src node %x\n' % (src_intf, src)) - #lg.info('added intf %s to dst node %x\n' % (dst_intf, dst)) - if src_node.inNamespace: - #lg.info('moving src w/inNamespace set\n') - retry(3, MOVEINTF_DELAY, move_intf, src_intf, src_node) - if dst_node.inNamespace: - #lg.info('moving dst w/inNamespace set\n') - retry(3, MOVEINTF_DELAY, move_intf, dst_intf, dst_node) - src_node.connection[src_intf] = (dst_node, dst_intf) - dst_node.connection[dst_intf] = (src_node, src_intf) - - def _add_controller(self, controller): - '''Add controller. - - @param controller Controller class - ''' - controller = self.controller('c0', self.in_namespace) + def _addController( self, controller ): + """Add controller. + controller: Controller class""" + controller = self.controller( 'c0', self.inNamespace ) if controller: # allow controller-less setups - self.controllers['c0'] = controller + self.controllers[ 'c0' ] = controller # Control network support: # @@ -187,7 +172,7 @@ class Mininet(object): # # Notes: # - # 1. If the controller and switches are in the same (e.g. root) + # 1. If the controller and switches are in the same ( e.g. root ) # namespace, they can just use the loopback connection. # We may wish to do this for the user datapath as well as the # kernel datapath. @@ -199,208 +184,203 @@ class Mininet(object): # # 4. Even if we dispense with this in general, it could still be # useful for people who wish to simulate a separate control - # network (since real networks may need one!) + # network ( since real networks may need one! ) - def _configureControlNetwork(self): - '''Configure control network.''' + def _configureControlNetwork( self ): + "Configure control network." self._configureRoutedControlNetwork() - def _configureRoutedControlNetwork(self): - '''Configure a routed control network on controller and switches. + def _configureRoutedControlNetwork( self ): + """Configure a routed control network on controller and switches. + For use with the user datapath only right now. + TODO( brandonh ) test this code! + """ - For use with the user datapath only right now. - - @todo(brandonh) Test this code! - ''' # params were: controller, switches, ips - controller = self.controllers['c0'] - lg.info('%s <-> ' % controller.name) - for switch_dpid in self.topo.switches(): - switch = self.nodes[switch_dpid] - lg.info('%s ' % switch.name) - sip = self.topo.ip(switch_dpid)#ips.next() - sintf = switch.intfs[0] - node, cintf = switch.connection[sintf] + controller = self.controllers[ 'c0' ] + lg.info( '%s <-> ' % controller.name ) + for switchDpid in self.topo.switches(): + switch = self.nodes[ switchDpid ] + lg.info( '%s ' % switch.name ) + sip = self.topo.ip( switchDpid )#ips.next() + sintf = switch.intfs[ 0 ] + node, cintf = switch.connection[ sintf ] if node != controller: - lg.error('*** Error: switch %s not connected to correct' + lg.error( '*** Error: switch %s not connected to correct' 'controller' % - switch.name) - exit(1) - controller.setIP(cintf, self.cparams.ip, '/' + - self.cparams.subnet_size) - switch.setIP(sintf, sip, '/' + self.cparams.subnet_size) - controller.setHostRoute(sip, cintf) - switch.setHostRoute(self.cparams.ip, sintf) - lg.info('\n') - lg.info('*** Testing control network\n') - while not controller.intfIsUp(controller.intfs[0]): - lg.info('*** Waiting for %s to come up\n', controller.intfs[0]) - sleep(1) - for switch_dpid in self.topo.switches(): - switch = self.nodes[switch_dpid] - while not switch.intfIsUp(switch.intfs[0]): - lg.info('*** Waiting for %s to come up\n' % switch.intfs[0]) - sleep(1) - if self.ping(hosts = [switch, controller]) != 0: - lg.error('*** Error: control network test failed\n') - exit(1) - lg.info('\n') + switch.name ) + exit( 1 ) + controller.setIP( cintf, self.cparams.ip, '/' + + self.cparams.subnetSize ) + switch.setIP( sintf, sip, '/' + self.cparams.subnetSize ) + controller.setHostRoute( sip, cintf ) + switch.setHostRoute( self.cparams.ip, sintf ) + lg.info( '\n' ) + lg.info( '*** Testing control network\n' ) + while not controller.intfIsUp( controller.intfs[ 0 ] ): + lg.info( '*** Waiting for %s to come up\n', controller.intfs[ 0 ] ) + sleep( 1 ) + for switchDpid in self.topo.switches(): + switch = self.nodes[ switchDpid ] + while not switch.intfIsUp( switch.intfs[ 0 ] ): + lg.info( '*** Waiting for %s to come up\n' % + switch.intfs[ 0 ] ) + sleep( 1 ) + if self.ping( hosts=[ switch, controller ] ) != 0: + lg.error( '*** Error: control network test failed\n' ) + exit( 1 ) + lg.info( '\n' ) - def _config_hosts(self): - '''Configure a set of hosts.''' + def _configHosts( self ): + "Configure a set of hosts." # params were: hosts, ips - for host_dpid in self.topo.hosts(): - host = self.nodes[host_dpid] - hintf = host.intfs[0] - host.setIP(hintf, self.topo.ip(host_dpid), - '/' + str(self.cparams.subnet_size)) - host.setDefaultRoute(hintf) + for hostDpid in self.topo.hosts(): + host = self.nodes[ hostDpid ] + hintf = host.intfs[ 0 ] + host.setIP( hintf, self.topo.ip( hostDpid ), + '/' + str( self.cparams.subnetSize ) ) + host.setDefaultRoute( hintf ) # You're low priority, dude! - quietRun('renice +18 -p ' + repr(host.pid)) - lg.info('%s ', host.name) - lg.info('\n') + quietRun( 'renice +18 -p ' + repr( host.pid ) ) + lg.info( '%s ', host.name ) + lg.info( '\n' ) - def build(self): - '''Build mininet. - - At the end of this function, everything should be connected and up. - ''' + def build( self ): + """Build mininet. + At the end of this function, everything should be connected + and up.""" if self.cleanup: pass # cleanup # validate topo? - lg.info('*** Adding controller\n') - self._add_controller(self.controller) - lg.info('*** Creating network\n') - lg.info('*** Adding hosts:\n') - for host in sorted(self.topo.hosts()): - self._add_host(host) - lg.info('0x%x ' % host) - lg.info('\n*** Adding switches:\n') - for switch in sorted(self.topo.switches()): - self._add_switch(switch) - lg.info('0x%x ' % switch) - lg.info('\n*** Adding edges:\n') - for src, dst in sorted(self.topo.edges()): - self._add_link(src, dst) - lg.info('(0x%x, 0x%x) ' % (src, dst)) - lg.info('\n') + lg.info( '*** Adding controller\n' ) + self._addController( self.controller ) + lg.info( '*** Creating network\n' ) + lg.info( '*** Adding hosts:\n' ) + for host in sorted( self.topo.hosts() ): + self._addHost( host ) + lg.info( '0x%x ' % host ) + lg.info( '\n*** Adding switches:\n' ) + for switch in sorted( self.topo.switches() ): + self._addSwitch( switch ) + lg.info( '0x%x ' % switch ) + lg.info( '\n*** Adding edges:\n' ) + for src, dst in sorted( self.topo.edges() ): + self._addLink( src, dst ) + lg.info( '(0x%x, 0x%x) ' % ( src, dst ) ) + lg.info( '\n' ) - if self.in_namespace: - lg.info('*** Configuring control network\n') + if self.inNamespace: + lg.info( '*** Configuring control network\n' ) self._configureControlNetwork() - lg.info('*** Configuring hosts\n') - self._config_hosts() + lg.info( '*** Configuring hosts\n' ) + self._configHosts() if self.xterms: - self.start_xterms() - if self.auto_set_macs: - self.set_macs() - if self.auto_static_arp: - self.static_arp() + self.startXterms() + if self.autoSetMacs: + self.setMacs() + if self.autoStaticArp: + self.staticArp() - def switch_nodes(self): - '''Return switch nodes.''' - return [self.nodes[dpid] for dpid in self.topo.switches()] + def switchNodes( self ): + "Return switch nodes." + return [ self.nodes[ dpid ] for dpid in self.topo.switches() ] - def host_nodes(self): - '''Return host nodes.''' - return [self.nodes[dpid] for dpid in self.topo.hosts()] + def hostNodes( self ): + "Return host nodes." + return [ self.nodes[ dpid ] for dpid in self.topo.hosts() ] - def start_xterms(self): - '''Start an xterm for each node in the topo.''' - lg.info("*** Running xterms on %s\n" % os.environ['DISPLAY']) + def startXterms( self ): + "Start an xterm for each node in the topo." + lg.info( "*** Running xterms on %s\n" % os.environ[ 'DISPLAY' ] ) cleanUpScreens() - self.terms += makeXterms(self.controllers.values(), 'controller') - self.terms += makeXterms(self.switch_nodes(), 'switch') - self.terms += makeXterms(self.host_nodes(), 'host') + self.terms += makeXterms( self.controllers.values(), 'controller' ) + self.terms += makeXterms( self.switchNodes(), 'switch' ) + self.terms += makeXterms( self.hostNodes(), 'host' ) - def stop_xterms(self): - '''Kill each xterm.''' + def stopXterms( self ): + "Kill each xterm." # Kill xterms for term in self.terms: - os.kill(term.pid, signal.SIGKILL) + os.kill( term.pid, signal.SIGKILL ) cleanUpScreens() - def set_macs(self): - '''Set MAC addrs to correspond to datapath IDs on hosts. - - Assume that the host only has one interface. - ''' + def setMacs( self ): + """Set MAC addrs to correspond to datapath IDs on hosts. + Assume that the host only has one interface.""" for dpid in self.topo.hosts(): - host_node = self.nodes[dpid] - host_node.setMAC(host_node.intfs[0], dpid) + hostNode = self.nodes[ dpid ] + hostNode.setMAC( hostNode.intfs[ 0 ], dpid ) - def static_arp(self): - '''Add all-pairs ARP entries to remove the need to handle broadcast.''' + def staticArp( self ): + "Add all-pairs ARP entries to remove the need to handle broadcast." for src in self.topo.hosts(): - src_node = self.nodes[src] + srcNode = self.nodes[ src ] for dst in self.topo.hosts(): if src != dst: - src_node.setARP(dst, dst) + srcNode.setARP( dst, dst ) - def start(self): - '''Start controller and switches\n''' - lg.info('*** Starting controller\n') + def start( self ): + "Start controller and switches\n" + lg.info( '*** Starting controller\n' ) for cnode in self.controllers.values(): cnode.start() - lg.info('*** Starting %s switches\n' % len(self.topo.switches())) - for switch_dpid in self.topo.switches(): - switch = self.nodes[switch_dpid] - #lg.info('switch = %s' % switch) - lg.info('0x%x ' % switch_dpid) - switch.start(self.controllers) - lg.info('\n') + lg.info( '*** Starting %s switches\n' % len( self.topo.switches() ) ) + for switchDpid in self.topo.switches(): + switch = self.nodes[ switchDpid ] + #lg.info( 'switch = %s' % switch ) + lg.info( '0x%x ' % switchDpid ) + switch.start( self.controllers ) + lg.info( '\n' ) - def stop(self): - '''Stop the controller(s), switches and hosts\n''' + def stop( self ): + "Stop the controller(s), switches and hosts\n" if self.terms: - lg.info('*** Stopping %i terms\n' % len(self.terms)) - self.stop_xterms() - lg.info('*** Stopping %i hosts\n' % len(self.topo.hosts())) - for host_dpid in self.topo.hosts(): - host = self.nodes[host_dpid] - lg.info('%s ' % host.name) + lg.info( '*** Stopping %i terms\n' % len( self.terms ) ) + self.stopXterms() + lg.info( '*** Stopping %i hosts\n' % len( self.topo.hosts() ) ) + for hostDpid in self.topo.hosts(): + host = self.nodes[ hostDpid ] + lg.info( '%s ' % host.name ) host.terminate() - lg.info('\n') - lg.info('*** Stopping %i switches\n' % len(self.topo.switches())) - for switch_dpid in self.topo.switches(): - switch = self.nodes[switch_dpid] - lg.info('%s' % switch.name) + lg.info( '\n' ) + lg.info( '*** Stopping %i switches\n' % len( self.topo.switches() ) ) + for switchDpid in self.topo.switches(): + switch = self.nodes[ switchDpid ] + lg.info( '%s' % switch.name ) switch.stop() - lg.info('\n') - lg.info('*** Stopping controller\n') + lg.info( '\n' ) + lg.info( '*** Stopping controller\n' ) for cnode in self.controllers.values(): cnode.stop() - lg.info('*** Test complete\n') + lg.info( '*** Test complete\n' ) - def run(self, test, **params): - '''Perform a complete start/test/stop cycle.''' + def run( self, test, **params ): + "Perform a complete start/test/stop cycle." self.start() - lg.info('*** Running test\n') - result = getattr(self, test)(**params) + lg.info( '*** Running test\n' ) + result = getattr( self, test )( **params ) self.stop() return result @staticmethod - def _parse_ping(pingOutput): - '''Parse ping output and return packets sent, received.''' + def _parsePing( pingOutput ): + "Parse ping output and return packets sent, received." r = r'(\d+) packets transmitted, (\d+) received' - m = re.search(r, pingOutput) + m = re.search( r, pingOutput ) if m == None: - lg.error('*** Error: could not parse ping output: %s\n' % - pingOutput) - exit(1) - sent, received = int(m.group(1)), int(m.group(2)) + lg.error( '*** Error: could not parse ping output: %s\n' % + pingOutput ) + exit( 1 ) + sent, received = int( m.group( 1 ) ), int( m.group( 2 ) ) return sent, received - def ping(self, hosts = None): - '''Ping between all specified hosts. - - @param hosts list of host DPIDs - @return ploss packet loss percentage - ''' + def ping( self, hosts=None ): + """Ping between all specified hosts. + hosts: list of host DPIDs + returns: ploss packet loss percentage""" #self.start() # check if running - only then, start? packets = 0 @@ -408,125 +388,117 @@ class Mininet(object): ploss = None if not hosts: hosts = self.topo.hosts() - lg.info('*** Ping: testing ping reachability\n') - for node_dpid in hosts: - node = self.nodes[node_dpid] - lg.info('%s -> ' % node.name) - for dest_dpid in hosts: - dest = self.nodes[dest_dpid] + lg.info( '*** Ping: testing ping reachability\n' ) + for nodeDpid in hosts: + node = self.nodes[ nodeDpid ] + lg.info( '%s -> ' % node.name ) + for destDpid in hosts: + dest = self.nodes[ destDpid ] if node != dest: - result = node.cmd('ping -c1 ' + dest.IP()) - sent, received = self._parse_ping(result) + result = node.cmd( 'ping -c1 ' + dest.IP() ) + sent, received = self._parsePing( result ) packets += sent if received > sent: - lg.error('*** Error: received too many packets') - lg.error('%s' % result) - node.cmdPrint('route') - exit(1) + lg.error( '*** Error: received too many packets' ) + lg.error( '%s' % result ) + node.cmdPrint( 'route' ) + exit( 1 ) lost += sent - received - lg.info(('%s ' % dest.name) if received else 'X ') - lg.info('\n') + lg.info( ( '%s ' % dest.name ) if received else 'X ' ) + lg.info( '\n' ) ploss = 100 * lost / packets - lg.info("*** Results: %i%% dropped (%d/%d lost)\n" % - (ploss, lost, packets)) + lg.info( "*** Results: %i%% dropped (%d/%d lost)\n" % + ( ploss, lost, packets ) ) return ploss - def ping_all(self): - '''Ping between all hosts. - - @return ploss packet loss percentage - ''' + def pingAll( self ): + """Ping between all hosts. + returns: ploss packet loss percentage""" return self.ping() - def ping_pair(self): - '''Ping between first two hosts, useful for testing. - - @return ploss packet loss percentage - ''' - hosts_sorted = sorted(self.topo.hosts()) - hosts = [hosts_sorted[0], hosts_sorted[1]] - return self.ping(hosts = hosts) + def pingPair( self ): + """Ping between first two hosts, useful for testing. + returns: ploss packet loss percentage""" + hostsSorted = sorted( self.topo.hosts() ) + hosts = [ hostsSorted[ 0 ], hostsSorted[ 1 ] ] + return self.ping( hosts=hosts ) @staticmethod - def _parseIperf(iperfOutput): - '''Parse iperf output and return bandwidth. - - @param iperfOutput string - @return result string - ''' + def _parseIperf( iperfOutput ): + """Parse iperf output and return bandwidth. + iperfOutput: string + returns: result string""" r = r'([\d\.]+ \w+/sec)' - m = re.search(r, iperfOutput) + m = re.search( r, iperfOutput ) if m: - return m.group(1) + return m.group( 1 ) 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', - verbose = False): - '''Run iperf between two hosts. - - @param hosts list of host DPIDs; if None, uses opposite hosts - @param l4_type string, one of [TCP, UDP] - @param verbose verbose printing - @return results two-element array of server and client speeds - ''' + def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', + verbose=False ): + """Run iperf between two hosts. + hosts: list of host DPIDs; if None, uses opposite hosts + l4Type: string, one of [ TCP, UDP ] + verbose: verbose printing + returns: results two-element array of server and client speeds""" if not hosts: - hosts_sorted = sorted(self.topo.hosts()) - hosts = [hosts_sorted[0], hosts_sorted[-1]] + hostsSorted = sorted( self.topo.hosts() ) + hosts = [ hostsSorted[ 0 ], hostsSorted[ -1 ] ] else: - assert len(hosts) == 2 - host0 = self.nodes[hosts[0]] - host1 = self.nodes[hosts[1]] - lg.info('*** Iperf: testing ' + l4_type + ' bandwidth between ') - lg.info("%s and %s\n" % (host0.name, host1.name)) - host0.cmd('killall -9 iperf') - iperf_args = 'iperf ' - bw_args = '' - if l4_type == 'UDP': - iperf_args += '-u ' - bw_args = '-b ' + udp_bw + ' ' - elif l4_type != 'TCP': - raise Exception('Unexpected l4 type: %s' % l4_type) - server = host0.cmd(iperf_args + '-s &') + assert len( hosts ) == 2 + host0 = self.nodes[ hosts[ 0 ] ] + host1 = self.nodes[ hosts[ 1 ] ] + lg.info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' ) + lg.info( "%s and %s\n" % ( host0.name, host1.name ) ) + host0.cmd( 'killall -9 iperf' ) + iperfArgs = 'iperf ' + bwArgs = '' + if l4Type == 'UDP': + iperfArgs += '-u ' + bwArgs = '-b ' + udpBw + ' ' + elif l4Type != 'TCP': + raise Exception( 'Unexpected l4 type: %s' % l4Type ) + server = host0.cmd( iperfArgs + '-s &' ) if verbose: - lg.info('%s\n' % server) - client = host1.cmd(iperf_args + '-t 5 -c ' + host0.IP() + ' ' + - bw_args) + lg.info( '%s\n' % server ) + client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' + + bwArgs ) if verbose: - lg.info('%s\n' % client) - server = host0.cmd('killall -9 iperf') + lg.info( '%s\n' % client ) + server = host0.cmd( 'killall -9 iperf' ) if verbose: - lg.info('%s\n' % server) - result = [self._parseIperf(server), self._parseIperf(client)] - if l4_type == 'UDP': - result.insert(0, udp_bw) - lg.info('*** Results: %s\n' % result) + lg.info( '%s\n' % server ) + result = [ self._parseIperf( server ), self._parseIperf( client ) ] + if l4Type == 'UDP': + result.insert( 0, udpBw ) + lg.info( '*** Results: %s\n' % result ) return result - def iperf_udp(self, udp_bw = '10M'): - '''Run iperf UDP test.''' - return self.iperf(l4_type = 'UDP', udp_bw = udp_bw) + def iperfUdp( self, udpBw='10M' ): + "Run iperf UDP test." + return self.iperf( l4Type='UDP', udpBw=udpBw ) - def interact(self): - '''Start network and run our simple CLI.''' + def interact( self ): + "Start network and run our simple CLI." self.start() - result = MininetCLI(self) + result = MininetCLI( self ) self.stop() return result -class MininetCLI(object): - '''Simple command-line interface to talk to nodes.''' - cmds = ['?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \ - 'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump'] +class MininetCLI( object ): + "Simple command-line interface to talk to nodes." + cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \ + 'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump' ] - def __init__(self, mininet): + def __init__( self, mininet ): self.mn = mininet self.nodemap = {} # map names to Node objects for node in self.mn.nodes.values(): - self.nodemap[node.name] = node + self.nodemap[ node.name ] = node for cname, cnode in self.mn.controllers.iteritems(): - self.nodemap[cname] = cnode + self.nodemap[ cname ] = cnode self.nodelist = self.nodemap.values() self.run() @@ -535,115 +507,116 @@ class MininetCLI(object): # pylint: disable-msg=W0613 # Commands - def help(self, args): - '''Semi-useful help for CLI.''' - help_str = 'Available commands are:' + str(self.cmds) + '\n' + \ - 'You may also send a command to a node using:\n' + \ - ' command {args}\n' + \ - 'For example:\n' + \ - ' mininet> h0 ifconfig\n' + \ - '\n' + \ - 'The interpreter automatically substitutes IP ' + \ - 'addresses\n' + \ - 'for node names, so commands like\n' + \ - ' mininet> h0 ping -c1 h1\n' + \ - 'should work.\n' + \ - '\n\n' + \ - 'Interactive commands are not really supported yet,\n' + \ - 'so please limit commands to ones that do not\n' + \ - 'require user interaction and will terminate\n' + \ - 'after a reasonable amount of time.\n' - print(help_str) + def help( self, args ): + "Semi-useful help for CLI." + helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n' + + 'You may also send a command to a node using:\n' + + ' command {args}\n' + + 'For example:\n' + + ' mininet> h0 ifconfig\n' + + '\n' + + 'The interpreter automatically substitutes IP ' + + 'addresses\n' + + 'for node names, so commands like\n' + + ' mininet> h0 ping -c1 h1\n' + + 'should work.\n' + + '\n\n' + + 'Interactive commands are not really supported yet,\n' + + 'so please limit commands to ones that do not\n' + + 'require user interaction and will terminate\n' + + 'after a reasonable amount of time.\n' ) + print( helpStr ) - def nodes(self, args): - '''List all nodes.''' - lg.info('available nodes are: \n%s\n', - ' '.join([node.name for node in sorted(self.nodelist)])) + def nodes( self, args ): + "List all nodes." + nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] ) + lg.info( 'available nodes are: \n%s\n' % nodes ) - def net(self, args): - '''List network connections.''' - for switch_dpid in self.mn.topo.switches(): - switch = self.mn.nodes[switch_dpid] - lg.info('%s <->', switch.name) + def net( self, args ): + "List network connections." + for switchDpid in self.mn.topo.switches(): + switch = self.mn.nodes[ switchDpid ] + lg.info( '%s <->', switch.name ) for intf in switch.intfs: - node = switch.connection[intf] - lg.info(' %s' % node.name) - lg.info('\n') + node = switch.connection[ intf ] + lg.info( ' %s' % node.name ) + lg.info( '\n' ) - def sh(self, args): - '''Run an external shell command''' - call(['sh', '-c'] + args) + def sh( self, args ): + "Run an external shell command" + call( [ 'sh', '-c' ] + args ) - def ping_all(self, args): - '''Ping between all hosts.''' - self.mn.ping_all() + def pingAll( self, args ): + "Ping between all hosts." + self.mn.pingAll() - def ping_pair(self, args): - '''Ping between first two hosts, useful for testing.''' - self.mn.ping_pair() + def pingPair( self, args ): + "Ping between first two hosts, useful for testing." + self.mn.pingPair() - def iperf(self, args): - '''Simple iperf TCP test between two hosts.''' + def iperf( self, args ): + "Simple iperf TCP test between two hosts." self.mn.iperf() - def iperf_udp(self, args): - '''Simple iperf UDP test between two hosts.''' - udp_bw = args[0] if len(args) else '10M' - self.mn.iperf_udp(udp_bw) + def iperfUdp( self, args ): + "Simple iperf UDP test between two hosts." + udpBw = args[ 0 ] if len( args ) else '10M' + self.mn.iperfUdp( udpBw ) - def intfs(self, args): - '''List interfaces.''' + def intfs( self, args ): + "List interfaces." 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): - '''Dump node info.''' + def dump( self, args ): + "Dump node info." for node in self.mn.nodes.values(): - lg.info('%s\n' % node) + lg.info( '%s\n' % node ) # Re-enable pylint "Unused argument: 'arg's'" messages. # pylint: enable-msg=W0613 - def run(self): - '''Read and execute commands.''' - lg.warn('*** Starting CLI:\n') + def run( self ): + "Read and execute commands." + lg.warn( '*** Starting CLI:\n' ) while True: - lg.warn('mininet> ') - input_line = sys.stdin.readline() - if input_line == '': + lg.warn( 'mininet> ' ) + inputLine = sys.stdin.readline() + if inputLine == '': break - if input_line[-1] == '\n': - input_line = input_line[:-1] - cmd = input_line.split(' ') - first = cmd[0] - rest = cmd[1:] - if first in self.cmds and hasattr(self, first): - getattr(self, first)(rest) + if inputLine[ -1 ] == '\n': + inputLine = inputLine[ :-1 ] + cmd = inputLine.split( ' ' ) + first = cmd[ 0 ] + rest = cmd[ 1: ] + if first in self.cmds and hasattr( self, first ): + getattr( self, first )( rest ) elif first in self.nodemap and rest != []: - node = self.nodemap[first] + node = self.nodemap[ first ] # Substitute IP addresses for node names in command - rest = [self.nodemap[arg].IP() if arg in self.nodemap else arg - for arg in rest] - rest = ' '.join(rest) + rest = [ self.nodemap[ arg ].IP() + if arg in self.nodemap else arg + for arg in rest ] + rest = ' '.join( rest ) # Interactive commands don't work yet, and # there are still issues with control-c - lg.warn('*** %s: running %s\n' % (node.name, rest)) - node.sendCmd(rest) + lg.warn( '*** %s: running %s\n' % ( node.name, rest ) ) + node.sendCmd( rest ) while True: try: done, data = node.monitor() - lg.info('%s\n' % data) + lg.info( '%s\n' % data ) if done: break except KeyboardInterrupt: node.sendInt() elif first == '': pass - elif first in ['exit', 'quit']: + elif first in [ 'exit', 'quit' ]: break elif first == '?': - self.help(rest) + self.help( rest ) else: - lg.error('CLI: unknown node or command: < %s >\n' % first) - #lg.info('*** CLI: command complete\n') + lg.error( 'CLI: unknown node or command: < %s >\n' % first ) + #lg.info( '*** CLI: command complete\n' ) return 'exited by user command' diff --git a/mininet/node.py b/mininet/node.py index 5edda82..020ceef 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -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 on controller. - - Log to /tmp/cN.log - ''' + def start( self ): + """Start 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 diff --git a/mininet/util.py b/mininet/util.py index 2e30c3b..4c0154b 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -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 ) diff --git a/mininet/xterm.py b/mininet/xterm.py old mode 100755 new mode 100644 index d401e4f..ae4ad0d --- a/mininet/xterm.py +++ b/mininet/xterm.py @@ -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 ] diff --git a/util/unpep8 b/util/unpep8 index b05b83b..931b217 100755 --- a/util/unpep8 +++ b/util/unpep8 @@ -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: