This increases flexibility by allowing a topology to be manually created in Python, or specified using another format, without having to create a Topo object first. However, Topos are useful, and are still the default topology object!
200 lines
7.2 KiB
Python
Executable File
200 lines
7.2 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
"""
|
|
Mininet runner
|
|
author: Brandon Heller (brandonh@stanford.edu)
|
|
"""
|
|
|
|
from optparse import OptionParser
|
|
import os.path
|
|
import time
|
|
|
|
try:
|
|
from ripcord.dctopo import TreeTopo, FatTreeTopo, VL2Topo
|
|
USERIPCORD = True
|
|
except ImportError:
|
|
USERIPCORD = False
|
|
|
|
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 ) ) }
|
|
if USERIPCORD:
|
|
TOPOSRIPCORD = {
|
|
'tree16': ( lambda: TreeTopo( depth=3, fanout=4 ) ),
|
|
'tree64': ( lambda: TreeTopo( depth=4, fanout=4 ) ),
|
|
'tree1024': ( lambda: TreeTopo( depth=3, fanout=32 ) ),
|
|
'fattree4': ( lambda: FatTreeTopo( k=4 ) ),
|
|
'fattree6': ( lambda: FatTreeTopo( k=6 ) ),
|
|
'vl2': ( lambda: VL2Topo( da=4, di=4 ) ),
|
|
'vl2reduced': ( lambda: VL2Topo( da=4, di=4, edgeDown=1 ) ) }
|
|
TOPOS.update( TOPOSRIPCORD )
|
|
|
|
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.parseArgs()
|
|
self.setup()
|
|
self.begin()
|
|
|
|
def parseArgs( self ):
|
|
"""Parse command-line args and return options object.
|
|
returns: opts parse options dict"""
|
|
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 mininet from current dir' )
|
|
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()
|
|
|
|
# check for invalid combinations
|
|
if ( self.options.controller == 'ref' and
|
|
( ( 'fattree' in self.options.topo ) or
|
|
( 'vl2' in self.options.topo ) ) ):
|
|
raise Exception( 'multipath topos require multipath-capable '
|
|
'controller.' )
|
|
|
|
if self.options.custom:
|
|
if not os.path.isfile( self.options.custom ):
|
|
raise Exception( 'could not find custom file: %s' %
|
|
self.options.custom )
|
|
|
|
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 )
|
|
|
|
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 = None
|
|
if not self.options.custom:
|
|
mn = Mininet( topo, switch, host, controller, controllerParams,
|
|
inNamespace=inNamespace,
|
|
xterms=xterms, autoSetMacs=mac,
|
|
autoStaticArp=arp )
|
|
else:
|
|
globals_ = {}
|
|
locals_ = {}
|
|
execfile( self.options.custom, globals_, locals_ )
|
|
if 'mn' not in locals_:
|
|
raise Exception( 'could not find mn var in custom file' )
|
|
else:
|
|
mn = locals_[ 'mn' ]
|
|
|
|
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()
|