First commit
diff --git a/examples/README b/examples/README
new file mode 100644
index 0000000..dd71bea
--- /dev/null
+++ b/examples/README
@@ -0,0 +1,102 @@
+
+Mininet Examples
+
+These examples are intended to help you get started using
+Mininet's Python API.
+
+---
+
+baresshd.py:
+
+This example uses Mininet's medium-level API to create an sshd
+process running in a namespace. Doesn't use OpenFlow.
+
+consoles.py:
+
+This example creates a grid of console windows, one for each node, 
+and allows interaction with and monitoring of each console, including
+graphical monitoring.
+
+controllers.py:
+
+This example creates a network and adds multiple controllers to it.
+
+cpu.py:
+
+This example tests iperf bandwidth for varying CPU limits.
+
+emptynet.py:
+
+This example demonstrates creating an empty network (i.e. with no
+topology object) and adding nodes to it.
+
+hwintf.py:
+
+This example shows how to add an interface (for example a real
+hardware interface) to a network after the network is created.
+
+limit.py:
+
+This example shows how to use link and CPU limits.
+
+linearbandwidth.py:
+
+This example shows how to create a custom topology programatically
+by subclassing Topo, and how to run a series of tests on it.
+
+miniedit.py:
+
+This example demonstrates creating a network via a graphical editor.
+
+multiping.py:
+
+This example demonstrates one method for 
+monitoring output from multiple hosts, using node.monitor().
+
+multipoll.py:
+
+This example demonstrates monitoring output files from multiple hosts.
+
+multitest.py:
+
+This example creates a network and runs multiple tests on it.
+
+popen.py:
+
+This example monitors a number of hosts using host.popen() and
+pmonitor().
+
+popenpoll.py:
+
+This example demonstrates monitoring output from multiple hosts using
+the node.popen() interface (which returns Popen objects) and pmonitor().
+
+scratchnet.py, scratchnetuser.py:
+
+These two examples demonstrate how to create a network by using the lowest-
+level Mininet functions. Generally the higher-level API is easier to use,
+but scratchnet shows what is going on behind the scenes.
+
+simpleperf.py:
+
+A simple example of configuring network and CPU bandwidth limits.
+
+sshd.py:
+
+This example shows how to run an sshd process in each host, allowing
+you to log in via ssh. This requires connecting the Mininet data network
+to an interface in the root namespace (generaly the control network
+already lives in the root namespace, so it does not need to be explicitly
+connected.)
+
+treeping64.py:
+
+This example creates a 64-host tree network, and attempts to check full
+connectivity using ping, for different switch/datapath types.
+
+tree1024.py:
+
+This example attempts to create a 1024-host network, and then runs the
+CLI on it. It may run into scalability limits, depending on available
+memory and sysctl configuration (see INSTALL.)
+
diff --git a/examples/baresshd.py b/examples/baresshd.py
new file mode 100644
index 0000000..a714edb
--- /dev/null
+++ b/examples/baresshd.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
+
+from mininet.node import Host
+from mininet.util import ensureRoot
+
+ensureRoot()
+
+print "*** Creating nodes"
+h1 = Host( 'h1' )
+
+root = Host( 'root', inNamespace=False )
+
+print "*** Creating links"
+h1.linkTo( root )
+
+print h1
+
+print "*** Configuring nodes"
+h1.setIP( '10.0.0.1', 8 )
+root.setIP( '10.0.0.2', 8 )
+
+print "*** Creating banner file"
+f = open( '/tmp/%s.banner' % h1.name, 'w' )
+f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
+f.close()
+
+print "*** Running sshd"
+h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
+
+print "*** You may now ssh into", h1.name, "at", h1.IP()
diff --git a/examples/consoles.py b/examples/consoles.py
new file mode 100644
index 0000000..ea2e28d
--- /dev/null
+++ b/examples/consoles.py
@@ -0,0 +1,456 @@
+#!/usr/bin/python
+
+"""
+consoles.py: bring up a bunch of miniature consoles on a virtual network
+
+This demo shows how to monitor a set of nodes by using
+Node's monitor() and Tkinter's createfilehandler().
+
+We monitor nodes in a couple of ways:
+
+- First, each individual node is monitored, and its output is added
+  to its console window
+
+- Second, each time a console window gets iperf output, it is parsed
+  and accumulated. Once we have output for all consoles, a bar is
+  added to the bandwidth graph.
+
+The consoles also support limited interaction:
+
+- Pressing "return" in a console will send a command to it
+
+- Pressing the console's title button will open up an xterm
+
+Bob Lantz, April 2010
+
+"""
+
+import re
+
+from Tkinter import Frame, Button, Label, Text, Scrollbar, Canvas, Wm, READABLE
+
+from mininet.log import setLogLevel
+from mininet.topolib import TreeNet
+from mininet.term import makeTerms, cleanUpScreens
+from mininet.util import quietRun
+
+class Console( Frame ):
+    "A simple console on a host."
+
+    def __init__( self, parent, net, node, height=10, width=32, title='Node' ):
+        Frame.__init__( self, parent )
+
+        self.net = net
+        self.node = node
+        self.prompt = node.name + '# '
+        self.height, self.width, self.title = height, width, title
+
+        # Initialize widget styles
+        self.buttonStyle = { 'font': 'Monaco 7' }
+        self.textStyle = {
+            'font': 'Monaco 7',
+            'bg': 'black',
+            'fg': 'green',
+            'width': self.width,
+            'height': self.height,
+            'relief': 'sunken',
+            'insertbackground': 'green',
+            'highlightcolor': 'green',
+            'selectforeground': 'black',
+            'selectbackground': 'green'
+        }
+
+        # Set up widgets
+        self.text = self.makeWidgets( )
+        self.bindEvents()
+        self.sendCmd( 'export TERM=dumb' )
+
+        self.outputHook = None
+
+    def makeWidgets( self ):
+        "Make a label, a text area, and a scroll bar."
+
+        def newTerm( net=self.net, node=self.node, title=self.title ):
+            "Pop up a new terminal window for a node."
+            net.terms += makeTerms( [ node ], title )
+        label = Button( self, text=self.node.name, command=newTerm,
+                        **self.buttonStyle )
+        label.pack( side='top', fill='x' )
+        text = Text( self, wrap='word', **self.textStyle )
+        ybar = Scrollbar( self, orient='vertical', width=7,
+                          command=text.yview )
+        text.configure( yscrollcommand=ybar.set )
+        text.pack( side='left', expand=True, fill='both' )
+        ybar.pack( side='right', fill='y' )
+        return text
+
+    def bindEvents( self ):
+        "Bind keyboard and file events."
+        # The text widget handles regular key presses, but we
+        # use special handlers for the following:
+        self.text.bind( '<Return>', self.handleReturn )
+        self.text.bind( '<Control-c>', self.handleInt )
+        self.text.bind( '<KeyPress>', self.handleKey )
+        # This is not well-documented, but it is the correct
+        # way to trigger a file event handler from Tk's
+        # event loop!
+        self.tk.createfilehandler( self.node.stdout, READABLE,
+                                   self.handleReadable )
+
+    # We're not a terminal (yet?), so we ignore the following
+    # control characters other than [\b\n\r]
+    ignoreChars = re.compile( r'[\x00-\x07\x09\x0b\x0c\x0e-\x1f]+' )
+
+    def append( self, text ):
+        "Append something to our text frame."
+        text = self.ignoreChars.sub( '', text )
+        self.text.insert( 'end', text )
+        self.text.mark_set( 'insert', 'end' )
+        self.text.see( 'insert' )
+        outputHook = lambda x, y: True  # make pylint happier
+        if self.outputHook:
+            outputHook = self.outputHook
+        outputHook( self, text )
+
+    def handleKey( self, event ):
+        "If it's an interactive command, send it to the node."
+        char = event.char
+        if self.node.waiting:
+            self.node.write( char )
+
+    def handleReturn( self, event ):
+        "Handle a carriage return."
+        cmd = self.text.get( 'insert linestart', 'insert lineend' )
+        # Send it immediately, if "interactive" command
+        if self.node.waiting:
+            self.node.write( event.char )
+            return
+        # Otherwise send the whole line to the shell
+        pos = cmd.find( self.prompt )
+        if pos >= 0:
+            cmd = cmd[ pos + len( self.prompt ): ]
+        self.sendCmd( cmd )
+
+    # Callback ignores event
+    def handleInt( self, _event=None ):
+        "Handle control-c."
+        self.node.sendInt()
+
+    def sendCmd( self, cmd ):
+        "Send a command to our node."
+        if not self.node.waiting:
+            self.node.sendCmd( cmd )
+
+    def handleReadable( self, _fds, timeoutms=None ):
+        "Handle file readable event."
+        data = self.node.monitor( timeoutms )
+        self.append( data )
+        if not self.node.waiting:
+            # Print prompt
+            self.append( self.prompt )
+
+    def waiting( self ):
+        "Are we waiting for output?"
+        return self.node.waiting
+
+    def waitOutput( self ):
+        "Wait for any remaining output."
+        while self.node.waiting:
+            # A bit of a trade-off here...
+            self.handleReadable( self, timeoutms=1000)
+            self.update()
+
+    def clear( self ):
+        "Clear all of our text."
+        self.text.delete( '1.0', 'end' )
+
+
+class Graph( Frame ):
+
+    "Graph that we can add bars to over time."
+
+    def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
+                  barwidth=10, ymax=3.5,):
+
+        Frame.__init__( self, parent )
+
+        self.bg = bg
+        self.gheight = gheight
+        self.gwidth = gwidth
+        self.barwidth = barwidth
+        self.ymax = float( ymax )
+        self.xpos = 0
+
+        # Create everything
+        self.title, self.scale, self.graph = self.createWidgets()
+        self.updateScrollRegions()
+        self.yview( 'moveto', '1.0' )
+
+    def createScale( self ):
+        "Create a and return a new canvas with scale markers."
+        height = float( self.gheight )
+        width = 25
+        ymax = self.ymax
+        scale = Canvas( self, width=width, height=height,
+                        background=self.bg )
+        opts = { 'fill': 'red' }
+        # Draw scale line
+        scale.create_line( width - 1, height, width - 1, 0, **opts )
+        # Draw ticks and numbers
+        for y in range( 0, int( ymax + 1 ) ):
+            ypos = height * (1 - float( y ) / ymax )
+            scale.create_line( width, ypos, width - 10, ypos, **opts )
+            scale.create_text( 10, ypos, text=str( y ), **opts )
+        return scale
+
+    def updateScrollRegions( self ):
+        "Update graph and scale scroll regions."
+        ofs = 20
+        height = self.gheight + ofs
+        self.graph.configure( scrollregion=( 0, -ofs,
+                              self.xpos * self.barwidth, height ) )
+        self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
+
+    def yview( self, *args ):
+        "Scroll both scale and graph."
+        self.graph.yview( *args )
+        self.scale.yview( *args )
+
+    def createWidgets( self ):
+        "Create initial widget set."
+
+        # Objects
+        title = Label( self, text='Bandwidth (Gb/s)', bg=self.bg )
+        width = self.gwidth
+        height = self.gheight
+        scale = self.createScale()
+        graph = Canvas( self, width=width, height=height, background=self.bg)
+        xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
+        ybar = Scrollbar( self, orient='vertical', command=self.yview )
+        graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
+                         scrollregion=(0, 0, width, height ) )
+        scale.configure( yscrollcommand=ybar.set )
+
+        # Layout
+        title.grid( row=0, columnspan=3, sticky='new')
+        scale.grid( row=1, column=0, sticky='nsew' )
+        graph.grid( row=1, column=1, sticky='nsew' )
+        ybar.grid( row=1, column=2, sticky='ns' )
+        xbar.grid( row=2, column=0, columnspan=2, sticky='ew' )
+        self.rowconfigure( 1, weight=1 )
+        self.columnconfigure( 1, weight=1 )
+        return title, scale, graph
+
+    def addBar( self, yval ):
+        "Add a new bar to our graph."
+        percent = yval / self.ymax
+        c = self.graph
+        x0 = self.xpos * self.barwidth
+        x1 = x0 + self.barwidth
+        y0 = self.gheight
+        y1 = ( 1 - percent ) * self.gheight
+        c.create_rectangle( x0, y0, x1, y1, fill='green' )
+        self.xpos += 1
+        self.updateScrollRegions()
+        self.graph.xview( 'moveto', '1.0' )
+
+    def clear( self ):
+        "Clear graph contents."
+        self.graph.delete( 'all' )
+        self.xpos = 0
+
+    def test( self ):
+        "Add a bar for testing purposes."
+        ms = 1000
+        if self.xpos < 10:
+            self.addBar( self.xpos / 10 * self.ymax  )
+            self.after( ms, self.test )
+
+    def setTitle( self, text ):
+        "Set graph title"
+        self.title.configure( text=text, font='Helvetica 9 bold' )
+
+
+class ConsoleApp( Frame ):
+
+    "Simple Tk consoles for Mininet."
+
+    menuStyle = { 'font': 'Geneva 7 bold' }
+
+    def __init__( self, net, parent=None, width=4 ):
+        Frame.__init__( self, parent )
+        self.top = self.winfo_toplevel()
+        self.top.title( 'Mininet' )
+        self.net = net
+        self.menubar = self.createMenuBar()
+        cframe = self.cframe = Frame( self )
+        self.consoles = {}  # consoles themselves
+        titles = {
+            'hosts': 'Host',
+            'switches': 'Switch',
+            'controllers': 'Controller'
+        }
+        for name in titles:
+            nodes = getattr( net, name )
+            frame, consoles = self.createConsoles(
+                cframe, nodes, width, titles[ name ] )
+            self.consoles[ name ] = Object( frame=frame, consoles=consoles )
+        self.selected = None
+        self.select( 'hosts' )
+        self.cframe.pack( expand=True, fill='both' )
+        cleanUpScreens()
+        # Close window gracefully
+        Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
+
+        # Initialize graph
+        graph = Graph( cframe )
+        self.consoles[ 'graph' ] = Object( frame=graph, consoles=[ graph ] )
+        self.graph = graph
+        self.graphVisible = False
+        self.updates = 0
+        self.hostCount = len( self.consoles[ 'hosts' ].consoles )
+        self.bw = 0
+
+        self.pack( expand=True, fill='both' )
+
+    def updateGraph( self, _console, output ):
+        "Update our graph."
+        m = re.search( r'(\d+) Mbits/sec', output )
+        if not m:
+            return
+        self.updates += 1
+        self.bw += .001 * float( m.group( 1 ) )
+        if self.updates >= self.hostCount:
+            self.graph.addBar( self.bw )
+            self.bw = 0
+            self.updates = 0
+
+    def setOutputHook( self, fn=None, consoles=None ):
+        "Register fn as output hook [on specific consoles.]"
+        if consoles is None:
+            consoles = self.consoles[ 'hosts' ].consoles
+        for console in consoles:
+            console.outputHook = fn
+
+    def createConsoles( self, parent, nodes, width, title ):
+        "Create a grid of consoles in a frame."
+        f = Frame( parent )
+        # Create consoles
+        consoles = []
+        index = 0
+        for node in nodes:
+            console = Console( f, self.net, node, title=title )
+            consoles.append( console )
+            row = index / width
+            column = index % width
+            console.grid( row=row, column=column, sticky='nsew' )
+            index += 1
+            f.rowconfigure( row, weight=1 )
+            f.columnconfigure( column, weight=1 )
+        return f, consoles
+
+    def select( self, groupName ):
+        "Select a group of consoles to display."
+        if self.selected is not None:
+            self.selected.frame.pack_forget()
+        self.selected = self.consoles[ groupName ]
+        self.selected.frame.pack( expand=True, fill='both' )
+
+    def createMenuBar( self ):
+        "Create and return a menu (really button) bar."
+        f = Frame( self )
+        buttons = [
+            ( 'Hosts', lambda: self.select( 'hosts' ) ),
+            ( 'Switches', lambda: self.select( 'switches' ) ),
+            ( 'Controllers', lambda: self.select( 'controllers' ) ),
+            ( 'Graph', lambda: self.select( 'graph' ) ),
+            ( 'Ping', self.ping ),
+            ( 'Iperf', self.iperf ),
+            ( 'Interrupt', self.stop ),
+            ( 'Clear', self.clear ),
+            ( 'Quit', self.quit )
+        ]
+        for name, cmd in buttons:
+            b = Button( f, text=name, command=cmd, **self.menuStyle )
+            b.pack( side='left' )
+        f.pack( padx=4, pady=4, fill='x' )
+        return f
+
+    def clear( self ):
+        "Clear selection."
+        for console in self.selected.consoles:
+            console.clear()
+
+    def waiting( self, consoles=None ):
+        "Are any of our hosts waiting for output?"
+        if consoles is None:
+            consoles = self.consoles[ 'hosts' ].consoles
+        for console in consoles:
+            if console.waiting():
+                return True
+        return False
+
+    def ping( self ):
+        "Tell each host to ping the next one."
+        consoles = self.consoles[ 'hosts' ].consoles
+        if self.waiting( consoles ):
+            return
+        count = len( consoles )
+        i = 0
+        for console in consoles:
+            i = ( i + 1 ) % count
+            ip = consoles[ i ].node.IP()
+            console.sendCmd( 'ping ' + ip )
+
+    def iperf( self ):
+        "Tell each host to iperf to the next one."
+        consoles = self.consoles[ 'hosts' ].consoles
+        if self.waiting( consoles ):
+            return
+        count = len( consoles )
+        self.setOutputHook( self.updateGraph )
+        for console in consoles:
+            console.node.cmd( 'iperf -sD' )
+        i = 0
+        for console in consoles:
+            i = ( i + 1 ) % count
+            ip = consoles[ i ].node.IP()
+            console.sendCmd( 'iperf -t 99999 -i 1 -c ' + ip )
+
+    def stop( self, wait=True ):
+        "Interrupt all hosts."
+        consoles = self.consoles[ 'hosts' ].consoles
+        for console in consoles:
+            console.handleInt()
+        if wait:
+            for console in consoles:
+                console.waitOutput()
+        self.setOutputHook( None )
+        # Shut down any iperfs that might still be running
+        quietRun( 'killall -9 iperf' )
+
+    def quit( self ):
+        "Stop everything and quit."
+        self.stop( wait=False)
+        Frame.quit( self )
+
+
+# Make it easier to construct and assign objects
+
+def assign( obj, **kwargs ):
+    "Set a bunch of fields in an object."
+    obj.__dict__.update( kwargs )
+
+class Object( object ):
+    "Generic object you can stuff junk into."
+    def __init__( self, **kwargs ):
+        assign( self, **kwargs )
+
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    network = TreeNet( depth=2, fanout=4 )
+    network.start()
+    app = ConsoleApp( network, width=4 )
+    app.mainloop()
+    network.stop()
diff --git a/examples/controllers.py b/examples/controllers.py
new file mode 100644
index 0000000..6eeef0e
--- /dev/null
+++ b/examples/controllers.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+"""
+This example creates a multi-controller network from
+semi-scratch; note a topo object could also be used and
+would be passed into the Mininet() constructor.
+"""
+
+from mininet.net import Mininet
+from mininet.node import Controller, OVSKernelSwitch
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+
+Switch = OVSKernelSwitch
+
+def addHost( net, N ):
+    "Create host hN and add to net."
+    name = 'h%d' % N
+    ip = '10.0.0.%d' % N
+    return net.addHost( name, ip=ip )
+
+def multiControllerNet():
+    "Create a network with multiple controllers."
+
+    net = Mininet( controller=Controller, switch=Switch)
+
+    print "*** Creating controllers"
+    c1 = net.addController( 'c1', port=6633 )
+    c2 = net.addController( 'c2', port=6634 )
+
+    print "*** Creating switches"
+    s1 = net.addSwitch( 's1' )
+    s2 = net.addSwitch( 's2' )
+
+    print "*** Creating hosts"
+    hosts1 = [ addHost( net, n ) for n in 3, 4 ]
+    hosts2 = [ addHost( net, n ) for n in 5, 6 ]
+
+    print "*** Creating links"
+    for h in hosts1:
+        s1.linkTo( h )
+    for h in hosts2:
+        s2.linkTo( h )
+    s1.linkTo( s2 )
+
+    print "*** Starting network"
+    net.build()
+    c1.start()
+    c2.start()
+    s1.start( [ c1 ] )
+    s2.start( [ c2 ] )
+
+    print "*** Testing network"
+    net.pingAll()
+
+    print "*** Running CLI"
+    CLI( net )
+
+    print "*** Stopping network"
+    net.stop()
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )  # for CLI output
+    multiControllerNet()
diff --git a/examples/cpu.py b/examples/cpu.py
new file mode 100644
index 0000000..6dfc936
--- /dev/null
+++ b/examples/cpu.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+"""
+cpu.py: test iperf bandwidth for varying cpu limits
+"""
+
+from mininet.net import Mininet
+from mininet.node import CPULimitedHost
+from mininet.topolib import TreeTopo
+from mininet.util import custom
+from mininet.log import setLogLevel, output
+
+from time import sleep
+
+def waitListening(client, server, port):
+    "Wait until server is listening on port"
+    if not client.cmd('which telnet'):
+        raise Exception('Could not find telnet')
+    cmd = ('sh -c "echo A | telnet -e A %s %s"' %
+           (server.IP(), port))
+    while 'Connected' not in client.cmd(cmd):
+        output('waiting for', server,
+               'to listen on port', port, '\n')
+        sleep(.5)
+
+
+def bwtest( cpuLimits, period_us=100000, seconds=5 ):
+    """Example/test of link and CPU bandwidth limits
+       cpu: cpu limit as fraction of overall CPU time"""
+
+    topo = TreeTopo( depth=1, fanout=2 )
+
+    results = {}
+
+    for sched in 'rt', 'cfs':
+        print '*** Testing with', sched, 'bandwidth limiting'
+        for cpu in cpuLimits:
+            host = custom( CPULimitedHost, sched=sched,
+                           period_us=period_us,
+                           cpu=cpu )
+            net = Mininet( topo=topo, host=host )
+            net.start()
+            net.pingAll()
+            hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
+            client, server = hosts[ 0 ], hosts[ -1 ]
+            server.cmd( 'iperf -s -p 5001 &' )
+            waitListening( client, server, 5001 )
+            result = client.cmd( 'iperf -yc -t %s -c %s' % (
+                seconds, server.IP() ) ).split( ',' )
+            bps = float( result[ -1 ] )
+            server.cmdPrint( 'kill %iperf' )
+            net.stop()
+            updated = results.get( sched, [] )
+            updated += [ ( cpu, bps ) ]
+            results[ sched ] = updated
+
+    return results
+
+
+def dump( results ):
+    "Dump results"
+
+    fmt = '%s\t%s\t%s'
+
+    print
+    print fmt % ( 'sched', 'cpu', 'client MB/s' )
+    print
+
+    for sched in sorted( results.keys() ):
+        entries = results[ sched ]
+        for cpu, bps in entries:
+            pct = '%.2f%%' % ( cpu * 100 )
+            mbps = bps / 1e6
+            print fmt % ( sched, pct, mbps )
+
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    limits = [ .45, .4, .3, .2, .1 ]
+    out = bwtest( limits )
+    dump( out )
diff --git a/examples/emptynet.py b/examples/emptynet.py
new file mode 100644
index 0000000..9f57855
--- /dev/null
+++ b/examples/emptynet.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+"""
+This example shows how to create an empty Mininet object
+(without a topology object) and add nodes to it manually.
+"""
+
+from mininet.net import Mininet
+from mininet.node import Controller
+from mininet.cli import CLI
+from mininet.log import setLogLevel, info
+
+def emptyNet():
+
+    "Create an empty network and add nodes to it."
+
+    net = Mininet( controller=Controller )
+
+    info( '*** Adding controller\n' )
+    net.addController( 'c0' )
+
+    info( '*** Adding hosts\n' )
+    h1 = net.addHost( 'h1', ip='10.0.0.1' )
+    h2 = net.addHost( 'h2', ip='10.0.0.2' )
+
+    info( '*** Adding switch\n' )
+    s3 = net.addSwitch( 's3' )
+
+    info( '*** Creating links\n' )
+    h1.linkTo( s3 )
+    h2.linkTo( s3 )
+
+    info( '*** Starting network\n')
+    net.start()
+
+    info( '*** Running CLI\n' )
+    CLI( net )
+
+    info( '*** Stopping network' )
+    net.stop()
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    emptyNet()
diff --git a/examples/hwintf.py b/examples/hwintf.py
new file mode 100644
index 0000000..e5960d9
--- /dev/null
+++ b/examples/hwintf.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+"""
+This example shows how to add an interface (for example a real
+hardware interface) to a network after the network is created.
+"""
+
+import re
+
+from mininet.cli import CLI
+from mininet.log import setLogLevel, info, error
+from mininet.net import Mininet
+from mininet.link import Intf
+from mininet.topolib import TreeTopo
+from mininet.util import quietRun
+
+def checkIntf( intf ):
+    "Make sure intf exists and is not configured."
+    if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
+        error( 'Error:', intf, 'does not exist!\n' )
+        exit( 1 )
+    ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
+    if ips:
+        error( 'Error:', intf, 'has an IP address,'
+               'and is probably in use!\n' )
+        exit( 1 )
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+
+    intfName = 'eth1'
+    info( '*** Checking', intfName, '\n' )
+    checkIntf( intfName )
+
+    info( '*** Creating network\n' )
+    net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
+
+    switch = net.switches[ 0 ]
+    info( '*** Adding hardware interface', intfName, 'to switch',
+          switch.name, '\n' )
+    _intf = Intf( intfName, node=switch )
+
+    info( '*** Note: you may need to reconfigure the interfaces for '
+          'the Mininet hosts:\n', net.hosts, '\n' )
+
+    net.start()
+    CLI( net )
+    net.stop()
diff --git a/examples/limit.py b/examples/limit.py
new file mode 100644
index 0000000..0b23ca1
--- /dev/null
+++ b/examples/limit.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+"""
+limit.py: example of using link and CPU limits
+"""
+
+from mininet.net import Mininet
+from mininet.link import TCIntf
+from mininet.node import CPULimitedHost
+from mininet.topolib import TreeTopo
+from mininet.util import custom
+from mininet.log import setLogLevel
+
+
+def testLinkLimit( net, bw ):
+    "Run bandwidth limit test"
+    print '*** Testing network %.2f Mbps bandwidth limit' % bw
+    net.iperf( )
+
+
+def limit( bw=10, cpu=.1 ):
+    """Example/test of link and CPU bandwidth limits
+       bw: interface bandwidth limit in Mbps
+       cpu: cpu limit as fraction of overall CPU time"""
+    intf = custom( TCIntf, bw=bw )
+    myTopo = TreeTopo( depth=1, fanout=2 )
+    for sched in 'rt', 'cfs':
+        print '*** Testing with', sched, 'bandwidth limiting'
+        host = custom( CPULimitedHost, sched=sched, cpu=cpu )
+        net = Mininet( topo=myTopo, intf=intf, host=host )
+        net.start()
+        testLinkLimit( net, bw=bw )
+        net.runCpuLimitTest( cpu=cpu )
+        net.stop()
+
+def verySimpleLimit( bw=150 ):
+    "Absurdly simple limiting test"
+    intf = custom( TCIntf, bw=bw )
+    net = Mininet( intf=intf )
+    h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' )
+    net.addLink( h1, h2 )
+    net.start()
+    net.pingAll()
+    net.iperf()
+    h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() )
+    h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
+    h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() )
+    h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
+    net.stop()
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    limit()
diff --git a/examples/linearbandwidth.py b/examples/linearbandwidth.py
new file mode 100644
index 0000000..3fd06c7
--- /dev/null
+++ b/examples/linearbandwidth.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+
+"""
+Test bandwidth (using iperf) on linear networks of varying size,
+using both kernel and user datapaths.
+
+We construct a network of N hosts and N-1 switches, connected as follows:
+
+h1 <-> s1 <-> s2 .. sN-1
+       |       |    |
+       h2      h3   hN
+
+WARNING: by default, the reference controller only supports 16
+switches, so this test WILL NOT WORK unless you have recompiled
+your controller to support 100 switches (or more.)
+
+In addition to testing the bandwidth across varying numbers
+of switches, this example demonstrates:
+
+- creating a custom topology, LinearTestTopo
+- using the ping() and iperf() tests from Mininet()
+- testing both the kernel and user switches
+
+"""
+
+from mininet.net import Mininet
+from mininet.node import UserSwitch, OVSKernelSwitch
+from mininet.topo import Topo
+from mininet.log import lg
+from mininet.util import irange
+
+import sys
+flush = sys.stdout.flush
+
+class LinearTestTopo( Topo ):
+    "Topology for a string of N hosts and N-1 switches."
+
+    def __init__( self, N, **params ):
+
+        # Initialize topology
+        Topo.__init__( self, **params )
+
+        # Create switches and hosts
+        hosts = [ self.addHost( 'h%s' % h )
+                  for h in irange( 1, N ) ]
+        switches = [ self.addSwitch( 's%s' % s )
+                     for s in irange( 1, N - 1 ) ]
+
+        # Wire up switches
+        last = None
+        for switch in switches:
+            if last:
+                self.addLink( last, switch )
+            last = switch
+
+        # Wire up hosts
+        self.addLink( hosts[ 0 ], switches[ 0 ] )
+        for host, switch in zip( hosts[ 1: ], switches ):
+            self.addLink( host, switch )
+
+
+def linearBandwidthTest( lengths ):
+
+    "Check bandwidth at various lengths along a switch chain."
+
+    results = {}
+    switchCount = max( lengths )
+    hostCount = switchCount + 1
+
+    switches = { 'reference user': UserSwitch,
+                 'Open vSwitch kernel': OVSKernelSwitch }
+
+    topo = LinearTestTopo( hostCount )
+
+    for datapath in switches.keys():
+        print "*** testing", datapath, "datapath"
+        Switch = switches[ datapath ]
+        results[ datapath ] = []
+        net = Mininet( topo=topo, switch=Switch )
+        net.start()
+        print "*** testing basic connectivity"
+        for n in lengths:
+            net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
+        print "*** testing bandwidth"
+        for n in lengths:
+            src, dst = net.hosts[ 0 ], net.hosts[ n ]
+            print "testing", src.name, "<->", dst.name,
+            bandwidth = net.iperf( [ src, dst ] )
+            print bandwidth
+            flush()
+            results[ datapath ] += [ ( n, bandwidth ) ]
+        net.stop()
+
+    for datapath in switches.keys():
+        print
+        print "*** Linear network results for", datapath, "datapath:"
+        print
+        result = results[ datapath ]
+        print "SwitchCount\tiperf Results"
+        for switchCount, bandwidth in result:
+            print switchCount, '\t\t',
+            print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
+        print
+    print
+
+if __name__ == '__main__':
+    lg.setLogLevel( 'info' )
+    sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
+    print "*** Running linearBandwidthTest", sizes
+    linearBandwidthTest( sizes  )
diff --git a/examples/miniedit.py b/examples/miniedit.py
new file mode 100644
index 0000000..c17cf3d
--- /dev/null
+++ b/examples/miniedit.py
@@ -0,0 +1,771 @@
+#!/usr/bin/python
+
+"""
+MiniEdit: a simple network editor for Mininet
+
+This is a simple demonstration of how one might build a
+GUI application using Mininet as the network model.
+
+Development version - not entirely functional!
+
+Bob Lantz, April 2010
+"""
+import optparse
+
+from Tkinter import Frame, Button, Label, Scrollbar, Canvas
+from Tkinter import Menu, BitmapImage, PhotoImage, Wm, Toplevel
+
+# someday: from ttk import *
+
+from mininet.log import setLogLevel
+from mininet.net import Mininet
+from mininet.util import ipStr
+from mininet.term import makeTerm, cleanUpScreens
+
+
+def parse_args():
+    usage="""Usage: miniccnxedit [template_file]
+	If no template_file is given, generated template will be written
+	to the file topo.miniccnx in the current directory.
+    """
+
+    parser = optparse.OptionParser(usage)
+
+    #parser.add_option("-t", "--template", help="template file name")
+    _, arg = parser.parse_args()
+
+    return arg
+
+class MiniEdit( Frame ):
+
+    "A simple network editor for Mininet."
+
+    def __init__( self, parent=None, cheight=200, cwidth=500, template_file='topo.miniccnx' ):
+
+        Frame.__init__( self, parent )
+        self.action = None
+        self.appName = 'Mini-CCNx'
+	self.template_file = template_file
+
+        # Style
+        self.font = ( 'Geneva', 9 )
+        self.smallFont = ( 'Geneva', 7 )
+        self.bg = 'white'
+
+        # Title
+        self.top = self.winfo_toplevel()
+        self.top.title( self.appName )
+
+        # Menu bar
+        self.createMenubar()
+
+        # Editing canvas
+        self.cheight, self.cwidth = cheight, cwidth
+        self.cframe, self.canvas = self.createCanvas()
+
+        # Toolbar
+        self.images = miniEditImages()
+        self.buttons = {}
+        self.active = None
+        self.tools = ( 'Select', 'Host', 'Switch', 'Link' )
+        self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' }
+        self.toolbar = self.createToolbar()
+
+        # Layout
+        self.toolbar.grid( column=0, row=0, sticky='nsew')
+        self.cframe.grid( column=1, row=0 )
+        self.columnconfigure( 1, weight=1 )
+        self.rowconfigure( 0, weight=1 )
+        self.pack( expand=True, fill='both' )
+
+        # About box
+        self.aboutBox = None
+
+        # Initialize node data
+        self.nodeBindings = self.createNodeBindings()
+        self.nodePrefixes = { 'Switch': 's', 'Host': 'h' }
+        self.widgetToItem = {}
+        self.itemToWidget = {}
+
+        # Initialize link tool
+        self.link = self.linkWidget = None
+
+        # Selection support
+        self.selection = None
+
+        # Keyboard bindings
+        self.bind( '<Control-q>', lambda event: self.quit() )
+        self.bind( '<KeyPress-Delete>', self.deleteSelection )
+        self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
+        self.focus()
+
+        # Event handling initalization
+        self.linkx = self.linky = self.linkItem = None
+        self.lastSelection = None
+
+        # Model initialization
+        self.links = {}
+        self.nodeCount = 0
+        self.net = None
+
+        # Close window gracefully
+        Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
+
+    def quit( self ):
+        "Stop our network, if any, then quit."
+        self.stop()
+        Frame.quit( self )
+
+    def createMenubar( self ):
+        "Create our menu bar."
+
+        font = self.font
+
+        mbar = Menu( self.top, font=font )
+        self.top.configure( menu=mbar )
+
+        # Application menu
+        appMenu = Menu( mbar, tearoff=False )
+        mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
+        appMenu.add_command( label='About Mini-CCNx', command=self.about,
+                             font=font)
+        appMenu.add_separator()
+        appMenu.add_command( label='Quit', command=self.quit, font=font )
+
+        #fileMenu = Menu( mbar, tearoff=False )
+        #mbar.add_cascade( label="File", font=font, menu=fileMenu )
+        #fileMenu.add_command( label="Load...", font=font )
+        #fileMenu.add_separator()
+        #fileMenu.add_command( label="Save", font=font )
+        #fileMenu.add_separator()
+        #fileMenu.add_command( label="Print", font=font )
+
+        editMenu = Menu( mbar, tearoff=False )
+        mbar.add_cascade( label="Edit", font=font, menu=editMenu )
+        editMenu.add_command( label="Cut", font=font,
+                              command=lambda: self.deleteSelection( None ) )
+
+       # runMenu = Menu( mbar, tearoff=False )
+       # mbar.add_cascade( label="Run", font=font, menu=runMenu )
+       # runMenu.add_command( label="Run", font=font, command=self.doRun )
+       # runMenu.add_command( label="Stop", font=font, command=self.doStop )
+       # runMenu.add_separator()
+       # runMenu.add_command( label='Xterm', font=font, command=self.xterm )
+
+    # Canvas
+
+    def createCanvas( self ):
+        "Create and return our scrolling canvas frame."
+        f = Frame( self )
+
+        canvas = Canvas( f, width=self.cwidth, height=self.cheight,
+                         bg=self.bg )
+
+        # Scroll bars
+        xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
+        ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
+        canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
+
+        # Resize box
+        resize = Label( f, bg='white' )
+
+        # Layout
+        canvas.grid( row=0, column=1, sticky='nsew')
+        ybar.grid( row=0, column=2, sticky='ns')
+        xbar.grid( row=1, column=1, sticky='ew' )
+        resize.grid( row=1, column=2, sticky='nsew' )
+
+        # Resize behavior
+        f.rowconfigure( 0, weight=1 )
+        f.columnconfigure( 1, weight=1 )
+        f.grid( row=0, column=0, sticky='nsew' )
+        f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
+
+        # Mouse bindings
+        canvas.bind( '<ButtonPress-1>', self.clickCanvas )
+        canvas.bind( '<B1-Motion>', self.dragCanvas )
+        canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
+
+        return f, canvas
+
+    def updateScrollRegion( self ):
+        "Update canvas scroll region to hold everything."
+        bbox = self.canvas.bbox( 'all' )
+        if bbox is not None:
+            self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
+                                   bbox[ 3 ] ) )
+
+    def canvasx( self, x_root ):
+        "Convert root x coordinate to canvas coordinate."
+        c = self.canvas
+        return c.canvasx( x_root ) - c.winfo_rootx()
+
+    def canvasy( self, y_root ):
+        "Convert root y coordinate to canvas coordinate."
+        c = self.canvas
+        return c.canvasy( y_root ) - c.winfo_rooty()
+
+    # Toolbar
+
+    def activate( self, toolName ):
+        "Activate a tool and press its button."
+        # Adjust button appearance
+        if self.active:
+            self.buttons[ self.active ].configure( relief='raised' )
+        self.buttons[ toolName ].configure( relief='sunken' )
+        # Activate dynamic bindings
+        self.active = toolName
+
+    def createToolbar( self ):
+        "Create and return our toolbar frame."
+
+        toolbar = Frame( self )
+
+        # Tools
+        for tool in self.tools:
+            cmd = ( lambda t=tool: self.activate( t ) )
+            b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
+            if tool in self.images:
+                b.config( height=50, image=self.images[ tool ] )
+                # b.config( compound='top' )
+            b.pack( fill='x' )
+            self.buttons[ tool ] = b
+        self.activate( self.tools[ 0 ] )
+
+        # Spacer
+        Label( toolbar, text='' ).pack()
+
+        # Commands
+        #for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
+        #    doCmd = getattr( self, 'do' + cmd )
+        #    b = Button( toolbar, text=cmd, font=self.smallFont,
+        #                fg=color, command=doCmd )
+        #    b.pack( fill='x', side='bottom' )
+
+	for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
+            doCmd = getattr( self, 'do' + cmd )
+            b = Button( toolbar, text=cmd, font=self.smallFont,
+                        fg=color, command=doCmd )
+            b.pack( fill='x', side='bottom' )
+
+
+        return toolbar
+
+    def doGenerate( self ):
+        "Generate template."
+        self.activate( 'Select' )
+        for tool in self.tools:
+            self.buttons[ tool ].config( state='disabled' )
+
+        self.buildTemplate()
+
+	for tool in self.tools:
+            self.buttons[ tool ].config( state='normal' )
+
+    def doStop( self ):
+        "Stop command."
+        self.stop()
+        for tool in self.tools:
+            self.buttons[ tool ].config( state='normal' )
+
+    def buildTemplate( self ):
+	"Generate template"
+	
+	template = open(self.template_file, 'w')
+
+	# hosts
+	template.write('[hosts]\n')
+	for widget in self.widgetToItem:
+            name = widget[ 'text' ]
+            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+	    if 'Host' in tags:
+		template.write(name + ':\n')	
+
+	# switches/routers
+	template.write('[routers]\n')
+	for widget in self.widgetToItem:
+            name = widget[ 'text' ]
+            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+	    if 'Switch' in tags:
+		template.write(name + ':\n')	
+
+        # Make links
+	template.write('[links]\n')
+        for link in self.links.values():
+             ( src, dst ) = link
+             srcName, dstName = src[ 'text' ], dst[ 'text' ]
+             template.write(srcName + ':' + dstName + '\n')
+
+	template.close()   
+
+
+    # Generic canvas handler
+    #
+    # We could have used bindtags, as in nodeIcon, but
+    # the dynamic approach used here
+    # may actually require less code. In any case, it's an
+    # interesting introspection-based alternative to bindtags.
+
+    def canvasHandle( self, eventName, event ):
+        "Generic canvas event handler"
+        if self.active is None:
+            return
+        toolName = self.active
+        handler = getattr( self, eventName + toolName, None )
+        if handler is not None:
+            handler( event )
+
+    def clickCanvas( self, event ):
+        "Canvas click handler."
+        self.canvasHandle( 'click', event )
+
+    def dragCanvas( self, event ):
+        "Canvas drag handler."
+        self.canvasHandle( 'drag', event )
+
+    def releaseCanvas( self, event ):
+        "Canvas mouse up handler."
+        self.canvasHandle( 'release', event )
+
+    # Currently the only items we can select directly are
+    # links. Nodes are handled by bindings in the node icon.
+
+    def findItem( self, x, y ):
+        "Find items at a location in our canvas."
+        items = self.canvas.find_overlapping( x, y, x, y )
+        if len( items ) == 0:
+            return None
+        else:
+            return items[ 0 ]
+
+    # Canvas bindings for Select, Host, Switch and Link tools
+
+    def clickSelect( self, event ):
+        "Select an item."
+        self.selectItem( self.findItem( event.x, event.y ) )
+
+    def deleteItem( self, item ):
+        "Delete an item."
+        # Don't delete while network is running
+        if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
+            return
+        # Delete from model
+        if item in self.links:
+            self.deleteLink( item )
+        if item in self.itemToWidget:
+            self.deleteNode( item )
+        # Delete from view
+        self.canvas.delete( item )
+
+    def deleteSelection( self, _event ):
+        "Delete the selected item."
+        if self.selection is not None:
+            self.deleteItem( self.selection )
+        self.selectItem( None )
+
+    def nodeIcon( self, node, name ):
+        "Create a new node icon."
+        icon = Button( self.canvas, image=self.images[ node ],
+                       text=name, compound='top' )
+        # Unfortunately bindtags wants a tuple
+        bindtags = [ str( self.nodeBindings ) ]
+        bindtags += list( icon.bindtags() )
+        icon.bindtags( tuple( bindtags ) )
+        return icon
+
+    def newNode( self, node, event ):
+        "Add a new node to our canvas."
+        c = self.canvas
+        x, y = c.canvasx( event.x ), c.canvasy( event.y )
+        self.nodeCount += 1
+        name = self.nodePrefixes[ node ] + str( self.nodeCount )
+        icon = self.nodeIcon( node, name )
+        item = self.canvas.create_window( x, y, anchor='c', window=icon,
+                                          tags=node )
+        self.widgetToItem[ icon ] = item
+        self.itemToWidget[ item ] = icon
+        self.selectItem( item )
+        icon.links = {}
+
+    def clickHost( self, event ):
+        "Add a new host to our canvas."
+        self.newNode( 'Host', event )
+
+    def clickSwitch( self, event ):
+        "Add a new switch to our canvas."
+        self.newNode( 'Switch', event )
+
+    def dragLink( self, event ):
+        "Drag a link's endpoint to another node."
+        if self.link is None:
+            return
+        # Since drag starts in widget, we use root coords
+        x = self.canvasx( event.x_root )
+        y = self.canvasy( event.y_root )
+        c = self.canvas
+        c.coords( self.link, self.linkx, self.linky, x, y )
+
+    def releaseLink( self, _event ):
+        "Give up on the current link."
+        if self.link is not None:
+            self.canvas.delete( self.link )
+        self.linkWidget = self.linkItem = self.link = None
+
+    # Generic node handlers
+
+    def createNodeBindings( self ):
+        "Create a set of bindings for nodes."
+        bindings = {
+            '<ButtonPress-1>': self.clickNode,
+            '<B1-Motion>': self.dragNode,
+            '<ButtonRelease-1>': self.releaseNode,
+            '<Enter>': self.enterNode,
+            '<Leave>': self.leaveNode,
+            '<Double-ButtonPress-1>': self.xterm
+        }
+        l = Label()  # lightweight-ish owner for bindings
+        for event, binding in bindings.items():
+            l.bind( event, binding )
+        return l
+
+    def selectItem( self, item ):
+        "Select an item and remember old selection."
+        self.lastSelection = self.selection
+        self.selection = item
+
+    def enterNode( self, event ):
+        "Select node on entry."
+        self.selectNode( event )
+
+    def leaveNode( self, _event ):
+        "Restore old selection on exit."
+        self.selectItem( self.lastSelection )
+
+    def clickNode( self, event ):
+        "Node click handler."
+        if self.active is 'Link':
+            self.startLink( event )
+        else:
+            self.selectNode( event )
+        return 'break'
+
+    def dragNode( self, event ):
+        "Node drag handler."
+        if self.active is 'Link':
+            self.dragLink( event )
+        else:
+            self.dragNodeAround( event )
+
+    def releaseNode( self, event ):
+        "Node release handler."
+        if self.active is 'Link':
+            self.finishLink( event )
+
+    # Specific node handlers
+
+    def selectNode( self, event ):
+        "Select the node that was clicked on."
+        item = self.widgetToItem.get( event.widget, None )
+        self.selectItem( item )
+
+    def dragNodeAround( self, event ):
+        "Drag a node around on the canvas."
+        c = self.canvas
+        # Convert global to local coordinates;
+        # Necessary since x, y are widget-relative
+        x = self.canvasx( event.x_root )
+        y = self.canvasy( event.y_root )
+        w = event.widget
+        # Adjust node position
+        item = self.widgetToItem[ w ]
+        c.coords( item, x, y )
+        # Adjust link positions
+        for dest in w.links:
+            link = w.links[ dest ]
+            item = self.widgetToItem[ dest ]
+            x1, y1 = c.coords( item )
+            c.coords( link, x, y, x1, y1 )
+
+    def startLink( self, event ):
+        "Start a new link."
+        if event.widget not in self.widgetToItem:
+            # Didn't click on a node
+            return
+        w = event.widget
+        item = self.widgetToItem[ w ]
+        x, y = self.canvas.coords( item )
+        self.link = self.canvas.create_line( x, y, x, y, width=4,
+                                             fill='blue', tag='link' )
+        self.linkx, self.linky = x, y
+        self.linkWidget = w
+        self.linkItem = item
+
+        # Link bindings
+        # Selection still needs a bit of work overall
+        # Callbacks ignore event
+
+        def select( _event, link=self.link ):
+            "Select item on mouse entry."
+            self.selectItem( link )
+
+        def highlight( _event, link=self.link ):
+            "Highlight item on mouse entry."
+            # self.selectItem( link )
+            self.canvas.itemconfig( link, fill='green' )
+
+        def unhighlight( _event, link=self.link ):
+            "Unhighlight item on mouse exit."
+            self.canvas.itemconfig( link, fill='blue' )
+            # self.selectItem( None )
+
+        self.canvas.tag_bind( self.link, '<Enter>', highlight )
+        self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
+        self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
+
+    def finishLink( self, event ):
+        "Finish creating a link"
+        if self.link is None:
+            return
+        source = self.linkWidget
+        c = self.canvas
+        # Since we dragged from the widget, use root coords
+        x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
+        target = self.findItem( x, y )
+        dest = self.itemToWidget.get( target, None )
+        if ( source is None or dest is None or source == dest
+                or dest in source.links or source in dest.links ):
+            self.releaseLink( event )
+            return
+        # For now, don't allow hosts to be directly linked
+#        stags = self.canvas.gettags( self.widgetToItem[ source ] )
+#        dtags = self.canvas.gettags( target )
+#        if 'Host' in stags and 'Host' in dtags:
+#            self.releaseLink( event )
+#            return
+        x, y = c.coords( target )
+        c.coords( self.link, self.linkx, self.linky, x, y )
+        self.addLink( source, dest )
+        # We're done
+        self.link = self.linkWidget = None
+
+    # Menu handlers
+
+    def about( self ):
+        "Display about box."
+        about = self.aboutBox
+        if about is None:
+            bg = 'white'
+            about = Toplevel( bg='white' )
+            about.title( 'About' )
+            info = self.appName + ': a simple network editor for Mini-CCNx - based on Miniedit '
+            warning = 'Development version - not entirely functional!'
+	    author = 'Carlos Cabral, Jan 2013'
+            author2 = 'Miniedit by Bob Lantz <rlantz@cs>, April 2010'
+            line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
+            line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
+            line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
+	    line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
+            line1.pack( padx=20, pady=10 )
+            line2.pack(pady=10 )
+            line3.pack(pady=10 )
+	    line4.pack(pady=10 )
+            hide = ( lambda about=about: about.withdraw() )
+            self.aboutBox = about
+            # Hide on close rather than destroying window
+            Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
+        # Show (existing) window
+        about.deiconify()
+
+    def createToolImages( self ):
+        "Create toolbar (and icon) images."
+
+    # Model interface
+    #
+    # Ultimately we will either want to use a topo or
+    # mininet object here, probably.
+
+    def addLink( self, source, dest ):
+        "Add link to model."
+        source.links[ dest ] = self.link
+        dest.links[ source ] = self.link
+        self.links[ self.link ] = ( source, dest )
+
+    def deleteLink( self, link ):
+        "Delete link from model."
+        pair = self.links.get( link, None )
+        if pair is not None:
+            source, dest = pair
+            del source.links[ dest ]
+            del dest.links[ source ]
+        if link is not None:
+            del self.links[ link ]
+
+    def deleteNode( self, item ):
+        "Delete node (and its links) from model."
+        widget = self.itemToWidget[ item ]
+        for link in widget.links.values():
+            # Delete from view and model
+            self.deleteItem( link )
+        del self.itemToWidget[ item ]
+        del self.widgetToItem[ widget ]
+
+    def build( self ):
+        "Build network based on our topology."
+
+        net = Mininet( topo=None )
+
+        # Make controller
+        net.addController( 'c0' )
+        # Make nodes
+        for widget in self.widgetToItem:
+            name = widget[ 'text' ]
+            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+            nodeNum = int( name[ 1: ] )
+            if 'Switch' in tags:
+                net.addSwitch( name )
+            elif 'Host' in tags:
+                net.addHost( name, ip=ipStr( nodeNum ) )
+            else:
+                raise Exception( "Cannot create mystery node: " + name )
+        # Make links
+        for link in self.links.values():
+            ( src, dst ) = link
+            srcName, dstName = src[ 'text' ], dst[ 'text' ]
+            src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
+            src.linkTo( dst )
+
+        # Build network (we have to do this separately at the moment )
+        net.build()
+
+        return net
+
+    def start( self ):
+        "Start network."
+        if self.net is None:
+            self.net = self.build()
+            self.net.start()
+
+    def stop( self ):
+        "Stop network."
+        if self.net is not None:
+            self.net.stop()
+        cleanUpScreens()
+        self.net = None
+
+    def xterm( self, _ignore=None ):
+        "Make an xterm when a button is pressed."
+        if ( self.selection is None or
+             self.net is None or
+             self.selection not in self.itemToWidget ):
+            return
+        name = self.itemToWidget[ self.selection ][ 'text' ]
+        if name not in self.net.nameToNode:
+            return
+        term = makeTerm( self.net.nameToNode[ name ], 'Host' )
+        self.net.terms.append( term )
+
+
+def miniEditImages():
+    "Create and return images for MiniEdit."
+
+    # Image data. Git will be unhappy. However, the alternative
+    # is to keep track of separate binary files, which is also
+    # unappealing.
+
+    return {
+        'Select': BitmapImage(
+            file='/usr/include/X11/bitmaps/left_ptr' ),
+
+	'Switch' : PhotoImage( data=r"""
+	   R0lGODlhOgArAMIEAB8aFwB7tQCb343L8P///////////////yH+GlNvZnR3YXJlOiBNaWNyb3NvZnQgT2ZmaWNlACwAAAAAOgArAAAD/ki63P4wykmrvTjr3YYfQigKH7d5Y6qmnjmBayyHg8vAAqDPaUTbowaA13OIahqcyEgEQEbIi7LIGA1FzsaSQK0QfbnH10sMa83VsqX53HLL7sgUTudR5s367F7PEq4CYDJRcngqfgshiAqAMwF3AYdWTCERjSoBjy+ZVItvMg6XIZmaEgOkSmJwlKOkkKSRlaqraaewr7ABhnqBNLmZuL+6vCzCrpvGsB9EH8m5wc7R0sbQ09bT1dOEBLbXwMjeEN7HpuO6Dt3hFObi7Ovj7d7bEOnYD+4V8PfqF/wN/lKsxZPmop6wBwaFzTsRbVvCWzYQmlMW0UKzZCUqatzICLGjx48gKyYAADs=
+"""),
+	'Host' : PhotoImage( data=r"""
+	    R0lGODlhKQAyAMIHAJyeoK+wsrW2uMHCxM7P0Ozt7fn5+f///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACwAAAAAKQAyAAAD63i63P4wykmrvS4cwLv/IEhxRxGeKGBM3pa+X9QeBmxrT3gMNpyLrt6rgcJgisKXgIFMopaLpjMEVUinn2pQ1ImSrN8uGKCVegHn8bl8CqbV7jFbJ47H650592sX4zl6MX9ocIOBLYNvhkxtiYV8eYx0kJSEi2d7WFmSmZqRmIKeHoddoqOcoaZkqIiqq6CtqqQkrq9jnaKzaLW6Wy8DBMHCp7ClPT+ArMY2t1u9Qs3Et6k+W87KtMfW0r6x1d7P2uDYu+LLtt3nQ9ufxeXM7MkOuCnR7UTe6/jyEOqeWj/SYQEowxXBfgYPJAAAOw==
+"""),
+        'Hosti': PhotoImage( data=r"""
+            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
+            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
+            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
+            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
+            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
+            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
+            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
+            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
+            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
+            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
+            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
+            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
+            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
+            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
+            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
+            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
+            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
+            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
+            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
+            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
+            ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
+            BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
+            HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
+            p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
+            C8cSBBAQADs=
+        """ ),
+
+        'Switchi': PhotoImage( data=r"""
+            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
+            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
+            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
+            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
+            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
+            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
+            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
+            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
+            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
+            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
+            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
+            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
+            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
+            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
+            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
+            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
+            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
+            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
+            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
+            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
+            ACH5BAEAAAAALAAAAAAgABgAAAhwAAEIHEiwoMGDCBMqXMiwocOH
+            ECNKnEixosWB3zJq3Mixo0eNAL7xG0mypMmTKPl9Cznyn8uWL/m5
+            /AeTpsyYI1eKlBnO5r+eLYHy9Ck0J8ubPmPOrMmUpM6UUKMa/Ui1
+            6saLWLNq3cq1q9evYB0GBAA7
+        """ ),
+
+        'Link': PhotoImage( data=r"""
+            R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
+            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
+            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
+            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
+            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
+            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
+            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
+            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
+            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
+            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
+            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
+            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
+            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
+            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
+            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
+            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
+            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
+            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
+            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
+            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
+            ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
+            Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
+            lBmxI8mSNknm1Dnx5sCAADs=
+        """ )
+    }
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    temp_file = parse_args()[0]
+    app = MiniEdit(template_file=temp_file)
+    app.mainloop()
diff --git a/examples/multiping.py b/examples/multiping.py
new file mode 100644
index 0000000..3bd231c
--- /dev/null
+++ b/examples/multiping.py
@@ -0,0 +1,86 @@
+#!/usr/bin/python
+
+"""
+multiping.py: monitor multiple sets of hosts using ping
+
+This demonstrates how one may send a simple shell script to
+multiple hosts and monitor their output interactively for a period=
+of time.
+"""
+
+from mininet.net import Mininet
+from mininet.node import Node
+from mininet.topo import SingleSwitchTopo
+from mininet.log import setLogLevel
+
+from select import poll, POLLIN
+from time import time
+
+def chunks( l, n ):
+    "Divide list l into chunks of size n - thanks Stackoverflow"
+    return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
+
+def startpings( host, targetips ):
+    "Tell host to repeatedly ping targets"
+
+    targetips.append( '10.0.0.200' )
+
+    targetips = ' '.join( targetips )
+
+    # BL: Not sure why loopback intf isn't up!
+    host.cmd( 'ifconfig lo up' )
+
+    # Simple ping loop
+    cmd = ( 'while true; do '
+            ' for ip in %s; do ' % targetips +
+            '  echo -n %s "->" $ip ' % host.IP() +
+            '   `ping -c1 -w 1 $ip | grep packets` ;'
+            '  sleep 1;'
+            ' done; '
+            'done &' )
+
+    print ( '*** Host %s (%s) will be pinging ips: %s' %
+            ( host.name, host.IP(), targetips ) )
+
+    host.cmd( cmd )
+
+def multiping( netsize, chunksize, seconds):
+    "Ping subsets of size chunksize in net of size netsize"
+
+    # Create network and identify subnets
+    topo = SingleSwitchTopo( netsize )
+    net = Mininet( topo=topo )
+    net.start()
+    hosts = net.hosts
+    subnets = chunks( hosts, chunksize )
+
+    # Create polling object
+    fds = [ host.stdout.fileno() for host in hosts ]
+    poller = poll()
+    for fd in fds:
+        poller.register( fd, POLLIN )
+
+    # Start pings
+    for subnet in subnets:
+        ips = [ host.IP() for host in subnet ]
+        for host in subnet:
+            startpings( host, ips )
+
+    # Monitor output
+    endTime = time() + seconds
+    while time() < endTime:
+        readable = poller.poll(1000)
+        for fd, _mask in readable:
+            node = Node.outToNode[ fd ]
+            print '%s:' % node.name, node.monitor().strip()
+
+    # Stop pings
+    for host in hosts:
+        host.cmd( 'kill %while' )
+
+    net.stop()
+
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    multiping( netsize=20, chunksize=4, seconds=10 )
diff --git a/examples/multipoll.py b/examples/multipoll.py
new file mode 100644
index 0000000..aef1b10
--- /dev/null
+++ b/examples/multipoll.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+"""
+Simple example of sending output to multiple files and
+monitoring them
+"""
+
+from mininet.topo import SingleSwitchTopo
+from mininet.net import Mininet
+from mininet.log import setLogLevel
+
+from time import time
+from select import poll, POLLIN
+from subprocess import Popen, PIPE
+
+def monitorFiles( outfiles, seconds, timeoutms ):
+    "Monitor set of files and return [(host, line)...]"
+    devnull = open( '/dev/null', 'w' )
+    tails, fdToFile, fdToHost = {}, {}, {}
+    for h, outfile in outfiles.iteritems():
+        tail = Popen( [ 'tail', '-f', outfile ],
+                      stdout=PIPE, stderr=devnull )
+        fd = tail.stdout.fileno()
+        tails[ h ] = tail
+        fdToFile[ fd ] = tail.stdout
+        fdToHost[ fd ] = h
+    # Prepare to poll output files
+    readable = poll()
+    for t in tails.values():
+        readable.register( t.stdout.fileno(), POLLIN )
+    # Run until a set number of seconds have elapsed
+    endTime = time() + seconds
+    while time() < endTime:
+        fdlist = readable.poll(timeoutms)
+        if fdlist:
+            for fd, _flags in fdlist:
+                f = fdToFile[ fd ]
+                host = fdToHost[ fd ]
+                # Wait for a line of output
+                line = f.readline().strip()
+                yield host, line
+        else:
+            # If we timed out, return nothing
+            yield None, ''
+    for t in tails.values():
+        t.terminate()
+    devnull.close()  # Not really necessary
+
+
+def monitorTest( N=3, seconds=3 ):
+    "Run pings and monitor multiple hosts"
+    topo = SingleSwitchTopo( N )
+    net = Mininet( topo )
+    net.start()
+    hosts = net.hosts
+    print "Starting test..."
+    server = hosts[ 0 ]
+    outfiles, errfiles = {}, {}
+    for h in hosts:
+        # Create and/or erase output files
+        outfiles[ h ] = '/tmp/%s.out' % h.name
+        errfiles[ h ] = '/tmp/%s.err' % h.name
+        h.cmd( 'echo >', outfiles[ h ] )
+        h.cmd( 'echo >', errfiles[ h ] )
+        # Start pings
+        h.cmdPrint('ping', server.IP(),
+                   '>', outfiles[ h ],
+                   '2>', errfiles[ h ],
+                   '&' )
+    print "Monitoring output for", seconds, "seconds"
+    for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
+        if h:
+            print '%s: %s' % ( h.name, line )
+    for h in hosts:
+        h.cmd('kill %ping')
+    net.stop()
+
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    monitorTest()
diff --git a/examples/multitest.py b/examples/multitest.py
new file mode 100644
index 0000000..bcb40f7
--- /dev/null
+++ b/examples/multitest.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+
+"""
+This example shows how to create a network and run multiple tests.
+For a more complicated test example, see udpbwtest.py.
+"""
+
+from mininet.cli import CLI
+from mininet.log import lg, info
+from mininet.net import Mininet
+from mininet.node import OVSKernelSwitch
+from mininet.topolib import TreeTopo
+
+def ifconfigTest( net ):
+    "Run ifconfig on all hosts in net."
+    hosts = net.hosts
+    for host in hosts:
+        info( host.cmd( 'ifconfig' ) )
+
+if __name__ == '__main__':
+    lg.setLogLevel( 'info' )
+    info( "*** Initializing Mininet and kernel modules\n" )
+    OVSKernelSwitch.setup()
+    info( "*** Creating network\n" )
+    network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch)
+    info( "*** Starting network\n" )
+    network.start()
+    info( "*** Running ping test\n" )
+    network.pingAll()
+    info( "*** Running ifconfig test\n" )
+    ifconfigTest( network )
+    info( "*** Starting CLI (type 'exit' to exit)\n" )
+    CLI( network )
+    info( "*** Stopping network\n" )
+    network.stop()
diff --git a/examples/popen.py b/examples/popen.py
new file mode 100644
index 0000000..332822f
--- /dev/null
+++ b/examples/popen.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+"""
+This example monitors a number of hosts using host.popen() and
+pmonitor()
+"""
+
+from mininet.net import Mininet
+from mininet.node import CPULimitedHost
+from mininet.topo import SingleSwitchTopo
+from mininet.log import setLogLevel
+from mininet.util import custom, pmonitor
+
+def monitorhosts( hosts=5, sched='cfs' ):
+    "Start a bunch of pings and monitor them using popen"
+    mytopo = SingleSwitchTopo( hosts )
+    cpu = .5 / hosts
+    myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
+    net = Mininet( topo=mytopo, host=myhost )
+    net.start()
+    # Start a bunch of pings
+    popens = {}
+    last = net.hosts[ -1 ]
+    for host in net.hosts:
+        popens[ host ] = host.popen( "ping -c5 %s" % last.IP() )
+        last = host
+    # Monitor them and print output
+    for host, line in pmonitor( popens ):
+        if host:
+            print "<%s>: %s" % ( host.name, line.strip() )
+    # Done
+    net.stop()
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    monitorhosts( hosts=5 )
diff --git a/examples/popenpoll.py b/examples/popenpoll.py
new file mode 100644
index 0000000..c581c27
--- /dev/null
+++ b/examples/popenpoll.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+"Monitor multiple hosts using popen()/pmonitor()"
+
+from mininet.net import Mininet
+from mininet.topo import SingleSwitchTopo
+from mininet.util import pmonitor
+from time import time
+from signal import SIGINT
+
+def pmonitorTest( N=3, seconds=10 ):
+    "Run pings and monitor multiple hosts using pmonitor"
+    topo = SingleSwitchTopo( N )
+    net = Mininet( topo )
+    net.start()
+    hosts = net.hosts
+    print "Starting test..."
+    server = hosts[ 0 ]
+    popens = {}
+    for h in hosts:
+        popens[ h ] = h.popen('ping', server.IP() )
+    print "Monitoring output for", seconds, "seconds"
+    endTime = time() + seconds
+    for h, line in pmonitor( popens, timeoutms=500 ):
+        if h:
+            print '%s: %s' % ( h.name, line ),
+        if time() >= endTime:
+            for p in popens.values():
+                p.send_signal( SIGINT )
+    net.stop()
+
+if __name__ == '__main__':
+    pmonitorTest()
diff --git a/examples/router.gif b/examples/router.gif
new file mode 100644
index 0000000..a670ac7
--- /dev/null
+++ b/examples/router.gif
Binary files differ
diff --git a/examples/scratchnet.py b/examples/scratchnet.py
new file mode 100644
index 0000000..966a183
--- /dev/null
+++ b/examples/scratchnet.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+
+"""
+Build a simple network from scratch, using mininet primitives.
+This is more complicated than using the higher-level classes,
+but it exposes the configuration details and allows customization.
+
+For most tasks, the higher-level API will be preferable.
+"""
+
+from mininet.net import Mininet
+from mininet.node import Node
+from mininet.link import Link
+from mininet.log import setLogLevel, info
+from mininet.util import quietRun
+
+from time import sleep
+
+def scratchNet( cname='controller', cargs='-v ptcp:' ):
+    "Create network from scratch using Open vSwitch."
+
+    info( "*** Creating nodes\n" )
+    controller = Node( 'c0', inNamespace=False )
+    switch = Node( 's0', inNamespace=False )
+    h0 = Node( 'h0' )
+    h1 = Node( 'h1' )
+
+    info( "*** Creating links\n" )
+    Link( h0, switch )
+    Link( h1, switch )
+
+    info( "*** Configuring hosts\n" )
+    h0.setIP( '192.168.123.1/24' )
+    h1.setIP( '192.168.123.2/24' )
+    info( str( h0 ) + '\n' )
+    info( str( h1 ) + '\n' )
+
+    info( "*** Starting network using Open vSwitch\n" )
+    controller.cmd( cname + ' ' + cargs + '&' )
+    switch.cmd( 'ovs-vsctl del-br dp0' )
+    switch.cmd( 'ovs-vsctl add-br dp0' )
+    for intf in switch.intfs.values():
+        print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
+
+    # Note: controller and switch are in root namespace, and we
+    # can connect via loopback interface
+    switch.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:6633' )
+
+    info( '*** Waiting for switch to connect to controller' )
+    while 'is_connected' not in quietRun( 'ovs-vsctl show' ):
+        sleep( 1 )
+        info( '.' )
+    info( '\n' )
+
+    info( "*** Running test\n" )
+    h0.cmdPrint( 'ping -c1 ' + h1.IP() )
+
+    info( "*** Stopping network\n" )
+    controller.cmd( 'kill %' + cname )
+    switch.cmd( 'ovs-vsctl del-br dp0' )
+    switch.deleteIntfs()
+    info( '\n' )
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    info( '*** Scratch network demo (kernel datapath)\n' )
+    Mininet.init()
+    scratchNet()
diff --git a/examples/scratchnetuser.py b/examples/scratchnetuser.py
new file mode 100644
index 0000000..ccd20e9
--- /dev/null
+++ b/examples/scratchnetuser.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+"""
+Build a simple network from scratch, using mininet primitives.
+This is more complicated than using the higher-level classes,
+but it exposes the configuration details and allows customization.
+
+For most tasks, the higher-level API will be preferable.
+
+This version uses the user datapath and an explicit control network.
+"""
+
+from mininet.net import Mininet
+from mininet.node import Node
+from mininet.link import Link
+from mininet.log import setLogLevel, info
+
+def linkIntfs( node1, node2 ):
+    "Create link from node1 to node2 and return intfs"
+    link = Link( node1, node2 )
+    return link.intf1, link.intf2
+
+def scratchNetUser( cname='controller', cargs='ptcp:' ):
+    "Create network from scratch using user switch."
+
+    # It's not strictly necessary for the controller and switches
+    # to be in separate namespaces. For performance, they probably
+    # should be in the root namespace. However, it's interesting to
+    # see how they could work even if they are in separate namespaces.
+
+    info( '*** Creating Network\n' )
+    controller = Node( 'c0' )
+    switch = Node( 's0')
+    h0 = Node( 'h0' )
+    h1 = Node( 'h1' )
+    cintf, sintf = linkIntfs( controller, switch )
+    h0intf, sintf1 = linkIntfs( h0, switch )
+    h1intf, sintf2 = linkIntfs( h1, switch )
+
+    info( '*** Configuring control network\n' )
+    controller.setIP( '10.0.123.1/24', intf=cintf )
+    switch.setIP( '10.0.123.2/24', intf=sintf)
+
+    info( '*** Configuring hosts\n' )
+    h0.setIP( '192.168.123.1/24', intf=h0intf )
+    h1.setIP( '192.168.123.2/24', intf=h1intf )
+
+    info( '*** Network state:\n' )
+    for node in controller, switch, h0, h1:
+        info( str( node ) + '\n' )
+
+    info( '*** Starting controller and user datapath\n' )
+    controller.cmd( cname + ' ' + cargs + '&' )
+    switch.cmd( 'ifconfig lo 127.0.0.1' )
+    intfs = [ str( i ) for i in sintf1, sintf2 ]
+    switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
+    switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
+
+    info( '*** Running test\n' )
+    h0.cmdPrint( 'ping -c1 ' + h1.IP() )
+
+    info( '*** Stopping network\n' )
+    controller.cmd( 'kill %' + cname )
+    switch.cmd( 'kill %ofdatapath' )
+    switch.cmd( 'kill %ofprotocol' )
+    switch.deleteIntfs()
+    info( '\n' )
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    info( '*** Scratch network demo (user datapath)\n' )
+    Mininet.init()
+    scratchNetUser()
diff --git a/examples/server.gif b/examples/server.gif
new file mode 100644
index 0000000..b16b0da
--- /dev/null
+++ b/examples/server.gif
Binary files differ
diff --git a/examples/simpleperf.py b/examples/simpleperf.py
new file mode 100644
index 0000000..1da4b66
--- /dev/null
+++ b/examples/simpleperf.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+
+"""
+Simple example of setting network and CPU parameters
+
+NOTE: link params limit BW, add latency, and loss.
+There is a high chance that pings WILL fail and that
+iperf will hang indefinitely if the TCP handshake fails
+to complete.
+"""
+
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.node import CPULimitedHost
+from mininet.link import TCLink
+from mininet.util import dumpNodeConnections
+from mininet.log import setLogLevel
+
+class SingleSwitchTopo(Topo):
+    "Single switch connected to n hosts."
+    def __init__(self, n=2, **opts):
+        Topo.__init__(self, **opts)
+        switch = self.addSwitch('s1')
+        for h in range(n):
+            # Each host gets 50%/n of system CPU
+            host = self.addHost('h%s' % (h + 1),
+                                cpu=.5 / n)
+            # 10 Mbps, 5ms delay, 10% loss
+            self.addLink(host, switch,
+                         bw=10, delay='5ms', loss=10, use_htb=True)
+
+def perfTest():
+    "Create network and run simple performance test"
+    topo = SingleSwitchTopo(n=4)
+    net = Mininet(topo=topo,
+                  host=CPULimitedHost, link=TCLink)
+    net.start()
+    print "Dumping host connections"
+    dumpNodeConnections(net.hosts)
+    print "Testing network connectivity"
+    net.pingAll()
+    print "Testing bandwidth between h1 and h4"
+    h1, h4 = net.getNodeByName('h1', 'h4')
+    net.iperf((h1, h4))
+    net.stop()
+
+if __name__ == '__main__':
+    setLogLevel('info')
+    perfTest()
diff --git a/examples/sshd.py b/examples/sshd.py
new file mode 100644
index 0000000..2bedb9c
--- /dev/null
+++ b/examples/sshd.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+
+"""
+Create a network and start sshd(8) on each host.
+
+While something like rshd(8) would be lighter and faster,
+(and perfectly adequate on an in-machine network)
+the advantage of running sshd is that scripts can work
+unchanged on mininet and hardware.
+
+In addition to providing ssh access to hosts, this example
+demonstrates:
+
+- creating a convenience function to construct networks
+- connecting the host network to the root namespace
+- running server processes (sshd in this case) on hosts
+"""
+
+from mininet.net import Mininet
+from mininet.cli import CLI
+from mininet.log import lg
+from mininet.node import Node, OVSKernelSwitch
+from mininet.topolib import TreeTopo
+from mininet.link import Link
+
+def TreeNet( depth=1, fanout=2, **kwargs ):
+    "Convenience function for creating tree networks."
+    topo = TreeTopo( depth, fanout )
+    return Mininet( topo, **kwargs )
+
+def connectToRootNS( network, switch, ip, prefixLen, routes ):
+    """Connect hosts to root namespace via switch. Starts network.
+      network: Mininet() network object
+      switch: switch to connect to root namespace
+      ip: IP address for root namespace node
+      prefixLen: IP address prefix length (e.g. 8, 16, 24)
+      routes: host networks to route to"""
+    # Create a node in root namespace and link to switch 0
+    root = Node( 'root', inNamespace=False )
+    intf = Link( root, switch ).intf1
+    root.setIP( ip, prefixLen, intf )
+    # Start network that now includes link to root namespace
+    network.start()
+    # Add routes from root ns to hosts
+    for route in routes:
+        root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
+
+def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
+    "Start a network, connect it to root ns, and run sshd on all hosts."
+    switch = network.switches[ 0 ]  # switch to use
+    ip = '10.123.123.1'  # our IP address on host network
+    routes = [ '10.0.0.0/8' ]  # host networks to route to
+    connectToRootNS( network, switch, ip, 8, routes )
+    for host in network.hosts:
+        host.cmd( cmd + ' ' + opts + '&' )
+    print
+    print "*** Hosts are running sshd at the following addresses:"
+    print
+    for host in network.hosts:
+        print host.name, host.IP()
+    print
+    print "*** Type 'exit' or control-D to shut down network"
+    CLI( network )
+    for host in network.hosts:
+        host.cmd( 'kill %' + cmd )
+    network.stop()
+
+if __name__ == '__main__':
+    lg.setLogLevel( 'info')
+    net = TreeNet( depth=1, fanout=4, switch=OVSKernelSwitch )
+    sshd( net )
diff --git a/examples/tree1024.py b/examples/tree1024.py
new file mode 100644
index 0000000..9397131
--- /dev/null
+++ b/examples/tree1024.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+"""
+Create a 1024-host network, and run the CLI on it.
+If this fails because of kernel limits, you may have
+to adjust them, e.g. by adding entries to /etc/sysctl.conf
+and running sysctl -p. Check util/sysctl_addon.
+"""
+
+from mininet.cli import CLI
+from mininet.log import setLogLevel
+from mininet.node import OVSKernelSwitch
+from mininet.topolib import TreeNet
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    network = TreeNet( depth=2, fanout=32, switch=OVSKernelSwitch )
+    network.run( CLI, network )
diff --git a/examples/treeping64.py b/examples/treeping64.py
new file mode 100644
index 0000000..ba60f1b
--- /dev/null
+++ b/examples/treeping64.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+"Create a 64-node tree network, and test connectivity using ping."
+
+from mininet.log import setLogLevel
+from mininet.node import UserSwitch, OVSKernelSwitch  # , KernelSwitch
+from mininet.topolib import TreeNet
+
+def treePing64():
+    "Run ping test on 64-node tree networks."
+
+    results = {}
+    switches = {  # 'reference kernel': KernelSwitch,
+                  'reference user': UserSwitch,
+                  'Open vSwitch kernel': OVSKernelSwitch }
+
+    for name in switches:
+        print "*** Testing", name, "datapath"
+        switch = switches[ name ]
+        network = TreeNet( depth=2, fanout=8, switch=switch )
+        result = network.run( network.pingAll )
+        results[ name ] = result
+
+    print
+    print "*** Tree network ping results:"
+    for name in switches:
+        print "%s: %d%% packet loss" % ( name, results[ name ] )
+    print
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    treePing64()