First commit
diff --git a/bin/mn b/bin/mn
new file mode 100644
index 0000000..0fb5bae
--- /dev/null
+++ b/bin/mn
@@ -0,0 +1,280 @@
+#!/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
+import sys
+import time
+
+# Fix setuptools' evil madness, and open up (more?) security holes
+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.log import lg, LEVELS, info
+from mininet.net import Mininet, MininetWithControlNet, VERSION
+from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
+                           NOX, RemoteController, UserSwitch, OVSKernelSwitch,
+                           OVSLegacyKernelSwitch )
+from mininet.link import Link, TCLink
+from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
+from mininet.topolib import TreeTopo
+from mininet.util import custom, customConstructor
+from mininet.util import buildTopo
+
+
+# built in topologies, created only when run
+TOPODEF = 'minimal'
+TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
+          'linear': LinearTopo,
+          'reversed': SingleSwitchReversedTopo,
+          'single': SingleSwitchTopo,
+          'tree': TreeTopo }
+
+SWITCHDEF = 'ovsk'
+SWITCHES = { 'user': UserSwitch,
+             'ovsk': OVSKernelSwitch,
+             'ovsl': OVSLegacyKernelSwitch }
+
+HOSTDEF = 'proc'
+HOSTS = { 'proc': Host,
+          'rt': custom( CPULimitedHost, sched='rt' ),
+          'cfs': custom( CPULimitedHost, sched='cfs' ) }
+
+CONTROLLERDEF = 'ovsc'
+CONTROLLERS = { 'ref': Controller,
+                'ovsc': OVSController,
+                'nox': NOX,
+                'remote': RemoteController,
+                'none': lambda name: None }
+
+LINKDEF = 'default'
+LINKS = { 'default': Link,
+          'tc': TCLink }
+
+
+# optional tests to run
+TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
+          'none' ]
+
+ALTSPELLING = { 'pingall': 'pingAll',
+                'pingpair': 'pingPair',
+                'iperfudp': 'iperfUdp',
+                'iperfUDP': 'iperfUdp',
+                'prefixlen': 'prefixLen' }
+
+
+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( sorted( choicesDict.keys() ) ) +
+                    '[,param=value...]' )
+    opts.add_option( '--' + name,
+                     type='string',
+                     default = default,
+                     help = helpStr )
+
+
+def version( *_args ):
+    "Print Mininet version and exit"
+    print "%s" % VERSION
+    exit()
+
+class MininetRunner( object ):
+    "Build, setup, and run Mininet."
+
+    def __init__( self ):
+        "Init."
+        self.options = None
+        self.args = None  # May be used someday for more CLI scripts
+        self.validate = None
+
+        self.parseArgs()
+        self.setup()
+        self.begin()
+
+    def setCustom( self, name, value ):
+        "Set custom parameters for MininetRunner."
+        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."
+        customs = {}
+        if os.path.isfile( fileName ):
+            execfile( fileName, customs, customs )
+            for name, val in customs.iteritems():
+                self.setCustom( name, val )
+        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:
+            index = sys.argv.index( '--custom' )
+            if len( sys.argv ) > index + 1:
+                filename = sys.argv[ index + 1 ]
+                self.parseCustomFile( filename )
+            else:
+                raise Exception( 'Custom file name not found' )
+
+        desc = ( "The %prog utility creates Mininet network from the\n"
+                 "command line. It can create parametrized topologies,\n"
+                 "invoke the Mininet CLI, and run tests." )
+
+        usage = ( '%prog [options]\n'
+                  '(type %prog -h for details)' )
+
+        opts = OptionParser( description=desc, usage=usage )
+        addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
+        addDictOption( opts, HOSTS, HOSTDEF, 'host' )
+        addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
+        addDictOption( opts, LINKS, LINKDEF, 'link' )
+        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
+
+        opts.add_option( '--clean', '-c', action='store_true',
+                         default=False, help='clean and exit' )
+        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( '--ipbase', '-i', type='string', default='10.0.0.0/8',
+                         help='base IP address for hosts' )
+        opts.add_option( '--mac', action='store_true',
+                         default=False, help='automatically set host MACs' )
+        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( '--innamespace', action='store_true',
+                         default=False, help='sw and ctrl in namespace?' )
+        opts.add_option( '--listenport', type='int', default=6634,
+                         help='base port for passive switch listening' )
+        opts.add_option( '--nolistenport', action='store_true',
+                         default=False, help="don't use passive listening " +
+                         "port")
+        opts.add_option( '--pre', type='string', default=None,
+                         help='CLI script to run before tests' )
+        opts.add_option( '--post', type='string', default=None,
+                         help='CLI script to run after tests' )
+        opts.add_option( '--prefixlen', type='int', default=8,
+                         help='prefix length (e.g. /8) for automatic '
+                         'network configuration' )
+        opts.add_option( '--pin', action='store_true',
+                         default=False, help="pin hosts to CPU cores "
+                         "(requires --host cfs or --host rt)" )
+        opts.add_option( '--version', action='callback', callback=version )
+
+        self.options, self.args = opts.parse_args()
+
+    def setup( self ):
+        "Setup and validate environment."
+
+        # set logging verbosity
+        if LEVELS[self.options.verbosity] > LEVELS['output']:
+            print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
+                    'output!\n'
+                    'Please restart Mininet with -v [debug, info, output].'
+                    % self.options.verbosity )
+        lg.setLogLevel( self.options.verbosity )
+
+    def begin( self ):
+        "Create and run mininet."
+
+        if self.options.clean:
+            cleanup()
+            exit()
+
+        start = time.time()
+
+        topo = buildTopo( TOPOS, self.options.topo )
+        switch = customConstructor( SWITCHES, self.options.switch )
+        host = customConstructor( HOSTS, self.options.host )
+        controller = customConstructor( CONTROLLERS, self.options.controller )
+        link = customConstructor( LINKS, self.options.link )
+
+        if self.validate:
+            self.validate( self.options )
+
+        inNamespace = self.options.innamespace
+        Net = MininetWithControlNet if inNamespace else Mininet
+        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
+        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 )
+
+        if self.options.pre:
+            CLI( mn, script=self.options.pre )
+
+        test = self.options.test
+        test = ALTSPELLING.get( test, test )
+
+        mn.start()
+
+        if test == 'none':
+            pass
+        elif test == 'all':
+            mn.start()
+            mn.ping()
+            mn.iperf()
+        elif test == 'cli':
+            CLI( mn )
+        elif test != 'build':
+            getattr( mn, test )()
+
+        if self.options.post:
+            CLI( mn, script=self.options.post )
+
+        mn.stop()
+
+        elapsed = float( time.time() - start )
+        info( 'completed in %0.3f seconds\n' % elapsed )
+
+
+if __name__ == "__main__":
+    MininetRunner()