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:
@@ -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
|
||||
|
||||
@@ -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
@@ -1 +1 @@
|
||||
'''Docstring to silence pylint; ignores --ignore option for __init__.py'''
|
||||
"Docstring to silence pylint; ignores --ignore option for __init__.py"
|
||||
|
||||
+26
-33
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user