Removed underscores for public Node methods. Minor cleanup & comments.
This commit is contained in:
+7
-7
@@ -4,12 +4,12 @@
|
|||||||
Mininet Cleanup
|
Mininet Cleanup
|
||||||
author: Bob Lantz (rlantz@cs.stanford.edu)
|
author: Bob Lantz (rlantz@cs.stanford.edu)
|
||||||
|
|
||||||
Unfortunately, Mininet and OpenFlow don't always clean up
|
Unfortunately, Mininet and OpenFlow (and the Linux kernel)
|
||||||
properly after themselves. Until they do (or until cleanup
|
don't always clean up properly after themselves. Until they do
|
||||||
functionality is integrated into the python code), this
|
(or until cleanup functionality is integrated into the Python
|
||||||
script may be used to get rid of unwanted garbage. It may
|
code), this script may be used to get rid of unwanted garbage.
|
||||||
also get rid of 'false positives', but hopefully nothing
|
It may also get rid of 'false positives', but hopefully
|
||||||
irreplaceable!
|
nothing irreplaceable!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
@@ -28,7 +28,7 @@ def cleanup():
|
|||||||
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
||||||
zombies += 'udpbwtest'
|
zombies += 'udpbwtest'
|
||||||
# Note: real zombie processes can't actually be killed, since they
|
# Note: real zombie processes can't actually be killed, since they
|
||||||
# are already ( un )dead. Then again,
|
# are already (un)dead. Then again,
|
||||||
# you can't connect to them either, so they're mostly harmless.
|
# you can't connect to them either, so they're mostly harmless.
|
||||||
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
||||||
|
|
||||||
|
|||||||
+23
-24
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Test bandwidth (using iperf) on linear networks of varying size,
|
Test bandwidth (using iperf) on linear networks of varying size,
|
||||||
using both kernel and user datapaths.
|
using both kernel and user datapaths.
|
||||||
|
|
||||||
We construct a network of N hosts and N-1 switches, connected as follows:
|
We construct a network of N hosts and N-1 switches, connected as follows:
|
||||||
@@ -9,7 +9,7 @@ We construct a network of N hosts and N-1 switches, connected as follows:
|
|||||||
h1 <-> sN+1 <-> sN+2 .. sN+N-1
|
h1 <-> sN+1 <-> sN+2 .. sN+N-1
|
||||||
| | |
|
| | |
|
||||||
h2 h3 hN
|
h2 h3 hN
|
||||||
|
|
||||||
Note: by default, the reference controller only supports 16
|
Note: by default, the reference controller only supports 16
|
||||||
switches, so this test WILL NOT WORK unless you have recompiled
|
switches, so this test WILL NOT WORK unless you have recompiled
|
||||||
your controller to support 100 switches (or more.)
|
your controller to support 100 switches (or more.)
|
||||||
@@ -25,9 +25,9 @@ of switches, this example demonstrates:
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
flush = sys.stdout.flush
|
flush = sys.stdout.flush
|
||||||
|
|
||||||
from mininet.net import init, Mininet
|
from mininet.net import init, Mininet
|
||||||
from mininet.node import Host, KernelSwitch, UserSwitch
|
from mininet.node import KernelSwitch, UserSwitch
|
||||||
from mininet.topo import Topo, Node
|
from mininet.topo import Topo, Node
|
||||||
from mininet.log import lg
|
from mininet.log import lg
|
||||||
|
|
||||||
@@ -35,26 +35,26 @@ class LinearTestTopo( Topo ):
|
|||||||
"Topology for a string of N hosts and N-1 switches."
|
"Topology for a string of N hosts and N-1 switches."
|
||||||
|
|
||||||
def __init__( self, N ):
|
def __init__( self, N ):
|
||||||
|
|
||||||
# Add default members to class.
|
# Add default members to class.
|
||||||
super( LinearTestTopo, self ).__init__()
|
super( LinearTestTopo, self ).__init__()
|
||||||
|
|
||||||
# Create switch and host nodes
|
# Create switch and host nodes
|
||||||
hosts = range( 1, N+1 )
|
hosts = range( 1, N + 1 )
|
||||||
switches = range( N+1, N+N )
|
switches = range( N + 1 , N + N )
|
||||||
for id in hosts:
|
for h in hosts:
|
||||||
self._add_node( id, Node( is_switch=False ) )
|
self.add_node( h, Node( is_switch=False ) )
|
||||||
for id in switches:
|
for s in switches:
|
||||||
self._add_node( id, Node( is_switch=True ) )
|
self.add_node( s, Node( is_switch=True ) )
|
||||||
|
|
||||||
# Wire up switches
|
# Wire up switches
|
||||||
for s in switches[ :-1 ]:
|
for s in switches[ :-1 ]:
|
||||||
self._add_edge( s, s + 1 )
|
self.add_edge( s, s + 1 )
|
||||||
|
|
||||||
# Wire up hosts
|
# Wire up hosts
|
||||||
self._add_edge( hosts[ 0 ], switches[ 0 ] )
|
self.add_edge( hosts[ 0 ], switches[ 0 ] )
|
||||||
for h in hosts[ 1: ]:
|
for h in hosts[ 1: ]:
|
||||||
self._add_edge( h, h+N-1 )
|
self.add_edge( h, h + N - 1 )
|
||||||
|
|
||||||
# Consider all switches and hosts 'on'
|
# Consider all switches and hosts 'on'
|
||||||
self.enable_all()
|
self.enable_all()
|
||||||
@@ -81,26 +81,25 @@ def linearBandwidthTest( lengths ):
|
|||||||
src, dst = net.hosts[ 0 ], net.hosts[ n ]
|
src, dst = net.hosts[ 0 ], net.hosts[ n ]
|
||||||
print "testing", src.name, "<->", dst.name
|
print "testing", src.name, "<->", dst.name
|
||||||
bandwidth = net.iperf( [ src, dst ] )
|
bandwidth = net.iperf( [ src, dst ] )
|
||||||
print bandwidth ; flush()
|
print bandwidth
|
||||||
|
flush()
|
||||||
results[ datapath ] += [ ( n, bandwidth ) ]
|
results[ datapath ] += [ ( n, bandwidth ) ]
|
||||||
net.stop()
|
net.stop()
|
||||||
|
|
||||||
for datapath in datapaths:
|
for datapath in datapaths:
|
||||||
print
|
print
|
||||||
print "*** Linear network results for", datapath, "datapath:"
|
print "*** Linear network results for", datapath, "datapath:"
|
||||||
print
|
print
|
||||||
result = results[ datapath ]
|
result = results[ datapath ]
|
||||||
print "SwitchCount\tiperf Results"
|
print "SwitchCount\tiperf Results"
|
||||||
for switchCount, bandwidth in result:
|
for switchCount, bandwidth in result:
|
||||||
print switchCount, '\t\t',
|
print switchCount, '\t\t',
|
||||||
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
|
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
|
||||||
print
|
print
|
||||||
print
|
print
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
lg.setLogLevel( 'info' )
|
lg.setLogLevel( 'info' )
|
||||||
init()
|
init()
|
||||||
print "*** Running linearBandwidthTest"
|
print "*** Running linearBandwidthTest"
|
||||||
linearBandwidthTest( [ 1, 10, 20 ] )
|
linearBandwidthTest( [ 1, 10, 20 ] )
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+10
-11
@@ -6,21 +6,20 @@ This is more complicated than using the higher-level classes,
|
|||||||
but it exposes the configuration details and allows customization.
|
but it exposes the configuration details and allows customization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from mininet.net import init
|
from mininet.net import init
|
||||||
from mininet.node import Node
|
from mininet.node import Node
|
||||||
from mininet.util import createLink
|
from mininet.util import createLink
|
||||||
from mininet.log import lg, info
|
from mininet.log import lg, info
|
||||||
|
|
||||||
def scratchNet( cname='controller', cargs='ptcp:'):
|
def scratchNet( cname='controller', cargs='ptcp:' ):
|
||||||
|
"Create network from scratch using kernel switch."
|
||||||
|
|
||||||
info( "*** Creating nodes\n" )
|
info( "*** Creating nodes\n" )
|
||||||
controller = Node( 'c0', inNamespace=False )
|
controller = Node( 'c0', inNamespace=False )
|
||||||
switch = Node( 's0', inNamespace=False )
|
switch = Node( 's0', inNamespace=False )
|
||||||
h0 = Node( 'h0' )
|
h0 = Node( 'h0' )
|
||||||
h1 = Node( 'h1' )
|
h1 = Node( 'h1' )
|
||||||
|
|
||||||
info( "*** Creating links\n" )
|
info( "*** Creating links\n" )
|
||||||
createLink( node1=h0, port1=0, node2=switch, port2=0 )
|
createLink( node1=h0, port1=0, node2=switch, port2=0 )
|
||||||
createLink( node1=h1, port1=0, node2=switch, port2=1 )
|
createLink( node1=h1, port1=0, node2=switch, port2=1 )
|
||||||
@@ -30,25 +29,25 @@ def scratchNet( cname='controller', cargs='ptcp:'):
|
|||||||
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
|
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
|
||||||
info( str( h0 ) + '\n' )
|
info( str( h0 ) + '\n' )
|
||||||
info( str( h1 ) + '\n' )
|
info( str( h1 ) + '\n' )
|
||||||
|
|
||||||
info( "*** Starting network using kernel datapath\n" )
|
info( "*** Starting network using kernel datapath\n" )
|
||||||
controller.cmd( cname + ' ' + cargs + '&' )
|
controller.cmd( cname + ' ' + cargs + '&' )
|
||||||
switch.cmd( 'dpctl deldp nl:0' )
|
switch.cmd( 'dpctl deldp nl:0' )
|
||||||
switch.cmd( 'dpctl adddp nl:0' )
|
switch.cmd( 'dpctl adddp nl:0' )
|
||||||
for intf in switch.intfs.values():
|
for intf in switch.intfs.values():
|
||||||
switch.cmd( 'dpctl addif nl:0 ' + intf )
|
switch.cmd( 'dpctl addif nl:0 ' + intf )
|
||||||
switch.cmd( 'ofprotocol nl:0 tcp:localhost &')
|
switch.cmd( 'ofprotocol nl:0 tcp:localhost &' )
|
||||||
|
|
||||||
info( "*** Running test\n" )
|
info( "*** Running test\n" )
|
||||||
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
|
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
|
||||||
|
|
||||||
info( "*** Stopping network\n" )
|
info( "*** Stopping network\n" )
|
||||||
controller.cmd( 'kill %' + cname)
|
controller.cmd( 'kill %' + cname )
|
||||||
switch.cmd( 'dpctl deldp nl:0' )
|
switch.cmd( 'dpctl deldp nl:0' )
|
||||||
switch.cmd( 'kill %ofprotocol' )
|
switch.cmd( 'kill %ofprotocol' )
|
||||||
switch.deleteIntfs()
|
switch.deleteIntfs()
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
lg.setLogLevel( 'info' )
|
lg.setLogLevel( 'info' )
|
||||||
info( '*** Scratch network demo (kernel datapath)\n' )
|
info( '*** Scratch network demo (kernel datapath)\n' )
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Build a simple network from scratch, using mininet primitives.
|
|||||||
This is more complicated than using the higher-level classes,
|
This is more complicated than using the higher-level classes,
|
||||||
but it exposes the configuration details and allows customization.
|
but it exposes the configuration details and allows customization.
|
||||||
|
|
||||||
This version uses the user datapath.
|
This version uses the user datapath and an explicit control network.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from mininet.net import init
|
from mininet.net import init
|
||||||
@@ -14,7 +14,8 @@ from mininet.util import createLink
|
|||||||
from mininet.log import lg, info
|
from mininet.log import lg, info
|
||||||
|
|
||||||
def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||||
# Create Network
|
"Create network from scratch using user switch."
|
||||||
|
|
||||||
# It's not strictly necessary for the controller and switches
|
# It's not strictly necessary for the controller and switches
|
||||||
# to be in separate namespaces. For performance, they probably
|
# to be in separate namespaces. For performance, they probably
|
||||||
# should be in the root namespace. However, it's interesting to
|
# should be in the root namespace. However, it's interesting to
|
||||||
@@ -32,15 +33,15 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
|||||||
info( '*** Configuring control network\n' )
|
info( '*** Configuring control network\n' )
|
||||||
controller.setIP( controller.intfs[ 0 ], '10.0.123.1', 24 )
|
controller.setIP( controller.intfs[ 0 ], '10.0.123.1', 24 )
|
||||||
switch.setIP( switch.intfs[ 0 ], '10.0.123.2', 24 )
|
switch.setIP( switch.intfs[ 0 ], '10.0.123.2', 24 )
|
||||||
|
|
||||||
info( '*** Configuring hosts\n' )
|
info( '*** Configuring hosts\n' )
|
||||||
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', 24 )
|
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', 24 )
|
||||||
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
|
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
|
||||||
|
|
||||||
info( '*** Network state:\n' )
|
info( '*** Network state:\n' )
|
||||||
for node in controller, switch, h0, h1:
|
for node in controller, switch, h0, h1:
|
||||||
info( str( node ) + '\n' )
|
info( str( node ) + '\n' )
|
||||||
|
|
||||||
info( '*** Starting controller and user datapath\n' )
|
info( '*** Starting controller and user datapath\n' )
|
||||||
controller.cmd( cname + ' ' + cargs + '&' )
|
controller.cmd( cname + ' ' + cargs + '&' )
|
||||||
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
||||||
@@ -57,9 +58,9 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
|||||||
switch.cmd( 'kill %ofprotocol' )
|
switch.cmd( 'kill %ofprotocol' )
|
||||||
switch.deleteIntfs()
|
switch.deleteIntfs()
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
lg.setLogLevel( 'info' )
|
lg.setLogLevel( 'info' )
|
||||||
info( '*** Scratch network demo (user datapath)\n' )
|
info( '*** Scratch network demo (user datapath)\n' )
|
||||||
init()
|
init()
|
||||||
scratchNetUser()
|
scratchNetUser()
|
||||||
|
|||||||
+36
-33
@@ -27,45 +27,48 @@ def TreeNet( depth=1, fanout=2, **kwargs ):
|
|||||||
"Convenience function for creating tree networks."
|
"Convenience function for creating tree networks."
|
||||||
topo = TreeTopo( depth, fanout )
|
topo = TreeTopo( depth, fanout )
|
||||||
return Mininet( topo, **kwargs )
|
return Mininet( topo, **kwargs )
|
||||||
|
|
||||||
def connectToRootNS( network, switch, ip, prefixLen, routes ):
|
def connectToRootNS( network, switch, ip, prefixLen, routes ):
|
||||||
"""Connect hosts to root namespace via switch. Starts network.
|
"""Connect hosts to root namespace via switch. Starts network.
|
||||||
network: Mininet() network object
|
network: Mininet() network object
|
||||||
switch: switch to connect to root namespace
|
switch: switch to connect to root namespace
|
||||||
ip: IP address for root namespace node
|
ip: IP address for root namespace node
|
||||||
prefixLen: IP address prefix length (e.g. 8, 16, 24)
|
prefixLen: IP address prefix length (e.g. 8, 16, 24)
|
||||||
routes: host networks to route to"""
|
routes: host networks to route to"""
|
||||||
# Create a node in root namespace and link to switch 0
|
# Create a node in root namespace and link to switch 0
|
||||||
root = Node( 'root', inNamespace=False )
|
root = Node( 'root', inNamespace=False )
|
||||||
port = max( switch.ports.values() ) + 1
|
port = max( switch.ports.values() ) + 1
|
||||||
createLink( root, 0, switch, port )
|
createLink( root, 0, switch, port )
|
||||||
root.setIP( root.intfs[ 0 ], ip, prefixLen )
|
root.setIP( root.intfs[ 0 ], ip, prefixLen )
|
||||||
# Start network that now includes link to root namespace
|
# Start network that now includes link to root namespace
|
||||||
network.start()
|
network.start()
|
||||||
intf = root.intfs[ 0 ]
|
intf = root.intfs[ 0 ]
|
||||||
# Add routes from root ns to hosts
|
# Add routes from root ns to hosts
|
||||||
for net in routes:
|
for route in routes:
|
||||||
root.cmd( 'route add -net ' + net + ' dev ' + intf )
|
root.cmd( 'route add -net ' + route + ' dev ' + intf )
|
||||||
|
|
||||||
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
|
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
|
||||||
"Start a network, connect it to root ns, and run sshd on all hosts."
|
"Start a network, connect it to root ns, and run sshd on all hosts."
|
||||||
switch = network.switches[ 0 ] # switch to use
|
switch = network.switches[ 0 ] # switch to use
|
||||||
ip = '10.123.123.1' # our IP address on host network
|
ip = '10.123.123.1' # our IP address on host network
|
||||||
routes = [ '10.0.0.0/8' ] # host networks to route to
|
routes = [ '10.0.0.0/8' ] # host networks to route to
|
||||||
connectToRootNS( network, switch, ip, 8, routes )
|
connectToRootNS( network, switch, ip, 8, routes )
|
||||||
for host in network.hosts: host.cmd( cmd + ' ' + opts + '&' )
|
for host in network.hosts:
|
||||||
print
|
host.cmd( cmd + ' ' + opts + '&' )
|
||||||
print "*** Hosts are running sshd at the following addresses:"
|
print
|
||||||
print
|
print "*** Hosts are running sshd at the following addresses:"
|
||||||
for host in network.hosts: print host.name, host.IP()
|
print
|
||||||
print
|
for host in network.hosts:
|
||||||
print "*** Type 'exit' or control-D to shut down network"
|
print host.name, host.IP()
|
||||||
CLI( network )
|
print
|
||||||
for host in network.hosts: host.cmd( 'kill %' + cmd )
|
print "*** Type 'exit' or control-D to shut down network"
|
||||||
network.stop()
|
CLI( network )
|
||||||
|
for host in network.hosts:
|
||||||
|
host.cmd( 'kill %' + cmd )
|
||||||
|
network.stop()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
lg.setLogLevel( 'info')
|
lg.setLogLevel( 'info')
|
||||||
init()
|
init()
|
||||||
network = TreeNet( depth=1, fanout=4, switch=KernelSwitch )
|
net = TreeNet( depth=1, fanout=4, switch=KernelSwitch )
|
||||||
sshd( network )
|
sshd( net )
|
||||||
|
|||||||
+97
-75
@@ -20,99 +20,121 @@ import os
|
|||||||
import re
|
import re
|
||||||
import select
|
import select
|
||||||
import sys
|
import sys
|
||||||
import time
|
from time import time
|
||||||
|
|
||||||
flush = sys.stdout.flush
|
flush = sys.stdout.flush
|
||||||
|
|
||||||
from mininet.log import lg
|
from mininet.log import lg
|
||||||
from mininet.net import init, Mininet
|
from mininet.net import init, Mininet
|
||||||
from mininet.node import Host, KernelSwitch
|
from mininet.node import KernelSwitch
|
||||||
from mininet.topolib import TreeTopo
|
from mininet.topolib import TreeTopo
|
||||||
from mininet.util import quietRun
|
from mininet.util import quietRun
|
||||||
|
|
||||||
# Some useful stuff: buffered readline and host monitoring
|
# Some useful stuff: buffered readline and host monitoring
|
||||||
|
|
||||||
def readline( host, buffer ):
|
def readline( host, buf ):
|
||||||
"Read a line from a host, buffering with buffer."
|
"Read a line from a host, buffering with buffer."
|
||||||
buffer += host.read( 1024 )
|
buf += host.read( 1024 )
|
||||||
if '\n' not in buffer: return None, buffer
|
if '\n' not in buffer:
|
||||||
pos = buffer.find( '\n' )
|
return None, buffer
|
||||||
line = buffer[ 0 : pos ]
|
pos = buf.find( '\n' )
|
||||||
rest = buffer[ pos + 1 :]
|
line = buf[ 0 : pos ]
|
||||||
return line, rest
|
rest = buf[ pos + 1: ]
|
||||||
|
return line, rest
|
||||||
|
|
||||||
def monitor( hosts, seconds ):
|
def monitor( hosts, seconds ):
|
||||||
"Monitor a set of hosts and yield their output."
|
"Monitor a set of hosts and yield their output."
|
||||||
poller = select.poll()
|
poller = select.poll()
|
||||||
Node = hosts[ 0 ] # so we can call class method fdToNode
|
Node = hosts[ 0 ] # so we can call class method fdToNode
|
||||||
buffers = {}
|
buffers = {}
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
poller.register( host.stdout )
|
poller.register( host.stdout )
|
||||||
buffers[ host ] = ''
|
buffers[ host ] = ''
|
||||||
quitTime = time.time() + seconds
|
quitTime = time() + seconds
|
||||||
while time.time() < quitTime:
|
while time() < quitTime:
|
||||||
ready = poller.poll()
|
ready = poller.poll()
|
||||||
for fd, event in ready:
|
for fd, event in ready:
|
||||||
host = Node.fdToNode( fd )
|
host = Node.fdToNode( fd )
|
||||||
line, buffers[ host ] = readline( host, buffers[ host ] )
|
if event & select.POLLIN:
|
||||||
if line: yield host, line
|
line, buffers[ host ] = readline( host, buffers[ host ] )
|
||||||
yield None, ''
|
if line:
|
||||||
|
yield host, line
|
||||||
|
yield None, ''
|
||||||
|
|
||||||
# bwtest support
|
# bwtest support
|
||||||
|
|
||||||
def parsebwtest( line,
|
def parsebwtest( line,
|
||||||
r=re.compile( r'(\d+) s: in ([\d\.]+) Mbps, out ([\d\.]+) Mbps' ) ):
|
r=re.compile( r'(\d+) s: in ([\d\.]+) Mbps, out ([\d\.]+) Mbps' ) ):
|
||||||
match = r.match( line )
|
"Parse udpbwtest.c output, returning seconds, inbw, outbw."
|
||||||
return match.group( 1, 2, 3 ) if match else ( None, None, None )
|
match = r.match( line )
|
||||||
|
if match:
|
||||||
|
seconds, inbw, outbw = match.group( 1, 2, 3 )
|
||||||
|
return int( seconds ), float( inbw ), float( outbw )
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
def printTotalHeader():
|
def printTotalHeader():
|
||||||
print
|
"Print header for bandwidth stats."
|
||||||
print "time(s)\thosts\ttotal in/out (Mbps)\tavg in/out (Mbps)"
|
print
|
||||||
|
print "time(s)\thosts\ttotal in/out (Mbps)\tavg in/out (Mbps)"
|
||||||
|
|
||||||
|
# Annoyingly, pylint isn't smart enough to notice
|
||||||
|
# when an unused variable is an iteration tuple
|
||||||
|
# pylint: disable-msg=W0612
|
||||||
|
|
||||||
|
def printTotal( seconds=None, result=None ):
|
||||||
|
"Compute and print total bandwidth for given results set."
|
||||||
|
intotal = outtotal = 0.0
|
||||||
|
count = len( result )
|
||||||
|
for host, inbw, outbw in result:
|
||||||
|
intotal += inbw
|
||||||
|
outtotal += outbw
|
||||||
|
inavg = intotal / count if count > 0 else 0
|
||||||
|
outavg = outtotal / count if count > 0 else 0
|
||||||
|
print '%d\t%d\t%.2f/%.2f\t\t%.2f/%.2f' % ( seconds, count,
|
||||||
|
intotal, outtotal, inavg, outavg )
|
||||||
|
|
||||||
|
# pylint: enable-msg=W0612
|
||||||
|
|
||||||
|
# Pylint also isn't smart enough to understand iterator.next()
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
|
|
||||||
def printTotal( time=None, result=None ):
|
|
||||||
intotal = outtotal = 0.0
|
|
||||||
count = len( result )
|
|
||||||
for host, inbw, outbw in result:
|
|
||||||
intotal += inbw
|
|
||||||
outtotal += outbw
|
|
||||||
inavg = intotal / count if count > 0 else 0
|
|
||||||
outavg = outtotal / count if count > 0 else 0
|
|
||||||
print '%d\t%d\t%.2f/%.2f\t\t%.2f/%.2f' % ( time, count, intotal, outtotal,
|
|
||||||
inavg, outavg )
|
|
||||||
|
|
||||||
def udpbwtest( net, seconds ):
|
def udpbwtest( net, seconds ):
|
||||||
"Start up and monitor udpbwtest on each of our hosts."
|
"Start up and monitor udpbwtest on each of our hosts."
|
||||||
hosts, switches = net.hosts, net.switches
|
hosts = net.hosts
|
||||||
hostCount = len( hosts )
|
hostCount = len( hosts )
|
||||||
print "*** Starting udpbwtest on hosts"
|
print "*** Starting udpbwtest on hosts"
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
ips = [ h.IP() for h in hosts if h != host ]
|
ips = [ h.IP() for h in hosts if h != host ]
|
||||||
print host.name, ; flush()
|
print host.name,
|
||||||
host.cmd( './udpbwtest ' + ' '.join( ips ) + ' &' )
|
flush()
|
||||||
print
|
host.cmd( './udpbwtest ' + ' '.join( ips ) + ' &' )
|
||||||
results = {}
|
print
|
||||||
print "*** Monitoring hosts"
|
results = {}
|
||||||
output = monitor( hosts, seconds )
|
print "*** Monitoring hosts"
|
||||||
while True:
|
output = monitor( hosts, seconds )
|
||||||
host, line = output.next()
|
while True:
|
||||||
if host is None: break
|
host, line = output.next()
|
||||||
time, inbw, outbw = parsebwtest( line )
|
if host is None:
|
||||||
if time is not None:
|
break
|
||||||
time, inbw, outbw = int( time ), float( inbw ), float( outbw )
|
seconds, inbw, outbw = parsebwtest( line )
|
||||||
result = results.get( time, [] ) + [ ( host, inbw, outbw ) ]
|
if seconds is not None:
|
||||||
if len( result ) == hostCount: printTotal( time, result )
|
result = results.get( seconds, [] ) + [ ( host, inbw, outbw ) ]
|
||||||
results[ time ] = result
|
if len( result ) == hostCount:
|
||||||
print "*** Stopping udpbwtest processes"
|
printTotal( seconds, result )
|
||||||
# We *really* don't want these things hanging around!
|
results[ seconds ] = result
|
||||||
quietRun( 'killall -9 udpbwtest' )
|
print "*** Stopping udpbwtest processes"
|
||||||
print
|
# We *really* don't want these things hanging around!
|
||||||
print "*** Results:"
|
quietRun( 'killall -9 udpbwtest' )
|
||||||
printTotalHeader()
|
print
|
||||||
times = sorted( results.keys() )
|
print "*** Results:"
|
||||||
for time in times:
|
printTotalHeader()
|
||||||
printTotal( time - times[ 0 ] , results[ time ] )
|
times = sorted( results.keys() )
|
||||||
print
|
for t in times:
|
||||||
|
printTotal( t - t[ 0 ] , results[ t ] )
|
||||||
|
print
|
||||||
|
|
||||||
|
# pylint: enable-msg=E1101
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
lg.setLogLevel( 'info' )
|
lg.setLogLevel( 'info' )
|
||||||
if not os.path.exists( './udpbwtest' ):
|
if not os.path.exists( './udpbwtest' ):
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ def makeListCompatible( fn ):
|
|||||||
newfn( 'a', 1, 'b' )"""
|
newfn( 'a', 1, 'b' )"""
|
||||||
|
|
||||||
def newfn( *args ):
|
def newfn( *args ):
|
||||||
"Generated function."
|
"Generated function. Closure-ish."
|
||||||
if len( args ) == 1:
|
if len( args ) == 1:
|
||||||
return fn( *args )
|
return fn( *args )
|
||||||
args = ' '.join( [ str( arg ) for arg in args ] )
|
args = ' '.join( [ str( arg ) for arg in args ] )
|
||||||
|
|||||||
+21
-16
@@ -49,27 +49,34 @@ which interfaces belong to which node.
|
|||||||
|
|
||||||
The basic naming scheme is as follows:
|
The basic naming scheme is as follows:
|
||||||
|
|
||||||
Host nodes are named h0-hN
|
Host nodes are named h1-hN
|
||||||
Switch nodes are named s0-sN
|
Switch nodes are named s1-sN
|
||||||
Controller nodes are named c0-cN
|
Controller nodes are named c0-cN
|
||||||
Interfaces are named {nodename}-eth0 .. {nodename}-ethN
|
Interfaces are named {nodename}-eth0 .. {nodename}-ethN
|
||||||
|
|
||||||
|
Note: If the network topology is created using mininet.topo, then
|
||||||
|
node numbers are unique among hosts and switches (e.g. we have
|
||||||
|
h1..hN and SN..SN+M) and also correspond to their default IP addresses
|
||||||
|
of 10.x.y.z/8 where x.y.z is the base-256 representation of N for
|
||||||
|
hN. This mapping allows easy determination of a node's IP
|
||||||
|
address from its name, e.g. h1 -> 10.0.0.1, h257 -> 10.0.1.1.
|
||||||
|
|
||||||
Currently we wrap the entire network in a 'mininet' object, which
|
Currently we wrap the entire network in a 'mininet' object, which
|
||||||
constructs a simulated network based on a network topology created
|
constructs a simulated network based on a network topology created
|
||||||
using a topology object (e.g. LinearTopo) from topo.py and a Controller
|
using a topology object (e.g. LinearTopo) from mininet.topo or
|
||||||
node which the switches will connect to. Several
|
mininet.topolib, and a Controller which the switches will connect
|
||||||
configuration options are provided for functions such as
|
to. Several configuration options are provided for functions such as
|
||||||
automatically setting MAC addresses, populating the ARP table, or
|
automatically setting MAC addresses, populating the ARP table, or
|
||||||
even running a set of xterms to allow direct interaction with nodes.
|
even running a set of xterms to allow direct interaction with nodes.
|
||||||
|
|
||||||
After the mininet is created, it can be started using start(), and a variety
|
After the network is created, it can be started using start(), and a
|
||||||
of useful tasks maybe performed, including basic connectivity and
|
variety of useful tasks maybe performed, including basic connectivity
|
||||||
bandwidth tests and running the mininet CLI.
|
and bandwidth tests and running the mininet CLI.
|
||||||
|
|
||||||
Once the network is up and running, test code can easily get access
|
Once the network is up and running, test code can easily get access
|
||||||
to host and switch objects, which can then be used
|
to host and switch objects which can then be used for arbitrary
|
||||||
for arbitrary experiments, typically involving running a series of
|
experiments, typically involving running a series of commands on the
|
||||||
commands on the hosts.
|
hosts.
|
||||||
|
|
||||||
After all desired tests or activities have been completed, the stop()
|
After all desired tests or activities have been completed, the stop()
|
||||||
method may be called to shut down the network.
|
method may be called to shut down the network.
|
||||||
@@ -187,10 +194,8 @@ class Mininet( object ):
|
|||||||
#
|
#
|
||||||
# Notes:
|
# Notes:
|
||||||
#
|
#
|
||||||
# 1. If the controller and switches are in the same ( e.g. root )
|
# 1. If the controller and switches are in the same (e.g. root)
|
||||||
# namespace, they can just use the loopback connection.
|
# namespace, they can just use the loopback connection.
|
||||||
# We may wish to do this for the user datapath as well as the
|
|
||||||
# kernel datapath.
|
|
||||||
#
|
#
|
||||||
# 2. If we can get unix domain sockets to work, we can use them
|
# 2. If we can get unix domain sockets to work, we can use them
|
||||||
# instead of an explicit control network.
|
# instead of an explicit control network.
|
||||||
@@ -244,7 +249,7 @@ class Mininet( object ):
|
|||||||
exit( 1 )
|
exit( 1 )
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
def _configHosts( self ):
|
def configHosts( self ):
|
||||||
"Configure a set of hosts."
|
"Configure a set of hosts."
|
||||||
# params were: hosts, ips
|
# params were: hosts, ips
|
||||||
for host in self.hosts:
|
for host in self.hosts:
|
||||||
@@ -294,7 +299,7 @@ class Mininet( object ):
|
|||||||
self._configureControlNetwork()
|
self._configureControlNetwork()
|
||||||
|
|
||||||
info( '*** Configuring hosts\n' )
|
info( '*** Configuring hosts\n' )
|
||||||
self._configHosts()
|
self.configHosts()
|
||||||
|
|
||||||
if self.xterms:
|
if self.xterms:
|
||||||
self.startXterms()
|
self.startXterms()
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ RemoteController: a remote controller node, which may use any
|
|||||||
arbitrary OpenFlow-compatible controller, and which is not
|
arbitrary OpenFlow-compatible controller, and which is not
|
||||||
created or managed by mininet.
|
created or managed by mininet.
|
||||||
|
|
||||||
|
Future enhancements:
|
||||||
|
|
||||||
|
- Possibly make Node, Switch and Controller more abstract so that
|
||||||
|
they can be used for both local and remote nodes
|
||||||
|
|
||||||
|
- Create proxy objects for remote nodes (Mininet: Cluster Edition)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
+11
-11
@@ -98,7 +98,7 @@ class Topo(object):
|
|||||||
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
||||||
self.id_gen = NodeID # class used to generate dpid
|
self.id_gen = NodeID # class used to generate dpid
|
||||||
|
|
||||||
def _add_node(self, dpid, node):
|
def add_node(self, dpid, node):
|
||||||
'''Add Node to graph.
|
'''Add Node to graph.
|
||||||
|
|
||||||
@param dpid dpid
|
@param dpid dpid
|
||||||
@@ -107,7 +107,7 @@ class Topo(object):
|
|||||||
self.g.add_node(dpid)
|
self.g.add_node(dpid)
|
||||||
self.node_info[dpid] = node
|
self.node_info[dpid] = node
|
||||||
|
|
||||||
def _add_edge(self, src, dst, edge = None):
|
def add_edge(self, src, dst, edge = None):
|
||||||
'''Add edge (Node, Node) to graph.
|
'''Add edge (Node, Node) to graph.
|
||||||
|
|
||||||
@param src src dpid
|
@param src src dpid
|
||||||
@@ -119,9 +119,9 @@ class Topo(object):
|
|||||||
if not edge:
|
if not edge:
|
||||||
edge = Edge()
|
edge = Edge()
|
||||||
self.edge_info[(src, dst)] = edge
|
self.edge_info[(src, dst)] = edge
|
||||||
self._add_port(src, dst)
|
self.add_port(src, dst)
|
||||||
|
|
||||||
def _add_port(self, src, dst):
|
def add_port(self, src, dst):
|
||||||
'''Generate port mapping for new edge.
|
'''Generate port mapping for new edge.
|
||||||
|
|
||||||
@param src source switch DPID
|
@param src source switch DPID
|
||||||
@@ -329,11 +329,11 @@ class SingleSwitchTopo(Topo):
|
|||||||
|
|
||||||
self.k = k
|
self.k = k
|
||||||
|
|
||||||
self._add_node(1, Node())
|
self.add_node(1, Node())
|
||||||
hosts = range(2, k + 2)
|
hosts = range(2, k + 2)
|
||||||
for h in hosts:
|
for h in hosts:
|
||||||
self._add_node(h, Node(is_switch = False))
|
self.add_node(h, Node(is_switch = False))
|
||||||
self._add_edge(h, 1, Edge())
|
self.add_edge(h, 1, Edge())
|
||||||
|
|
||||||
if enable_all:
|
if enable_all:
|
||||||
self.enable_all()
|
self.enable_all()
|
||||||
@@ -388,12 +388,12 @@ class LinearTopo(Topo):
|
|||||||
switches = range(1, k + 1)
|
switches = range(1, k + 1)
|
||||||
for s in switches:
|
for s in switches:
|
||||||
h = s + k
|
h = s + k
|
||||||
self._add_node(s, Node())
|
self.add_node(s, Node())
|
||||||
self._add_node(h, Node(is_switch = False))
|
self.add_node(h, Node(is_switch = False))
|
||||||
self._add_edge(s, h, Edge())
|
self.add_edge(s, h, Edge())
|
||||||
for s in switches:
|
for s in switches:
|
||||||
if s != k:
|
if s != k:
|
||||||
self._add_edge(s, s + 1, Edge())
|
self.add_edge(s, s + 1, Edge())
|
||||||
|
|
||||||
if enable_all:
|
if enable_all:
|
||||||
self.enable_all()
|
self.enable_all()
|
||||||
|
|||||||
+2
-2
@@ -20,11 +20,11 @@ class TreeTopo( Topo ):
|
|||||||
returns: last node added"""
|
returns: last node added"""
|
||||||
me = n
|
me = n
|
||||||
isSwitch = depth > 0
|
isSwitch = depth > 0
|
||||||
self._add_node( me, Node( is_switch=isSwitch ) )
|
self.add_node( me, Node( is_switch=isSwitch ) )
|
||||||
if isSwitch:
|
if isSwitch:
|
||||||
for i in range( 0, fanout ):
|
for i in range( 0, fanout ):
|
||||||
child = n + 1
|
child = n + 1
|
||||||
self._add_edge( me, child )
|
self.add_edge( me, child )
|
||||||
n = self.addTree( child, depth-1, fanout )
|
n = self.addTree( child, depth-1, fanout )
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -141,7 +141,7 @@ def macColonHex( mac ):
|
|||||||
return _colonHex( mac, 6 )
|
return _colonHex( mac, 6 )
|
||||||
|
|
||||||
def ipStr( ip ):
|
def ipStr( ip ):
|
||||||
"""Generate IP address string
|
"""Generate IP address string from an unsigned int
|
||||||
ip: unsigned int of form x << 16 | y << 8 | z
|
ip: unsigned int of form x << 16 | y << 8 | z
|
||||||
returns: ip address string 10.x.y.z """
|
returns: ip address string 10.x.y.z """
|
||||||
hi = ( ip & 0xff0000 ) >> 16
|
hi = ( ip & 0xff0000 ) >> 16
|
||||||
|
|||||||
Reference in New Issue
Block a user