#!/usr/bin/python """ Mininet: A simple networking testbed for OpenFlow! Mininet creates simple OpenFlow test networks by using process-based virtualization and network namespaces. This file supports use of either the kernel or user space datapath from the OpenFlow reference implementation. Up to 32 switches are supported using the kernel datapath, and 512 (or more) switches are supported via the user datapath. Simulated hosts are created as processes in separate network namespaces. This allows a complete OpenFlow network to be simulated on top of a single Linux kernel. Each host has: A virtual console (pipes to a shell) A virtual interfaces (half of a veth pair) A parent shell (and possibly some child processes) in a namespace Hosts have a network interface which is configured via ifconfig/ip link/etc. with data network IP addresses (e.g. 192.168.123.2 ) In kernel datapath mode, the controller and switches are simply processes in the root namespace. Kernel OpenFlow datapaths are instantiated using dpctl(8), and are attached to the one side of a veth pair; the other side resides in the host namespace. In this mode, switch processes can simply connect to the controller via the loopback interface. In user datapath mode, the controller and switches are full-service nodes that live in their own network namespaces and have management interfaces and IP addresses on a control network (e.g. 10.0.123.1, currently routed although it could be bridged.) In addition to a management interface, user mode switches also have several switch interfaces, halves of veth pairs whose other halves reside in the host nodes that the switches are connected to. Naming: Host nodes are named h1-hN Switch nodes are named s0-sN Interfaces are named {nodename}-eth0 .. {nodename}-ethN, Thoughts/TBD: It should be straightforward to add a function to read OpenFlowVMS spec files, but I haven't done so yet. For the moment, specifying configurations and tests in Python is straightforward and concise. Soon, we'll want to split the various subsystems (core, cli, tests, etc.) into multiple modules. We may be able to get better performance by using the kernel datapath (using its multiple datapath feature on multiple interfaces.) This would eliminate the ofdatapath user processes. OpenVSwitch would still run at user level. Bob Lantz rlantz@cs.stanford.edu History: 11/19/09 Initial revision (user datapath only) 12/08/09 Kernel datapath support complete 12/09/09 Moved controller and switch routines into classes """ from subprocess import call, check_call, Popen, PIPE, STDOUT from time import sleep import os, re, signal, sys, select flush = sys.stdout.flush from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE # Utility routines to make it easier to run commands def run( cmd ): "Simple interface to subprocess.call()" return call( cmd.split( ' ' ) ) def checkRun( cmd ): "Simple interface to subprocess.check_call()" check_call( cmd.split( ' ' ) ) def quietRun( cmd ): "Run a command, routing stderr to stdout, and return the output." if isinstance( cmd, str ): cmd = cmd.split( ' ' ) popen = Popen( cmd, stdout=PIPE, stderr=STDOUT) # We can't use Popen.communicate() because it uses # select(), which can't handle # high file descriptor numbers! poll() can, however. output = '' readable = select.poll() readable.register( popen.stdout ) while True: while readable.poll(): data = popen.stdout.read( 1024 ) if len( data ) == 0: break output += data popen.poll() if popen.returncode != None: break return output class Node( object ): """A virtual network node is simply a shell in a network namespace. We communicate with it using pipes.""" def __init__( self, name, inNamespace=True ): self.name = name closeFds = False # speed vs. memory use # xpg_echo is needed so we can echo our sentinel in sendCmd cmd = [ '/bin/bash', '-O', 'xpg_echo' ] self.inNamespace = inNamespace if self.inNamespace: cmd = [ 'netns' ] + cmd self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=closeFds ) self.stdin = self.shell.stdin self.stdout = self.shell.stdout self.pollOut = select.poll() self.pollOut.register( self.stdout ) outToNode[ self.stdout ] = self inToNode[ self.stdin ] = self self.pid = self.shell.pid self.intfCount = 0 self.intfs = [] self.ips = {} self.connection = {} self.waiting = False self.execed = False def cleanup( self ): # Help python collect its garbage self.shell = None # Subshell I/O, commands and control def read( self, max ): return os.read( self.stdout.fileno(), max ) def write( self, data ): os.write( self.stdin.fileno(), data ) def terminate( self ): self.cleanup() os.kill( self.pid, signal.SIGKILL ) def waitReadable( self ): self.pollOut.poll() def sendCmd( self, cmd ): """Send a command, followed by a command to echo a sentinel, and return without waiting for the command to complete.""" assert not self.waiting if cmd[ -1 ] == '&': separator = '&' cmd = cmd[ : -1 ] else: separator = ';' if isinstance( cmd, list): cmd = ' '.join( cmd ) self.write( cmd + separator + " echo -n '\\0177' \n") self.waiting = True def monitor( self ): "Monitor a command's output, returning (done, data)." assert self.waiting self.waitReadable() data = self.read( 1024 ) if len( data ) > 0 and data[ -1 ] == chr( 0177 ): self.waiting = False return True, data[ : -1 ] else: return False, data def sendInt( self ): "Send ^C, hopefully interrupting a running subprocess." self.write( chr( 3 ) ) def waitOutput( self ): """Wait for a command to complete (signaled by a sentinel character, ASCII(127) appearing in the output stream) and return the output, including trailing newline.""" assert self.waiting output = "" while True: self.waitReadable() data = self.read( 1024 ) if len(data) > 0 and data[ -1 ] == chr( 0177 ): output += data[ : -1 ] break else: output += data self.waiting = False return output def cmd( self, cmd ): "Send a command, wait for output, and return it." self.sendCmd( cmd ) return self.waitOutput() def cmdPrint( self, cmd ): "Call cmd, printing the command and output" print "***", self.name, ":", cmd result = self.cmd( cmd ) print result, return result # Interface management, configuration, and routing def intfName( self, n): "Construct a canonical interface name node-intf for interface N." return self.name + '-eth' + `n` def newIntf( self ): "Reserve and return a new interface name for this node." intfName = self.intfName( self.intfCount) self.intfCount += 1 self.intfs += [ intfName ] return intfName def setIP( self, intf, ip, bits ): "Set an interface's IP address." result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] ) self.ips[ intf ] = ip return result def setHostRoute( self, ip, intf ): "Add a route to the given IP address via intf." return self.cmd( 'route add -host ' + ip + ' dev ' + intf ) def setDefaultRoute( self, intf ): "Set the default route to go through intf." self.cmd( 'ip route flush' ) return self.cmd( 'route add default ' + intf ) def IP( self ): "Return IP address of first interface" return self.ips[ self.intfs[ 0 ] ] def intfIsUp( self, intf ): "Check if one of our interfaces is up." return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] ) # Other methods def __str__( self ): result = self.name result += ": IP=" + self.IP() + " intfs=" + self.intfs result += " waiting=", self.waiting return result # Maintain mapping between i/o pipes and nodes # This could be useful for monitoring multiple nodes # using select.poll() inToNode = {} outToNode = {} def outputs(): return outToNode.keys() def nodes(): return outToNode.values() def inputs(): return [ node.stdin for node in nodes() ] def nodeFromFile( f ): node = outToNode.get( f ) return node or inToNode.get( f ) class Host( Node ): """A host is simply a Node.""" pass class Controller( Node ): """A Controller is a Node that is running (or has execed) an OpenFlow controller.""" def __init__( self, name, kernel=True ): Node.__init__( self, name, inNamespace=( not kernel ) ) def start( self, controller='controller', args='ptcp:' ): "Start <controller> <args> on controller, logging to /tmp/cN.log" cout = '/tmp/' + self.name + '.log' self.cmdPrint( controller + ' ' + args + ' 1> ' + cout + ' 2> ' + cout + ' &' ) def stop( self, controller='controller' ): "Stop controller cprog on controller" self.cmd( "kill %" + controller ) class Switch( Node ): """A Switch is a Node that is running (or has execed) an OpenFlow switch.""" def __init__( self, name, datapath=None ): self.dp = datapath Node.__init__( self, name, inNamespace=( datapath == None ) ) def startUserDatapath( self, controller ): """Start OpenFlow reference user datapath, logging to /tmp/sN-{ofd,ofp}.log""" ofdlog = '/tmp/' + self.name + '-ofd.log' ofplog = '/tmp/' + self.name + '-ofp.log' self.cmd( 'ifconfig lo up' ) intfs = self.intfs[ 1 : ] # 0 is mgmt interface self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: 1> ' + ofdlog + ' 2> '+ ofdlog + ' &' ) self.cmdPrint( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost 1> ' + ofplog + ' 2>' + ofplog + ' &' ) def stopUserDatapath( self ): "Stop OpenFlow reference user datapath." self.cmd( "kill %ofdatapath" ) self.cmd( "kill %ofprotocol" ) def startKernelDatapath( self, controller): "Start up switch using OpenFlow reference kernel datapath." ofplog = '/tmp/' + self.name + '-ofp.log' quietRun( 'ifconfig lo up' ) # Delete local datapath if it exists; # then create a new one monitoring the given interfaces quietRun( 'dpctl deldp ' + self.dp ) self.cmdPrint( 'dpctl adddp ' + self.dp ) self.cmdPrint( 'dpctl addif ' + self.dp + ' ' + ' '.join( self.intfs ) ) # Become protocol daemon self.cmdPrint( 'exec ofprotocol' + ' ' + self.dp + ' tcp:127.0.0.1 1> ' + ofplog + ' 2>' + ofplog + ' &' ) self.execed = True def stopKernelDatapath( self ): "Terminate a switch using OpenFlow reference kernel datapath." quietRun( 'dpctl deldp ' + self.dp ) for intf in self.intfs: quietRun( 'ip link del ' + intf ) self.terminate() def start( self, controller ): if self.dp is None: self.startUserDatapath( controller ) else: self.startKernelDatapath( controller ) def stop( self ): if self.dp is None: self.stopUserDatapath() else: self.stopKernelDatapath() # Handle non-interaction if we've execed def sendCmd( self, cmd ): if not self.execed: return Node.sendCmd( self, cmd ) else: print "*** Error:", self.name, "has execed and cannot accept commands" def monitor( self ): if not self.execed: return Node.monitor( self ) else: return True, '' # Interface management # # Interfaces are managed as strings which are simply the # interface names, of the form "nodeN-ethM". # # To connect nodes, we create a pair of veth interfaces, and then place them # in the pair of nodes that we want to communicate. We then update the node's # list of interfaces and connectivity map. # # For the kernel datapath, switch interfaces # live in the root namespace and thus do not have to be # explicitly moved. def makeIntfPair( intf1, intf2 ): "Make a veth pair of intf1 and intf2." # Delete any old interfaces with the same names quietRun( 'ip link del ' + intf1 ) quietRun( 'ip link del ' + intf2 ) # Create new pair cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 return checkRun( cmd ) def moveIntf( intf, node ): "Move intf to node." cmd = 'ip link set ' + intf + ' netns ' + `node.pid` checkRun( cmd ) links = node.cmd( 'ip link show' ) if not intf in links: print "*** Error: moveIntf:", intf, "not successfully moved to", print node.name,":" exit( 1 ) return def createLink( node1, node2 ): "Create a link node1-intf1 <---> node2-intf2." intf1 = node1.newIntf() intf2 = node2.newIntf() makeIntfPair( intf1, intf2 ) if node1.inNamespace: moveIntf( intf1, node1 ) if node2.inNamespace: moveIntf( intf2, node2 ) node1.connection[ intf1 ] = ( node2, intf2 ) node2.connection[ intf2 ] = ( node1, intf1 ) return intf1, intf2 # Handy utilities def createNodes( name, count ): "Create and return a list of nodes." nodes = [ Node( name + `i` ) for i in range( 0, count ) ] # print "*** CreateNodes: created:", nodes return nodes def dumpNodes( nodes ): "Dump ifconfig of each node." for node in nodes: print "*** Dumping node", node.name print node.cmd( 'ip link show' ) print node.cmd( 'route' ) def ipGen( A, B, c, d ): "Generate next IP class B IP address, starting at A.B.c.d" while True: yield '%d.%d.%d.%d' % ( A, B, c, d ) d += 1 if d > 254: d = 1 c += 1 if c > 254: break def nameGen( prefix ): "Generate names starting with prefix." i = 0 while True: yield prefix + `i`; i += 1 # Control network support # For the user datapath, we create an explicit control network. # Note: Instead of routing, we could bridge or use "in-band" control def configRoutedControlNetwork( controller, switches, startAddr=( 10, 123, 0, 1 ) ): """Configure a routed control network on controller and switches, for use with the user datapath.""" ips = apply( ipGen, startAddr ) cip = ips.next() print controller.name, '<->', for switch in switches: print switch.name, ; flush() sip = ips.next() sintf = switch.intfs[ 0 ] node, cintf = switch.connection[ sintf ] if node != controller: print "*** Error: switch", switch.name, print "not connected to correct controller" exit( 1 ) controller.setIP( cintf, cip, '/24' ) switch.setIP( sintf, sip, '/24' ) controller.setHostRoute( sip, cintf ) switch.setHostRoute( cip, sintf ) print print "*** Testing control network" while not controller.intfIsUp( controller.intfs[ 0 ] ): print "*** Waiting for ", controller.intfs[ 0 ], "to come up" sleep( 1 ) for switch in switches: while not switch.intfIsUp( switch.intfs[ 0 ] ): print "*** Waiting for ", switch.intfs[ 0 ], "to come up" sleep( 1 ) if pingTest( hosts=[ switch, controller ] ) != 0: print "*** Error: control network test failed" exit( 1 ) def configHosts( hosts, ( a, b, c, d ) ): "Configure a set of hosts, starting at IP address a.b.c.d" ips = ipGen( a, b, c, d ) for host in hosts: hintf = host.intfs[ 0 ] host.setIP( hintf, ips.next(), '/24' ) host.setDefaultRoute( hintf ) # You're low priority, dude! quietRun( 'renice +18 -p ' + `host.pid` ) print host.name, ; flush() print # Test driver and topologies class Network( object ): "Network topology (and test driver) base class." def __init__( self, kernel=True, startAddr=( 192, 168, 123, 1) ): self.kernel, self.startAddr = kernel, startAddr # Check for kernel modules tun = quietRun( [ 'sh', '-c', 'lsmod | grep tun' ] ) ofdatapath = quietRun( [ 'sh', '-c', 'lsmod | grep ofdatapath' ] ) if tun == '' and not kernel: print "*** Error: kernel module tun not loaded:", print " user datapath not supported" exit( 1 ) if ofdatapath == '' and kernel: print "*** Error: ofdatapath not loaded:", print " kernel datapath not supported" exit( 1 ) # Create network, but don't start things up yet! self.prepareNet() def prepareNet( self ): """Create a network by calling makeNet as follows: (switches, hosts ) = makeNet() Create a controller here as well.""" kernel = self.kernel if kernel: print "*** Using kernel datapath" else: print "*** Using user datapath" print "*** Creating controller" controller = Controller( 'c0', kernel ) print "*** Creating network" switches, hosts = self.makeNet( controller ) print if not kernel: print "*** Configuring control network" configRoutedControlNetwork( controller, switches ) print "*** Configuring hosts" configHosts( hosts, self.startAddr ) self.controllers = [ controller ] self.switches = switches self.hosts = hosts def start( self ): "Start controller and switches" print "*** Starting reference controller" for controller in self.controllers: controller.start() print "*** Starting", len( self.switches ), "switches" for switch in self.switches: switch.start( self.controllers[ 0 ] ) def stop( self ): "Stop the controller(s), switches and hosts" print "*** Stopping controller" for controller in self.controllers: controller.stop(); controller.terminate() print "*** Stopping switches" for switch in self.switches: switch.stop() ; switch.terminate() print "*** Stopping hosts" for host in self.hosts: host.terminate() print "*** Test complete" def runTest( self, test ): "Run a given test, called as test( controllers, switches, hosts)" return test( self.controllers, self.switches, self.hosts ) def run( self, test ): """Perform a complete start/test/stop cycle; test is of the form test( controllers, switches, hosts )""" self.start() print "*** Running test" result = self.runTest( test ) self.stop() return result def interact( self ): "Create a network and run our simple CLI." self.run( self, Cli ) def defaultNames( snames=None, hnames=None, dpnames=None ): "Reinitialize default names from generators, if necessary." if snames is None: snames = nameGen( 's' ) if hnames is None: hnames = nameGen( 'h' ) if dpnames is None: dpnames = nameGen( 'nl:' ) return snames, hnames, dpnames # Tree network class TreeNet( Network ): "A tree-structured network with the specified depth and fanout" def __init__( self, depth, fanout, kernel=True): self.depth, self.fanout = depth, fanout Network.__init__( self, kernel ) def treeNet( self, controller, depth, fanout, kernel=True, snames=None, hnames=None, dpnames=None ): """Return a tree network of the given depth and fanout as a triple: ( root, switches, hosts ), using the given switch, host and datapath name generators, with the switches connected to the given controller. If kernel=True, use the kernel datapath; otherwise the user datapath will be used.""" # Ugly, but necessary (?) since defaults are only evaluated once snames, hnames, dpnames = defaultNames( snames, hnames, dpnames ) if ( depth == 0 ): host = Host( hnames.next() ) print host.name, ; flush() return host, [], [ host ] dp = dpnames.next() if kernel else None switch = Switch( snames.next(), dp ) if not kernel: createLink( switch, controller ) print switch.name, ; flush() switches, hosts = [ switch ], [] for i in range( 0, fanout ): child, slist, hlist = self.treeNet( controller, depth - 1, fanout, kernel, snames, hnames, dpnames ) createLink( switch, child ) switches += slist hosts += hlist return switch, switches, hosts def makeNet( self, controller ): root, switches, hosts = self.treeNet( controller, self.depth, self.fanout, self.kernel) return switches, hosts # Grid network class GridNet( Network ): "An N x M grid/mesh network of switches, with hosts at the edges." def __init__( self, n, m, kernel=True, linear=False ): self.n, self.m, self.linear = n, m, linear and m == 1 Network.__init__( self, kernel ) def makeNet( self, controller ): snames, hnames, dpnames = defaultNames() n, m = self.n, self.m hosts = [] switches = [] kernel = self.kernel rows = [] if not self.linear: print "*** gridNet: creating", n, "x", m, "grid of switches" ; flush() for y in range( 0, m ): row = [] for x in range( 0, n ): dp = dpnames.next() if kernel else None switch = Switch( snames.next(), dp ) if not kernel: createLink( switch, controller ) row.append( switch ) switches += [ switch ] print switch.name, ; flush() rows += [ row ] # Hook up rows for row in rows: previous = None for switch in row: if previous is not None: createLink( switch, previous ) previous = switch h1, h2 = Host( hnames.next() ), Host( hnames.next() ) createLink( h1, row[ 0 ] ) createLink( h2, row[ -1 ] ) hosts += [ h1, h2 ] print h1.name, h2.name, ; flush() # Return here if we're using this to make a linear network if self.linear: return switches, hosts # Hook up columns for x in range( 0, n ): previous = None for y in range( 0, m ): switch = rows[ y ][ x ] if previous is not None: createLink( switch, previous ) previous = switch h1, h2 = Host( hnames.next() ), Host( hnames.next() ) createLink( h1, rows[ 0 ][ x ] ) createLink( h2, rows[ -1 ][ x ] ) hosts += [ h1, h2 ] print h1.name, h2.name, ; flush() return switches, hosts class LinearNet( GridNet ): def __init__( self, switchCount, kernel=True ): self.switchCount = switchCount GridNet.__init__( self, switchCount, 1, kernel, linear=True ) # Tests def parsePing( pingOutput ): "Parse ping output and return packets sent, received." r = r'(\d+) packets transmitted, (\d+) received' m = re.search( r, pingOutput ) if m == None: print "*** Error: could not parse ping output:", pingOutput exit( 1 ) sent, received = int( m.group( 1 ) ), int( m.group( 2 ) ) return sent, received def pingTest( controllers=[], switches=[], hosts=[], verbose=False ): "Test that each host can reach every other host." packets = 0 ; lost = 0 for node in hosts: if verbose: print node.name, "->", ; flush() for dest in hosts: if node != dest: result = node.cmd( 'ping -c1 ' + dest.IP() ) sent, received = parsePing( result ) packets += sent if received > sent: print "*** Error: received too many packets" print result node.cmdPrint( 'route' ) exit( 1 ) lost += sent - received if verbose: print ( dest.name if received else "X" ), ; flush() if verbose: print ploss = 100 * lost/packets if verbose: print "%d%% packet loss (%d/%d lost)" % ( ploss, lost, packets ) flush() return ploss def pingTestVerbose( controllers, switches, hosts ): return "%d %% packet loss" % \ pingTest( controllers, switches, hosts, verbose=True ) def parseIperf( iperfOutput ): "Parse iperf output and return bandwidth." r = r'([\d\.]+ \w+/sec)' m = re.search( r, iperfOutput ) return m.group( 1 ) if m is not None else "could not parse iperf output" def iperf( hosts, verbose=False ): "Run iperf between two hosts." assert len( hosts ) == 2 host1, host2 = hosts[ 0 ], hosts[ 1 ] # dumpNodes( [ host1, host2 ] ) host1.cmd( 'killall -9 iperf') # XXX shouldn't be global killall server = host1.cmd( 'iperf -s &' ) if verbose: print server ; flush() client = host2.cmd( 'iperf -t 5 -c ' + host1.IP() ) if verbose: print client ; flush() server = host1.cmd( 'kill -9 %iperf' ) if verbose: print server; flush() return [ parseIperf( server ), parseIperf( client ) ] def iperfTest( controllers, switches, hosts, verbose=False ): "Simple iperf test between two hosts." if verbose: print "*** Starting ping test" h0, hN = hosts[ 0 ], hosts[ -1 ] print "*** iperfTest: Testing bandwidth between", print h0.name, "and", hN.name result = iperf( [ h0, hN], verbose ) print "*** result:", result return result # Simple CLI class Cli( object ): "Simple command-line interface to talk to nodes." cmds = [ '?', 'help', 'nodes', 'sh', 'pingtest', 'iperf', 'net', 'exit' ] def __init__( self, controllers, switches, hosts ): self.controllers = controllers self.switches = switches self.hosts = hosts self.nodemap = {} self.nodelist = controllers + switches + hosts for node in self.nodelist: self.nodemap[ node.name ] = node self.run() # Commands def help( self, args ): "Semi-useful help for CLI" print "Available commands are:", self.cmds print print "You may also send a command to a node using:" print " <node> command {args}" print "For example:" print " mininet> h0 ifconfig" print print "The interpreter automatically substitutes IP addresses" print "for node names, so commands like" print " mininet> h0 ping -c1 h1" print "should work." print print "Interactive commands are not really supported yet," print "so please limit commands to ones that do not" print "require user interaction, and that will terminate" print "after a reasonable amount of time." def nodes( self, args ): "List available nodes" print "available nodes are:", [ node.name for node in self.nodelist] def sh( self, args ): "Run an external shell command" call( [ 'sh', '-c' ] + args ) def pingtest( self, args ): pingTest( self.controllers, self.switches, self.hosts, verbose=True ) def net( self, args ): for switch in self.switches: print switch.name, "<->", for intf in switch.intfs: node, rintf = switch.connection[ intf ] print node.name, print def iperf( self, args ): print "iperf: got args", args if len( args ) != 2: print "usage: iperf <h1> <h2>" return for host in args: if host not in self.nodemap: print "iperf: cannot find host:", host return iperf( [ self.nodemap[ h ] for h in args ] ) # Interpreter def run( self ): "Read and execute commands." print "*** cli: starting" while True: print "mininet> ", ; flush() input = sys.stdin.readline() if input == '': break if input[ -1 ] == '\n': input = input[ : -1 ] cmd = input.split( ' ' ) first = cmd[ 0 ] rest = cmd[ 1: ] if first in self.cmds and hasattr( self, first ): getattr( self, first )( rest ) elif first in self.nodemap and rest != []: node = self.nodemap[ first ] # Substitute IP addresses for node names in command rest = [ self.nodemap[ arg ].IP() if arg in self.nodemap else arg for arg in rest ] rest = ' '.join( rest ) # Interactive commands don't work yet, and # there are still issues with control-c print "***", node.name, ": running", rest node.sendCmd( rest ) while True: try: done, data = node.monitor() print data, if done: break except KeyboardInterrupt: node.sendInt() print elif first == '': pass elif first in [ 'exit', 'quit' ]: break elif first == '?': self.help( rest ) else: print "cli: unknown node or command: <", first, ">" print "*** cli: exiting" def fixLimits(): "Fix ridiculously small resource limits." setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) ) setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) ) def init(): "Initialize Mininet." # Note: this script must be run as root # Perhaps we should do so automatically! if os.getuid() != 0: print "*** Mininet must run as root."; exit( 1 ) fixLimits() if __name__ == '__main__': init() results = {} exit( 1 ) print "*** Testing Mininet with kernel and user datapath" for datapath in [ 'kernel', 'user' ]: k = datapath == 'kernel' # results += [ TreeNet( depth=2, fanout=2, kernel=k ). # run( pingTestVerbose ) ] results[ datapath ] = [] for switchCount in range( 1, 4 ): results[ datapath ] += [ ( switchCount, LinearNet( switchCount, k).run( iperfTest ) ) ] # GridNet( 2, 2 ).run( Cli ) print "*** Test results:", results