Add TESTS parameters, multiple tests with +
It's very useful to be able to pass parameters to tests. Unfortunately, our previous --test argument syntax didn't permit it, because , was used as a delimiter for multiple tests. With this change, we change , to become a delimiter for arguments, as it is with other options to mn. For now, we introduce + as a separator, as it is legal in filenames and therefore shouldn't conflict with special characters used by Unix shells. We also reorganize mn a bit to make it hopefully slightly clearer.
This commit is contained in:
@@ -21,7 +21,7 @@ if 'PYTHONPATH' in os.environ:
|
||||
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||
|
||||
from mininet.clean import cleanup
|
||||
from mininet.cli import CLI
|
||||
from mininet.cli import CLI as CLI
|
||||
from mininet.log import lg, LEVELS, info, debug, warn, error
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
@@ -87,16 +87,48 @@ LINKS = { 'default': Link,
|
||||
'tc': TCLink,
|
||||
'ovs': OVSLink }
|
||||
|
||||
# TESTS dict can contain functions and/or Mininet() method names
|
||||
# XXX: it would be nice if we could specify a default test, but
|
||||
# this may be tricky
|
||||
TESTS = { name: True
|
||||
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
|
||||
|
||||
# Names of tests that are Mininet() methods
|
||||
TESTNAMES = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||
'none' ]
|
||||
|
||||
# Map to alternate functions and/or spellings of Mininet() methods
|
||||
TESTS = { 'pingall': 'pingAll',
|
||||
'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp',
|
||||
'iperfUDP': 'iperfUdp' }
|
||||
# Locally defined tests
|
||||
def allTest( net ):
|
||||
"Run ping and iperf tests"
|
||||
net.waitConnected()
|
||||
net.start()
|
||||
net.ping()
|
||||
net.iperf()
|
||||
def nullTest( net ):
|
||||
"Null 'test' (does nothing)"
|
||||
pass
|
||||
TESTS.update( all=allTest, none=nullTest, build=nullTest )
|
||||
|
||||
# Map to alternate spellings of Mininet() methods
|
||||
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp' }
|
||||
|
||||
def runTests( mn, options ):
|
||||
"""Run tests
|
||||
mn: Mininet object
|
||||
option: list of test optinos """
|
||||
# Split option into test name and parameters
|
||||
for option in options:
|
||||
# Multiple tests may be separated by '+' for now
|
||||
for test in option.split( '+' ):
|
||||
test, args, kwargs = splitArgs( test )
|
||||
test = ALTSPELLING.get( test.lower(), test )
|
||||
testfn = TESTS.get( test, test )
|
||||
if callable( testfn ):
|
||||
testfn( mn, *args, **kwargs )
|
||||
elif hasattr( mn, test ):
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )( *args, **kwargs )
|
||||
else:
|
||||
raise Exception( 'Test %s is unknown - please specify one of '
|
||||
'%s ' % ( test, TESTS.keys() ) )
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
@@ -117,7 +149,7 @@ def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
print( "%s" % VERSION )
|
||||
info( "%s\n" % VERSION )
|
||||
exit()
|
||||
|
||||
|
||||
@@ -210,9 +242,8 @@ class MininetRunner( object ):
|
||||
type='string',
|
||||
help='read custom classes or params from .py file(s)'
|
||||
)
|
||||
testList = TESTNAMES + TESTS.keys()
|
||||
opts.add_option( '--test', default=[], action='append',
|
||||
dest='tests', help='|'.join( testList ) )
|
||||
dest='test', help='|'.join( TESTS.keys() ) )
|
||||
opts.add_option( '--xterms', '-x', action='store_true',
|
||||
default=False, help='spawn xterms for each node' )
|
||||
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
|
||||
@@ -269,9 +300,9 @@ class MininetRunner( object ):
|
||||
|
||||
# set logging verbosity
|
||||
if LEVELS[self.options.verbosity] > LEVELS['output']:
|
||||
print( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
'output!\n'
|
||||
'Please restart Mininet with -v [debug, info, output].'
|
||||
'Please restart Mininet with -v [debug, info, output].\n'
|
||||
% self.options.verbosity )
|
||||
lg.setLogLevel( self.options.verbosity )
|
||||
|
||||
@@ -281,108 +312,84 @@ class MininetRunner( object ):
|
||||
def begin( self ):
|
||||
"Create and run mininet."
|
||||
|
||||
if self.options.cluster:
|
||||
servers = self.options.cluster.split( ',' )
|
||||
opts = self.options
|
||||
|
||||
if opts.cluster:
|
||||
servers = opts.cluster.split( ',' )
|
||||
for server in servers:
|
||||
ClusterCleanup.add( server )
|
||||
|
||||
if self.options.clean:
|
||||
if opts.clean:
|
||||
cleanup()
|
||||
exit()
|
||||
|
||||
start = time.time()
|
||||
|
||||
if not self.options.controller:
|
||||
if not opts.controller:
|
||||
# Update default based on available controllers
|
||||
CONTROLLERS[ 'default' ] = findController()
|
||||
self.options.controller = [ 'default' ]
|
||||
opts.controller = [ 'default' ]
|
||||
if not CONTROLLERS[ 'default' ]:
|
||||
self.options.controller = [ 'none' ]
|
||||
if self.options.switch == 'default':
|
||||
opts.controller = [ 'none' ]
|
||||
if opts.switch == 'default':
|
||||
info( '*** No default OpenFlow controller found '
|
||||
'for default switch!\n' )
|
||||
info( '*** Falling back to OVS Bridge\n' )
|
||||
self.options.switch = 'ovsbr'
|
||||
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
opts.switch = 'ovsbr'
|
||||
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
raise Exception( "Could not find a default controller "
|
||||
"for switch %s" %
|
||||
self.options.switch )
|
||||
opts.switch )
|
||||
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customClass( SWITCHES, self.options.switch )
|
||||
host = customClass( HOSTS, self.options.host )
|
||||
topo = buildTopo( TOPOS, opts.topo )
|
||||
switch = customClass( SWITCHES, opts.switch )
|
||||
host = customClass( HOSTS, opts.host )
|
||||
controller = [ customClass( CONTROLLERS, c )
|
||||
for c in self.options.controller ]
|
||||
link = customClass( LINKS, self.options.link )
|
||||
for c in opts.controller ]
|
||||
link = customClass( LINKS, opts.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( self.options )
|
||||
self.validate( opts )
|
||||
|
||||
ipBase = self.options.ipbase
|
||||
xterms = self.options.xterms
|
||||
mac = self.options.mac
|
||||
arp = self.options.arp
|
||||
pin = self.options.pin
|
||||
listenPort = None
|
||||
if not self.options.nolistenport:
|
||||
listenPort = self.options.listenport
|
||||
if opts.nolistenport:
|
||||
opts.listenport = None
|
||||
|
||||
# Handle inNamespace, cluster options
|
||||
inNamespace = self.options.innamespace
|
||||
cluster = self.options.cluster
|
||||
if inNamespace and cluster:
|
||||
print( "Please specify --innamespace OR --cluster" )
|
||||
# Handle innamespace, cluster options
|
||||
if opts.innamespace and opts.cluster:
|
||||
error( "Please specify --innamespace OR --cluster\n" )
|
||||
exit()
|
||||
Net = MininetWithControlNet if inNamespace else Mininet
|
||||
cli = ClusterCLI if cluster else CLI
|
||||
if cluster:
|
||||
Net = MininetWithControlNet if opts.innamespace else Mininet
|
||||
if opts.cluster:
|
||||
warn( '*** WARNING: Experimental cluster mode!\n'
|
||||
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
|
||||
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
|
||||
Net = partial( MininetCluster, servers=servers,
|
||||
placement=PLACEMENT[ self.options.placement ] )
|
||||
placement=PLACEMENT[ opts.placement ] )
|
||||
global CLI
|
||||
CLI = ClusterCLI
|
||||
|
||||
mn = Net( topo=topo,
|
||||
switch=switch, host=host, controller=controller,
|
||||
link=link,
|
||||
ipBase=ipBase,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, autoPinCpus=pin,
|
||||
listenPort=listenPort )
|
||||
switch=switch, host=host, controller=controller, link=link,
|
||||
ipBase=opts.ipbase, inNamespace=opts.innamespace,
|
||||
xterms=opts.xterms, autoSetMacs=opts.mac,
|
||||
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
|
||||
listenPort=opts.listenport )
|
||||
|
||||
if self.options.ensure_value( 'nat', False ):
|
||||
nat = mn.addNAT( *self.options.nat_args,
|
||||
**self.options.nat_kwargs )
|
||||
nat.configDefault()
|
||||
if opts.ensure_value( 'nat', False ):
|
||||
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
|
||||
|
||||
if self.options.pre:
|
||||
cli( mn, script=self.options.pre )
|
||||
if opts.pre:
|
||||
CLI( mn, script=opts.pre )
|
||||
|
||||
mn.start()
|
||||
|
||||
if not self.options.tests:
|
||||
cli( mn )
|
||||
if opts.test:
|
||||
runTests( mn, opts.test )
|
||||
else:
|
||||
CLI( mn )
|
||||
|
||||
for test in self.options.tests:
|
||||
test = TESTS.get( test, test )
|
||||
if callable( test ): # user added TESTMAP={'mytest': testfn}
|
||||
test( mn )
|
||||
elif test == 'none':
|
||||
pass
|
||||
elif test == 'all':
|
||||
mn.waitConnected()
|
||||
mn.start()
|
||||
mn.ping()
|
||||
mn.iperf()
|
||||
elif test == 'cli':
|
||||
cli( mn )
|
||||
elif test != 'build':
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )()
|
||||
|
||||
if self.options.post:
|
||||
cli( mn, script=self.options.post )
|
||||
if opts.post:
|
||||
CLI( mn, script=opts.post )
|
||||
|
||||
mn.stop()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user