There is probably a better way of doing this, but currently parseCustomFile can modify globals (e.g. TOPOS) as well as instance variables (self.validate) and classes (e.g. MyTopo), which are also in the global name space. Inconveniently enough, lambdas don't seem to be full closures in Python; if they were, this trickiness would be unnecessary. Even so, using execfile() seems like it might be a bit dubious...
205 lines
7.3 KiB
Python
Executable File
205 lines
7.3 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
"""
|
|
Mininet runner
|
|
author: Brandon Heller (brandonh@stanford.edu)
|
|
|
|
To see options:
|
|
sudo mn -h
|
|
|
|
Example to pull custom params (topo, switch, etc.) from a file:
|
|
sudo mn --custom ~/mininet/custom/custom_example.py
|
|
"""
|
|
|
|
from optparse import OptionParser
|
|
import os.path
|
|
import sys
|
|
import time
|
|
|
|
from mininet.log import lg, LEVELS
|
|
from mininet.net import Mininet, init
|
|
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
|
|
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
|
|
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
|
|
|
# built in topologies, created only when run
|
|
TOPODEF = 'minimal'
|
|
TOPOS = { 'minimal': ( lambda: SingleSwitchTopo( k=2 ) ),
|
|
'reversed': ( lambda: SingleSwitchReversedTopo( k=2 ) ),
|
|
'single4': ( lambda: SingleSwitchTopo( k=4 ) ),
|
|
'single100': ( lambda: SingleSwitchTopo( k=100 ) ),
|
|
'linear2': ( lambda: LinearTopo( k=2 ) ),
|
|
'linear100': ( lambda: LinearTopo( k=100 ) ) }
|
|
|
|
SWITCHDEF = 'kernel'
|
|
SWITCHES = { 'kernel': KernelSwitch,
|
|
'user': UserSwitch,
|
|
'ovsk': OVSKernelSwitch }
|
|
|
|
HOSTDEF = 'process'
|
|
HOSTS = { 'process': Host }
|
|
|
|
CONTROLLERDEF = 'ref'
|
|
# a and b are the name and inNamespace params.
|
|
CONTROLLERS = { 'ref': Controller,
|
|
'nox_dump': lambda a, b: NOX( a, b, 'packetdump' ),
|
|
'nox_pysw': lambda a, b: NOX( a, b, 'pyswitch' ),
|
|
'remote': lambda a, b: None,
|
|
'none': lambda a, b: None }
|
|
|
|
# optional tests to run
|
|
TESTS = [ 'cli', 'build', 'pingAll', 'pingPair', 'iperf', 'all', 'iperfUdp' ]
|
|
|
|
|
|
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
|
"""Convenience function to add choices dicts to OptionParser.
|
|
opts: OptionParser instance
|
|
choicesDict: dictionary of valid choices, must include default
|
|
default: default choice key
|
|
name: long option name
|
|
help: string"""
|
|
if default not in choicesDict:
|
|
raise Exception( 'Invalid default %s for choices dict: %s' %
|
|
( default, name ) )
|
|
if not helpStr:
|
|
helpStr = '[' + ' '.join( choicesDict.keys() ) + ']'
|
|
opts.add_option( '--' + name,
|
|
type='choice',
|
|
choices=choicesDict.keys(),
|
|
default = default,
|
|
help = helpStr )
|
|
|
|
|
|
class MininetRunner( object ):
|
|
"Build, setup, and run Mininet."
|
|
|
|
def __init__( self ):
|
|
"Init."
|
|
self.options = None
|
|
self.validate = None
|
|
|
|
self.parseArgs()
|
|
self.setup()
|
|
self.begin()
|
|
|
|
def setCustom( self, name, value ):
|
|
print "got", self, name, value
|
|
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
|
|
# Update dictionaries
|
|
param = name.upper()
|
|
globals()[ param ].update( value )
|
|
elif name == 'validate':
|
|
# Add custom validate function
|
|
self.validate = value
|
|
else:
|
|
# Add or modify global variable or class
|
|
globals()[ name ] = value
|
|
|
|
def parseCustomFile( self, fileName ):
|
|
"Parse custom file and add params before parsing cmd-line options."
|
|
custom = {}
|
|
if os.path.isfile( fileName ):
|
|
execfile( fileName, custom, custom )
|
|
for name in custom:
|
|
self.setCustom( name, custom[ name ] )
|
|
else:
|
|
raise Exception( 'could not find custom file: %s' % fileName )
|
|
|
|
def parseArgs( self ):
|
|
"""Parse command-line args and return options object.
|
|
returns: opts parse options dict"""
|
|
if '--custom' in sys.argv:
|
|
print "custom in sys.argv"
|
|
index = sys.argv.index( '--custom' )
|
|
if len( sys.argv ) > index + 1:
|
|
custom = sys.argv[ index + 1 ]
|
|
print "custom = %s" % custom
|
|
self.parseCustomFile( custom )
|
|
else:
|
|
raise Exception( 'Custom file name not found' )
|
|
|
|
opts = OptionParser()
|
|
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
|
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
|
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
|
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
|
|
|
opts.add_option( '--custom', type='string', default=None,
|
|
help='read custom topo and node params from .py file' )
|
|
opts.add_option( '--test', type='choice', choices=TESTS,
|
|
default=TESTS[ 0 ],
|
|
help='[' + ' '.join( TESTS ) + ']' )
|
|
opts.add_option( '--xterms', '-x', action='store_true',
|
|
default=False, help='spawn xterms for each node' )
|
|
opts.add_option( '--mac', action='store_true',
|
|
default=False, help='set MACs equal to DPIDs' )
|
|
opts.add_option( '--arp', action='store_true',
|
|
default=False, help='set all-pairs ARP entries' )
|
|
opts.add_option( '--verbosity', '-v', type='choice',
|
|
choices=LEVELS.keys(), default = 'info',
|
|
help = '[' + ' '.join( LEVELS.keys() ) + ']' )
|
|
opts.add_option( '--ip', type='string', default='127.0.0.1',
|
|
help='[ip address as a dotted decimal string for a'
|
|
'remote controller]' )
|
|
opts.add_option( '--port', type='string', default=6633,
|
|
help='[port integer for a listening remote'
|
|
' controller]' )
|
|
opts.add_option( '--inNamespace', action='store_true',
|
|
default=False, help='sw and ctrl in namespace?' )
|
|
self.options = opts.parse_args()[ 0 ]
|
|
|
|
def setup( self ):
|
|
"Setup and validate environment."
|
|
|
|
# set logging verbosity
|
|
lg.setLogLevel( self.options.verbosity )
|
|
|
|
# validate environment setup
|
|
init()
|
|
|
|
def begin( self ):
|
|
"Create and run mininet."
|
|
|
|
start = time.time()
|
|
|
|
topo = TOPOS[ self.options.topo ]() # build topology object
|
|
switch = SWITCHES[ self.options.switch ]
|
|
host = HOSTS[ self.options.host ]
|
|
controller = CONTROLLERS[ self.options.controller ]
|
|
if self.options.controller == 'remote':
|
|
controller = lambda a, b: RemoteController( a, b,
|
|
ipAddress=self.options.ip,
|
|
port=self.options.port )
|
|
|
|
if self.validate:
|
|
self.validate( self.options )
|
|
|
|
controllerParams = ControllerParams( 0x0a000000, 8 ) # 10.0.0.0/8
|
|
inNamespace = self.options.inNamespace
|
|
xterms = self.options.xterms
|
|
mac = self.options.mac
|
|
arp = self.options.arp
|
|
mn = Mininet( topo, switch, host, controller, controllerParams,
|
|
inNamespace=inNamespace,
|
|
xterms=xterms, autoSetMacs=mac,
|
|
autoStaticArp=arp )
|
|
|
|
test = self.options.test
|
|
if test != 'build':
|
|
if test == 'cli':
|
|
mn.interact()
|
|
elif test == 'all':
|
|
mn.start()
|
|
mn.ping()
|
|
mn.iperf()
|
|
mn.stop()
|
|
else:
|
|
mn.run( test )
|
|
|
|
elapsed = float( time.time() - start )
|
|
print ( 'completed in %0.3f seconds' % elapsed )
|
|
|
|
|
|
if __name__ == "__main__":
|
|
MininetRunner()
|