From 8895862acf36edfb2f0fda3a07de09107f1fb2d5 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Mon, 8 Feb 2010 15:42:31 -0800 Subject: [PATCH] Munged mn and mnclean into mininet style. --- bin/mn | 237 ++++++++++++++++++++++++++-------------------------- bin/mnclean | 26 +++--- 2 files changed, 130 insertions(+), 133 deletions(-) mode change 100755 => 100644 bin/mn mode change 100755 => 100644 bin/mnclean diff --git a/bin/mn b/bin/mn old mode 100755 new mode 100644 index 7f22f5c..a314afe --- a/bin/mn +++ b/bin/mn @@ -1,8 +1,9 @@ #!/usr/bin/env python -'''Mininet runner -@author Brandon Heller (brandonh@stanford.edu) -''' +""" +Mininet runner +author: Brandon Heller (brandonh@stanford.edu) +""" from optparse import OptionParser import os.path @@ -10,9 +11,9 @@ import time try: from ripcord.dctopo import TreeTopo, FatTreeTopo, VL2Topo - USE_RIPCORD = True + USERIPCORD = True except ImportError: - USE_RIPCORD = False + USERIPCORD = False from mininet.log import lg, LEVELS from mininet.net import Mininet, init @@ -21,164 +22,162 @@ from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo # built in topologies, created only when run -TOPO_DEF = '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 USE_RIPCORD: - TOPOS_RIPCORD = { - '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, edge_down = 1))} - TOPOS.update(TOPOS_RIPCORD) +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 ) -SWITCH_DEF = 'kernel' -SWITCHES = {'kernel': KernelSwitch, +SWITCHDEF = 'kernel' +SWITCHES = { 'kernel': KernelSwitch, 'user': UserSwitch, - 'ovsk': OVSKernelSwitch} + 'ovsk': OVSKernelSwitch } -HOST_DEF = 'process' -HOSTS = {'process': Host} +HOSTDEF = 'process' +HOSTS = { 'process': Host } -CONTROLLER_DEF = 'ref' +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'), +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} + 'none': lambda a, b: None } # 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): - '''Convenience function to add choices dicts to OptionParser. - - @param opts OptionParser instance - @param choices_dict dictionary of valid choices, must include default - @param default default choice key - @param name long option name - @param help string - ''' - if default not in choices_dict: - raise Exception('Invalid default %s for choices dict: %s' % - (default, name)) - if not help_str: - help_str = '[' + ' '.join(choices_dict.keys()) + ']' - opts.add_option('--' + name, - type = 'choice', - choices = choices_dict.keys(), +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 = help_str) + help = helpStr ) -class MininetRunner(object): - '''Build, setup, and run Mininet.''' +class MininetRunner( object ): + "Build, setup, and run Mininet." - def __init__(self): - '''Init.''' + def __init__( self ): + "Init." self.options = None - self.parse_args() + self.parseArgs() self.setup() self.begin() - def parse_args(self): - '''Parse command-line args and return options object. - - @return opts parse options dict - ''' + def parseArgs( self ): + """Parse command-line args and return options object. + returns: opts parse options dict""" opts = OptionParser() - add_dict_option(opts, TOPOS, TOPO_DEF, 'topo') - add_dict_option(opts, SWITCHES, SWITCH_DEF, 'switch') - add_dict_option(opts, HOSTS, HOST_DEF, 'host') - add_dict_option(opts, CONTROLLERS, CONTROLLER_DEF, 'controller') + 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('--in_namespace', action = 'store_true', - default = False, help = 'sw and ctrl in namespace?') - self.options = opts.parse_args()[0] + 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( '--in_namespace', action='store_true', + default=False, help='sw and ctrl in namespace?' ) + self.options = opts.parse_args()[ 0 ] - def setup(self): - '''Setup and validate environment.''' + def setup( self ): + "Setup and validate environment." # set logging verbosity - lg.setLogLevel(self.options.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.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) + 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.''' + 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] + 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, - ip_address = self.options.ip, - port = self.options.port) + controller = lambda a, b: RemoteController( a, b, + ipAddress=self.options.ip, + port=self.options.port ) - controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8 - in_namespace = self.options.in_namespace + 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, controller_params, - in_namespace = in_namespace, - xterms = xterms, auto_set_macs = mac, - auto_static_arp = arp) + mn = Mininet( topo, switch, host, controller, controllerParams, + inNamespace=inNamespace, + xterms=xterms, autoSetMacs=mac, + autoStaticArp=arp ) else: globals_ = {} locals_ = {} - execfile(self.options.custom, globals_, locals_) + execfile( self.options.custom, globals_, 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: - mn = locals_['mn'] + mn = locals_[ 'mn' ] test = self.options.test if test != 'build': @@ -190,10 +189,10 @@ class MininetRunner(object): mn.iperf() mn.stop() else: - mn.run(test) + mn.run( test ) - elapsed = float(time.time() - start) - print ('completed in %0.3f seconds' % elapsed) + elapsed = float( time.time() - start ) + print ( 'completed in %0.3f seconds' % elapsed ) if __name__ == "__main__": diff --git a/bin/mnclean b/bin/mnclean old mode 100755 new mode 100644 index 56e2d93..ff041cf --- a/bin/mnclean +++ b/bin/mnclean @@ -1,7 +1,8 @@ #!/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 properly after themselves. Until they do (or until cleanup @@ -15,42 +16,39 @@ from subprocess import Popen, PIPE from mininet.xterm import cleanUpScreens - -def sh(cmd): +def sh( cmd ): "Print a command and send it to the shell" print cmd - return Popen(['/bin/sh', '-c', cmd], stdout=PIPE).communicate()[0] - + return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ] def cleanup(): """Clean up junk which might be left over from old runs; do fast stuff before slow dp and link removal!""" - print "*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes" zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core ' zombies += 'udpbwtest' # 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. - sh('killall -9 ' + zombies + ' 2> /dev/null') + sh( 'killall -9 ' + zombies + ' 2> /dev/null' ) 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" cleanUpScreens() 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: if dp != '': - sh('dpctl deldp ' + dp) + sh( 'dpctl deldp ' + dp ) 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: if link != '': - sh("ip link del " + link) + sh( "ip link del " + link ) print "*** Cleanup complete."