First commit
diff --git a/mininet/__init__.py b/mininet/__init__.py
new file mode 100644
index 0000000..c15ea6a
--- /dev/null
+++ b/mininet/__init__.py
@@ -0,0 +1 @@
+"Docstring to silence pylint; ignores --ignore option for __init__.py"
diff --git a/mininet/clean.py b/mininet/clean.py
new file mode 100644
index 0000000..eac8fda
--- /dev/null
+++ b/mininet/clean.py
@@ -0,0 +1,60 @@
+"""
+Mininet Cleanup
+author: Bob Lantz (rlantz@cs.stanford.edu)
+
+Unfortunately, Mininet and OpenFlow (and the Linux kernel)
+don't always clean up properly after themselves. Until they do
+(or until cleanup functionality is integrated into the Python
+code), this script may be used to get rid of unwanted garbage.
+It may also get rid of 'false positives', but hopefully
+nothing irreplaceable!
+"""
+
+from subprocess import Popen, PIPE
+
+from mininet.log import info
+from mininet.term import cleanUpScreens
+
+def sh( cmd ):
+    "Print a command and send it to the shell"
+    info( cmd + '\n' )
+    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!"""
+
+    info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
+         "\n")
+    zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
+    zombies += 'ovs-openflowd udpbwtest'
+    # Note: real zombie processes can't actually be killed, since they
+    # 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' )
+
+    info( "*** Removing junk from /tmp\n" )
+    sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
+
+    info( "*** Removing old screen sessions\n" )
+    cleanUpScreens()
+
+    info( "*** Removing excess kernel datapaths\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 )
+
+    info( "***  Removing OVS datapaths" )
+    dps = sh("ovs-vsctl list-br").split( '\n' )
+    for dp in dps:
+        if dp:
+            sh( 'ovs-vsctl del-br ' + dp )
+
+    info( "*** Removing all links of the pattern foo-ethX\n" )
+    links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
+    for link in links:
+        if link != '':
+            sh( "ip link del " + link )
+
+    info( "*** Cleanup complete.\n" )
diff --git a/mininet/cli.py b/mininet/cli.py
new file mode 100644
index 0000000..efb1808
--- /dev/null
+++ b/mininet/cli.py
@@ -0,0 +1,366 @@
+"""
+A simple command-line interface for Mininet.
+
+The Mininet CLI provides a simple control console which
+makes it easy to talk to nodes. For example, the command
+
+mininet> h27 ifconfig
+
+runs 'ifconfig' on host h27.
+
+Having a single console rather than, for example, an xterm for each
+node is particularly convenient for networks of any reasonable
+size.
+
+The CLI automatically substitutes IP addresses for node names,
+so commands like
+
+mininet> h2 ping h3
+
+should work correctly and allow host h2 to ping host h3
+
+Several useful commands are provided, including the ability to
+list all nodes ('nodes'), to print out the network topology
+('net') and to check connectivity ('pingall', 'pingpair')
+and bandwidth ('iperf'.)
+"""
+
+from subprocess import call
+from cmd import Cmd
+from os import isatty
+from select import poll, POLLIN
+import sys
+import time
+
+from mininet.log import info, output, error
+from mininet.term import makeTerms
+from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
+
+class CLI( Cmd ):
+    "Simple command-line interface to talk to nodes."
+
+    prompt = 'mininet> '
+
+    def __init__( self, mininet, stdin=sys.stdin, script=None ):
+        self.mn = mininet
+        self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
+        self.nodemap = {}  # map names to Node objects
+        for node in self.nodelist:
+            self.nodemap[ node.name ] = node
+        # Local variable bindings for py command
+        self.locals = { 'net': mininet }
+        self.locals.update( self.nodemap )
+        # Attempt to handle input
+        self.stdin = stdin
+        self.inPoller = poll()
+        self.inPoller.register( stdin )
+        self.inputFile = script
+        Cmd.__init__( self )
+        info( '*** Starting CLI:\n' )
+        if self.inputFile:
+            self.do_source( self.inputFile )
+            return
+        while True:
+            try:
+                # Make sure no nodes are still waiting
+                for node in self.nodelist:
+                    while node.waiting:
+                        node.sendInt()
+                        node.monitor()
+                if self.isatty():
+                    quietRun( 'stty sane' )
+                self.cmdloop()
+                break
+            except KeyboardInterrupt:
+                output( '\nInterrupt\n' )
+
+    def emptyline( self ):
+        "Don't repeat last command when you hit return."
+        pass
+
+    # Disable pylint "Unused argument: 'arg's'" messages, as well as
+    # "method could be a function" warning, since each CLI function
+    # must have the same interface
+    # pylint: disable-msg=R0201
+
+    helpStr = (
+        'You may also send a command to a node using:\n'
+        '  <node> command {args}\n'
+        'For example:\n'
+        '  mininet> h1 ifconfig\n'
+        '\n'
+        'The interpreter automatically substitutes IP addresses\n'
+        'for node names when a node is the first arg, so commands\n'
+        'like\n'
+        '  mininet> h2 ping h3\n'
+        'should work.\n'
+        '\n'
+        'Some character-oriented interactive commands require\n'
+        'noecho:\n'
+        '  mininet> noecho h2 vi foo.py\n'
+        'However, starting up an xterm/gterm is generally better:\n'
+        '  mininet> xterm h2\n\n'
+    )
+
+    def do_help( self, line ):
+        "Describe available CLI commands."
+        Cmd.do_help( self, line )
+        if line is '':
+            output( self.helpStr )
+
+    def do_nodes( self, _line ):
+        "List all nodes."
+        nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
+        output( 'available nodes are: \n%s\n' % nodes )
+
+    def do_net( self, _line ):
+        "List network connections."
+        dumpNodeConnections( self.nodelist )
+
+    def do_sh( self, line ):
+        "Run an external shell command"
+        call( line, shell=True )
+
+    # do_py() needs to catch any exception during eval()
+    # pylint: disable-msg=W0703
+
+    def do_py( self, line ):
+        """Evaluate a Python expression.
+           Node names may be used, e.g.: h1.cmd('ls')"""
+        try:
+            result = eval( line, globals(), self.locals )
+            if not result:
+                return
+            elif isinstance( result, str ):
+                output( result + '\n' )
+            else:
+                output( repr( result ) + '\n' )
+        except Exception, e:
+            output( str( e ) + '\n' )
+
+    # pylint: enable-msg=W0703
+
+    def do_pingall( self, _line ):
+        "Ping between all hosts."
+        self.mn.pingAll()
+
+    def do_pingpair( self, _line ):
+        "Ping between first two hosts, useful for testing."
+        self.mn.pingPair()
+
+    def do_pingallfull( self, _line ):
+        "Ping between first two hosts, returns all ping results."
+        self.mn.pingAllFull()
+
+    def do_pingpairfull( self, _line ):
+        "Ping between first two hosts, returns all ping results."
+        self.mn.pingPairFull()
+
+    def do_iperf( self, line ):
+        "Simple iperf TCP test between two (optionally specified) hosts."
+        args = line.split()
+        if not args:
+            self.mn.iperf()
+        elif len(args) == 2:
+            hosts = []
+            err = False
+            for arg in args:
+                if arg not in self.nodemap:
+                    err = True
+                    error( "node '%s' not in network\n" % arg )
+                else:
+                    hosts.append( self.nodemap[ arg ] )
+            if not err:
+                self.mn.iperf( hosts )
+        else:
+            error( 'invalid number of args: iperf src dst\n' )
+
+    def do_iperfudp( self, line ):
+        "Simple iperf TCP test between two (optionally specified) hosts."
+        args = line.split()
+        if not args:
+            self.mn.iperf( l4Type='UDP' )
+        elif len(args) == 3:
+            udpBw = args[ 0 ]
+            hosts = []
+            err = False
+            for arg in args[ 1:3 ]:
+                if arg not in self.nodemap:
+                    err = True
+                    error( "node '%s' not in network\n" % arg )
+                else:
+                    hosts.append( self.nodemap[ arg ] )
+            if not err:
+                self.mn.iperf( hosts, l4Type='UDP', udpBw=udpBw )
+        else:
+            error( 'invalid number of args: iperfudp bw src dst\n' +
+                   'bw examples: 10M\n' )
+
+    def do_intfs( self, _line ):
+        "List interfaces."
+        for node in self.nodelist:
+            output( '%s: %s\n' %
+                    ( node.name, ','.join( node.intfNames() ) ) )
+
+    def do_dump( self, _line ):
+        "Dump node info."
+        for node in self.nodelist:
+            output( '%s\n' % repr( node ) )
+
+    def do_link( self, line ):
+        "Bring link(s) between two nodes up or down."
+        args = line.split()
+        if len(args) != 3:
+            error( 'invalid number of args: link end1 end2 [up down]\n' )
+        elif args[ 2 ] not in [ 'up', 'down' ]:
+            error( 'invalid type: link end1 end2 [up down]\n' )
+        else:
+            self.mn.configLinkStatus( *args )
+
+    def do_xterm( self, line, term='xterm' ):
+        "Spawn xterm(s) for the given node(s)."
+        args = line.split()
+        if not args:
+            error( 'usage: %s node1 node2 ...\n' % term )
+        else:
+            for arg in args:
+                if arg not in self.nodemap:
+                    error( "node '%s' not in network\n" % arg )
+                else:
+                    node = self.nodemap[ arg ]
+                    self.mn.terms += makeTerms( [ node ], term = term )
+
+    def do_gterm( self, line ):
+        "Spawn gnome-terminal(s) for the given node(s)."
+        self.do_xterm( line, term='gterm' )
+
+    def do_exit( self, _line ):
+        "Exit"
+        return 'exited by user command'
+
+    def do_quit( self, line ):
+        "Exit"
+        return self.do_exit( line )
+
+    def do_EOF( self, line ):
+        "Exit"
+        output( '\n' )
+        return self.do_exit( line )
+
+    def isatty( self ):
+        "Is our standard input a tty?"
+        return isatty( self.stdin.fileno() )
+
+    def do_noecho( self, line ):
+        "Run an interactive command with echoing turned off."
+        if self.isatty():
+            quietRun( 'stty -echo' )
+        self.default( line )
+        if self.isatty():
+            quietRun( 'stty echo' )
+
+    def do_source( self, line ):
+        "Read commands from an input file."
+        args = line.split()
+        if len(args) != 1:
+            error( 'usage: source <file>\n' )
+            return
+        try:
+            self.inputFile = open( args[ 0 ] )
+            while True:
+                line = self.inputFile.readline()
+                if len( line ) > 0:
+                    self.onecmd( line )
+                else:
+                    break
+        except IOError:
+            error( 'error reading file %s\n' % args[ 0 ] )
+        self.inputFile = None
+
+    def do_dpctl( self, line ):
+        "Run dpctl command on all switches."
+        args = line.split()
+        if len(args) < 1:
+            error( 'usage: dpctl command [arg1] [arg2] ...\n' )
+            return
+        for sw in self.mn.switches:
+            output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
+            output( sw.dpctl( *args ) )
+
+    def do_time( self, line ):
+        "Measure time taken for any command in Mininet."
+        start = time.time()
+        self.onecmd(line)
+        elapsed = time.time() - start
+        self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
+
+    def default( self, line ):
+        """Called on an input line when the command prefix is not recognized.
+        Overridden to run shell commands when a node is the first CLI argument.
+        Past the first CLI argument, node names are automatically replaced with
+        corresponding IP addrs."""
+
+        first, args, line = self.parseline( line )
+        if not args:
+            return
+        if args and len(args) > 0 and args[ -1 ] == '\n':
+            args = args[ :-1 ]
+        rest = args.split( ' ' )
+
+        if first in self.nodemap:
+            node = self.nodemap[ first ]
+            # Substitute IP addresses for node names in command
+            rest = [ self.nodemap[ arg ].IP()
+                     if arg in self.nodemap else arg
+                     for arg in rest ]
+            rest = ' '.join( rest )
+            # Run cmd on node:
+            builtin = isShellBuiltin( first )
+            node.sendCmd( rest, printPid=( not builtin ) )
+            self.waitForNode( node )
+        else:
+            error( '*** Unknown command: %s\n' % first )
+
+    # pylint: enable-msg=R0201
+
+    def waitForNode( self, node ):
+        "Wait for a node to finish, and  print its output."
+        # Pollers
+        nodePoller = poll()
+        nodePoller.register( node.stdout )
+        bothPoller = poll()
+        bothPoller.register( self.stdin, POLLIN )
+        bothPoller.register( node.stdout, POLLIN )
+        if self.isatty():
+            # Buffer by character, so that interactive
+            # commands sort of work
+            quietRun( 'stty -icanon min 1' )
+        while True:
+            try:
+                bothPoller.poll()
+                # XXX BL: this doesn't quite do what we want.
+                if False and self.inputFile:
+                    key = self.inputFile.read( 1 )
+                    if key is not '':
+                        node.write(key)
+                    else:
+                        self.inputFile = None
+                if isReadable( self.inPoller ):
+                    key = self.stdin.read( 1 )
+                    node.write( key )
+                if isReadable( nodePoller ):
+                    data = node.monitor()
+                    output( data )
+                if not node.waiting:
+                    break
+            except KeyboardInterrupt:
+                node.sendInt()
+
+# Helper functions
+
+def isReadable( poller ):
+    "Check whether a Poll object has a readable fd."
+    for fdmask in poller.poll( 0 ):
+        mask = fdmask[ 1 ]
+        if mask & POLLIN:
+            return True
diff --git a/mininet/conf_parser.py b/mininet/conf_parser.py
new file mode 100644
index 0000000..68792ac
--- /dev/null
+++ b/mininet/conf_parser.py
@@ -0,0 +1,108 @@
+import ConfigParser
+
+class confCCNHost():
+    
+    def __init__(self, name, app='', uri_tuples=''):
+        self.name = name
+        self.app = app
+        self.uri_tuples = uri_tuples
+        
+    def __repr__(self):
+        return 'Name: ' + self.name + ' App: ' + self.app + ' URIS: ' + str(self.uri_tuples) 
+
+class confCCNLink():
+    
+    def __init__(self,h1,h2,linkDict=None):
+        self.h1 = h1
+        self.h2 = h2
+        self.linkDict = linkDict
+        
+    def __repr__(self):
+        return 'h1: ' + self.h1 + ' h2: ' + self.h2 + ' params: ' + str(self.linkDict)
+    
+def extrai_hosts(conf_arq):
+    'Extrai hosts da secao hosts do arquivo de configuracao'
+    config = ConfigParser.RawConfigParser()
+    config.read(conf_arq)
+    
+    hosts = []
+    
+    items = config.items('hosts')
+    
+    for item in items:
+
+        name = item[0]
+  
+        rest = item[1].split()
+
+        app = rest.pop(0)
+        
+        uris = rest
+        uri_list=[]
+        for uri in uris:
+            uri_list.append((uri.split(',')[0],uri.split(',')[1]))
+        
+        hosts.append(confCCNHost(name , app, uri_list))
+    
+    return hosts
+
+def extrai_routers(conf_arq):
+    'Extrai routers da secao routers do arquivo de configuracao'
+    config = ConfigParser.RawConfigParser()
+    config.read(conf_arq)
+
+    routers = []
+    
+    items = config.items('routers')
+    
+    for item in items:
+        name = item[0]
+  
+        rest = item[1].split()
+
+        uris = rest
+        uri_list=[]
+        for uri in uris:
+            uri_list.append((uri.split(',')[0],uri.split(',')[1]))
+        
+        routers.append(confCCNHost(name=name , uri_tuples=uri_list))
+    
+    return routers
+
+def extrai_links(conf_arq):
+    'Extrai links da secao links do arquivo de configuracao'
+    arq = open(conf_arq,'r')
+    
+    links = []
+     
+    while True:
+        line = arq.readline()
+        if line == '[links]\n':
+            break
+    
+    while True:
+        line = arq.readline()
+        if line == '':
+            break
+        
+        args = line.split()
+        h1, h2 = args.pop(0).split(':')
+        
+        link_dict = {}
+        
+        for arg in args:
+            arg_name, arg_value = arg.split('=')
+            key = arg_name
+            value = arg_value
+            if key in ['loss','bw','jitter']:
+                value = int(value)
+            
+            link_dict[key] = value
+            
+        links.append(confCCNLink(h1,h2,link_dict))
+        
+        
+    return links
+        
+        
+    
diff --git a/mininet/link.py b/mininet/link.py
new file mode 100644
index 0000000..21e18ba
--- /dev/null
+++ b/mininet/link.py
@@ -0,0 +1,397 @@
+"""
+link.py: interface and link abstractions for mininet
+
+It seems useful to bundle functionality for interfaces into a single
+class.
+
+Also it seems useful to enable the possibility of multiple flavors of
+links, including:
+
+- simple veth pairs
+- tunneled links
+- patchable links (which can be disconnected and reconnected via a patchbay)
+- link simulators (e.g. wireless)
+
+Basic division of labor:
+
+  Nodes: know how to execute commands
+  Intfs: know how to configure themselves
+  Links: know how to connect nodes together
+
+Intf: basic interface object that can configure itself
+TCIntf: interface with bandwidth limiting and delay via tc
+
+Link: basic link class for creating veth pairs
+"""
+
+from mininet.log import info, error, debug
+from mininet.util import makeIntfPair
+from time import sleep
+import re
+
+class Intf( object ):
+
+    "Basic interface object that can configure itself."
+
+    def __init__( self, name, node=None, port=None, link=None, **params ):
+        """name: interface name (e.g. h1-eth0)
+           node: owning node (where this intf most likely lives)
+           link: parent link if we're part of a link
+           other arguments are passed to config()"""
+        self.node = node
+        self.name = name
+        self.link = link
+        self.mac, self.ip, self.prefixLen = None, None, None
+        # Add to node (and move ourselves if necessary )
+        node.addIntf( self, port=port )
+        # Save params for future reference
+        self.params = params
+        self.config( **params )
+
+    def cmd( self, *args, **kwargs ):
+        "Run a command in our owning node"
+        return self.node.cmd( *args, **kwargs )
+
+    def ifconfig( self, *args ):
+        "Configure ourselves using ifconfig"
+        return self.cmd( 'ifconfig', self.name, *args )
+
+    def setIP( self, ipstr, prefixLen=None ):
+        """Set our IP address"""
+        # This is a sign that we should perhaps rethink our prefix
+        # mechanism and/or the way we specify IP addresses
+        if '/' in ipstr:
+            self.ip, self.prefixLen = ipstr.split( '/' )
+            return self.ifconfig( ipstr, 'up' )
+        else:
+            self.ip, self.prefixLen = ipstr, prefixLen
+            return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
+
+    def setMAC( self, macstr ):
+        """Set the MAC address for an interface.
+           macstr: MAC address as string"""
+        self.mac = macstr
+        return ( self.ifconfig( 'down' ) +
+                 self.ifconfig( 'hw', 'ether', macstr ) +
+                 self.ifconfig( 'up' ) )
+
+    _ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
+    _macMatchRegex = re.compile( r'..:..:..:..:..:..' )
+
+    def updateIP( self ):
+        "Return updated IP address based on ifconfig"
+        ifconfig = self.ifconfig()
+        ips = self._ipMatchRegex.findall( ifconfig )
+        self.ip = ips[ 0 ] if ips else None
+        return self.ip
+
+    def updateMAC( self ):
+        "Return updated MAC address based on ifconfig"
+        ifconfig = self.ifconfig()
+        macs = self._macMatchRegex.findall( ifconfig )
+        self.mac = macs[ 0 ] if macs else None
+        return self.mac
+
+    def IP( self ):
+        "Return IP address"
+        return self.ip
+
+    def MAC( self ):
+        "Return MAC address"
+        return self.mac
+
+    def isUp( self, setUp=False ):
+        "Return whether interface is up"
+        if setUp:
+            self.ifconfig( 'up' )
+        return "UP" in self.ifconfig()
+
+    def rename( self, newname ):
+        "Rename interface"
+        self.ifconfig( 'down' )
+        result = self.cmd( 'ip link set', self.name, 'name', newname )
+        self.name = newname
+        self.ifconfig( 'up' )
+        return result
+
+    # The reason why we configure things in this way is so
+    # That the parameters can be listed and documented in
+    # the config method.
+    # Dealing with subclasses and superclasses is slightly
+    # annoying, but at least the information is there!
+
+    def setParam( self, results, method, **param ):
+        """Internal method: configure a *single* parameter
+           results: dict of results to update
+           method: config method name
+           param: arg=value (ignore if value=None)
+           value may also be list or dict"""
+        name, value = param.items()[ 0 ]
+        f = getattr( self, method, None )
+        if not f or value is None:
+            return
+        if type( value ) is list:
+            result = f( *value )
+        elif type( value ) is dict:
+            result = f( **value )
+        else:
+            result = f( value )
+        results[ name ] = result
+        return result
+
+    def config( self, mac=None, ip=None, ifconfig=None,
+                up=True, **_params ):
+        """Configure Node according to (optional) parameters:
+           mac: MAC address
+           ip: IP address
+           ifconfig: arbitrary interface configuration
+           Subclasses should override this method and call
+           the parent class's config(**params)"""
+        # If we were overriding this method, we would call
+        # the superclass config method here as follows:
+        # r = Parent.config( **params )
+        r = {}
+        self.setParam( r, 'setMAC', mac=mac )
+        self.setParam( r, 'setIP', ip=ip )
+        self.setParam( r, 'isUp', up=up )
+        self.setParam( r, 'ifconfig', ifconfig=ifconfig )
+        self.updateIP()
+        self.updateMAC()
+        return r
+
+    def delete( self ):
+        "Delete interface"
+        self.cmd( 'ip link del ' + self.name )
+        # Does it help to sleep to let things run?
+        sleep( 0.001 )
+
+    def __repr__( self ):
+        return '<%s %s>' % ( self.__class__.__name__, self.name )
+
+    def __str__( self ):
+        return self.name
+
+
+class TCIntf( Intf ):
+    """Interface customized by tc (traffic control) utility
+       Allows specification of bandwidth limits (various methods)
+       as well as delay, loss and max queue length"""
+
+    def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
+                latency_ms=None, enable_ecn=False, enable_red=False ):
+        "Return tc commands to set bandwidth"
+
+        cmds, parent = [], ' root '
+
+        if bw and ( bw < 0 or bw > 1000 ):
+            error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
+
+        elif bw is not None:
+            # BL: this seems a bit brittle...
+            if ( speedup > 0 and
+                 self.node.name[0:1] == 's' ):
+                bw = speedup
+            # This may not be correct - we should look more closely
+            # at the semantics of burst (and cburst) to make sure we
+            # are specifying the correct sizes. For now I have used
+            # the same settings we had in the mininet-hifi code.
+            if use_hfsc:
+                cmds += [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
+                          '%s class add dev %s parent 1:0 classid 1:1 hfsc sc '
+                          + 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
+            elif use_tbf:
+                if latency_ms is None:
+                    latency_ms = 15 * 8 / bw
+                cmds += [ '%s qdisc add dev %s root handle 1: tbf ' +
+                          'rate %fMbit burst 15000 latency %fms' %
+                          ( bw, latency_ms ) ]
+            else:
+                cmds += [ '%s qdisc add dev %s root handle 1:0 htb default 1',
+                          '%s class add dev %s parent 1:0 classid 1:1 htb ' +
+                          'rate %fMbit burst 15k' % bw ]
+            parent = ' parent 1:1 '
+
+            # ECN or RED
+            if enable_ecn:
+                cmds += [ '%s qdisc add dev %s' + parent +
+                          'handle 10: red limit 1000000 ' +
+                          'min 30000 max 35000 avpkt 1500 ' +
+                          'burst 20 ' +
+                          'bandwidth %fmbit probability 1 ecn' % bw ]
+                parent = ' parent 10: '
+            elif enable_red:
+                cmds += [ '%s qdisc add dev %s' + parent +
+                          'handle 10: red limit 1000000 ' +
+                          'min 30000 max 35000 avpkt 1500 ' +
+                          'burst 20 ' +
+                          'bandwidth %fmbit probability 1' % bw ]
+                parent = ' parent 10: '
+        return cmds, parent
+
+    @staticmethod
+    def delayCmds( parent, delay=None, jitter=None,
+                   loss=None, max_queue_size=None ):
+        "Internal method: return tc commands for delay and loss"
+        cmds = []
+        if delay and delay < 0:
+            error( 'Negative delay', delay, '\n' )
+        elif jitter and jitter < 0:
+            error( 'Negative jitter', jitter, '\n' )
+        elif loss and ( loss < 0 or loss > 100 ):
+            error( 'Bad loss percentage', loss, '%%\n' )
+        else:
+            # Delay/jitter/loss/max queue size
+            netemargs = '%s%s%s%s' % (
+                'delay %s ' % delay if delay is not None else '',
+                '%s ' % jitter if jitter is not None else '',
+                'loss %d ' % loss if loss is not None else '',
+                'limit %d' % max_queue_size if max_queue_size is not None
+                else '' )
+            if netemargs:
+                cmds = [ '%s qdisc add dev %s ' + parent +
+                         ' handle 10: netem ' +
+                         netemargs ]
+        return cmds
+
+    def tc( self, cmd, tc='tc' ):
+        "Execute tc command for our interface"
+        c = cmd % (tc, self)  # Add in tc command and our name
+        debug(" *** executing command: %s\n" % c)
+        return self.cmd( c )
+
+    def config( self, bw=None, delay=None, jitter=None, loss=None,
+                disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
+                latency_ms=None, enable_ecn=False, enable_red=False,
+                max_queue_size=None, **params ):
+        "Configure the port and set its properties."
+
+        result = Intf.config( self, **params)
+
+        # Disable GRO
+        if disable_gro:
+            self.cmd( 'ethtool -K %s gro off' % self )
+
+        # Optimization: return if nothing else to configure
+        # Question: what happens if we want to reset things?
+        if ( bw is None and not delay and not loss
+             and max_queue_size is None ):
+            return
+
+        # Clear existing configuration
+        cmds = [ '%s qdisc del dev %s root' ]
+
+        # Bandwidth limits via various methods
+        bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
+                                      use_hfsc=use_hfsc, use_tbf=use_tbf,
+                                      latency_ms=latency_ms,
+                                      enable_ecn=enable_ecn,
+                                      enable_red=enable_red )
+        cmds += bwcmds
+
+        # Delay/jitter/loss/max_queue_size using netem
+        cmds += self.delayCmds( delay=delay, jitter=jitter, loss=loss,
+                                max_queue_size=max_queue_size,
+                                parent=parent )
+
+        # Ugly but functional: display configuration info
+        stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
+                  ( [ '%s delay' % delay ] if delay is not None else [] ) +
+                  ( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
+                  ( ['%d%% loss' % loss ] if loss is not None else [] ) +
+                  ( [ 'ECN' ] if enable_ecn else [ 'RED' ]
+                    if enable_red else [] ) )
+        info( '(' + ' '.join( stuff ) + ') ' )
+
+        # Execute all the commands in our node
+        debug("at map stage w/cmds: %s\n" % cmds)
+        tcoutputs = [ self.tc(cmd) for cmd in cmds ]
+        debug( "cmds:", cmds, '\n' )
+        debug( "outputs:", tcoutputs, '\n' )
+        result[ 'tcoutputs'] = tcoutputs
+
+        return result
+
+
+class Link( object ):
+
+    """A basic link is just a veth pair.
+       Other types of links could be tunnels, link emulators, etc.."""
+
+    def __init__( self, node1, node2, port1=None, port2=None,
+                  intfName1=None, intfName2=None,
+                  intf=Intf, cls1=None, cls2=None, params1=None,
+                  params2=None ):
+        """Create veth link to another node, making two new interfaces.
+           node1: first node
+           node2: second node
+           port1: node1 port number (optional)
+           port2: node2 port number (optional)
+           intf: default interface class/constructor
+           cls1, cls2: optional interface-specific constructors
+           intfName1: node1 interface name (optional)
+           intfName2: node2  interface name (optional)
+           params1: parameters for interface 1
+           params2: parameters for interface 2"""
+        # This is a bit awkward; it seems that having everything in
+        # params would be more orthogonal, but being able to specify
+        # in-line arguments is more convenient!
+        if port1 is None:
+            port1 = node1.newPort()
+        if port2 is None:
+            port2 = node2.newPort()
+        if not intfName1:
+            intfName1 = self.intfName( node1, port1 )
+        if not intfName2:
+            intfName2 = self.intfName( node2, port2 )
+
+        self.makeIntfPair( intfName1, intfName2 )
+
+        if not cls1:
+            cls1 = intf
+        if not cls2:
+            cls2 = intf
+        if not params1:
+            params1 = {}
+        if not params2:
+            params2 = {}
+
+        intf1 = cls1( name=intfName1, node=node1, port=port1,
+                      link=self, **params1  )
+        intf2 = cls2( name=intfName2, node=node2, port=port2,
+                      link=self, **params2 )
+
+        # All we are is dust in the wind, and our two interfaces
+        self.intf1, self.intf2 = intf1, intf2
+
+    @classmethod
+    def intfName( cls, node, n ):
+        "Construct a canonical interface name node-ethN for interface n."
+        return node.name + '-eth' + repr( n )
+
+    @classmethod
+    def makeIntfPair( cls, intf1, intf2 ):
+        """Create pair of interfaces
+           intf1: name of interface 1
+           intf2: name of interface 2
+           (override this class method [and possibly delete()]
+           to change link type)"""
+        makeIntfPair( intf1, intf2  )
+
+    def delete( self ):
+        "Delete this link"
+        self.intf1.delete()
+        self.intf2.delete()
+
+    def __str__( self ):
+        return '%s<->%s' % ( self.intf1, self.intf2 )
+
+class TCLink( Link ):
+    "Link with symmetric TC interfaces configured via opts"
+    def __init__( self, node1, node2, port1=None, port2=None,
+                  intfName1=None, intfName2=None, **params ):
+        Link.__init__( self, node1, node2, port1=port1, port2=port2,
+                       intfName1=intfName1, intfName2=intfName2,
+                       cls1=TCIntf,
+                       cls2=TCIntf,
+                       params1=params,
+                       params2=params)
diff --git a/mininet/log.py b/mininet/log.py
new file mode 100644
index 0000000..cd00821
--- /dev/null
+++ b/mininet/log.py
@@ -0,0 +1,178 @@
+"Logging functions for Mininet."
+
+import logging
+from logging import Logger
+import types
+
+# Create a new loglevel, 'CLI info', which enables a Mininet user to see only
+# the output of the commands they execute, plus any errors or warnings.  This
+# level is in between info and warning.  CLI info-level commands should not be
+# printed during regression tests.
+OUTPUT = 25
+
+LEVELS = { 'debug': logging.DEBUG,
+           'info': logging.INFO,
+           'output': OUTPUT,
+           'warning': logging.WARNING,
+           'error': logging.ERROR,
+           'critical': logging.CRITICAL }
+
+# change this to logging.INFO to get printouts when running unit tests
+LOGLEVELDEFAULT = OUTPUT
+
+#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+LOGMSGFORMAT = '%(message)s'
+
+
+# Modified from python2.5/__init__.py
+class StreamHandlerNoNewline( logging.StreamHandler ):
+    """StreamHandler that doesn't print newlines by default.
+       Since StreamHandler automatically adds newlines, define a mod to more
+       easily support interactive mode when we want it, or errors-only logging
+       for running unit tests."""
+
+    def emit( self, record ):
+        """Emit a record.
+           If a formatter is specified, it is used to format the record.
+           The record is then written to the stream with a trailing newline
+           [ N.B. this may be removed depending on feedback ]. If exception
+           information is present, it is formatted using
+           traceback.printException and appended to the stream."""
+        try:
+            msg = self.format( record )
+            fs = '%s'  # was '%s\n'
+            if not hasattr( types, 'UnicodeType' ):  # if no unicode support...
+                self.stream.write( fs % msg )
+            else:
+                try:
+                    self.stream.write( fs % msg )
+                except UnicodeError:
+                    self.stream.write( fs % msg.encode( 'UTF-8' ) )
+            self.flush()
+        except ( KeyboardInterrupt, SystemExit ):
+            raise
+        except:
+            self.handleError( record )
+
+
+class Singleton( type ):
+    """Singleton pattern from Wikipedia
+       See http://en.wikipedia.org/wiki/SingletonPattern#Python
+
+       Intended to be used as a __metaclass_ param, as shown for the class
+       below.
+
+       Changed cls first args to mcs to satisfy pylint."""
+
+    def __init__( mcs, name, bases, dict_ ):
+        super( Singleton, mcs ).__init__( name, bases, dict_ )
+        mcs.instance = None
+
+    def __call__( mcs, *args, **kw ):
+        if mcs.instance is None:
+            mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
+            return mcs.instance
+
+
+class MininetLogger( Logger, object ):
+    """Mininet-specific logger
+       Enable each mininet .py file to with one import:
+
+       from mininet.log import [lg, info, error]
+
+       ...get a default logger that doesn't require one newline per logging
+       call.
+
+       Inherit from object to ensure that we have at least one new-style base
+       class, and can then use the __metaclass__ directive, to prevent this
+       error:
+
+       TypeError: Error when calling the metaclass bases
+       a new-style class can't have only classic bases
+
+       If Python2.5/logging/__init__.py defined Filterer as a new-style class,
+       via Filterer( object ): rather than Filterer, we wouldn't need this.
+
+       Use singleton pattern to ensure only one logger is ever created."""
+
+    __metaclass__ = Singleton
+
+    def __init__( self ):
+
+        Logger.__init__( self, "mininet" )
+
+        # create console handler
+        ch = StreamHandlerNoNewline()
+        # create formatter
+        formatter = logging.Formatter( LOGMSGFORMAT )
+        # add formatter to ch
+        ch.setFormatter( formatter )
+        # add ch to lg
+        self.addHandler( ch )
+
+        self.setLogLevel()
+
+    def setLogLevel( self, levelname=None ):
+        """Setup loglevel.
+           Convenience function to support lowercase names.
+           levelName: level name from LEVELS"""
+        level = LOGLEVELDEFAULT
+        if levelname is not None:
+            if levelname not in LEVELS:
+                raise Exception( 'unknown levelname seen in setLogLevel' )
+            else:
+                level = LEVELS.get( levelname, level )
+
+        self.setLevel( level )
+        self.handlers[ 0 ].setLevel( level )
+
+    # pylint: disable-msg=E0202
+    # "An attribute inherited from mininet.log hide this method"
+    # Not sure why this is occurring - this function definitely gets called.
+
+    # See /usr/lib/python2.5/logging/__init__.py; modified from warning()
+    def output( self, msg, *args, **kwargs ):
+        """Log 'msg % args' with severity 'OUTPUT'.
+
+           To pass exception information, use the keyword argument exc_info
+           with a true value, e.g.
+
+           logger.warning("Houston, we have a %s", "cli output", exc_info=1)
+        """
+        if self.manager.disable >= OUTPUT:
+            return
+        if self.isEnabledFor( OUTPUT ):
+            self._log( OUTPUT, msg, args, kwargs )
+
+    # pylint: enable-msg=E0202
+
+lg = MininetLogger()
+
+# Make things a bit more convenient by adding aliases
+# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
+# In the future we may wish to make things more efficient by only
+# doing the join (and calling the function) unless the logging level
+# is high enough.
+
+def makeListCompatible( fn ):
+    """Return a new function allowing fn( 'a 1 b' ) to be called as
+       newfn( 'a', 1, 'b' )"""
+
+    def newfn( *args ):
+        "Generated function. Closure-ish."
+        if len( args ) == 1:
+            return fn( *args )
+        args = ' '.join( [ str( arg ) for arg in args ] )
+        return fn( args )
+
+    # Fix newfn's name and docstring
+    setattr( newfn, '__name__', fn.__name__ )
+    setattr( newfn, '__doc__', fn.__doc__ )
+    return newfn
+
+info, output, warn, error, debug = (
+    lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
+        makeListCompatible( f ) for f in
+            lg.info, lg.output, lg.warn, lg.error, lg.debug ]
+
+setLogLevel = lg.setLogLevel
diff --git a/mininet/moduledeps.py b/mininet/moduledeps.py
new file mode 100644
index 0000000..862c1f6
--- /dev/null
+++ b/mininet/moduledeps.py
@@ -0,0 +1,68 @@
+"Module dependency utility functions for Mininet."
+
+from mininet.util import quietRun
+from mininet.log import info, error, debug
+from os import environ
+
+def lsmod():
+    "Return output of lsmod."
+    return quietRun( 'lsmod' )
+
+def rmmod( mod ):
+    """Return output of lsmod.
+       mod: module string"""
+    return quietRun( [ 'rmmod', mod ] )
+
+def modprobe( mod ):
+    """Return output of modprobe
+       mod: module string"""
+    return quietRun( [ 'modprobe', mod ] )
+
+OF_KMOD = 'ofdatapath'
+OVS_KMOD = 'openvswitch_mod'  # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
+TUN = 'tun'
+
+def moduleDeps( subtract=None, add=None ):
+    """Handle module dependencies.
+       subtract: string or list of module names to remove, if already loaded
+       add: string or list of module names to add, if not already loaded"""
+    subtract = subtract if subtract is not None else []
+    add = add if add is not None else []
+    if type( subtract ) is str:
+        subtract = [ subtract ]
+    if type( add ) is str:
+        add = [ add ]
+    for mod in subtract:
+        if mod in lsmod():
+            info( '*** Removing ' + mod + '\n' )
+            rmmodOutput = rmmod( mod )
+            if rmmodOutput:
+                error( 'Error removing ' + mod + ': "%s">\n' % rmmodOutput )
+                exit( 1 )
+            if mod in lsmod():
+                error( 'Failed to remove ' + mod + '; still there!\n' )
+                exit( 1 )
+    for mod in add:
+        if mod not in lsmod():
+            info( '*** Loading ' + mod + '\n' )
+            modprobeOutput = modprobe( mod )
+            if modprobeOutput:
+                error( 'Error inserting ' + mod +
+                       ' - is it installed and available via modprobe?\n' +
+                       'Error was: "%s"\n' % modprobeOutput )
+            if mod not in lsmod():
+                error( 'Failed to insert ' + mod + ' - quitting.\n' )
+                exit( 1 )
+        else:
+            debug( '*** ' + mod + ' already loaded\n' )
+
+
+def pathCheck( *args, **kwargs ):
+    "Make sure each program in *args can be found in $PATH."
+    moduleName = kwargs.get( 'moduleName', 'it' )
+    for arg in args:
+        if not quietRun( 'which ' + arg ):
+            error( 'Cannot find required executable %s.\n' % arg +
+                   'Please make sure that %s is installed ' % moduleName +
+                   'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
+            exit( 1 )
diff --git a/mininet/net.py b/mininet/net.py
new file mode 100644
index 0000000..1255e12
--- /dev/null
+++ b/mininet/net.py
@@ -0,0 +1,770 @@
+"""
+
+    Mininet: A simple networking testbed for OpenFlow/SDN!
+
+author: Bob Lantz (rlantz@cs.stanford.edu)
+author: Brandon Heller (brandonh@stanford.edu)
+
+Mininet creates scalable OpenFlow test networks by using
+process-based virtualization and network namespaces.
+
+Simulated hosts are created as processes in separate network
+namespaces. This allows a complete OpenFlow network to be simulated on
+top of a single Linux kernel.
+
+Each host has:
+
+A virtual console (pipes to a shell)
+A virtual interfaces (half of a veth pair)
+A parent shell (and possibly some child processes) in a namespace
+
+Hosts have a network interface which is configured via ifconfig/ip
+link/etc.
+
+This version supports both the kernel and user space datapaths
+from the OpenFlow reference implementation (openflowswitch.org)
+as well as OpenVSwitch (openvswitch.org.)
+
+In kernel datapath mode, the controller and switches are simply
+processes in the root namespace.
+
+Kernel OpenFlow datapaths are instantiated using dpctl(8), and are
+attached to the one side of a veth pair; the other side resides in the
+host namespace. In this mode, switch processes can simply connect to the
+controller via the loopback interface.
+
+In user datapath mode, the controller and switches can be full-service
+nodes that live in their own network namespaces and have management
+interfaces and IP addresses on a control network (e.g. 192.168.123.1,
+currently routed although it could be bridged.)
+
+In addition to a management interface, user mode switches also have
+several switch interfaces, halves of veth pairs whose other halves
+reside in the host nodes that the switches are connected to.
+
+Consistent, straightforward naming is important in order to easily
+identify hosts, switches and controllers, both from the CLI and
+from program code. Interfaces are named to make it easy to identify
+which interfaces belong to which node.
+
+The basic naming scheme is as follows:
+
+    Host nodes are named h1-hN
+    Switch nodes are named s1-sN
+    Controller nodes are named c0-cN
+    Interfaces are named {nodename}-eth0 .. {nodename}-ethN
+
+Note: If the network topology is created using mininet.topo, then
+node numbers are unique among hosts and switches (e.g. we have
+h1..hN and SN..SN+M) and also correspond to their default IP addresses
+of 10.x.y.z/8 where x.y.z is the base-256 representation of N for
+hN. This mapping allows easy determination of a node's IP
+address from its name, e.g. h1 -> 10.0.0.1, h257 -> 10.0.1.1.
+
+Note also that 10.0.0.1 can often be written as 10.1 for short, e.g.
+"ping 10.1" is equivalent to "ping 10.0.0.1".
+
+Currently we wrap the entire network in a 'mininet' object, which
+constructs a simulated network based on a network topology created
+using a topology object (e.g. LinearTopo) from mininet.topo or
+mininet.topolib, and a Controller which the switches will connect
+to. Several configuration options are provided for functions such as
+automatically setting MAC addresses, populating the ARP table, or
+even running a set of terminals to allow direct interaction with nodes.
+
+After the network is created, it can be started using start(), and a
+variety of useful tasks maybe performed, including basic connectivity
+and bandwidth tests and running the mininet CLI.
+
+Once the network is up and running, test code can easily get access
+to host and switch objects which can then be used for arbitrary
+experiments, typically involving running a series of commands on the
+hosts.
+
+After all desired tests or activities have been completed, the stop()
+method may be called to shut down the network.
+
+"""
+
+import os
+import re
+import select
+import signal
+from time import sleep
+
+from mininet.cli import CLI
+from mininet.log import info, error, debug, output
+from mininet.node import Host, OVSKernelSwitch, Controller
+from mininet.link import Link, Intf
+from mininet.util import quietRun, fixLimits, numCores, ensureRoot
+from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd, nextCCNnet
+from mininet.term import cleanUpScreens, makeTerms
+
+# Mininet version: should be consistent with README and LICENSE
+VERSION = "2.0.0"
+
+class Mininet( object ):
+    "Network emulation with hosts spawned in network namespaces."
+
+    def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
+                  controller=Controller, link=Link, intf=Intf,
+                  build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
+                  inNamespace=False,
+                  autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
+                  listenPort=None ):
+        """Create Mininet object.
+           topo: Topo (topology) object or None
+           switch: default Switch class
+           host: default Host class/constructor
+           controller: default Controller class/constructor
+           link: default Link class/constructor
+           intf: default Intf class/constructor
+           ipBase: base IP address for hosts,
+           build: build now from topo?
+           xterms: if build now, spawn xterms?
+           cleanup: if build now, cleanup before creating?
+           inNamespace: spawn switches and controller in net namespaces?
+           autoSetMacs: set MAC addrs automatically like IP addresses?
+           autoStaticArp: set all-pairs static MAC addrs?
+           autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
+           listenPort: base listening port to open; will be incremented for
+               each additional switch in the net if inNamespace=False"""
+        self.topo = topo
+        self.switch = switch
+        self.host = host
+        self.controller = controller
+        self.link = link
+        self.intf = intf
+        self.ipBase = ipBase
+        self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
+        self.nextIP = 1  # start for address allocation
+        self.ccnNetBase = '1.0.0.0'
+        self.inNamespace = inNamespace
+        self.xterms = xterms
+        self.cleanup = cleanup
+        self.autoSetMacs = autoSetMacs
+        self.autoStaticArp = autoStaticArp
+        self.autoPinCpus = autoPinCpus
+        self.numCores = numCores()
+        self.nextCore = 0  # next core for pinning hosts to CPUs
+        self.listenPort = listenPort
+
+        self.hosts = []
+        self.switches = []
+        self.controllers = []
+
+        self.nameToNode = {}  # name to Node (Host/Switch) objects
+
+        self.terms = []  # list of spawned xterm processes
+
+        Mininet.init()  # Initialize Mininet if necessary
+
+        self.built = False
+        if topo and build:
+            self.build()
+
+    def isCCNhost(self, node):
+        if 'fib' in node.params:
+            return True
+        else:
+            return False
+        
+    def addHost( self, name, cls=None, **params ):
+        """Add host.
+           name: name of host to add
+           cls: custom host class/constructor (optional)
+           params: parameters for host
+           returns: added host"""
+        # Default IP and MAC addresses
+
+        defaults = { 'ip': ipAdd( self.nextIP,
+                                  ipBaseNum=self.ipBaseNum,
+                                  prefixLen=self.prefixLen ) +
+                                  '/%s' % self.prefixLen }
+        if self.autoSetMacs:
+            defaults[ 'mac'] = macColonHex( self.nextIP )
+        if self.autoPinCpus:
+            defaults[ 'cores' ] = self.nextCore
+            self.nextCore = ( self.nextCore + 1 ) % self.numCores        
+        self.nextIP += 1
+        defaults.update( params )
+        if not cls:
+            cls = self.host
+        h = cls( name, **defaults )
+        self.hosts.append( h )
+        self.nameToNode[ name ] = h
+        return h
+
+    def addSwitch( self, name, cls=None, **params ):
+        """Add switch.
+           name: name of switch to add
+           cls: custom switch class/constructor (optional)
+           returns: added switch
+           side effect: increments listenPort ivar ."""
+        defaults = { 'listenPort': self.listenPort,
+                     'inNamespace': self.inNamespace }
+        defaults.update( params )
+        if not cls:
+            cls = self.switch
+        sw = cls( name, **defaults )
+        if not self.inNamespace and self.listenPort:
+            self.listenPort += 1
+        self.switches.append( sw )
+        self.nameToNode[ name ] = sw
+        return sw
+
+    def addController( self, name='c0', controller=None, **params ):
+        """Add controller.
+           controller: Controller class"""
+        if not controller:
+            controller = self.controller
+        controller_new = controller( name, **params )
+        if controller_new:  # allow controller-less setups
+            self.controllers.append( controller_new )
+            self.nameToNode[ name ] = controller_new
+        return controller_new
+
+    # BL: is this better than just using nameToNode[] ?
+    # Should it have a better name?
+    def getNodeByName( self, *args ):
+        "Return node(s) with given name(s)"
+        if len( args ) == 1:
+            return self.nameToNode[ args[ 0 ] ]
+        return [ self.nameToNode[ n ] for n in args ]
+
+    def get( self, *args ):
+        "Convenience alias for getNodeByName"
+        return self.getNodeByName( *args )
+
+    def addLink( self, node1, node2, port1=None, port2=None,
+                 cls=None, **params ):
+        """"Add a link from node1 to node2
+            node1: source node
+            node2: dest node
+            port1: source port
+            port2: dest port
+            returns: link object"""
+        defaults = { 'port1': port1,
+                     'port2': port2,
+                     'intf': self.intf }
+        defaults.update( params )
+        if not cls:
+            cls = self.link
+        return cls( node1, node2, **defaults )
+
+    def configHosts( self ):
+        "Configure a set of hosts."
+        for host in self.hosts:
+            info( host.name + ' ' )
+            intf = host.defaultIntf()
+            if self.isCCNhost(host):
+                host.configCCN()    
+                host.configDefault(ip=None,mac=None)         
+            elif intf:
+                host.configDefault( defaultRoute=intf )
+            else:
+                # Don't configure nonexistent intf
+                host.configDefault( ip=None, mac=None )
+            # You're low priority, dude!
+            # BL: do we want to do this here or not?
+            # May not make sense if we have CPU lmiting...
+            # quietRun( 'renice +18 -p ' + repr( host.pid ) )
+            # This may not be the right place to do this, but
+            # it needs to be done somewhere.
+            host.cmd( 'ifconfig lo up' )
+        info( '\n' )
+
+    def buildFromTopo( self, topo=None ):
+        """Build mininet from a topology object
+           At the end of this function, everything should be connected
+           and up."""
+
+        # Possibly we should clean up here and/or validate
+        # the topo
+        if self.cleanup:
+            pass
+
+        info( '*** Creating network\n' )
+
+        if not self.controllers:
+            # Add a default controller
+            info( '*** Adding controller\n' )
+            classes = self.controller
+            if type( classes ) is not list:
+                classes = [ classes ]
+            for i, cls in enumerate( classes ):
+                self.addController( 'c%d' % i, cls )
+
+        info( '*** Adding hosts:\n' )
+        for hostName in topo.hosts():
+            self.addHost( hostName, **topo.nodeInfo( hostName ) )
+            info( hostName + ' ' )
+
+        info( '\n*** Adding switches:\n' )
+        for switchName in topo.switches():
+            self.addSwitch( switchName, **topo.nodeInfo( switchName) )
+            info( switchName + ' ' )
+
+        info( '\n*** Adding links:\n' )
+        for srcName, dstName in topo.links(sort=True):
+            src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
+            params = topo.linkInfo( srcName, dstName )
+            srcPort, dstPort = topo.port( srcName, dstName )
+            self.addLink( src, dst, srcPort, dstPort, **params )
+            if self.isCCNhost(src):
+                src.setIP(ipStr(ipParse(self.ccnNetBase) + 1) + '/30', intf=src.name + '-eth' + str(srcPort))
+                dst.setIP(ipStr(ipParse(self.ccnNetBase) + 2) + '/30', intf=dst.name + '-eth' + str(dstPort))
+                self.ccnNetBase=nextCCNnet(self.ccnNetBase)
+                
+            info( '(%s, %s) ' % ( src.name, dst.name ) )
+
+        info( '\n' )
+        
+        
+    def configureControlNetwork( self ):
+        "Control net config hook: override in subclass"
+        raise Exception( 'configureControlNetwork: '
+                         'should be overriden in subclass', self )
+
+    def build( self ):
+        "Build mininet."
+        if self.topo:
+            self.buildFromTopo( self.topo )
+        if ( self.inNamespace ):
+            self.configureControlNetwork()
+        info( '*** Configuring hosts\n' )
+        self.configHosts()
+        if self.xterms:
+            self.startTerms()
+        if self.autoStaticArp:
+            self.staticArp()
+        self.built = True
+
+    def startTerms( self ):
+        "Start a terminal for each node."
+        info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
+        cleanUpScreens()
+        self.terms += makeTerms( self.controllers, 'controller' )
+        self.terms += makeTerms( self.switches, 'switch' )
+        self.terms += makeTerms( self.hosts, 'host' )
+
+    def stopXterms( self ):
+        "Kill each xterm."
+        for term in self.terms:
+            os.kill( term.pid, signal.SIGKILL )
+        cleanUpScreens()
+
+    def staticArp( self ):
+        "Add all-pairs ARP entries to remove the need to handle broadcast."
+        for src in self.hosts:
+            for dst in self.hosts:
+                if src != dst:
+                    src.setARP( ip=dst.IP(), mac=dst.MAC() )
+
+    def start( self ):
+        "Start controller and switches."
+        if not self.built:
+            self.build()
+        info( '*** Starting controller\n' )
+        for controller in self.controllers:
+            controller.start()
+        info( '*** Starting %s switches\n' % len( self.switches ) )
+        for switch in self.switches:
+            info( switch.name + ' ')
+            switch.start( self.controllers )
+        info( '\n' )
+
+    def stop( self ):
+        "Stop the controller(s), switches and hosts"
+        if self.terms:
+            info( '*** Stopping %i terms\n' % len( self.terms ) )
+            self.stopXterms()
+        info( '*** Stopping %i hosts\n' % len( self.hosts ) )
+        for host in self.hosts:
+            info( host.name + ' ' )
+            host.terminate()
+        info( '\n' )
+        info( '*** Stopping %i switches\n' % len( self.switches ) )
+        for switch in self.switches:
+            info( switch.name + ' ' )
+            switch.stop()
+        info( '\n' )
+        info( '*** Stopping %i controllers\n' % len( self.controllers ) )
+        for controller in self.controllers:
+            info( controller.name + ' ' )
+            controller.stop()
+        info( '\n*** Done\n' )
+
+    def run( self, test, *args, **kwargs ):
+        "Perform a complete start/test/stop cycle."
+        self.start()
+        info( '*** Running test\n' )
+        result = test( *args, **kwargs )
+        self.stop()
+        return result
+
+    def monitor( self, hosts=None, timeoutms=-1 ):
+        """Monitor a set of hosts (or all hosts by default),
+           and return their output, a line at a time.
+           hosts: (optional) set of hosts to monitor
+           timeoutms: (optional) timeout value in ms
+           returns: iterator which returns host, line"""
+        if hosts is None:
+            hosts = self.hosts
+        poller = select.poll()
+        Node = hosts[ 0 ]  # so we can call class method fdToNode
+        for host in hosts:
+            poller.register( host.stdout )
+        while True:
+            ready = poller.poll( timeoutms )
+            for fd, event in ready:
+                host = Node.fdToNode( fd )
+                if event & select.POLLIN:
+                    line = host.readline()
+                    if line is not None:
+                        yield host, line
+            # Return if non-blocking
+            if not ready and timeoutms >= 0:
+                yield None, None
+
+    # XXX These test methods should be moved out of this class.
+    # Probably we should create a tests.py for them
+
+    @staticmethod
+    def _parsePing( pingOutput ):
+        "Parse ping output and return packets sent, received."
+        # Check for downed link
+        if 'connect: Network is unreachable' in pingOutput:
+            return (1, 0)
+        r = r'(\d+) packets transmitted, (\d+) received'
+        m = re.search( r, pingOutput )
+        if m is None:
+            error( '*** Error: could not parse ping output: %s\n' %
+                   pingOutput )
+            return (1, 0)
+        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
+        return sent, received
+
+    def ping( self, hosts=None, timeout=None ):
+        """Ping between all specified hosts.
+           hosts: list of hosts
+           timeout: time to wait for a response, as string
+           returns: ploss packet loss percentage"""
+        # should we check if running?
+        packets = 0
+        lost = 0
+        ploss = None
+        if not hosts:
+            hosts = self.hosts
+            output( '*** Ping: testing ping reachability\n' )
+        for node in hosts:
+            output( '%s -> ' % node.name )
+            for dest in hosts:
+                if node != dest:
+                    opts = ''
+                    if timeout:
+                        opts = '-W %s' % timeout
+                    result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
+                    sent, received = self._parsePing( result )
+                    packets += sent
+                    if received > sent:
+                        error( '*** Error: received too many packets' )
+                        error( '%s' % result )
+                        node.cmdPrint( 'route' )
+                        exit( 1 )
+                    lost += sent - received
+                    output( ( '%s ' % dest.name ) if received else 'X ' )
+            output( '\n' )
+            ploss = 100 * lost / packets
+        output( "*** Results: %i%% dropped (%d/%d lost)\n" %
+                ( ploss, lost, packets ) )
+        return ploss
+
+    @staticmethod
+    def _parsePingFull( pingOutput ):
+        "Parse ping output and return all data."
+        # Check for downed link
+        if 'connect: Network is unreachable' in pingOutput:
+            return (1, 0)
+        r = r'(\d+) packets transmitted, (\d+) received'
+        m = re.search( r, pingOutput )
+        if m is None:
+            error( '*** Error: could not parse ping output: %s\n' %
+                   pingOutput )
+            return (1, 0, 0, 0, 0, 0)
+        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
+        r = r'rtt min/avg/max/mdev = '
+        r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
+        m = re.search( r, pingOutput )
+        rttmin = float( m.group( 1 ) )
+        rttavg = float( m.group( 2 ) )
+        rttmax = float( m.group( 3 ) )
+        rttdev = float( m.group( 4 ) )
+        return sent, received, rttmin, rttavg, rttmax, rttdev
+
+    def pingFull( self, hosts=None, timeout=None ):
+        """Ping between all specified hosts and return all data.
+           hosts: list of hosts
+           timeout: time to wait for a response, as string
+           returns: all ping data; see function body."""
+        # should we check if running?
+        # Each value is a tuple: (src, dsd, [all ping outputs])
+        all_outputs = []
+        if not hosts:
+            hosts = self.hosts
+            output( '*** Ping: testing ping reachability\n' )
+        for node in hosts:
+            output( '%s -> ' % node.name )
+            for dest in hosts:
+                if node != dest:
+                    opts = ''
+                    if timeout:
+                        opts = '-W %s' % timeout
+                    result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
+                    outputs = self._parsePingFull( result )
+                    sent, received, rttmin, rttavg, rttmax, rttdev = outputs
+                    all_outputs.append( (node, dest, outputs) )
+                    output( ( '%s ' % dest.name ) if received else 'X ' )
+            output( '\n' )
+        output( "*** Results: \n" )
+        for outputs in all_outputs:
+            src, dest, ping_outputs = outputs
+            sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
+            output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
+            output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
+                    (rttmin, rttavg, rttmax, rttdev) )
+        return all_outputs
+
+    def pingAll( self ):
+        """Ping between all hosts.
+           returns: ploss packet loss percentage"""
+        return self.ping()
+
+    def pingPair( self ):
+        """Ping between first two hosts, useful for testing.
+           returns: ploss packet loss percentage"""
+        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
+        return self.ping( hosts=hosts )
+
+    def pingAllFull( self ):
+        """Ping between all hosts.
+           returns: ploss packet loss percentage"""
+        return self.pingFull()
+
+    def pingPairFull( self ):
+        """Ping between first two hosts, useful for testing.
+           returns: ploss packet loss percentage"""
+        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
+        return self.pingFull( hosts=hosts )
+
+    @staticmethod
+    def _parseIperf( iperfOutput ):
+        """Parse iperf output and return bandwidth.
+           iperfOutput: string
+           returns: result string"""
+        r = r'([\d\.]+ \w+/sec)'
+        m = re.findall( r, iperfOutput )
+        if m:
+            return m[-1]
+        else:
+            # was: raise Exception(...)
+            error( 'could not parse iperf output: ' + iperfOutput )
+            return ''
+
+    # XXX This should be cleaned up
+
+    def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
+        """Run iperf between two hosts.
+           hosts: list of hosts; if None, uses opposite hosts
+           l4Type: string, one of [ TCP, UDP ]
+           returns: results two-element array of server and client speeds"""
+        if not quietRun( 'which telnet' ):
+            error( 'Cannot find telnet in $PATH - required for iperf test' )
+            return
+        if not hosts:
+            hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
+        else:
+            assert len( hosts ) == 2
+        client, server = hosts
+        output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
+        output( "%s and %s\n" % ( client.name, server.name ) )
+        server.cmd( 'killall -9 iperf' )
+        iperfArgs = 'iperf '
+        bwArgs = ''
+        if l4Type == 'UDP':
+            iperfArgs += '-u '
+            bwArgs = '-b ' + udpBw + ' '
+        elif l4Type != 'TCP':
+            raise Exception( 'Unexpected l4 type: %s' % l4Type )
+        server.sendCmd( iperfArgs + '-s', printPid=True )
+        servout = ''
+        while server.lastPid is None:
+            servout += server.monitor()
+        if l4Type == 'TCP':
+            while 'Connected' not in client.cmd(
+                    'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
+                output('waiting for iperf to start up...')
+                sleep(.5)
+        cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
+                             bwArgs )
+        debug( 'Client output: %s\n' % cliout )
+        server.sendInt()
+        servout += server.waitOutput()
+        debug( 'Server output: %s\n' % servout )
+        result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
+        if l4Type == 'UDP':
+            result.insert( 0, udpBw )
+        output( '*** Results: %s\n' % result )
+        return result
+
+    def runCpuLimitTest( self, cpu, duration=5 ):
+        """run CPU limit test with 'while true' processes.
+        cpu: desired CPU fraction of each host
+        duration: test duration in seconds
+        returns a single list of measured CPU fractions as floats.
+        """
+        pct = cpu * 100
+        info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
+        hosts = self.hosts
+        for h in hosts:
+            h.cmd( 'while true; do a=1; done &' )
+        pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
+        pids_str = ",".join(["%s" % pid for pid in pids])
+        cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
+        # It's a shame that this is what pylint prefers
+        outputs = []
+        for _ in range( duration ):
+            sleep( 1 )
+            outputs.append( quietRun( cmd ).strip() )
+        for h in hosts:
+            h.cmd( 'kill %1' )
+        cpu_fractions = []
+        for test_output in outputs:
+            # Split by line.  Ignore first line, which looks like this:
+            # PID %CPU COMMAND\n
+            for line in test_output.split('\n')[1:]:
+                r = r'\d+\s*(\d+\.\d+)'
+                m = re.search( r, line )
+                if m is None:
+                    error( '*** Error: could not extract CPU fraction: %s\n' %
+                           line )
+                    return None
+                cpu_fractions.append( float( m.group( 1 ) ) )
+        output( '*** Results: %s\n' % cpu_fractions )
+        return cpu_fractions
+
+    # BL: I think this can be rewritten now that we have
+    # a real link class.
+    def configLinkStatus( self, src, dst, status ):
+        """Change status of src <-> dst links.
+           src: node name
+           dst: node name
+           status: string {up, down}"""
+        if src not in self.nameToNode:
+            error( 'src not in network: %s\n' % src )
+        elif dst not in self.nameToNode:
+            error( 'dst not in network: %s\n' % dst )
+        else:
+            if type( src ) is str:
+                src = self.nameToNode[ src ]
+            if type( dst ) is str:
+                dst = self.nameToNode[ dst ]
+            connections = src.connectionsTo( dst )
+            if len( connections ) == 0:
+                error( 'src and dst not connected: %s %s\n' % ( src, dst) )
+            for srcIntf, dstIntf in connections:
+                result = srcIntf.ifconfig( status )
+                if result:
+                    error( 'link src status change failed: %s\n' % result )
+                result = dstIntf.ifconfig( status )
+                if result:
+                    error( 'link dst status change failed: %s\n' % result )
+
+    def interact( self ):
+        "Start network and run our simple CLI."
+        self.start()
+        result = CLI( self )
+        self.stop()
+        return result
+
+    inited = False
+
+    @classmethod
+    def init( cls ):
+        "Initialize Mininet"
+        if cls.inited:
+            return
+        ensureRoot()
+        fixLimits()
+        cls.inited = True
+
+
+class MininetWithControlNet( Mininet ):
+
+    """Control network support:
+
+       Create an explicit control network. Currently this is only
+       used/usable with the user datapath.
+
+       Notes:
+
+       1. If the controller and switches are in the same (e.g. root)
+          namespace, they can just use the loopback connection.
+
+       2. If we can get unix domain sockets to work, we can use them
+          instead of an explicit control network.
+
+       3. Instead of routing, we could bridge or use 'in-band' control.
+
+       4. Even if we dispense with this in general, it could still be
+          useful for people who wish to simulate a separate control
+          network (since real networks may need one!)
+
+       5. Basically nobody ever used this code, so it has been moved
+          into its own class.
+
+       6. Ultimately we may wish to extend this to allow us to create a
+          control network which every node's control interface is
+          attached to."""
+
+    def configureControlNetwork( self ):
+        "Configure control network."
+        self.configureRoutedControlNetwork()
+
+    # We still need to figure out the right way to pass
+    # in the control network location.
+
+    def configureRoutedControlNetwork( self, ip='192.168.123.1',
+                                       prefixLen=16 ):
+        """Configure a routed control network on controller and switches.
+           For use with the user datapath only right now."""
+        controller = self.controllers[ 0 ]
+        info( controller.name + ' <->' )
+        cip = ip
+        snum = ipParse( ip )
+        for switch in self.switches:
+            info( ' ' + switch.name )
+            link = self.link( switch, controller, port1=0 )
+            sintf, cintf = link.intf1, link.intf2
+            switch.controlIntf = sintf
+            snum += 1
+            while snum & 0xff in [ 0, 255 ]:
+                snum += 1
+            sip = ipStr( snum )
+            cintf.setIP( cip, prefixLen )
+            sintf.setIP( sip, prefixLen )
+            controller.setHostRoute( sip, cintf )
+            switch.setHostRoute( cip, sintf )
+        info( '\n' )
+        info( '*** Testing control network\n' )
+        while not cintf.isUp():
+            info( '*** Waiting for', cintf, 'to come up\n' )
+            sleep( 1 )
+        for switch in self.switches:
+            while not sintf.isUp():
+                info( '*** Waiting for', sintf, 'to come up\n' )
+                sleep( 1 )
+            if self.ping( hosts=[ switch, controller ] ) != 0:
+                error( '*** Error: control network test failed\n' )
+                exit( 1 )
+        info( '\n' )
diff --git a/mininet/node.py b/mininet/node.py
new file mode 100644
index 0000000..4c54d75
--- /dev/null
+++ b/mininet/node.py
@@ -0,0 +1,1235 @@
+"""
+Node objects for Mininet.
+
+Nodes provide a simple abstraction for interacting with hosts, switches
+and controllers. Local nodes are simply one or more processes on the local
+machine.
+
+Node: superclass for all (primarily local) network nodes.
+
+Host: a virtual host. By default, a host is simply a shell; commands
+    may be sent using Cmd (which waits for output), or using sendCmd(),
+    which returns immediately, allowing subsequent monitoring using
+    monitor(). Examples of how to run experiments using this
+    functionality are provided in the examples/ directory.
+
+CPULimitedHost: a virtual host whose CPU bandwidth is limited by
+    RT or CFS bandwidth limiting.
+
+Switch: superclass for switch nodes.
+
+UserSwitch: a switch using the user-space switch from the OpenFlow
+    reference implementation.
+
+KernelSwitch: a switch using the kernel switch from the OpenFlow reference
+    implementation.
+
+OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
+    implementation (openvswitch.org).
+
+Controller: superclass for OpenFlow controllers. The default controller
+    is controller(8) from the reference implementation.
+
+NOXController: a controller node using NOX (noxrepo.org).
+
+RemoteController: a remote controller node, which may use any
+    arbitrary OpenFlow-compatible controller, and which is not
+    created or managed by mininet.
+
+Future enhancements:
+
+- Possibly make Node, Switch and Controller more abstract so that
+  they can be used for both local and remote nodes
+
+- Create proxy objects for remote nodes (Mininet: Cluster Edition)
+"""
+
+import os
+import re
+import signal
+import select
+from subprocess import Popen, PIPE, STDOUT
+
+from mininet.log import info, error, warn, debug
+from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
+                           numCores, retry, mountCgroups )
+from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
+from mininet.link import Link, Intf, TCIntf
+
+class Node( object ):
+    """A virtual network node is simply a shell in a network namespace.
+       We communicate with it using pipes."""
+
+    portBase = 0  # Nodes always start with eth0/port0, even in OF 1.0
+
+    def __init__( self, name, inNamespace=True, **params ):
+        """name: name of node
+           inNamespace: in network namespace?
+           params: Node parameters (see config() for details)"""
+
+        # Make sure class actually works
+        self.checkSetup()
+
+        self.name = name
+        self.inNamespace = inNamespace
+
+        # Stash configuration parameters for future reference
+        self.params = params
+
+        self.intfs = {}  # dict of port numbers to interfaces
+        self.ports = {}  # dict of interfaces to port numbers
+                         # replace with Port objects, eventually ?
+        self.nameToIntf = {}  # dict of interface names to Intfs
+
+        # Make pylint happy
+        ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
+            self.lastPid, self.lastCmd, self.pollOut ) = (
+                None, None, None, None, None, None, None, None )
+        self.waiting = False
+        self.readbuf = ''
+
+        # Start command interpreter shell
+        self.startShell()
+
+    # File descriptor to node mapping support
+    # Class variables and methods
+
+    inToNode = {}  # mapping of input fds to nodes
+    outToNode = {}  # mapping of output fds to nodes
+
+    @classmethod
+    def fdToNode( cls, fd ):
+        """Return node corresponding to given file descriptor.
+           fd: file descriptor
+           returns: node"""
+        node = cls.outToNode.get( fd )
+        return node or cls.inToNode.get( fd )
+
+    # Command support via shell process in namespace
+
+    def startShell( self ):
+        "Start a shell process for running commands"
+        if self.shell:
+            error( "%s: shell is already running" )
+            return
+        # mnexec: (c)lose descriptors, (d)etach from tty,
+        # (p)rint pid, and run in (n)amespace
+        opts = '-cdp'
+        if self.inNamespace:
+            opts += 'n'
+        # bash -m: enable job control
+        cmd = [ 'mnexec', opts, 'bash', '-m' ]
+        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
+                            close_fds=True )
+        self.stdin = self.shell.stdin
+        self.stdout = self.shell.stdout
+        self.pid = self.shell.pid
+        self.pollOut = select.poll()
+        self.pollOut.register( self.stdout )
+        # Maintain mapping between file descriptors and nodes
+        # This is useful for monitoring multiple nodes
+        # using select.poll()
+        self.outToNode[ self.stdout.fileno() ] = self
+        self.inToNode[ self.stdin.fileno() ] = self
+        self.execed = False
+        self.lastCmd = None
+        self.lastPid = None
+        self.readbuf = ''
+        self.waiting = False
+
+    def cleanup( self ):
+        "Help python collect its garbage."
+        if not self.inNamespace:
+            for intfName in self.intfNames():
+                if self.name in intfName:
+                    quietRun( 'ip link del ' + intfName )
+        self.shell = None
+
+    # Subshell I/O, commands and control
+
+    def read( self, maxbytes=1024 ):
+        """Buffered read from node, non-blocking.
+           maxbytes: maximum number of bytes to return"""
+        count = len( self.readbuf )
+        if count < maxbytes:
+            data = os.read( self.stdout.fileno(), maxbytes - count )
+            self.readbuf += data
+        if maxbytes >= len( self.readbuf ):
+            result = self.readbuf
+            self.readbuf = ''
+        else:
+            result = self.readbuf[ :maxbytes ]
+            self.readbuf = self.readbuf[ maxbytes: ]
+        return result
+
+    def readline( self ):
+        """Buffered readline from node, non-blocking.
+           returns: line (minus newline) or None"""
+        self.readbuf += self.read( 1024 )
+        if '\n' not in self.readbuf:
+            return None
+        pos = self.readbuf.find( '\n' )
+        line = self.readbuf[ 0: pos ]
+        self.readbuf = self.readbuf[ pos + 1: ]
+        return line
+
+    def write( self, data ):
+        """Write data to node.
+           data: string"""
+        os.write( self.stdin.fileno(), data )
+
+    def terminate( self ):
+        "Send kill signal to Node and clean up after it."
+        os.kill( self.pid, signal.SIGKILL )
+        self.cleanup()
+
+    def stop( self ):
+        "Stop node."
+        self.terminate()
+
+    def waitReadable( self, timeoutms=None ):
+        """Wait until node's output is readable.
+           timeoutms: timeout in ms or None to wait indefinitely."""
+        if len( self.readbuf ) == 0:
+            self.pollOut.poll( timeoutms )
+
+    def sendCmd( self, *args, **kwargs ):
+        """Send a command, followed by a command to echo a sentinel,
+           and return without waiting for the command to complete.
+           args: command and arguments, or string
+           printPid: print command's PID?"""
+        assert not self.waiting
+        printPid = kwargs.get( 'printPid', True )
+        # Allow sendCmd( [ list ] )
+        if len( args ) == 1 and type( args[ 0 ] ) is list:
+            cmd = args[ 0 ]
+        # Allow sendCmd( cmd, arg1, arg2... )
+        elif len( args ) > 0:
+            cmd = args
+        # Convert to string
+        if not isinstance( cmd, str ):
+            cmd = ' '.join( [ str( c ) for c in cmd ] )
+        if not re.search( r'\w', cmd ):
+            # Replace empty commands with something harmless
+            cmd = 'echo -n'
+        self.lastCmd = cmd
+        printPid = printPid and not isShellBuiltin( cmd )
+        if len( cmd ) > 0 and cmd[ -1 ] == '&':
+            # print ^A{pid}\n{sentinel}
+            cmd += ' printf "\\001%d\n\\177" $! \n'
+        else:
+            # print sentinel
+            cmd += '; printf "\\177"'
+            if printPid and not isShellBuiltin( cmd ):
+                cmd = 'mnexec -p ' + cmd
+        self.write( cmd + '\n' )
+        self.lastPid = None
+        self.waiting = True
+
+    def sendInt( self, sig=signal.SIGINT ):
+        "Interrupt running command."
+        if self.lastPid:
+            try:
+                os.kill( self.lastPid, sig )
+            except OSError:
+                pass
+
+    def monitor( self, timeoutms=None ):
+        """Monitor and return the output of a command.
+           Set self.waiting to False if command has completed.
+           timeoutms: timeout in ms or None to wait indefinitely."""
+        self.waitReadable( timeoutms )
+        data = self.read( 1024 )
+        # Look for PID
+        marker = chr( 1 ) + r'\d+\n'
+        if chr( 1 ) in data:
+            markers = re.findall( marker, data )
+            if markers:
+                self.lastPid = int( markers[ 0 ][ 1: ] )
+                data = re.sub( marker, '', data )
+        # Look for sentinel/EOF
+        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
+            self.waiting = False
+            data = data[ :-1 ]
+        elif chr( 127 ) in data:
+            self.waiting = False
+            data = data.replace( chr( 127 ), '' )
+        return data
+
+    def waitOutput( self, verbose=False ):
+        """Wait for a command to complete.
+           Completion is signaled by a sentinel character, ASCII(127)
+           appearing in the output stream.  Wait for the sentinel and return
+           the output, including trailing newline.
+           verbose: print output interactively"""
+        log = info if verbose else debug
+        output = ''
+        while self.waiting:
+            data = self.monitor()
+            output += data
+            log( data )
+        return output
+
+    def cmd( self, *args, **kwargs ):
+        """Send a command, wait for output, and return it.
+           cmd: string"""
+        verbose = kwargs.get( 'verbose', False )
+        log = info if verbose else debug
+        log( '*** %s : %s\n' % ( self.name, args ) )
+        self.sendCmd( *args, **kwargs )
+        return self.waitOutput( verbose )
+
+    def cmdPrint( self, *args):
+        """Call cmd and printing its output
+           cmd: string"""
+        return self.cmd( *args, **{ 'verbose': True } )
+
+    def popen( self, *args, **kwargs ):
+        """Return a Popen() object in our namespace
+           args: Popen() args, single list, or string
+           kwargs: Popen() keyword args"""
+        defaults = { 'stdout': PIPE, 'stderr': PIPE,
+                     'mncmd':
+                     [ 'mnexec', '-a', str( self.pid ) ] }
+        defaults.update( kwargs )
+        if len( args ) == 1:
+            if type( args[ 0 ] ) is list:
+                # popen([cmd, arg1, arg2...])
+                cmd = args[ 0 ]
+            elif type( args[ 0 ] ) is str:
+                # popen("cmd arg1 arg2...")
+                cmd = args[ 0 ].split()
+            else:
+                raise Exception( 'popen() requires a string or list' )
+        elif len( args ) > 0:
+            # popen( cmd, arg1, arg2... )
+            cmd = list( args )
+        # Attach to our namespace  using mnexec -a
+        mncmd = defaults[ 'mncmd' ]
+        del defaults[ 'mncmd' ]
+        cmd = mncmd + cmd
+        # Shell requires a string, not a list!
+        if defaults.get( 'shell', False ):
+            cmd = ' '.join( cmd )
+        return Popen( cmd, **defaults )
+
+    def pexec( self, *args, **kwargs ):
+        """Execute a command using popen
+           returns: out, err, exitcode"""
+        popen = self.popen( *args, **kwargs)
+        out, err = popen.communicate()
+        exitcode = popen.wait()
+        return out, err, exitcode
+
+    # Interface management, configuration, and routing
+
+    # BL notes: This might be a bit redundant or over-complicated.
+    # However, it does allow a bit of specialization, including
+    # changing the canonical interface names. It's also tricky since
+    # the real interfaces are created as veth pairs, so we can't
+    # make a single interface at a time.
+
+    def newPort( self ):
+        "Return the next port number to allocate."
+        if len( self.ports ) > 0:
+            return max( self.ports.values() ) + 1
+        return self.portBase
+
+    def addIntf( self, intf, port=None ):
+        """Add an interface.
+           intf: interface
+           port: port number (optional, typically OpenFlow port number)"""
+        if port is None:
+            port = self.newPort()
+        self.intfs[ port ] = intf
+        self.ports[ intf ] = port
+        self.nameToIntf[ intf.name ] = intf
+        debug( '\n' )
+        debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
+        if self.inNamespace:
+            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
+            moveIntf( intf.name, self )
+
+    def defaultIntf( self ):
+        "Return interface for lowest port"
+        ports = self.intfs.keys()
+        if ports:
+            return self.intfs[ min( ports ) ]
+        else:
+            warn( '*** defaultIntf: warning:', self.name,
+                  'has no interfaces\n' )
+
+    def intf( self, intf='' ):
+        """Return our interface object with given string name,
+           default intf if name is falsy (None, empty string, etc).
+           or the input intf arg.
+
+        Having this fcn return its arg for Intf objects makes it
+        easier to construct functions with flexible input args for
+        interfaces (those that accept both string names and Intf objects).
+        """
+        if not intf:
+            return self.defaultIntf()
+        elif type( intf) is str:
+            return self.nameToIntf[ intf ]
+        else:
+            return intf
+
+    def connectionsTo( self, node):
+        "Return [ intf1, intf2... ] for all intfs that connect self to node."
+        # We could optimize this if it is important
+        connections = []
+        for intf in self.intfList():
+            link = intf.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+                if node1 == self and node2 == node:
+                    connections += [ ( intf, link.intf2 ) ]
+                elif node1 == node and node2 == self:
+                    connections += [ ( intf, link.intf1 ) ]
+        return connections
+
+    def deleteIntfs( self ):
+        "Delete all of our interfaces."
+        # In theory the interfaces should go away after we shut down.
+        # However, this takes time, so we're better off removing them
+        # explicitly so that we won't get errors if we run before they
+        # have been removed by the kernel. Unfortunately this is very slow,
+        # at least with Linux kernels before 2.6.33
+        for intf in self.intfs.values():
+            intf.delete()
+            info( '.' )
+
+    # Routing support
+
+    def setARP( self, ip, mac ):
+        """Add an ARP entry.
+           ip: IP address as string
+           mac: MAC address as string"""
+        result = self.cmd( 'arp', '-s', ip, mac )
+        return result
+
+    def setHostRoute( self, ip, intf ):
+        """Add route to host.
+           ip: IP address as dotted decimal
+           intf: string, interface name"""
+        return self.cmd( 'route add -host', ip, 'dev', intf )
+
+    def setDefaultRoute( self, intf=None ):
+        """Set the default route to go through intf.
+           intf: string, interface name"""
+        if not intf:
+            intf = self.defaultIntf()
+        self.cmd( 'ip route flush root 0/0' )
+        return self.cmd( 'route add default %s' % intf )
+
+    # Convenience and configuration methods
+
+    def setMAC( self, mac, intf=None ):
+        """Set the MAC address for an interface.
+           intf: intf or intf name
+           mac: MAC address as string"""
+        return self.intf( intf ).setMAC( mac )
+
+    def setIP( self, ip, prefixLen=8, intf=None ):
+        """Set the IP address for an interface.
+           intf: intf or intf name
+           ip: IP address as a string
+           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
+        # This should probably be rethought
+        if '/' not in ip:
+            ip = '%s/%s' % ( ip, prefixLen )
+        return self.intf( intf ).setIP( ip )
+
+    def IP( self, intf=None ):
+        "Return IP address of a node or specific interface."
+        return self.intf( intf ).IP()
+
+    def MAC( self, intf=None ):
+        "Return MAC address of a node or specific interface."
+        return self.intf( intf ).MAC()
+
+    def intfIsUp( self, intf=None ):
+        "Check if an interface is up."
+        return self.intf( intf ).isUp()
+
+    # The reason why we configure things in this way is so
+    # That the parameters can be listed and documented in
+    # the config method.
+    # Dealing with subclasses and superclasses is slightly
+    # annoying, but at least the information is there!
+
+    def setParam( self, results, method, **param ):
+        """Internal method: configure a *single* parameter
+           results: dict of results to update
+           method: config method name
+           param: arg=value (ignore if value=None)
+           value may also be list or dict"""
+        name, value = param.items()[ 0 ]
+        f = getattr( self, method, None )
+        if not f or value is None:
+            return
+        if type( value ) is list:
+            result = f( *value )
+        elif type( value ) is dict:
+            result = f( **value )
+        else:
+            result = f( value )
+        results[ name ] = result
+        return result
+
+    def config( self, mac=None, ip=None,
+                defaultRoute=None, lo='up', **_params ):
+        """Configure Node according to (optional) parameters:
+           mac: MAC address for default interface
+           ip: IP address for default interface
+           ifconfig: arbitrary interface configuration
+           Subclasses should override this method and call
+           the parent class's config(**params)"""
+        # If we were overriding this method, we would call
+        # the superclass config method here as follows:
+        # r = Parent.config( **_params )
+        r = {}
+        self.setParam( r, 'setMAC', mac=mac )
+        self.setParam( r, 'setIP', ip=ip )
+        self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
+        # This should be examined
+        self.cmd( 'ifconfig lo ' + lo )
+        return r
+
+    def configDefault( self, **moreParams ):
+        "Configure with default parameters"
+        self.params.update( moreParams )
+        self.config( **self.params )
+
+    # This is here for backward compatibility
+    def linkTo( self, node, link=Link ):
+        """(Deprecated) Link to another node
+           replace with Link( node1, node2)"""
+        return link( self, node )
+
+    # Other methods
+
+    def intfList( self ):
+        "List of our interfaces sorted by port number"
+        return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
+
+    def intfNames( self ):
+        "The names of our interfaces sorted by port number"
+        return [ str( i ) for i in self.intfList() ]
+
+    def __repr__( self ):
+        "More informative string representation"
+        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
+                              for i in self.intfList() ] ) )
+        return '<%s %s: %s pid=%s> ' % (
+            self.__class__.__name__, self.name, intfs, self.pid )
+
+    def __str__( self ):
+        "Abbreviated string representation"
+        return self.name
+
+    # Automatic class setup support
+
+    isSetup = False
+
+    @classmethod
+    def checkSetup( cls ):
+        "Make sure our class and superclasses are set up"
+        while cls and not getattr( cls, 'isSetup', True ):
+            cls.setup()
+            cls.isSetup = True
+            # Make pylint happy
+            cls = getattr( type( cls ), '__base__', None )
+
+    @classmethod
+    def setup( cls ):
+        "Make sure our class dependencies are available"
+        pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
+
+
+class Host( Node ):
+    "A host is simply a Node"
+    pass
+
+
+        
+class CPULimitedHost( Host ):
+
+    "CPU limited host"
+
+    def __init__( self, name, sched='cfs', **kwargs ):
+        Host.__init__( self, name, **kwargs )
+        # Initialize class if necessary
+        if not CPULimitedHost.inited:
+            CPULimitedHost.init()
+        # Create a cgroup and move shell into it
+        self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name
+        errFail( 'cgcreate -g ' + self.cgroup )
+        # We don't add ourselves to a cpuset because you must
+        # specify the cpu and memory placement first
+        errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
+        # BL: Setting the correct period/quota is tricky, particularly
+        # for RT. RT allows very small quotas, but the overhead
+        # seems to be high. CFS has a mininimum quota of 1 ms, but
+        # still does better with larger period values.
+        self.period_us = kwargs.get( 'period_us', 100000 )
+        self.sched = sched
+        self.rtprio = 20
+
+    def cgroupSet( self, param, value, resource='cpu' ):
+        "Set a cgroup parameter and return its value"
+        cmd = 'cgset -r %s.%s=%s /%s' % (
+            resource, param, value, self.name )
+        quietRun( cmd )
+        nvalue = int( self.cgroupGet( param, resource ) )
+        if nvalue != value:
+            error( '*** error: cgroupSet: %s set to %s instead of %s\n'
+                   % ( param, nvalue, value ) )
+        return nvalue
+
+    def cgroupGet( self, param, resource='cpu' ):
+        "Return value of cgroup parameter"
+        cmd = 'cgget -r %s.%s /%s' % (
+            resource, param, self.name )
+        return int( quietRun( cmd ).split()[ -1 ] )
+
+    def cgroupDel( self ):
+        "Clean up our cgroup"
+        # info( '*** deleting cgroup', self.cgroup, '\n' )
+        _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
+        return exitcode != 0
+
+    def popen( self, *args, **kwargs ):
+        """Return a Popen() object in node's namespace
+           args: Popen() args, single list, or string
+           kwargs: Popen() keyword args"""
+        # Tell mnexec to execute command in our cgroup
+        mncmd = [ 'mnexec', '-a', str( self.pid ),
+                  '-g', self.name ]
+        if self.sched == 'rt':
+            mncmd += [ '-r', str( self.rtprio ) ]
+        return Host.popen( self, *args, mncmd=mncmd, **kwargs )
+
+    def cleanup( self ):
+        "Clean up our cgroup"
+        retry( retries=3, delaySecs=1, fn=self.cgroupDel )
+
+    def chrt( self ):
+        "Set RT scheduling priority"
+        quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
+        result = quietRun( 'chrt -p %s' % self.pid )
+        firstline = result.split( '\n' )[ 0 ]
+        lastword = firstline.split( ' ' )[ -1 ]
+        if lastword != 'SCHED_RR':
+            error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
+        return lastword
+
+    def rtInfo( self, f ):
+        "Internal method: return parameters for RT bandwidth"
+        pstr, qstr = 'rt_period_us', 'rt_runtime_us'
+        # RT uses wall clock time for period and quota
+        quota = int( self.period_us * f * numCores() )
+        return pstr, qstr, self.period_us, quota
+
+    def cfsInfo( self, f):
+        "Internal method: return parameters for CFS bandwidth"
+        pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
+        # CFS uses wall clock time for period and CPU time for quota.
+        quota = int( self.period_us * f * numCores() )
+        period = self.period_us
+        if f > 0 and quota < 1000:
+            debug( '(cfsInfo: increasing default period) ' )
+            quota = 1000
+            period = int( quota / f / numCores() )
+        return pstr, qstr, period, quota
+
+    # BL comment:
+    # This may not be the right API,
+    # since it doesn't specify CPU bandwidth in "absolute"
+    # units the way link bandwidth is specified.
+    # We should use MIPS or SPECINT or something instead.
+    # Alternatively, we should change from system fraction
+    # to CPU seconds per second, essentially assuming that
+    # all CPUs are the same.
+
+    def setCPUFrac( self, f=-1, sched=None):
+        """Set overall CPU fraction for this host
+           f: CPU bandwidth limit (fraction)
+           sched: 'rt' or 'cfs'
+           Note 'cfs' requires CONFIG_CFS_BANDWIDTH"""
+        if not f:
+            return
+        if not sched:
+            sched = self.sched
+        if sched == 'rt':
+            pstr, qstr, period, quota = self.rtInfo( f )
+        elif sched == 'cfs':
+            pstr, qstr, period, quota = self.cfsInfo( f )
+        else:
+            return
+        if quota < 0:
+            # Reset to unlimited
+            quota = -1
+        # Set cgroup's period and quota
+        self.cgroupSet( pstr, period )
+        self.cgroupSet( qstr, quota )
+        if sched == 'rt':
+            # Set RT priority if necessary
+            self.chrt()
+        info( '(%s %d/%dus) ' % ( sched, quota, period ) )
+
+    def setCPUs( self, cores, mems=0 ):
+        "Specify (real) cores that our cgroup can run on"
+        if type( cores ) is list:
+            cores = ','.join( [ str( c ) for c in cores ] )
+        self.cgroupSet( resource='cpuset', param='cpus',
+                        value=cores )
+        # Memory placement is probably not relevant, but we
+        # must specify it anyway
+        self.cgroupSet( resource='cpuset', param='mems',
+                        value=mems)
+        # We have to do this here after we've specified
+        # cpus and mems
+        errFail( 'cgclassify -g cpuset:/%s %s' % (
+                 self.name, self.pid ) )
+
+    def config( self, cpu=None, cores=None, **params ):
+        """cpu: desired overall system CPU fraction
+           cores: (real) core(s) this host can run on
+           params: parameters for Node.config()"""
+        r = Node.config( self, **params )
+        # Was considering cpu={'cpu': cpu , 'sched': sched}, but
+        # that seems redundant
+
+        self.setParam( r, 'setCPUFrac', cpu=cpu )
+        self.setParam( r, 'setCPUs', cores=cores )
+
+        return r
+
+    inited = False
+
+    @classmethod
+    def init( cls ):
+        "Initialization for CPULimitedHost class"
+        mountCgroups()
+        cls.inited = True
+
+class CCNHost( Host ):
+    "CCNHost is a Host that always runs the ccnd daemon"
+
+    def __init__( self, name, **kwargs ):
+
+        
+        Host.__init__( self, name, **kwargs )
+        if not CCNHost.inited:
+            CCNHost.init()
+            
+        self.cmd("export CCN_LOCAL_SOCKNAME=/tmp/.sock.ccnx.{0}".format(self.name))
+        self.cmd("ccndstart")
+        self.peerList = {}
+
+    def config( self, fib=None, app=None, **params ):
+             
+        r = Node.config( self, **params )
+        
+        self.setParam( r, 'app', fib=fib )
+        self.setParam( r, 'fib', app=app)
+        
+        return r
+    
+    def configCCN(self):
+        
+        self.buildPeerIP()
+        self.setFIB()
+    
+    def buildPeerIP(self):
+        for iface in self.intfList():
+            link = iface.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+                if node1 == self:
+                    self.peerList[node2.name] = link.intf2.node.IP(link.intf2)
+                else:
+                    self.peerList[node1.name] = link.intf1.node.IP(link.intf1)
+        
+        
+    def setFIB(self):
+
+        for name in self.params['fib']:
+            if not name:
+                pass
+            else:
+                self.insert_fib(name[0],self.peerList[name[1]])
+            
+        
+    def insert_fib(self, uri, host):
+        self.cmd('ccndc add {0} tcp {1}'.format(uri,host))
+        
+    def terminate( self ):
+        "Stop node."
+        self.cmd('ccndstop')
+        Host.terminate(self)
+        
+    inited = False
+
+    
+    @classmethod
+    def init( cls ):
+        "Initialization for CCNHost class"
+        cls.inited = True
+        
+class CPULimitedCCNHost( CPULimitedHost ):
+    '''CPULimitedCCNHost is a Host that always runs the ccnd daemon and extends CPULimitedHost.
+       It should be used when one wants to limit the resources of CCN routers and hosts '''
+    
+
+    def __init__( self, name, sched='cfs', **kwargs ):
+        
+        CPULimitedHost.__init__( self, name, sched, **kwargs )
+        if not CCNHost.inited:
+            CCNHost.init()
+            
+        self.cmd("export CCN_LOCAL_SOCKNAME=/tmp/.sock.ccnx.{0}".format(self.name))
+        self.cmd("ccndstart")
+        self.peerList = {}
+
+    def config( self, fib=None, app=None, cpu=None, cores=None, **params):   
+
+        r = CPULimitedHost.config(self,cpu,cores, **params)
+        
+        print 'nozess'
+        self.setParam( r, 'app', fib=fib )
+        self.setParam( r, 'fib', app=app)
+        
+        return r
+    
+    def configCCN(self):
+        
+        self.buildPeerIP()
+        self.setFIB()
+    
+    def buildPeerIP(self):
+        for iface in self.intfList():
+            link = iface.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+                if node1 == self:
+                    self.peerList[node2.name] = link.intf2.node.IP(link.intf2)
+                else:
+                    self.peerList[node1.name] = link.intf1.node.IP(link.intf1)
+        
+        
+    def setFIB(self):
+
+        for name in self.params['fib']:
+            if not name:
+                pass
+            else:
+                self.insert_fib(name[0],self.peerList[name[1]])
+            
+        
+    def insert_fib(self, uri, host):
+        self.cmd('ccndc add {0} tcp {1}'.format(uri,host))
+        
+    def terminate( self ):
+        "Stop node."
+        self.cmd('ccndstop')
+        Host.terminate(self)
+        
+    inited = False
+
+    
+    @classmethod
+    def init( cls ):
+        "Initialization for CCNHost class"
+        cls.inited = True
+
+# Some important things to note:
+#
+# The "IP" address which setIP() assigns to the switch is not
+# an "IP address for the switch" in the sense of IP routing.
+# Rather, it is the IP address for the control interface,
+# on the control network, and it is only relevant to the
+# controller. If you are running in the root namespace
+# (which is the only way to run OVS at the moment), the
+# control interface is the loopback interface, and you
+# normally never want to change its IP address!
+#
+# In general, you NEVER want to attempt to use Linux's
+# network stack (i.e. ifconfig) to "assign" an IP address or
+# MAC address to a switch data port. Instead, you "assign"
+# the IP and MAC addresses in the controller by specifying
+# packets that you want to receive or send. The "MAC" address
+# reported by ifconfig for a switch data port is essentially
+# meaningless. It is important to understand this if you
+# want to create a functional router using OpenFlow.
+
+class Switch( Node ):
+    """A Switch is a Node that is running (or has execed?)
+       an OpenFlow switch."""
+
+    portBase = 1  # Switches start with port 1 in OpenFlow
+    dpidLen = 16  # digits in dpid passed to switch
+
+    def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
+        """dpid: dpid for switch (or None to derive from name, e.g. s1 -> 1)
+           opts: additional switch options
+           listenPort: port to listen on for dpctl connections"""
+        Node.__init__( self, name, **params )
+        self.dpid = dpid if dpid else self.defaultDpid()
+        self.opts = opts
+        self.listenPort = listenPort
+        if not self.inNamespace:
+            self.controlIntf = Intf( 'lo', self, port=0 )
+
+    def defaultDpid( self ):
+        "Derive dpid from switch name, s1 -> 1"
+        try:
+            dpid = int( re.findall( '\d+', self.name )[ 0 ] )
+            dpid = hex( dpid )[ 2: ]
+            dpid = '0' * ( self.dpidLen - len( dpid ) ) + dpid
+            return dpid
+        except IndexError:
+            raise Exception( 'Unable to derive default datapath ID - '
+                             'please either specify a dpid or use a '
+                             'canonical switch name such as s23.' )
+
+    def defaultIntf( self ):
+        "Return control interface"
+        if self.controlIntf:
+            return self.controlIntf
+        else:
+            return Node.defaultIntf( self )
+
+    def sendCmd( self, *cmd, **kwargs ):
+        """Send command to Node.
+           cmd: string"""
+        kwargs.setdefault( 'printPid', False )
+        if not self.execed:
+            return Node.sendCmd( self, *cmd, **kwargs )
+        else:
+            error( '*** Error: %s has execed and cannot accept commands' %
+                   self.name )
+
+    def __repr__( self ):
+        "More informative string representation"
+        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
+                              for i in self.intfList() ] ) )
+        return '<%s %s: %s pid=%s> ' % (
+            self.__class__.__name__, self.name, intfs, self.pid )
+
+class UserSwitch( Switch ):
+    "User-space switch."
+
+    dpidLen = 12
+
+    def __init__( self, name, **kwargs ):
+        """Init.
+           name: name for the switch"""
+        Switch.__init__( self, name, **kwargs )
+        pathCheck( 'ofdatapath', 'ofprotocol',
+                   moduleName='the OpenFlow reference user switch' +
+                              '(openflow.org)' )
+        if self.listenPort:
+            self.opts += ' --listen=ptcp:%i ' % self.listenPort
+
+    @classmethod
+    def setup( cls ):
+        "Ensure any dependencies are loaded; if not, try to load them."
+        if not os.path.exists( '/dev/net/tun' ):
+            moduleDeps( add=TUN )
+
+    def dpctl( self, *args ):
+        "Run dpctl command"
+        if not self.listenPort:
+            return "can't run dpctl without passive listening port"
+        return self.cmd( 'dpctl ' + ' '.join( args ) +
+                         ' tcp:127.0.0.1:%i' % self.listenPort )
+
+    def start( self, controllers ):
+        """Start OpenFlow reference user datapath.
+           Log to /tmp/sN-{ofd,ofp}.log.
+           controllers: list of controller objects"""
+        # Add controllers
+        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
+                            for c in controllers ] )
+        ofdlog = '/tmp/' + self.name + '-ofd.log'
+        ofplog = '/tmp/' + self.name + '-ofp.log'
+        self.cmd( 'ifconfig lo up' )
+        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
+        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
+                  ' punix:/tmp/' + self.name + ' -d ' + self.dpid +
+                  ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
+        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
+                  ' ' + clist +
+                  ' --fail=closed ' + self.opts +
+                  ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
+
+    def stop( self ):
+        "Stop OpenFlow reference user datapath."
+        self.cmd( 'kill %ofdatapath' )
+        self.cmd( 'kill %ofprotocol' )
+        self.deleteIntfs()
+
+
+class OVSLegacyKernelSwitch( Switch ):
+    """Open VSwitch legacy kernel-space switch using ovs-openflowd.
+       Currently only works in the root namespace."""
+
+    def __init__( self, name, dp=None, **kwargs ):
+        """Init.
+           name: name for switch
+           dp: netlink id (0, 1, 2, ...)
+           defaultMAC: default MAC as unsigned int; random value if None"""
+        Switch.__init__( self, name, **kwargs )
+        self.dp = dp if dp else self.name
+        self.intf = self.dp
+        if self.inNamespace:
+            error( "OVSKernelSwitch currently only works"
+                   " in the root namespace.\n" )
+            exit( 1 )
+
+    @classmethod
+    def setup( cls ):
+        "Ensure any dependencies are loaded; if not, try to load them."
+        pathCheck( 'ovs-dpctl', 'ovs-openflowd',
+                   moduleName='Open vSwitch (openvswitch.org)')
+        moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
+
+    def start( self, controllers ):
+        "Start up kernel datapath."
+        ofplog = '/tmp/' + self.name + '-ofp.log'
+        quietRun( 'ifconfig lo up' )
+        # Delete local datapath if it exists;
+        # then create a new one monitoring the given interfaces
+        self.cmd( 'ovs-dpctl del-dp ' + self.dp )
+        self.cmd( 'ovs-dpctl add-dp ' + self.dp )
+        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
+        self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
+        # Run protocol daemon
+        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
+                            for c in controllers ] )
+        self.cmd( 'ovs-openflowd ' + self.dp +
+                  ' ' + clist +
+                  ' --fail=secure ' + self.opts +
+                  ' --datapath-id=' + self.dpid +
+                  ' 1>' + ofplog + ' 2>' + ofplog + '&' )
+        self.execed = False
+
+    def stop( self ):
+        "Terminate kernel datapath."
+        quietRun( 'ovs-dpctl del-dp ' + self.dp )
+        self.cmd( 'kill %ovs-openflowd' )
+        self.deleteIntfs()
+
+
+class OVSSwitch( Switch ):
+    "Open vSwitch switch. Depends on ovs-vsctl."
+
+    def __init__( self, name, failMode='secure', **params ):
+        """Init.
+           name: name for switch
+           failMode: controller loss behavior (secure|open)"""
+        Switch.__init__( self, name, **params )
+        self.failMode = failMode
+
+    @classmethod
+    def setup( cls ):
+        "Make sure Open vSwitch is installed and working"
+        pathCheck( 'ovs-vsctl',
+                   moduleName='Open vSwitch (openvswitch.org)')
+        # This should no longer be needed, and it breaks
+        # with OVS 1.7 which has renamed the kernel module:
+        #  moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
+        out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
+        if exitcode:
+            error( out + err +
+                   'ovs-vsctl exited with code %d\n' % exitcode +
+                   '*** Error connecting to ovs-db with ovs-vsctl\n'
+                   'Make sure that Open vSwitch is installed, '
+                   'that ovsdb-server is running, and that\n'
+                   '"ovs-vsctl show" works correctly.\n'
+                   'You may wish to try '
+                   '"service openvswitch-switch start".\n' )
+            exit( 1 )
+
+    def dpctl( self, *args ):
+        "Run ovs-dpctl command"
+        return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
+
+    @staticmethod
+    def TCReapply( intf ):
+        """Unfortunately OVS and Mininet are fighting
+           over tc queuing disciplines. As a quick hack/
+           workaround, we clear OVS's and reapply our own."""
+        if type( intf ) is TCIntf:
+            intf.config( **intf.params )
+
+    def attach( self, intf ):
+        "Connect a data port"
+        self.cmd( 'ovs-vsctl add-port', self, intf )
+        self.cmd( 'ifconfig', intf, 'up' )
+        self.TCReapply( intf )
+
+    def detach( self, intf ):
+        "Disconnect a data port"
+        self.cmd( 'ovs-vsctl del-port', self, intf )
+
+    def start( self, controllers ):
+        "Start up a new OVS OpenFlow switch using ovs-vsctl"
+        if self.inNamespace:
+            raise Exception(
+                'OVS kernel switch does not work in a namespace' )
+        # We should probably call config instead, but this
+        # requires some rethinking...
+        self.cmd( 'ifconfig lo up' )
+        # Annoyingly, --if-exists option seems not to work
+        self.cmd( 'ovs-vsctl del-br', self )
+        self.cmd( 'ovs-vsctl add-br', self )
+        self.cmd( 'ovs-vsctl -- set Bridge', self,
+                  'other_config:datapath-id=' + self.dpid )
+        self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode )
+        for intf in self.intfList():
+            if not intf.IP():
+                self.attach( intf )
+        # Add controllers
+        clist = ' '.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
+                            for c in controllers ] )
+        if self.listenPort:
+            clist += ' ptcp:%s' % self.listenPort
+        self.cmd( 'ovs-vsctl set-controller', self, clist )
+
+    def stop( self ):
+        "Terminate OVS switch."
+        self.cmd( 'ovs-vsctl del-br', self )
+        self.deleteIntfs()
+
+OVSKernelSwitch = OVSSwitch
+
+
+class Controller( Node ):
+    """A Controller is a Node that is running (or has execed?) an
+       OpenFlow controller."""
+
+    def __init__( self, name, inNamespace=False, command='controller',
+                  cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
+                  port=6633, **params ):
+        self.command = command
+        self.cargs = cargs
+        self.cdir = cdir
+        self.ip = ip
+        self.port = port
+        Node.__init__( self, name, inNamespace=inNamespace,
+                       ip=ip, **params  )
+        self.cmd( 'ifconfig lo up' )  # Shouldn't be necessary
+        self.checkListening()
+
+    def checkListening( self ):
+        "Make sure no controllers are running on our port"
+        # Verify that Telnet is installed first:
+        out, _err, returnCode = errRun( "which telnet" )
+        if 'telnet' not in out or returnCode != 0:
+            raise Exception( "Error running telnet to check for listening "
+                             "controllers; please check that it is "
+                             "installed." )
+        listening = self.cmd( "echo A | telnet -e A %s %d" %
+                              ( self.ip, self.port ) )
+        if 'Unable' not in listening:
+            servers = self.cmd( 'netstat -atp' ).split( '\n' )
+            pstr = ':%d ' % self.port
+            clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
+            raise Exception( "Please shut down the controller which is"
+                             " running on port %d:\n" % self.port +
+                             '\n'.join( clist ) )
+
+    def start( self ):
+        """Start <controller> <args> on controller.
+           Log to /tmp/cN.log"""
+        pathCheck( self.command )
+        cout = '/tmp/' + self.name + '.log'
+        if self.cdir is not None:
+            self.cmd( 'cd ' + self.cdir )
+        self.cmd( self.command + ' ' + self.cargs % self.port +
+                  ' 1>' + cout + ' 2>' + cout + '&' )
+        self.execed = False
+
+    def stop( self ):
+        "Stop controller."
+        self.cmd( 'kill %' + self.command )
+        self.terminate()
+
+    def IP( self, intf=None ):
+        "Return IP address of the Controller"
+        if self.intfs:
+            ip = Node.IP( self, intf )
+        else:
+            ip = self.ip
+        return ip
+
+    def __repr__( self ):
+        "More informative string representation"
+        return '<%s %s: %s:%s pid=%s> ' % (
+            self.__class__.__name__, self.name,
+            self.IP(), self.port, self.pid )
+
+
+class OVSController( Controller ):
+    "Open vSwitch controller"
+    def __init__( self, name, command='ovs-controller', **kwargs ):
+        Controller.__init__( self, name, command=command, **kwargs )
+
+
+class NOX( Controller ):
+    "Controller to run a NOX application."
+
+    def __init__( self, name, *noxArgs, **kwargs ):
+        """Init.
+           name: name to give controller
+           noxArgs: arguments (strings) to pass to NOX"""
+        if not noxArgs:
+            warn( 'warning: no NOX modules specified; '
+                  'running packetdump only\n' )
+            noxArgs = [ 'packetdump' ]
+        elif type( noxArgs ) not in ( list, tuple ):
+            noxArgs = [ noxArgs ]
+
+        if 'NOX_CORE_DIR' not in os.environ:
+            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
+        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
+
+        Controller.__init__( self, name,
+                             command=noxCoreDir + '/nox_core',
+                             cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
+                             ' '.join( noxArgs ),
+                             cdir=noxCoreDir,
+                             **kwargs )
+
+
+class RemoteController( Controller ):
+    "Controller running outside of Mininet's control."
+
+    def __init__( self, name, ip='127.0.0.1',
+                  port=6633, **kwargs):
+        """Init.
+           name: name to give controller
+           ip: the IP address where the remote controller is
+           listening
+           port: the port where the remote controller is listening"""
+        Controller.__init__( self, name, ip=ip, port=port, **kwargs )
+
+    def start( self ):
+        "Overridden to do nothing."
+        return
+
+    def stop( self ):
+        "Overridden to do nothing."
+        return
+
+    def checkListening( self ):
+        "Warn if remote controller is not accessible"
+        listening = self.cmd( "echo A | telnet -e A %s %d" %
+                              ( self.ip, self.port ) )
+        if 'Unable' in listening:
+            warn( "Unable to contact the remote controller"
+                  " at %s:%d\n" % ( self.ip, self.port ) )
diff --git a/mininet/term.py b/mininet/term.py
new file mode 100644
index 0000000..3cd70f2
--- /dev/null
+++ b/mininet/term.py
@@ -0,0 +1,60 @@
+"""
+Terminal creation and cleanup.
+Utility functions to run a term (connected via screen(1)) on each host.
+
+Requires GNU screen(1) and xterm(1).
+Optionally uses gnome-terminal.
+"""
+
+import re
+from subprocess import Popen
+
+from mininet.log import error
+from mininet.util import quietRun
+
+def quoteArg( arg ):
+    "Quote an argument if it contains spaces."
+    return repr( arg ) if ' ' in arg else arg
+
+def makeTerm( node, title='Node', term='xterm' ):
+    """Run screen on a node, and hook up a terminal.
+       node: Node object
+       title: base title
+       term: 'xterm' or 'gterm'
+       returns: process created"""
+    title += ': ' + node.name
+    if not node.inNamespace:
+        title += ' (root)'
+    cmds = {
+        'xterm': [ 'xterm', '-title', title, '-e' ],
+        'gterm': [ 'gnome-terminal', '--title', title, '-e' ]
+    }
+    if term not in cmds:
+        error( 'invalid terminal type: %s' % term )
+        return
+    if not node.execed:
+        node.cmd( 'screen -dmS ' + 'mininet.' + node.name)
+        args = [ 'screen', '-D', '-RR', '-S', 'mininet.' + node.name ]
+    else:
+        args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
+    if term == 'gterm':
+        # Compress these for gnome-terminal, which expects one token
+        # to follow the -e option
+        args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
+    return Popen( cmds[ term ] + args )
+
+def cleanUpScreens():
+    "Remove moldy old screen sessions."
+    r = r'(\d+\.mininet\.[hsc]\d+)'
+    output = quietRun( 'screen -ls' ).split( '\n' )
+    for line in output:
+        m = re.search( r, line )
+        if m:
+            quietRun( 'screen -S ' + m.group( 1 ) + ' -X quit' )
+
+def makeTerms( nodes, title='Node', term='xterm' ):
+    """Create terminals.
+       nodes: list of Node objects
+       title: base title for each
+       returns: list of created terminal processes"""
+    return [ makeTerm( node, title, term ) for node in nodes ]
diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py
new file mode 100644
index 0000000..ace7bb5
--- /dev/null
+++ b/mininet/test/test_hifi.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+"""Package: mininet
+   Test creation and pings for topologies with link and/or CPU options."""
+
+import unittest
+
+from mininet.net import Mininet
+from mininet.node import OVSKernelSwitch
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.topo import Topo
+from mininet.log import setLogLevel
+
+
+SWITCH = OVSKernelSwitch
+# Number of hosts for each test
+N = 2
+
+
+class SingleSwitchOptionsTopo(Topo):
+    "Single switch connected to n hosts."
+    def __init__(self, n=2, hopts=None, lopts=None):
+        if not hopts:
+            hopts = {}
+        if not lopts:
+            lopts = {}
+        Topo.__init__(self, hopts=hopts, lopts=lopts)
+        switch = self.addSwitch('s1')
+        for h in range(n):
+            host = self.addHost('h%s' % (h + 1))
+            self.addLink(host, switch)
+
+
+class testOptionsTopo( unittest.TestCase ):
+    "Verify ability to create networks with host and link options."
+
+    def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
+        "Generic topology-with-options test runner."
+        mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
+                                                    lopts=lopts ),
+                      host=CPULimitedHost, link=TCLink )
+        dropped = mn.run( mn.ping )
+        self.assertEqual( dropped, 0 )
+
+    def assertWithinTolerance(self, measured, expected, tolerance_frac):
+        """Check that a given value is within a tolerance of expected
+        tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
+        """
+        self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
+        self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
+
+    def testCPULimits( self ):
+        "Verify topology creation with CPU limits set for both schedulers."
+        CPU_FRACTION = 0.1
+        CPU_TOLERANCE = 0.8  # CPU fraction below which test should fail
+        hopts = { 'cpu': CPU_FRACTION }
+        #self.runOptionsTopoTest( N, hopts=hopts )
+
+        mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
+                      host=CPULimitedHost )
+        mn.start()
+        results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
+        mn.stop()
+        for cpu in results:
+            self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE )
+
+    def testLinkBandwidth( self ):
+        "Verify that link bandwidths are accurate within a bound."
+        BW = 5  # Mbps
+        BW_TOLERANCE = 0.8  # BW fraction below which test should fail
+        # Verify ability to create limited-link topo first;
+        lopts = { 'bw': BW, 'use_htb': True }
+        # Also verify correctness of limit limitng within a bound.
+        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
+                      link=TCLink )
+        bw_strs = mn.run( mn.iperf )
+        for bw_str in bw_strs:
+            bw = float( bw_str.split(' ')[0] )
+            self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
+
+    def testLinkDelay( self ):
+        "Verify that link delays are accurate within a bound."
+        DELAY_MS = 15
+        DELAY_TOLERANCE = 0.8  # Delay fraction below which test should fail
+        lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
+        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
+                      link=TCLink )
+        ping_delays = mn.run( mn.pingFull )
+        test_outputs = ping_delays[0]
+        # Ignore unused variables below
+        # pylint: disable-msg=W0612
+        node, dest, ping_outputs = test_outputs
+        sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
+        self.assertEqual( sent, received )
+        # pylint: enable-msg=W0612
+        for rttval in [rttmin, rttavg, rttmax]:
+            # Multiply delay by 4 to cover there & back on two links
+            self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
+                                        DELAY_TOLERANCE)
+
+    def testLinkLoss( self ):
+        "Verify that we see packet drops with a high configured loss rate."
+        LOSS_PERCENT = 99
+        REPS = 1
+        lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
+        mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
+                      host=CPULimitedHost, link=TCLink )
+        # Drops are probabilistic, but the chance of no dropped packets is
+        # 1 in 100 million with 4 hops for a link w/99% loss.
+        dropped_total = 0
+        mn.start()
+        for _ in range(REPS):
+            dropped_total += mn.ping(timeout='1')
+        mn.stop()
+        self.assertTrue(dropped_total > 0)
+
+    def testMostOptions( self ):
+        "Verify topology creation with most link options and CPU limits."
+        lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
+        hopts = { 'cpu': 0.5 / N }
+        self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )
+
+
+if __name__ == '__main__':
+    setLogLevel( 'warning' )
+    unittest.main()
diff --git a/mininet/test/test_nets.py b/mininet/test/test_nets.py
new file mode 100644
index 0000000..fde8e87
--- /dev/null
+++ b/mininet/test/test_nets.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+"""Package: mininet
+   Test creation and all-pairs ping for each included mininet topo type."""
+
+import unittest
+
+from mininet.net import Mininet
+from mininet.node import Host, Controller
+from mininet.node import UserSwitch, OVSKernelSwitch
+from mininet.topo import SingleSwitchTopo, LinearTopo
+from mininet.log import setLogLevel
+
+SWITCHES = { 'user': UserSwitch,
+             'ovsk': OVSKernelSwitch,
+}
+
+
+class testSingleSwitch( unittest.TestCase ):
+    "For each datapath type, test ping with single switch topologies."
+
+    def testMinimal( self ):
+        "Ping test with both datapaths on minimal topology"
+        for switch in SWITCHES.values():
+            mn = Mininet( SingleSwitchTopo(), switch, Host, Controller )
+            dropped = mn.run( mn.ping )
+            self.assertEqual( dropped, 0 )
+
+    def testSingle5( self ):
+        "Ping test with both datapaths on 5-host single-switch topology"
+        for switch in SWITCHES.values():
+            mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller )
+            dropped = mn.run( mn.ping )
+            self.assertEqual( dropped, 0 )
+
+
+class testLinear( unittest.TestCase ):
+    "For each datapath type, test all-pairs ping with LinearNet."
+
+    def testLinear5( self ):
+        "Ping test with both datapaths on a 5-switch topology"
+        for switch in SWITCHES.values():
+            mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller )
+            dropped = mn.run( mn.ping )
+            self.assertEqual( dropped, 0 )
+
+
+if __name__ == '__main__':
+    setLogLevel( 'warning' )
+    unittest.main()
diff --git a/mininet/topo.py b/mininet/topo.py
new file mode 100644
index 0000000..fff9604
--- /dev/null
+++ b/mininet/topo.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+'''@package topo
+
+Network topology creation.
+
+@author Brandon Heller (brandonh@stanford.edu)
+
+This package includes code to represent network topologies.
+
+A Topo object can be a topology database for NOX, can represent a physical
+setup for testing, and can even be emulated with the Mininet package.
+'''
+
+# BL: we may have to fix compatibility here.
+# networkx is also a fairly heavyweight dependency
+# from networkx.classes.graph import Graph
+
+from networkx import Graph
+from mininet.util import irange, natural, naturalSeq
+
+class Topo(object):
+    "Data center network representation for structured multi-trees."
+
+    def __init__(self, hopts=None, sopts=None, lopts=None):
+        """Topo object:
+           hinfo: default host options
+           sopts: default switch options
+           lopts: default link options"""
+        self.g = Graph()
+        self.node_info = {}
+        self.link_info = {}  # (src, dst) tuples hash to EdgeInfo objects
+        self.hopts = {} if hopts is None else hopts
+        self.sopts = {} if sopts is None else sopts
+        self.lopts = {} if lopts is None else lopts
+        self.ports = {}  # ports[src][dst] is port on src that connects to dst
+
+    def addNode(self, name, **opts):
+        """Add Node to graph.
+           name: name
+           opts: node options
+           returns: node name"""
+        self.g.add_node(name)
+        self.node_info[name] = opts
+        return name
+
+    def addHost(self, name, **opts):
+        """Convenience method: Add host to graph.
+           name: host name
+           opts: host options
+           returns: host name"""
+        if not opts and self.hopts:
+            opts = self.hopts
+        return self.addNode(name, **opts)
+
+    def addSwitch(self, name, **opts):
+        """Convenience method: Add switch to graph.
+           name: switch name
+           opts: switch options
+           returns: switch name"""
+        if not opts and self.sopts:
+            opts = self.sopts
+        result = self.addNode(name, isSwitch=True, **opts)
+        return result
+
+    def addLink(self, node1, node2, port1=None, port2=None,
+                **opts):
+        """node1, node2: nodes to link together
+           port1, port2: ports (optional)
+           opts: link options (optional)
+           returns: link info key"""
+        if not opts and self.lopts:
+            opts = self.lopts
+        self.addPort(node1, node2, port1, port2)
+        key = tuple(self.sorted([node1, node2]))
+        self.link_info[key] = opts
+        self.g.add_edge(*key)
+        return key
+
+    def addPort(self, src, dst, sport=None, dport=None):
+        '''Generate port mapping for new edge.
+        @param src source switch name
+        @param dst destination switch name
+        '''
+        self.ports.setdefault(src, {})
+        self.ports.setdefault(dst, {})
+        # New port: number of outlinks + base
+        src_base = 1 if self.isSwitch(src) else 0
+        dst_base = 1 if self.isSwitch(dst) else 0
+        if sport is None:
+            sport = len(self.ports[src]) + src_base
+        if dport is None:
+            dport = len(self.ports[dst]) + dst_base
+        self.ports[src][dst] = sport
+        self.ports[dst][src] = dport
+
+    def nodes(self, sort=True):
+        "Return nodes in graph"
+        if sort:
+            return self.sorted( self.g.nodes() )
+        else:
+            return self.g.nodes()
+
+    def isSwitch(self, n):
+        '''Returns true if node is a switch.'''
+        info = self.node_info[n]
+        return info and info.get('isSwitch', False)
+
+    def switches(self, sort=True):
+        '''Return switches.
+        sort: sort switches alphabetically
+        @return dpids list of dpids
+        '''
+        return [n for n in self.nodes(sort) if self.isSwitch(n)]
+
+    def hosts(self, sort=True):
+        '''Return hosts.
+        sort: sort hosts alphabetically
+        @return dpids list of dpids
+        '''
+        return [n for n in self.nodes(sort) if not self.isSwitch(n)]
+
+    def links(self, sort=True):
+        '''Return links.
+        sort: sort links alphabetically
+        @return links list of name pairs
+        '''
+        if not sort:
+            return self.g.edges()
+        else:
+            links = [tuple(self.sorted(e)) for e in self.g.edges()]
+            return sorted( links, key=naturalSeq )
+
+    def port(self, src, dst):
+        '''Get port number.
+
+        @param src source switch name
+        @param dst destination switch name
+        @return tuple (src_port, dst_port):
+            src_port: port on source switch leading to the destination switch
+            dst_port: port on destination switch leading to the source switch
+        '''
+        if src in self.ports and dst in self.ports[src]:
+            assert dst in self.ports and src in self.ports[dst]
+            return (self.ports[src][dst], self.ports[dst][src])
+
+    def linkInfo( self, src, dst ):
+        "Return link metadata"
+        src, dst = self.sorted([src, dst])
+        return self.link_info[(src, dst)]
+
+    def setlinkInfo( self, src, dst, info ):
+        "Set link metadata"
+        src, dst = self.sorted([src, dst])
+        self.link_info[(src, dst)] = info
+
+    def nodeInfo( self, name ):
+        "Return metadata (dict) for node"
+        info = self.node_info[ name ]
+        return info if info is not None else {}
+
+    def setNodeInfo( self, name, info ):
+        "Set metadata (dict) for node"
+        self.node_info[ name ] = info
+
+    @staticmethod
+    def sorted( items ):
+        "Items sorted in natural (i.e. alphabetical) order"
+        return sorted(items, key=natural)
+
+class SingleSwitchTopo(Topo):
+    '''Single switch connected to k hosts.'''
+
+    def __init__(self, k=2, **opts):
+        '''Init.
+
+        @param k number of hosts
+        @param enable_all enables all nodes and switches?
+        '''
+        super(SingleSwitchTopo, self).__init__(**opts)
+
+        self.k = k
+
+        switch = self.addSwitch('s1')
+        for h in irange(1, k):
+            host = self.addHost('h%s' % h)
+            self.addLink(host, switch)
+
+
+class SingleSwitchReversedTopo(Topo):
+    '''Single switch connected to k hosts, with reversed ports.
+
+    The lowest-numbered host is connected to the highest-numbered port.
+
+    Useful to verify that Mininet properly handles custom port numberings.
+    '''
+    def __init__(self, k=2, **opts):
+        '''Init.
+
+        @param k number of hosts
+        @param enable_all enables all nodes and switches?
+        '''
+        super(SingleSwitchReversedTopo, self).__init__(**opts)
+        self.k = k
+        switch = self.addSwitch('s1')
+        for h in irange(1, k):
+            host = self.addHost('h%s' % h)
+            self.addLink(host, switch,
+                         port1=0, port2=(k - h + 1))
+
+class LinearTopo(Topo):
+    "Linear topology of k switches, with one host per switch."
+
+    def __init__(self, k=2, **opts):
+        """Init.
+           k: number of switches (and hosts)
+           hconf: host configuration options
+           lconf: link configuration options"""
+
+        super(LinearTopo, self).__init__(**opts)
+
+        self.k = k
+
+        lastSwitch = None
+        for i in irange(1, k):
+            host = self.addHost('h%s' % i)
+            switch = self.addSwitch('s%s' % i)
+            self.addLink( host, switch)
+            if lastSwitch:
+                self.addLink( switch, lastSwitch)
+            lastSwitch = switch
diff --git a/mininet/topolib.py b/mininet/topolib.py
new file mode 100644
index 0000000..63ba36d
--- /dev/null
+++ b/mininet/topolib.py
@@ -0,0 +1,36 @@
+"Library of potentially useful topologies for Mininet"
+
+from mininet.topo import Topo
+from mininet.net import Mininet
+
+class TreeTopo( Topo ):
+    "Topology for a tree network with a given depth and fanout."
+
+    def __init__( self, depth=1, fanout=2 ):
+        super( TreeTopo, self ).__init__()
+        # Numbering:  h1..N, s1..M
+        self.hostNum = 1
+        self.switchNum = 1
+        # Build topology
+        self.addTree( depth, fanout )
+
+    def addTree( self, depth, fanout ):
+        """Add a subtree starting with node n.
+           returns: last node added"""
+        isSwitch = depth > 0
+        if isSwitch:
+            node = self.addSwitch( 's%s' % self.switchNum )
+            self.switchNum += 1
+            for _ in range( fanout ):
+                child = self.addTree( depth - 1, fanout )
+                self.addLink( node, child )
+        else:
+            node = self.addHost( 'h%s' % self.hostNum )
+            self.hostNum += 1
+        return node
+
+
+def TreeNet( depth=1, fanout=2, **kwargs ):
+    "Convenience function for creating tree networks."
+    topo = TreeTopo( depth, fanout )
+    return Mininet( topo, **kwargs )
diff --git a/mininet/util.py b/mininet/util.py
new file mode 100644
index 0000000..6a3dc14
--- /dev/null
+++ b/mininet/util.py
@@ -0,0 +1,473 @@
+"Utility functions for Mininet."
+
+from mininet.log import output, info, error, warn
+
+from time import sleep
+from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
+from select import poll, POLLIN
+from subprocess import call, check_call, Popen, PIPE, STDOUT
+import re
+from fcntl import fcntl, F_GETFL, F_SETFL
+from os import O_NONBLOCK
+import os
+
+# Command execution support
+
+def run( cmd ):
+    """Simple interface to subprocess.call()
+       cmd: list of command params"""
+    return call( cmd.split( ' ' ) )
+
+def checkRun( cmd ):
+    """Simple interface to subprocess.check_call()
+       cmd: list of command params"""
+    return check_call( cmd.split( ' ' ) )
+
+# pylint doesn't understand explicit type checking
+# pylint: disable-msg=E1103
+
+def oldQuietRun( *cmd ):
+    """Run a command, routing stderr to stdout, and return the output.
+       cmd: list of command params"""
+    if len( cmd ) == 1:
+        cmd = cmd[ 0 ]
+        if isinstance( cmd, str ):
+            cmd = cmd.split( ' ' )
+    popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
+    # We can't use Popen.communicate() because it uses
+    # select(), which can't handle
+    # high file descriptor numbers! poll() can, however.
+    out = ''
+    readable = poll()
+    readable.register( popen.stdout )
+    while True:
+        while readable.poll():
+            data = popen.stdout.read( 1024 )
+            if len( data ) == 0:
+                break
+            out += data
+        popen.poll()
+        if popen.returncode is not None:
+            break
+    return out
+
+
+# This is a bit complicated, but it enables us to
+# monitor command output as it is happening
+
+def errRun( *cmd, **kwargs ):
+    """Run a command and return stdout, stderr and return code
+       cmd: string or list of command and args
+       stderr: STDOUT to merge stderr with stdout
+       shell: run command using shell
+       echo: monitor output to console"""
+    # Allow passing in a list or a string
+    if len( cmd ) == 1:
+        cmd = cmd[ 0 ]
+        if isinstance( cmd, str ):
+            cmd = cmd.split( ' ' )
+    cmd = [ str( arg ) for arg in cmd ]
+    # By default we separate stderr, don't run in a shell, and don't echo
+    stderr = kwargs.get( 'stderr', PIPE )
+    shell = kwargs.get( 'shell', False )
+    echo = kwargs.get( 'echo', False )
+    if echo:
+        # cmd goes to stderr, output goes to stdout
+        info( cmd, '\n' )
+    popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
+    # We use poll() because select() doesn't work with large fd numbers,
+    # and thus communicate() doesn't work either
+    out, err = '', ''
+    poller = poll()
+    poller.register( popen.stdout, POLLIN )
+    fdtofile = { popen.stdout.fileno(): popen.stdout }
+    outDone, errDone = False, True
+    if popen.stderr:
+        fdtofile[ popen.stderr.fileno() ] = popen.stderr
+        poller.register( popen.stderr, POLLIN )
+        errDone = False
+    while not outDone or not errDone:
+        readable = poller.poll()
+        for fd, _event in readable:
+            f = fdtofile[ fd ]
+            data = f.read( 1024 )
+            if echo:
+                output( data )
+            if f == popen.stdout:
+                out += data
+                if data == '':
+                    outDone = True
+            elif f == popen.stderr:
+                err += data
+                if data == '':
+                    errDone = True
+    returncode = popen.wait()
+    return out, err, returncode
+
+def errFail( *cmd, **kwargs ):
+    "Run a command using errRun and raise exception on nonzero exit"
+    out, err, ret = errRun( *cmd, **kwargs )
+    if ret:
+        raise Exception( "errFail: %s failed with return code %s: %s"
+                         % ( cmd, ret, err ) )
+    return out, err, ret
+
+def quietRun( cmd, **kwargs ):
+    "Run a command and return merged stdout and stderr"
+    return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
+
+# pylint: enable-msg=E1103
+# pylint: disable-msg=E1101
+
+def isShellBuiltin( cmd ):
+    "Return True if cmd is a bash builtin."
+    if isShellBuiltin.builtIns is None:
+        isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
+    space = cmd.find( ' ' )
+    if space > 0:
+        cmd = cmd[ :space]
+    return cmd in isShellBuiltin.builtIns
+
+isShellBuiltin.builtIns = None
+
+# pylint: enable-msg=E1101
+
+# Interface management
+#
+# Interfaces are managed as strings which are simply the
+# interface names, of the form 'nodeN-ethM'.
+#
+# To connect nodes, we create a pair of veth interfaces, and then place them
+# in the pair of nodes that we want to communicate. We then update the node's
+# list of interfaces and connectivity map.
+#
+# For the kernel datapath, switch interfaces
+# live in the root namespace and thus do not have to be
+# explicitly moved.
+
+def makeIntfPair( intf1, intf2 ):
+    """Make a veth pair connecting intf1 and intf2.
+       intf1: string, interface
+       intf2: string, interface
+       returns: success boolean"""
+    # Delete any old interfaces with the same names
+    quietRun( 'ip link del ' + intf1 )
+    quietRun( 'ip link del ' + intf2 )
+    # Create new pair
+    cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
+    return quietRun( cmd )
+
+def retry( retries, delaySecs, fn, *args, **keywords ):
+    """Try something several times before giving up.
+       n: number of times to retry
+       delaySecs: wait this long between tries
+       fn: function to call
+       args: args to apply to function call"""
+    tries = 0
+    while not fn( *args, **keywords ) and tries < retries:
+        sleep( delaySecs )
+        tries += 1
+    if tries >= retries:
+        error( "*** gave up after %i retries\n" % tries )
+        exit( 1 )
+
+def moveIntfNoRetry( intf, node, printError=False ):
+    """Move interface to node, without retrying.
+       intf: string, interface
+       node: Node object
+       printError: if true, print error"""
+    cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
+    quietRun( cmd )
+    links = node.cmd( 'ip link show' )
+    if not ( ' %s:' % intf ) in links:
+        if printError:
+            error( '*** Error: moveIntf: ' + intf +
+                   ' not successfully moved to ' + node.name + '\n' )
+        return False
+    return True
+
+def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
+    """Move interface to node, retrying on failure.
+       intf: string, interface
+       node: Node object
+       printError: if true, print error"""
+    retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
+
+# Support for dumping network
+
+def dumpNodeConnections( nodes ):
+    "Dump connections to/from nodes."
+
+    def dumpConnections( node ):
+        "Helper function: dump connections to node"
+        for intf in node.intfList():
+            output( ' %s:' % intf )
+            if intf.link:
+                intfs = [ intf.link.intf1, intf.link.intf2 ]
+                intfs.remove( intf )
+                output( intfs[ 0 ] )
+            else:
+                output( ' ' )
+
+    for node in nodes:
+        output( node.name )
+        dumpConnections( node )
+        output( '\n' )
+
+def dumpNetConnections( net ):
+    "Dump connections in network"
+    nodes = net.controllers + net.switches + net.hosts
+    dumpNodeConnections( nodes )
+
+# IP and Mac address formatting and parsing
+
+def _colonHex( val, bytecount ):
+    """Generate colon-hex string.
+       val: input as unsigned int
+       bytecount: number of bytes to convert
+       returns: chStr colon-hex string"""
+    pieces = []
+    for i in range( bytecount - 1, -1, -1 ):
+        piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
+        pieces.append( '%02x' % piece )
+    chStr = ':'.join( pieces )
+    return chStr
+
+def macColonHex( mac ):
+    """Generate MAC colon-hex string from unsigned int.
+       mac: MAC address as unsigned int
+       returns: macStr MAC colon-hex string"""
+    return _colonHex( mac, 6 )
+
+def ipStr( ip ):
+    """Generate IP address string from an unsigned int.
+       ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
+       returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
+    w = ( ip >> 24 ) & 0xff
+    w = 10 if w == 0 else w
+    x = ( ip >> 16 ) & 0xff
+    y = ( ip >> 8 ) & 0xff
+    z = ip & 0xff
+    return "%i.%i.%i.%i" % ( w, x, y, z )
+
+def ipNum( w, x, y, z ):
+    """Generate unsigned int from components of IP address
+       returns: w << 24 | x << 16 | y << 8 | z"""
+    return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
+
+def nextCCNnet(curCCNnet):
+    netNum = ipParse(curCCNnet)
+    return ipStr(netNum+4)
+
+def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
+    """Return IP address string from ints
+       i: int to be added to ipbase
+       prefixLen: optional IP prefix length
+       ipBaseNum: option base IP address as int
+       returns IP address as string"""
+    # Ugly but functional
+    assert i < ( 1 << ( 32 - prefixLen ) )
+    mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
+    ipnum = i + ( ipBaseNum & mask )
+    return ipStr( ipnum )
+
+def ipParse( ip ):
+    "Parse an IP address and return an unsigned int."
+    args = [ int( arg ) for arg in ip.split( '.' ) ]
+    return ipNum( *args )
+
+def netParse( ipstr ):
+    """Parse an IP network specification, returning
+       address and prefix len as unsigned ints"""
+    prefixLen = 0
+    if '/' in ipstr:
+        ip, pf = ipstr.split( '/' )
+        prefixLen = int( pf )
+    return ipParse( ip ), prefixLen
+
+def checkInt( s ):
+    "Check if input string is an int"
+    try:
+        int( s )
+        return True
+    except ValueError:
+        return False
+
+def checkFloat( s ):
+    "Check if input string is a float"
+    try:
+        float( s )
+        return True
+    except ValueError:
+        return False
+
+def makeNumeric( s ):
+    "Convert string to int or float if numeric."
+    if checkInt( s ):
+        return int( s )
+    elif checkFloat( s ):
+        return float( s )
+    else:
+        return s
+
+# Popen support
+
+def pmonitor(popens, timeoutms=500, readline=True,
+             readmax=1024 ):
+    """Monitor dict of hosts to popen objects
+       a line at a time
+       timeoutms: timeout for poll()
+       readline: return single line of output
+       yields: host, line/output (if any)
+       terminates: when all EOFs received"""
+    poller = poll()
+    fdToHost = {}
+    for host, popen in popens.iteritems():
+        fd = popen.stdout.fileno()
+        fdToHost[ fd ] = host
+        poller.register( fd, POLLIN )
+        if not readline:
+            # Use non-blocking reads
+            flags = fcntl( fd, F_GETFL )
+            fcntl( fd, F_SETFL, flags | O_NONBLOCK )
+    while True:
+        fds = poller.poll( timeoutms )
+        if fds:
+            for fd, _event in fds:
+                host = fdToHost[ fd ]
+                popen = popens[ host ]
+                if readline:
+                    # Attempt to read a line of output
+                    # This blocks until we receive a newline!
+                    line = popen.stdout.readline()
+                else:
+                    line = popen.stdout.read( readmax )
+                yield host, line
+                # Check for EOF
+                if not line:
+                    popen.poll()
+                    if popen.returncode is not None:
+                        poller.unregister( fd )
+                        del popens[ host ]
+                        if not popens:
+                            return
+        else:
+            yield None, ''
+
+# Other stuff we use
+
+def fixLimits():
+    "Fix ridiculously small resource limits."
+    setrlimit( RLIMIT_NPROC, ( 8192, 8192 ) )
+    setrlimit( RLIMIT_NOFILE, ( 16384, 16384 ) )
+
+def mountCgroups():
+    "Make sure cgroups file system is mounted"
+    mounts = quietRun( 'mount' )
+    cgdir = '/sys/fs/cgroup'
+    csdir = cgdir + '/cpuset'
+    if ('cgroup on %s' % cgdir not in mounts and
+            'cgroups on %s' % cgdir not in mounts):
+        raise Exception( "cgroups not mounted on " + cgdir )
+    if 'cpuset on %s' % csdir not in mounts:
+        errRun( 'mkdir -p ' + csdir )
+        errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
+
+def natural( text ):
+    "To sort sanely/alphabetically: sorted( l, key=natural )"
+    def num( s ):
+        "Convert text segment to int if necessary"
+        return int( s ) if s.isdigit() else s
+    return [  num( s ) for s in re.split( r'(\d+)', text ) ]
+
+def naturalSeq( t ):
+    "Natural sort key function for sequences"
+    return [ natural( x ) for x in t ]
+
+def numCores():
+    "Returns number of CPU cores based on /proc/cpuinfo"
+    if hasattr( numCores, 'ncores' ):
+        return numCores.ncores
+    try:
+        numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
+    except ValueError:
+        return 0
+    return numCores.ncores
+
+def irange(start, end):
+    """Inclusive range from start to end (vs. Python insanity.)
+       irange(1,5) -> 1, 2, 3, 4, 5"""
+    return range( start, end + 1 )
+
+def custom( cls, **params ):
+    "Returns customized constructor for class cls."
+    # Note: we may wish to see if we can use functools.partial() here
+    # and in customConstructor
+    def customized( *args, **kwargs):
+        "Customized constructor"
+        kwargs = kwargs.copy()
+        kwargs.update( params )
+        return cls( *args, **kwargs )
+    customized.__name__ = 'custom(%s,%s)' % ( cls, params )
+    return customized
+
+def splitArgs( argstr ):
+    """Split argument string into usable python arguments
+       argstr: argument string with format fn,arg2,kw1=arg3...
+       returns: fn, args, kwargs"""
+    split = argstr.split( ',' )
+    fn = split[ 0 ]
+    params = split[ 1: ]
+    # Convert int and float args; removes the need for function
+    # to be flexible with input arg formats.
+    args = [ makeNumeric( s ) for s in params if '=' not in s ]
+    kwargs = {}
+    for s in [ p for p in params if '=' in p ]:
+        key, val = s.split( '=' )
+        kwargs[ key ] = makeNumeric( val )
+    return fn, args, kwargs
+
+def customConstructor( constructors, argStr ):
+    """Return custom constructor based on argStr
+    The args and key/val pairs in argsStr will be automatically applied
+    when the generated constructor is later used.
+    """
+    cname, newargs, kwargs = splitArgs( argStr )
+    constructor = constructors.get( cname, None )
+
+    if not constructor:
+        raise Exception( "error: %s is unknown - please specify one of %s" %
+                         ( cname, constructors.keys() ) )
+
+    def customized( name, *args, **params ):
+        "Customized constructor, useful for Node, Link, and other classes"
+        params = params.copy()
+        params.update( kwargs )
+        if not newargs:
+            return constructor( name, *args, **params )
+        if args:
+            warn( 'warning: %s replacing %s with %s\n' % (
+                  constructor, args, newargs ) )
+        return constructor( name, *newargs, **params )
+
+    customized.__name__ = 'customConstructor(%s)' % argStr
+    return customized
+
+def buildTopo( topos, topoStr ):
+    """Create topology from string with format (object, arg1, arg2,...).
+    input topos is a dict of topo names to constructors, possibly w/args.
+    """
+    topo, args, kwargs = splitArgs( topoStr )
+    if topo not in topos:
+        raise Exception( 'Invalid topo name %s' % topo )
+    return topos[ topo ]( *args, **kwargs )
+
+def ensureRoot():
+    """Ensure that we are running as root.
+
+    Probably we should only sudo when needed as per Big Switch's patch.
+    """
+    if os.getuid() != 0:
+        print "*** Mininet must run as root."
+        exit( 1 )
+    return