Munged mn and mnclean into mininet style.
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
'''Mininet runner
|
|
||||||
|
|
||||||
@author Brandon Heller (brandonh@stanford.edu)
|
"""
|
||||||
'''
|
Mininet runner
|
||||||
|
author: Brandon Heller (brandonh@stanford.edu)
|
||||||
|
"""
|
||||||
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import os.path
|
import os.path
|
||||||
@@ -10,9 +11,9 @@ import time
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from ripcord.dctopo import TreeTopo, FatTreeTopo, VL2Topo
|
from ripcord.dctopo import TreeTopo, FatTreeTopo, VL2Topo
|
||||||
USE_RIPCORD = True
|
USERIPCORD = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
USE_RIPCORD = False
|
USERIPCORD = False
|
||||||
|
|
||||||
from mininet.log import lg, LEVELS
|
from mininet.log import lg, LEVELS
|
||||||
from mininet.net import Mininet, init
|
from mininet.net import Mininet, init
|
||||||
@@ -21,164 +22,162 @@ from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
|
|||||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||||
|
|
||||||
# built in topologies, created only when run
|
# built in topologies, created only when run
|
||||||
TOPO_DEF = 'minimal'
|
TOPODEF = 'minimal'
|
||||||
TOPOS = {'minimal': (lambda: SingleSwitchTopo(k = 2)),
|
TOPOS = { 'minimal': ( lambda: SingleSwitchTopo( k=2 ) ),
|
||||||
'reversed': (lambda: SingleSwitchReversedTopo(k = 2)),
|
'reversed': ( lambda: SingleSwitchReversedTopo( k=2 ) ),
|
||||||
'single4': (lambda: SingleSwitchTopo(k = 4)),
|
'single4': ( lambda: SingleSwitchTopo( k=4 ) ),
|
||||||
'single100': (lambda: SingleSwitchTopo(k = 100)),
|
'single100': ( lambda: SingleSwitchTopo( k=100 ) ),
|
||||||
'linear2': (lambda: LinearTopo(k = 2)),
|
'linear2': ( lambda: LinearTopo( k=2 ) ),
|
||||||
'linear100': (lambda: LinearTopo(k = 100))}
|
'linear100': ( lambda: LinearTopo( k=100 ) ) }
|
||||||
if USE_RIPCORD:
|
if USERIPCORD:
|
||||||
TOPOS_RIPCORD = {
|
TOPOSRIPCORD = {
|
||||||
'tree16': (lambda: TreeTopo(depth = 3, fanout = 4)),
|
'tree16': ( lambda: TreeTopo( depth=3, fanout=4 ) ),
|
||||||
'tree64': (lambda: TreeTopo(depth = 4, fanout = 4)),
|
'tree64': ( lambda: TreeTopo( depth=4, fanout=4 ) ),
|
||||||
'tree1024': (lambda: TreeTopo(depth = 3, fanout = 32)),
|
'tree1024': ( lambda: TreeTopo( depth=3, fanout=32 ) ),
|
||||||
'fattree4': (lambda: FatTreeTopo(k = 4)),
|
'fattree4': ( lambda: FatTreeTopo( k=4 ) ),
|
||||||
'fattree6': (lambda: FatTreeTopo(k = 6)),
|
'fattree6': ( lambda: FatTreeTopo( k=6 ) ),
|
||||||
'vl2': (lambda: VL2Topo(da = 4, di = 4)),
|
'vl2': ( lambda: VL2Topo( da=4, di=4 ) ),
|
||||||
'vl2reduced': (lambda: VL2Topo(da = 4, di = 4, edge_down = 1))}
|
'vl2reduced': ( lambda: VL2Topo( da=4, di=4, edgeDown=1 ) ) }
|
||||||
TOPOS.update(TOPOS_RIPCORD)
|
TOPOS.update( TOPOSRIPCORD )
|
||||||
|
|
||||||
SWITCH_DEF = 'kernel'
|
SWITCHDEF = 'kernel'
|
||||||
SWITCHES = {'kernel': KernelSwitch,
|
SWITCHES = { 'kernel': KernelSwitch,
|
||||||
'user': UserSwitch,
|
'user': UserSwitch,
|
||||||
'ovsk': OVSKernelSwitch}
|
'ovsk': OVSKernelSwitch }
|
||||||
|
|
||||||
HOST_DEF = 'process'
|
HOSTDEF = 'process'
|
||||||
HOSTS = {'process': Host}
|
HOSTS = { 'process': Host }
|
||||||
|
|
||||||
CONTROLLER_DEF = 'ref'
|
CONTROLLERDEF = 'ref'
|
||||||
# a and b are the name and inNamespace params.
|
# a and b are the name and inNamespace params.
|
||||||
CONTROLLERS = {'ref': Controller,
|
CONTROLLERS = { 'ref': Controller,
|
||||||
'nox_dump': lambda a, b: NOX(a, b, 'packetdump'),
|
'nox_dump': lambda a, b: NOX( a, b, 'packetdump' ),
|
||||||
'nox_pysw': lambda a, b: NOX(a, b, 'pyswitch'),
|
'nox_pysw': lambda a, b: NOX( a, b, 'pyswitch' ),
|
||||||
'remote': lambda a, b: None,
|
'remote': lambda a, b: None,
|
||||||
'none': lambda a, b: None}
|
'none': lambda a, b: None }
|
||||||
|
|
||||||
# optional tests to run
|
# optional tests to run
|
||||||
TESTS = ['cli', 'build', 'ping_all', 'ping_pair', 'iperf', 'all', 'iperf_udp']
|
TESTS = [ 'cli', 'build', 'ping_all', 'ping_pair', 'iperf', 'all',
|
||||||
|
'iperf_udp' ]
|
||||||
|
|
||||||
|
|
||||||
def add_dict_option(opts, choices_dict, default, name, help_str = None):
|
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||||
'''Convenience function to add choices dicts to OptionParser.
|
"""Convenience function to add choices dicts to OptionParser.
|
||||||
|
opts: OptionParser instance
|
||||||
@param opts OptionParser instance
|
choicesDict: dictionary of valid choices, must include default
|
||||||
@param choices_dict dictionary of valid choices, must include default
|
default: default choice key
|
||||||
@param default default choice key
|
name: long option name
|
||||||
@param name long option name
|
help: string"""
|
||||||
@param help string
|
if default not in choicesDict:
|
||||||
'''
|
raise Exception( 'Invalid default %s for choices dict: %s' %
|
||||||
if default not in choices_dict:
|
( default, name ) )
|
||||||
raise Exception('Invalid default %s for choices dict: %s' %
|
if not helpStr:
|
||||||
(default, name))
|
helpStr = '[' + ' '.join( choicesDict.keys() ) + ']'
|
||||||
if not help_str:
|
opts.add_option( '--' + name,
|
||||||
help_str = '[' + ' '.join(choices_dict.keys()) + ']'
|
type='choice',
|
||||||
opts.add_option('--' + name,
|
choices=choicesDict.keys(),
|
||||||
type = 'choice',
|
|
||||||
choices = choices_dict.keys(),
|
|
||||||
default = default,
|
default = default,
|
||||||
help = help_str)
|
help = helpStr )
|
||||||
|
|
||||||
|
|
||||||
class MininetRunner(object):
|
class MininetRunner( object ):
|
||||||
'''Build, setup, and run Mininet.'''
|
"Build, setup, and run Mininet."
|
||||||
|
|
||||||
def __init__(self):
|
def __init__( self ):
|
||||||
'''Init.'''
|
"Init."
|
||||||
self.options = None
|
self.options = None
|
||||||
|
|
||||||
self.parse_args()
|
self.parseArgs()
|
||||||
self.setup()
|
self.setup()
|
||||||
self.begin()
|
self.begin()
|
||||||
|
|
||||||
def parse_args(self):
|
def parseArgs( self ):
|
||||||
'''Parse command-line args and return options object.
|
"""Parse command-line args and return options object.
|
||||||
|
returns: opts parse options dict"""
|
||||||
@return opts parse options dict
|
|
||||||
'''
|
|
||||||
opts = OptionParser()
|
opts = OptionParser()
|
||||||
add_dict_option(opts, TOPOS, TOPO_DEF, 'topo')
|
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||||
add_dict_option(opts, SWITCHES, SWITCH_DEF, 'switch')
|
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||||
add_dict_option(opts, HOSTS, HOST_DEF, 'host')
|
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
||||||
add_dict_option(opts, CONTROLLERS, CONTROLLER_DEF, 'controller')
|
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
||||||
|
|
||||||
opts.add_option('--custom', type = 'string', default = None,
|
opts.add_option( '--custom', type='string', default=None,
|
||||||
help = 'read custom mininet from current dir')
|
help='read custom mininet from current dir' )
|
||||||
opts.add_option('--test', type = 'choice', choices = TESTS,
|
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||||
default = TESTS[0],
|
default=TESTS[ 0 ],
|
||||||
help = '[' + ' '.join(TESTS) + ']')
|
help='[' + ' '.join( TESTS ) + ']' )
|
||||||
opts.add_option('--xterms', '-x', action = 'store_true',
|
opts.add_option( '--xterms', '-x', action='store_true',
|
||||||
default = False, help = 'spawn xterms for each node')
|
default=False, help='spawn xterms for each node' )
|
||||||
opts.add_option('--mac', action = 'store_true',
|
opts.add_option( '--mac', action='store_true',
|
||||||
default = False, help = 'set MACs equal to DPIDs')
|
default=False, help='set MACs equal to DPIDs' )
|
||||||
opts.add_option('--arp', action = 'store_true',
|
opts.add_option( '--arp', action='store_true',
|
||||||
default = False, help = 'set all-pairs ARP entries')
|
default=False, help='set all-pairs ARP entries' )
|
||||||
opts.add_option('--verbosity', '-v', type = 'choice',
|
opts.add_option( '--verbosity', '-v', type='choice',
|
||||||
choices = LEVELS.keys(), default = 'info',
|
choices=LEVELS.keys(), default = 'info',
|
||||||
help = '[' + ' '.join(LEVELS.keys()) + ']')
|
help = '[' + ' '.join( LEVELS.keys() ) + ']' )
|
||||||
opts.add_option('--ip', type = 'string', default = '127.0.0.1',
|
opts.add_option( '--ip', type='string', default='127.0.0.1',
|
||||||
help = '[ip address as a dotted decimal string for a'
|
help='[ip address as a dotted decimal string for a'
|
||||||
'remote controller]')
|
'remote controller]' )
|
||||||
opts.add_option('--port', type = 'string', default = 6633,
|
opts.add_option( '--port', type='string', default=6633,
|
||||||
help = '[port integer for a listening remote'
|
help='[port integer for a listening remote'
|
||||||
' controller]')
|
' controller]' )
|
||||||
opts.add_option('--in_namespace', action = 'store_true',
|
opts.add_option( '--in_namespace', action='store_true',
|
||||||
default = False, help = 'sw and ctrl in namespace?')
|
default=False, help='sw and ctrl in namespace?' )
|
||||||
self.options = opts.parse_args()[0]
|
self.options = opts.parse_args()[ 0 ]
|
||||||
|
|
||||||
def setup(self):
|
def setup( self ):
|
||||||
'''Setup and validate environment.'''
|
"Setup and validate environment."
|
||||||
|
|
||||||
# set logging verbosity
|
# set logging verbosity
|
||||||
lg.setLogLevel(self.options.verbosity)
|
lg.setLogLevel( self.options.verbosity )
|
||||||
|
|
||||||
# validate environment setup
|
# validate environment setup
|
||||||
init()
|
init()
|
||||||
|
|
||||||
# check for invalid combinations
|
# check for invalid combinations
|
||||||
if self.options.controller == 'ref' and \
|
if ( self.options.controller == 'ref' and
|
||||||
(('fattree' in self.options.topo) or ('vl2' in self.options.topo)):
|
( ( 'fattree' in self.options.topo ) or
|
||||||
raise Exception('multipath topos require multipath-capable '
|
( 'vl2' in self.options.topo ) ) ):
|
||||||
'controller.')
|
raise Exception( 'multipath topos require multipath-capable '
|
||||||
|
'controller.' )
|
||||||
|
|
||||||
if self.options.custom:
|
if self.options.custom:
|
||||||
if not os.path.isfile(self.options.custom):
|
if not os.path.isfile( self.options.custom ):
|
||||||
raise Exception('could not find custom file: %s' %
|
raise Exception( 'could not find custom file: %s' %
|
||||||
self.options.custom)
|
self.options.custom )
|
||||||
|
|
||||||
def begin(self):
|
def begin( self ):
|
||||||
'''Create and run mininet.'''
|
"Create and run mininet."
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
topo = TOPOS[self.options.topo]() # build topology object
|
topo = TOPOS[ self.options.topo ]() # build topology object
|
||||||
switch = SWITCHES[self.options.switch]
|
switch = SWITCHES[ self.options.switch ]
|
||||||
host = HOSTS[self.options.host]
|
host = HOSTS[ self.options.host ]
|
||||||
controller = CONTROLLERS[self.options.controller]
|
controller = CONTROLLERS[ self.options.controller ]
|
||||||
if self.options.controller == 'remote':
|
if self.options.controller == 'remote':
|
||||||
controller = lambda a, b: RemoteController(a, b,
|
controller = lambda a, b: RemoteController( a, b,
|
||||||
ip_address = self.options.ip,
|
ipAddress=self.options.ip,
|
||||||
port = self.options.port)
|
port=self.options.port )
|
||||||
|
|
||||||
controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
|
controllerParams = ControllerParams( 0x0a000000, 8 ) # 10.0.0.0/8
|
||||||
in_namespace = self.options.in_namespace
|
inNamespace = self.options.inNamespace
|
||||||
xterms = self.options.xterms
|
xterms = self.options.xterms
|
||||||
mac = self.options.mac
|
mac = self.options.mac
|
||||||
arp = self.options.arp
|
arp = self.options.arp
|
||||||
mn = None
|
mn = None
|
||||||
if not self.options.custom:
|
if not self.options.custom:
|
||||||
mn = Mininet(topo, switch, host, controller, controller_params,
|
mn = Mininet( topo, switch, host, controller, controllerParams,
|
||||||
in_namespace = in_namespace,
|
inNamespace=inNamespace,
|
||||||
xterms = xterms, auto_set_macs = mac,
|
xterms=xterms, autoSetMacs=mac,
|
||||||
auto_static_arp = arp)
|
autoStaticArp=arp )
|
||||||
else:
|
else:
|
||||||
globals_ = {}
|
globals_ = {}
|
||||||
locals_ = {}
|
locals_ = {}
|
||||||
execfile(self.options.custom, globals_, locals_)
|
execfile( self.options.custom, globals_, locals_ )
|
||||||
if 'mn' not in locals_:
|
if 'mn' not in locals_:
|
||||||
raise Exception('could not find mn var in custom file')
|
raise Exception( 'could not find mn var in custom file' )
|
||||||
else:
|
else:
|
||||||
mn = locals_['mn']
|
mn = locals_[ 'mn' ]
|
||||||
|
|
||||||
test = self.options.test
|
test = self.options.test
|
||||||
if test != 'build':
|
if test != 'build':
|
||||||
@@ -190,10 +189,10 @@ class MininetRunner(object):
|
|||||||
mn.iperf()
|
mn.iperf()
|
||||||
mn.stop()
|
mn.stop()
|
||||||
else:
|
else:
|
||||||
mn.run(test)
|
mn.run( test )
|
||||||
|
|
||||||
elapsed = float(time.time() - start)
|
elapsed = float( time.time() - start )
|
||||||
print ('completed in %0.3f seconds' % elapsed)
|
print ( 'completed in %0.3f seconds' % elapsed )
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Executable → Regular
+12
-14
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""Mininet Cleanup
|
|
||||||
|
|
||||||
@author Bob Lantz (rlantz@cs.stanford.edu)
|
"""
|
||||||
|
Mininet Cleanup
|
||||||
|
author: Bob Lantz (rlantz@cs.stanford.edu)
|
||||||
|
|
||||||
Unfortunately, Mininet and OpenFlow don't always clean up
|
Unfortunately, Mininet and OpenFlow don't always clean up
|
||||||
properly after themselves. Until they do (or until cleanup
|
properly after themselves. Until they do (or until cleanup
|
||||||
@@ -15,42 +16,39 @@ from subprocess import Popen, PIPE
|
|||||||
|
|
||||||
from mininet.xterm import cleanUpScreens
|
from mininet.xterm import cleanUpScreens
|
||||||
|
|
||||||
|
def sh( cmd ):
|
||||||
def sh(cmd):
|
|
||||||
"Print a command and send it to the shell"
|
"Print a command and send it to the shell"
|
||||||
print cmd
|
print cmd
|
||||||
return Popen(['/bin/sh', '-c', cmd], stdout=PIPE).communicate()[0]
|
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||||
|
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
"""Clean up junk which might be left over from old runs;
|
"""Clean up junk which might be left over from old runs;
|
||||||
do fast stuff before slow dp and link removal!"""
|
do fast stuff before slow dp and link removal!"""
|
||||||
|
|
||||||
print "*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
|
print "*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
|
||||||
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
||||||
zombies += 'udpbwtest'
|
zombies += 'udpbwtest'
|
||||||
# Note: real zombie processes can't actually be killed, since they
|
# Note: real zombie processes can't actually be killed, since they
|
||||||
# are already (un)dead. Then again,
|
# are already ( un )dead. Then again,
|
||||||
# you can't connect to them either, so they're mostly harmless.
|
# you can't connect to them either, so they're mostly harmless.
|
||||||
sh('killall -9 ' + zombies + ' 2> /dev/null')
|
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
||||||
|
|
||||||
print "*** Removing junk from /tmp"
|
print "*** Removing junk from /tmp"
|
||||||
sh('rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log')
|
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
|
||||||
|
|
||||||
print "*** Removing old screen sessions"
|
print "*** Removing old screen sessions"
|
||||||
cleanUpScreens()
|
cleanUpScreens()
|
||||||
|
|
||||||
print "*** Removing excess kernel datapaths"
|
print "*** Removing excess kernel datapaths"
|
||||||
dps = sh("ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'").split('\n')
|
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' )
|
||||||
for dp in dps:
|
for dp in dps:
|
||||||
if dp != '':
|
if dp != '':
|
||||||
sh('dpctl deldp ' + dp)
|
sh( 'dpctl deldp ' + dp )
|
||||||
|
|
||||||
print "*** Removing all links of the pattern foo-ethX"
|
print "*** Removing all links of the pattern foo-ethX"
|
||||||
links = sh("ip link show | egrep -o '(\w+-eth\w+)'").split('\n')
|
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
|
||||||
for link in links:
|
for link in links:
|
||||||
if link != '':
|
if link != '':
|
||||||
sh("ip link del " + link)
|
sh( "ip link del " + link )
|
||||||
|
|
||||||
print "*** Cleanup complete."
|
print "*** Cleanup complete."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user