First crack at restoring mininet python style, assisted by handy

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

- topo.py is still in pep8
- not all examples work, but this is due to other issues
This commit is contained in:
Bob Lantz
2010-02-05 02:33:34 -08:00
parent bebe9dbed2
commit 80a8fa62d5
9 changed files with 849 additions and 999 deletions
+3 -1
View File
@@ -8,10 +8,12 @@ TEST = mininet/test/*.py
BIN = bin/mn bin/mnclean
PYSRC = $(MININET) $(TEST) $(BIN)
P8IGN = E251,E201,E302
codecheck: $(PYSRC)
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
pep8 --ignore=E251 $(PYSRC)
pep8 --ignore=$(P8IGN) $(PYSRC)
test: $(MININET) $(TEST)
mininet/test/test_nets.py
+1 -1
View File
@@ -130,7 +130,7 @@ class MininetRunner(object):
'''Setup and validate environment.'''
# set logging verbosity
lg.set_loglevel(self.options.verbosity)
lg.setLogLevel(self.options.verbosity)
# validate environment setup
init()
+1 -1
View File
@@ -1 +1 @@
'''Docstring to silence pylint; ignores --ignore option for __init__.py'''
"Docstring to silence pylint; ignores --ignore option for __init__.py"
+26 -33
View File
@@ -1,4 +1,4 @@
'''Logging functions for Mininet.'''
"Logging functions for Mininet."
import logging
from logging import Logger
@@ -11,31 +11,26 @@ LEVELS = {'debug': logging.DEBUG,
'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.
"""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.
'''
easily support interactive mode when we want it, or errors-only logging
for running unit tests."""
def emit( self, record ):
'''
Emit a 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.
'''
traceback.printException and appended to the stream."""
try:
msg = self.format( record )
fs = '%s' # was '%s\n'
@@ -54,14 +49,13 @@ class StreamHandlerNoNewline(logging.StreamHandler):
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 satsify pylint.
'''
Changed cls first args to mcs to satisfy pylint."""
def __init__( mcs, name, bases, dict_ ):
super( Singleton, mcs ).__init__( name, bases, dict_ )
@@ -74,16 +68,17 @@ class Singleton(type):
class MininetLogger( Logger, object ):
'''Mininet-specific logger
"""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:
class, and can then use the __metaclass__ directive, to prevent this
error:
TypeError: Error when calling the metaclass 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,
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 ):
@@ -102,22 +97,20 @@ class MininetLogger(Logger, object):
# create console handler
ch = StreamHandlerNoNewline()
# create formatter
formatter = logging.Formatter(LOG_MSG_FORMAT)
formatter = logging.Formatter( LOGMSGFORMAT )
# add formatter to ch
ch.setFormatter( formatter )
# add ch to lg
self.addHandler( ch )
self.set_loglevel()
def set_loglevel(self, levelname = None):
'''Setup loglevel.
self.setLogLevel()
def setLogLevel( self, levelname=None ):
"""Setup loglevel.
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' )
+214 -241
View File
@@ -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.
@@ -42,9 +41,7 @@ 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,
"""
Interfaces are named { nodename }-eth0 .. { nodename }-ethN,"""
import os
import re
import signal
@@ -55,12 +52,11 @@ 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' ]
def init():
"Initialize Mininet."
if os.getuid() != 0:
@@ -74,28 +70,25 @@ def init():
raise Exception( "Could not find netns; see INSTALL" )
fixLimits()
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,
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?
'''
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
@@ -104,79 +97,71 @@ class Mininet(object):
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
'''
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 )
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)
dpid = swDpid )
self.dps += 1
else:
sw = self.switch( 's_' + self.topo.name( dpid ) )
self.nodes[ dpid ] = sw
def _add_link(self, src, dst):
'''Add link.
@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
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' % (src_intf, src))
#lg.info('added intf %s to dst node %x\n' % (dst_intf, dst))
if src_node.inNamespace:
#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' )
retry(3, MOVEINTF_DELAY, move_intf, src_intf, src_node)
if dst_node.inNamespace:
moveIntf( srcIntf, srcNode )
if dstNode.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)
moveIntf( dstIntf, dstNode )
srcNode.connection[ srcIntf ] = ( dstNode, dstIntf )
dstNode.connection[ dstIntf ] = ( srcNode, srcIntf )
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
@@ -202,24 +187,23 @@ class Mininet(object):
# network ( since real networks may need one! )
def _configureControlNetwork( self ):
'''Configure control network.'''
"Configure control network."
self._configureRoutedControlNetwork()
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.
TODO( brandonh ) test this code!
"""
@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]
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
lg.info( '%s ' % switch.name )
sip = self.topo.ip(switch_dpid)#ips.next()
sip = self.topo.ip( switchDpid )#ips.next()
sintf = switch.intfs[ 0 ]
node, cintf = switch.connection[ sintf ]
if node != controller:
@@ -228,8 +212,8 @@ class Mininet(object):
switch.name )
exit( 1 )
controller.setIP( cintf, self.cparams.ip, '/' +
self.cparams.subnet_size)
switch.setIP(sintf, sip, '/' + self.cparams.subnet_size)
self.cparams.subnetSize )
switch.setIP( sintf, sip, '/' + self.cparams.subnetSize )
controller.setHostRoute( sip, cintf )
switch.setHostRoute( self.cparams.ip, sintf )
lg.info( '\n' )
@@ -237,24 +221,25 @@ class Mininet(object):
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]
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])
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]
for hostDpid in self.topo.hosts():
host = self.nodes[ hostDpid ]
hintf = host.intfs[ 0 ]
host.setIP(hintf, self.topo.ip(host_dpid),
'/' + str(self.cparams.subnet_size))
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 ) )
@@ -262,111 +247,108 @@ class Mininet(object):
lg.info( '\n' )
def build( self ):
'''Build mininet.
At the end of this function, everything should be connected and up.
'''
"""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)
self._addController( self.controller )
lg.info( '*** Creating network\n' )
lg.info( '*** Adding hosts:\n' )
for host in sorted( self.topo.hosts() ):
self._add_host(host)
self._addHost( host )
lg.info( '0x%x ' % host )
lg.info( '\n*** Adding switches:\n' )
for switch in sorted( self.topo.switches() ):
self._add_switch(switch)
self._addSwitch( 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)
self._addLink( src, dst )
lg.info( '(0x%x, 0x%x) ' % ( src, dst ) )
lg.info( '\n' )
if self.in_namespace:
if self.inNamespace:
lg.info( '*** Configuring control network\n' )
self._configureControlNetwork()
lg.info( '*** Configuring hosts\n' )
self._config_hosts()
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.'''
def switchNodes( self ):
"Return switch nodes."
return [ self.nodes[ dpid ] for dpid in self.topo.switches() ]
def host_nodes(self):
'''Return host nodes.'''
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.'''
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.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 )
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'''
"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]
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
#lg.info( 'switch = %s' % switch )
lg.info('0x%x ' % switch_dpid)
lg.info( '0x%x ' % switchDpid )
switch.start( self.controllers )
lg.info( '\n' )
def stop( self ):
'''Stop the controller(s), switches and hosts\n'''
"Stop the controller(s), switches and hosts\n"
if 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() ) )
for host_dpid in self.topo.hosts():
host = self.nodes[host_dpid]
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]
for switchDpid in self.topo.switches():
switch = self.nodes[ switchDpid ]
lg.info( '%s' % switch.name )
switch.stop()
lg.info( '\n' )
@@ -376,7 +358,7 @@ class Mininet(object):
lg.info( '*** Test complete\n' )
def run( self, test, **params ):
'''Perform a complete start/test/stop cycle.'''
"Perform a complete start/test/stop cycle."
self.start()
lg.info( '*** Running test\n' )
result = getattr( self, test )( **params )
@@ -384,8 +366,8 @@ class Mininet(object):
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 )
if m == None:
@@ -396,11 +378,9 @@ class Mininet(object):
return sent, received
def ping( self, hosts=None ):
'''Ping between all specified hosts.
@param hosts list of host DPIDs
@return ploss packet loss percentage
'''
"""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
@@ -409,14 +389,14 @@ class Mininet(object):
if not hosts:
hosts = self.topo.hosts()
lg.info( '*** Ping: testing ping reachability\n' )
for node_dpid in hosts:
node = self.nodes[node_dpid]
for nodeDpid in hosts:
node = self.nodes[ nodeDpid ]
lg.info( '%s -> ' % node.name )
for dest_dpid in hosts:
dest = self.nodes[dest_dpid]
for destDpid in hosts:
dest = self.nodes[ destDpid ]
if node != dest:
result = node.cmd( 'ping -c1 ' + dest.IP() )
sent, received = self._parse_ping(result)
sent, received = self._parsePing( result )
packets += sent
if received > sent:
lg.error( '*** Error: received too many packets' )
@@ -431,29 +411,23 @@ class Mininet(object):
( 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]]
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
'''
"""Parse iperf output and return bandwidth.
iperfOutput: string
returns: result string"""
r = r'([\d\.]+ \w+/sec)'
m = re.search( r, iperfOutput )
if m:
@@ -461,54 +435,52 @@ class Mininet(object):
else:
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 ):
'''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
'''
"""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( '*** Iperf: testing ' + l4Type + ' 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 &')
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)
client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' +
bwArgs )
if verbose:
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)
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.'''
"Start network and run our simple CLI."
self.start()
result = MininetCLI( self )
self.stop()
@@ -516,7 +488,7 @@ class Mininet(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', \
'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump' ]
@@ -536,34 +508,34 @@ class MininetCLI(object):
# 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' + \
' <node> 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)
"Semi-useful help for CLI."
helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n' +
'You may also send a command to a node using:\n' +
' <node> 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)]))
"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]
"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 ]
@@ -571,33 +543,33 @@ class MininetCLI(object):
lg.info( '\n' )
def sh( self, args ):
'''Run an external shell command'''
"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.'''
"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.'''
"List interfaces."
for node in self.mn.nodes.values():
lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) )
def dump( self, args ):
'''Dump node info.'''
"Dump node info."
for node in self.mn.nodes.values():
lg.info( '%s\n' % node )
@@ -605,16 +577,16 @@ class MininetCLI(object):
# pylint: enable-msg=W0613
def run( self ):
'''Read and execute commands.'''
"Read and execute commands."
lg.warn( '*** Starting CLI:\n' )
while True:
lg.warn( 'mininet> ' )
input_line = sys.stdin.readline()
if input_line == '':
inputLine = sys.stdin.readline()
if inputLine == '':
break
if input_line[-1] == '\n':
input_line = input_line[:-1]
cmd = input_line.split(' ')
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 ):
@@ -622,7 +594,8 @@ class MininetCLI(object):
elif first in self.nodemap and rest != []:
node = self.nodemap[ first ]
# 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 ]
rest = ' '.join( rest )
# Interactive commands don't work yet, and
+126 -172
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
'''Node objects for Mininet.'''
"Node objects for Mininet."
from subprocess import Popen, PIPE, STDOUT
import os
@@ -14,21 +14,21 @@ 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.'''
"""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 ):
self.name = name
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' ]
self.inNamespace = inNamespace
if self.inNamespace:
cmd = [ 'netns' ] + cmd
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
close_fds = closeFds)
closeFds=closeFds )
self.stdin = self.shell.stdin
self.stdout = self.shell.stdout
self.pollOut = select.poll()
@@ -49,49 +49,43 @@ class Node(object):
# replace with Port object, eventually
def fdToNode( self, f ):
'''Insert docstring.
@param f unknown
@return bool unknown
'''
"""Insert docstring.
f: unknown
returns: bool unknown"""
node = self.outToNode.get( f )
return node or self.inToNode.get( f )
def cleanup( self ):
'''Help python collect its garbage.'''
"Help python collect its garbage."
self.shell = None
# Subshell I/O, commands and control
def read(self, fileno_max):
'''Insert docstring.
@param fileno_max unknown
'''
return os.read(self.stdout.fileno(), fileno_max)
def read( self, filenoMax ):
"""Insert docstring.
filenoMax: unknown"""
return os.read( self.stdout.fileno(), filenoMax )
def write( self, data ):
'''Write data to node.
@param data string
'''
"""Write data to node.
data: string"""
os.write( self.stdin.fileno(), data )
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 )
self.cleanup()
def stop( self ):
'''Stop node.'''
"Stop node."
self.terminate()
def waitReadable( self ):
'''Poll on node.'''
"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.'''
"""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 ] == '&':
separator = '&'
@@ -104,7 +98,7 @@ class Node(object):
self.waiting = True
def monitor( self ):
'''Monitor the output of a command, returning (done, data).'''
"Monitor the output of a command, returning (done, data)."
assert self.waiting
self.waitReadable()
data = self.read( 1024 )
@@ -115,16 +109,14 @@ class Node(object):
return False, data
def sendInt( self ):
'''Send ^C, hopefully interrupting a running subprocess.'''
"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.
'''
"""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:
@@ -139,18 +131,14 @@ class Node(object):
return output
def cmd( self, cmd ):
'''Send a command, wait for output, and return it.
@param cmd string
'''
"""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
'''
"""Call cmd and printing its output
cmd: string"""
#lg.info( '*** %s : %s', self.name, cmd )
result = self.cmd( cmd )
#lg.info( '%s\n', result )
@@ -158,72 +146,62 @@ class Node(object):
# Interface management, configuration, and routing
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 )
def newIntf( self ):
'''Reserve and return a new interface name.'''
"Reserve and return a new interface name."
intfName = self.intfName( self.intfCount )
self.intfCount += 1
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)
"""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', mac_str])
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])
"""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
'''
"""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.
@param ip IP address as dotted decimal
@param intf string, interface name
'''
"""Add route to host.
ip: IP address as dotted decimal
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.
@param intf string, interface name
'''
"""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 IP( self ):
'''Return IP address of first interface'''
"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.'''
"Check if one of our interfaces is up."
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
# Other methods
@@ -237,19 +215,17 @@ class Node(object):
class Host( Node ):
'''A host is simply a 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.'''
"""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
'''
"""Send command to Node.
cmd: string"""
if not self.execed:
return Node.sendCmd( self, cmd )
else:
@@ -257,7 +233,7 @@ class Switch(Node):
self.name )
def monitor( self ):
'''Monitor node.'''
"Monitor node."
if not self.execed:
return Node.monitor( self )
else:
@@ -265,25 +241,18 @@ class Switch(Node):
class UserSwitch( Switch ):
'''User-space switch.
Currently only works in the root namespace.
'''
"""User-space switch.
Currently only works in the root namespace."""
def __init__( self, name ):
'''Init.
@param name
'''
"""Init.
name: name for the switch"""
Switch.__init__( self, name, inNamespace=False )
def start( self, controllers ):
'''Start OpenFlow reference user datapath.
"""Start OpenFlow reference user datapath.
Log to /tmp/sN-{ ofd,ofp }.log.
@param controllers dict of controller names to objects
'''
controllers: dict of controller names to objects"""
if 'c0' not in controllers:
raise Exception( 'User datapath start() requires controller c0' )
controller = controllers[ 'c0' ]
@@ -298,30 +267,26 @@ class UserSwitch(Switch):
ofplog + ' &' )
def stop( self ):
'''Stop OpenFlow reference user datapath.'''
"Stop OpenFlow reference user datapath."
self.cmd( 'kill %ofdatapath' )
self.cmd( 'kill %ofprotocol' )
class KernelSwitch( Switch ):
'''Kernel-space switch.
Currently only works in the root namespace.
'''
"""Kernel-space switch.
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
'''
"""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.'''
"Start up reference kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log'
quietRun( 'ifconfig lo up' )
# Delete local datapath if it exists;
@@ -330,14 +295,15 @@ class KernelSwitch(Switch):
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))
self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
' '.join( intfs ) )
# Run protocol daemon
self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
controllers[ 'c0' ].IP() + ':' +
@@ -346,7 +312,7 @@ class KernelSwitch(Switch):
self.execed = False
def stop( self ):
'''Terminate kernel datapath.'''
"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
@@ -359,24 +325,20 @@ class KernelSwitch(Switch):
class OVSKernelSwitch( Switch ):
'''Open VSwitch kernel-space switch.
Currently only works in the root namespace.
'''
"""Open VSwitch kernel-space switch.
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
'''
"""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.'''
"Start up kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log'
quietRun( 'ifconfig lo up' )
# Delete local datapath if it exists;
@@ -385,8 +347,8 @@ class OVSKernelSwitch(Switch):
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'
@@ -401,7 +363,7 @@ class OVSKernelSwitch(Switch):
self.execed = False
def stop( self ):
'''Terminate kernel datapath.'''
"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
@@ -414,24 +376,22 @@ class OVSKernelSwitch(Switch):
class Controller( Node ):
'''A Controller is a Node that is running (or has execed) an
OpenFlow controller.'''
"""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",
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 )
def start( self ):
'''Start <controller> <args> on controller.
Log to /tmp/cN.log
'''
"""Start <controller> <args> on controller.
Log to /tmp/cN.log"""
cout = '/tmp/' + self.name + '.log'
if self.cdir is not None:
self.cmdPrint( 'cd ' + self.cdir )
@@ -440,69 +400,63 @@ class Controller(Node):
self.execed = False
def stop( self ):
'''Stop controller.'''
"Stop controller."
self.cmd( 'kill %' + self.controller )
self.terminate()
def IP( self ):
'''Return IP address of the Controller'''
return self.ip_address
"Return IP address of the Controller"
return self.ipAddress
class ControllerParams( object ):
'''Container for controller IP parameters.'''
"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.'''
"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:
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 = nox_core_dir + '/nox_core',
controller=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \
' '.join(nox_args),
cdir = nox_core_dir, **kwargs)
' '.join( noxArgs ),
cdir = noxCoreDir, **kwargs )
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 ):
'''Init.
@param name name to give controller
@param ip_address the IP address where the remote controller is
"""Init.
name: name to give controller
ipAddress: 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)
port: the port where the remote controller is listening"""
Controller.__init__( self, name, ipAddress=ipAddress, port=port )
def start( self ):
'''Overridden to do nothing.'''
"Overridden to do nothing."
return
def stop( self ):
'''Overridden to do nothing.'''
"Overridden to do nothing."
return
+58 -123
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
'''Utility functions for Mininet.'''
"Utility functions for Mininet."
from time import sleep
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
@@ -8,28 +8,19 @@ from subprocess import call, check_call, Popen, PIPE, STDOUT
from mininet.log import lg
def run( cmd ):
'''Simple interface to subprocess.call()
@param cmd list of command params
'''
"""Simple interface to subprocess.call()
cmd: list of command params"""
return call( cmd.split( ' ' ) )
def checkRun( cmd ):
'''Simple interface to subprocess.check_call()
@param cmd list of command params
'''
"""Simple interface to subprocess.check_call()
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
'''
"""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 )
@@ -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,14 +54,11 @@ 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
'''
"""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 )
@@ -114,100 +66,83 @@ def makeIntfPair(intf1, intf2):
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
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
'''
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 )
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)
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.'''
"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
'''
"""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
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.
@param mac MAC address as unsigned int
@return mac_str MAC colon-hex string
'''
"""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 ipStr( ip ):
'''Generate IP address string
@return ip addr string
'''
"""Generate IP address string
returns: ip addr string"""
hi = ( ip & 0xff0000 ) >> 16
mid = ( ip & 0xff00 ) >> 8
lo = ip & 0xff
Executable → Regular
+11 -18
View File
@@ -1,6 +1,7 @@
#!/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.
Requires xterm( 1 ) and GNU screen( 1 ).
@@ -8,17 +9,13 @@ 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
'''
"""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)'
@@ -30,9 +27,8 @@ def makeXterm(node, title):
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' )
for line in output:
@@ -40,12 +36,9 @@ def cleanUpScreens():
if m:
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
'''
"""Create XTerms.
nodes: list of Node objects
title: base title for each
returns: list of created xterm processes"""
return [ makeXterm( node, title ) for node in nodes ]
+8 -8
View File
@@ -1,16 +1,16 @@
#!/usr/bin/python
"""
unpep8:
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Python style:
Python style
- Reinstates CapWords for methods and instance variables.
- Gets rid of triple single quotes.
- Eliminates triple quotes on single lines.
- Inserts extra spaces to improve readability.
- Fixes Doxygen (or doxypy) ugliness.
usage: unpep8 < old.py > new.py
- Reinstates CapWords for methods and instance variables
- Gets rid of triple single quotes
- Eliminates triple quotes on single lines
- Inserts extra spaces to improve readability
- Fixes Doxygen (or doxypy) ugliness
Does the following translations: