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()