Compare commits

...

128 Commits

Author SHA1 Message Date
Nikhil Handigol d8137a9219 do system monitoring only if net.set_debug() is called 2012-05-02 01:02:48 +00:00
Nikhil Handigol 891ea4854d create output dir only upon starting monitoring 2012-05-02 00:55:42 +00:00
Nikhil Handigol 2e0e2d97ec cwnd monitoring via tcp_probe 2012-04-27 03:38:50 +00:00
Nikhil Handigol 71b3128122 move obsolete functions to the bottom 2012-04-27 03:21:32 +00:00
Nikhil Handigol 5ca1fb7e0f monitoring output dir name change 2012-04-27 03:14:20 +00:00
Nikhil Handigol 18a0b7e4ac cpuacct monitoring 2012-04-27 03:13:53 +00:00
Nikhil Handigol e27673aeec cleanup afterwards 2012-04-27 02:56:04 +00:00
Nikhil Handigol 035d4d20fe integrating monitoring code into net -- tested 2012-04-27 02:42:22 +00:00
Nikhil Handigol 550ee2469e code to monitor cpuacct 2012-04-17 06:59:21 +00:00
Nikhil Handigol d8d9035242 move monitoring functions to a separate class 2012-04-16 21:05:54 +00:00
Nikhil Handigol f1aca0d9a6 basic monitoring script 2012-04-16 21:05:22 +00:00
Bob Lantz 669e420cc4 Add default value mems=0 for memory placement. 2012-04-13 07:38:35 +00:00
Bob Lantz 5507550c53 Fix wireshark dissector install on 11.10 2012-04-11 13:20:18 -07:00
Bob Lantz 7cb340b7c9 Pass code check. 2012-04-10 00:12:37 +00:00
Bob Lantz 92b601aba9 Allow fail-mode to be set.
Probably we should have a generic mechanism to specify OVS options.
2012-04-10 00:12:03 +00:00
Bob Lantz 350fdbfe50 Allow modules (node.py) to be 1500 lines. Maybe reduce this someday. 2012-04-10 00:02:56 +00:00
Bob Lantz 548580d817 Allow lists of nodes to be passed to getNodeByName
....which should perhaps be renamed!!!
2012-04-09 23:54:17 +00:00
Bob Lantz 149a1f5639 Apparently errRun isn't as flexible as I thought... 2012-04-08 20:49:21 -07:00
Bob Lantz 197b083fbc Add static cpu (and memory) assignment. 2012-04-08 20:49:12 -07:00
Bob Lantz a7648e78fb Add mountCgroups() and tweak/correct fixLimits() 2012-04-08 20:49:04 -07:00
Bob Lantz 50202e1246 Off by one... I dislike range() 2012-04-05 21:38:02 -07:00
Bob Lantz a4338de38c Fix error message. 2012-04-05 20:57:29 -07:00
Bob Lantz d08d101eba Added simpleperf.py to examples. 2012-04-03 18:49:39 -07:00
Bob Lantz 32f7847bca Change doxypy.py to doxypy. 2012-04-03 17:50:59 -07:00
Bob Lantz 1bb990357f Added multipoll and multiping examples. 2012-04-03 17:32:55 -07:00
Bob Lantz a9c28885f3 Bring up loopback interface when configuring hosts. 2012-04-03 17:22:28 -07:00
Bob Lantz d776bd3a4f Add handle 10: to netem for hifi compat, reconfiguration. 2012-04-02 16:39:37 -07:00
Bob Lantz 5d6fda932d Add openvswitch-datapath-dkms if no datapath installed. 2012-03-31 21:30:16 -07:00
Bob Lantz c1a6ae2b48 Remove blank line. 2012-03-31 21:29:56 -07:00
Bob Lantz 78606a35c9 Removed unused param in add_link. 2012-03-31 21:29:27 -07:00
Bob Lantz 1dd3de0d04 Remove unused burst. 2012-03-31 21:29:09 -07:00
Bob Lantz ece14ff4b5 Check out CS244 branch for class. 2012-03-31 20:48:12 -07:00
Bob Lantz e5653fb63b Change back to match mininet-hifi, except for max_queue_len=1000. 2012-03-27 00:31:37 -07:00
Bob Lantz 3f61ea7104 Restore deleted deleteIntfs in OVSSwitch.stop() 2012-03-25 22:10:04 -07:00
Bob Lantz 2ec866d2c5 TCLink: pass correct parameters to superclass. 2012-03-25 20:18:35 -07:00
Bob Lantz 26c61734da Add cgroup and ethtool dependencies for mininet (w/hifi integration.) 2012-03-25 15:41:02 -07:00
Bob Lantz e1246c3741 Simplify port specification.
For the moment, I've removed the ability to specify
a dict of options without using **. This is a slightly
unfortunate trade-off since it simplifies implementation
at the expense of making the API slightly less convenient
(if somewhat more consistent.)
2012-03-25 15:39:18 -07:00
Bob Lantz 2d924f8a65 Add Mininet object to locals as 'net' 2012-03-25 15:38:32 -07:00
Bob Lantz 00d9b78035 Reinstate more complicated test. 2012-03-23 18:41:25 -07:00
Bob Lantz 612b21cbe7 Pass code check. 2012-03-23 18:38:49 -07:00
Bob Lantz 8139695d46 Use 's%s' for bw speedup; change burst to fix tbf and htb performance. 2012-03-23 18:37:32 -07:00
Bob Lantz e8146dd130 Change to allow addLink() without specifying ports. 2012-03-23 18:36:13 -07:00
Bob Lantz 44af37bc2b Change default period to 100 ms, which seems to help cfs at least...
rt is still somewhat broken.
2012-03-23 18:18:48 -07:00
Bob Lantz beb05a71c8 Move dumpNetConnections to util() because it's useful! 2012-03-23 18:17:49 -07:00
Bob Lantz 74ea006d92 Increase the quota and cpu fraction to get max cfs performance. 2012-03-23 18:17:08 -07:00
Bob Lantz 335ba99b60 Add --switch ovsl for legacy OVS. 2012-03-23 13:33:31 -07:00
Bob Lantz 8dcefd5ff9 Fix OVS legacy switch. 2012-03-23 13:33:12 -07:00
Bob Lantz 28833d864c Retry deleting cgroup for the moment because it seems flaky.
Ultimately we may wish to create a mininet/ cgroup and do a recursive
delete at the end.
2012-03-22 19:08:45 -07:00
Bob Lantz a5af91d0d3 Have errFail report cmd and stderr as well as exit code. 2012-03-22 19:08:09 -07:00
Bob Lantz 4deb735425 Simple cpu limiting example. 2012-03-22 14:44:20 -07:00
Bob Lantz f89d9a4d6b Fix typo inadvertently saved in editor. 2012-03-22 14:43:52 -07:00
Bob Lantz d1b29d58df Fix printing pid for background tasks. 2012-03-22 14:43:27 -07:00
Bob Lantz ba8d4f9bd6 Add verySimpleLimit() for debugging. 2012-03-21 23:31:20 -07:00
Bob Lantz 0b7c277ec2 Save parameters for future reference (e.g. OVS/tc workaround.) 2012-03-21 23:07:40 -07:00
Bob Lantz 1aec55d960 Workaround: reapply tc config after OVS destroys it. 2012-03-21 22:39:46 -07:00
Bob Lantz 595427842c Make CPULimitedHost method sig friendlier, and make 'cfs' default sched. 2012-03-21 17:28:00 -07:00
Bob Lantz 41245f5087 Add getNodeByName for hifi compatibility. 2012-03-21 17:27:40 -07:00
Bob Lantz b684ff7844 Fix convenience configuration methods. 2012-03-20 16:23:17 -07:00
Bob Lantz ea7c326017 Ignore emacs autosaves. 2012-03-20 15:50:44 -07:00
Bob Lantz f85c1cefb4 Use upstream OVS packages. 2012-03-20 15:49:39 -07:00
Bob Lantz 9005ce3255 Whitespace fixes. 2012-03-20 15:48:06 -07:00
Bob Lantz efc991547e Add warning in defaultIntf() if host has no interfaces.
Possibly this should be in intf() instead, as intf() is assumed
to always succeed.
2012-03-20 15:46:54 -07:00
Bob Lantz 8bebd37759 Fix is_switch() to always succeed + whitespace edits. 2012-03-20 15:45:46 -07:00
Bob Lantz e52d0ee1de Fix to work with new Topo class. 2012-03-20 15:44:50 -07:00
Bob Lantz ff56881946 Add TCLink for simplified tc-limited link creation. 2012-03-20 15:43:43 -07:00
Bob Lantz 5a8bb48951 Attempt at revised/simplified topo class:
- keys are strings
- metadata is simply a dict
- buildFromTopo greatly simplified
2012-03-20 00:17:30 -07:00
Bob Lantz 318ae55e35 Allow sendCmd( [ cmd, arg1, ... ] ) 2012-03-20 00:10:11 -07:00
Bob Lantz bf9c6ab7b4 Clarify comments and finally remove ControllerParams definition. 2012-03-12 00:29:55 -07:00
Bob Lantz 14c1926081 Use port 0 for control interface on switches. 2012-03-12 00:20:48 -07:00
Bob Lantz d7e5dfc5b6 Minor tweaks: specify port, new repr() 2012-03-12 00:20:26 -07:00
Bob Lantz 8856d284c0 Fix CLI commands. 2012-03-11 19:44:04 -07:00
Bob Lantz 14ff3ad3d0 Fix codecheck and MininetWithControlNet. 2012-03-10 20:44:34 -08:00
Bob Lantz 1d814c606d disabled-msg -> disabled for current pylint 2012-03-09 19:27:04 -08:00
Bob Lantz 82f483f559 Add support for specifying host IP range with --ipbase. 2012-03-09 17:44:47 -08:00
Bob Lantz a49c85a610 Fix examples to work with new API (and vice-versa.) 2012-03-09 16:06:23 -08:00
Bob Lantz 8e3699eca6 Move init() into Mininet() and remove calls (since called automatically.)
Note: we should probably rename it "setup()" to avoid confusion.
2012-03-09 14:10:20 -08:00
Bob Lantz e3c074b881 Remove deprecated ControllerParams (for now.) 2012-03-09 13:53:13 -08:00
Bob Lantz 9addfc13ce Add OVSController to complete out-of-box Ubuntu experience. 2012-03-08 23:48:07 -08:00
Bob Lantz d27a3c52b9 Allow various subsets of (delay, bw, loss) and clean up status output. 2012-03-08 22:08:40 -08:00
Bob Lantz 2db4268ba8 Fix NOX controller so that mn --controller nox,pyswitch,... works. 2012-03-08 22:07:19 -08:00
Bob Lantz 0dbfd3a636 Add CPULimitedHost to file comment. 2012-03-08 13:48:46 -08:00
Bob Lantz a908fafad7 Change default to vanilla Intf. Also edit comments. 2012-03-08 13:48:25 -08:00
Bob Lantz 8688ca9249 Remove debugging message. 2012-03-08 13:40:03 -08:00
Bob Lantz 8a622c3a9a Reorganize CPULimitedHost and add cgroup cleanup. 2012-03-08 13:39:28 -08:00
Bob Lantz bf5becc7d5 Get rid of SWITCH_PORT_BASE since it's 1 for OF >= 1.0. 2012-03-08 13:38:46 -08:00
Bob Lantz 216a4b7c9d Support for CFS bandwidth limiting.
Also trying to fix NOX cmdline opt, but broken at the moment.
2012-03-08 00:05:45 -08:00
Bob Lantz cbe20c7587 Remove unused imports. 2012-03-08 00:05:25 -08:00
Bob Lantz edf46e9570 Slightly cleaned up setParam to match node.py. 2012-03-07 23:38:08 -08:00
Bob Lantz b1f90976a3 Remove default classes since Mininet() really handles them. 2012-03-07 00:03:38 -08:00
Bob Lantz 4ac1148e9f Example/test of link and CPU bandwidth limits. 2012-03-06 23:52:26 -08:00
Bob Lantz 84a91a14a4 New configuration scheme and support for CPU limits (RT). 2012-03-06 23:52:00 -08:00
Bob Lantz 94c02695fd Clarify precedence of default classes. 2012-03-06 23:50:46 -08:00
Bob Lantz d8c88bedf3 Add custom() function for customizing constructors. 2012-03-06 23:49:51 -08:00
Bob Lantz 7d557fd759 Remove deprecated reference kernel switch. 2012-03-06 23:48:26 -08:00
Bob Lantz 551a3666eb Tweak errRun; add errFail and numCores. 2012-03-05 15:01:08 -08:00
Bob Lantz 542fb6167e Ignore build, dist and emacs autosaves. 2012-03-02 20:36:57 -08:00
Bob Lantz ee222055f1 Use install(1) to install mnexec so that setup.py develop works. 2012-03-02 20:34:56 -08:00
Bob Lantz 03dd914edc Tease out intfList() from intfNames(). 2012-03-02 20:34:37 -08:00
Bob Lantz a6bcad8f48 Intf and Link classes. Latter support bandwidth limits using tc. 2012-03-02 15:45:21 -08:00
Bob Lantz 6f446f6e55 Make pylint happy. 2012-03-02 15:45:10 -08:00
Bob Lantz 134a75ef38 Fix pylint complaint and add natural sort key function. 2012-03-02 15:43:07 -08:00
Bob Lantz e6d8e974ce Added errcheck target which only checks for errors. 2012-03-02 15:39:05 -08:00
Bob Lantz 176856bc9b Added setting NOX_CORE_DIR in .bashrc - this should not be necessary. 2012-02-14 16:35:12 -08:00
Bob Lantz 7a106d9b0d Script for installing mininet + tutorial into new VM. 2012-02-14 15:25:22 -08:00
Bob Lantz de5d31184f Ugh, typo. 2012-02-13 20:34:59 -08:00
Bob Lantz 65d46518c0 Don't crash if we can't uninstall kernel. 2012-02-13 20:33:59 -08:00
Bob Lantz 7a0ee56c80 openvswitch-switch needs python-argparse 2012-02-13 18:52:07 -08:00
Bob Lantz 1c0b54e52a Update OVS build suffix. 2012-02-13 18:47:22 -08:00
Bob Lantz 148a3f5735 Still dealing with install directory issues... 2012-02-13 18:30:49 -08:00
Bob Lantz 8996208084 dkms needs kernel headers. 2012-02-13 18:19:24 -08:00
Bob Lantz 7eb869af85 Force config files to be installed even if removed/edited. ;-/ 2012-02-13 17:27:32 -08:00
Bob Lantz 2b26161000 More OVS install fixes. 2012-02-13 17:13:53 -08:00
Bob Lantz e5b54a3143 Only install module manually if we built OVS from source. 2012-02-13 11:35:17 -08:00
Bob Lantz a24705d784 More controller-stopping madness. 2012-02-13 11:31:53 -08:00
Bob Lantz 9d275262e2 sudo cp for wireshark plugin 2012-02-13 11:26:11 -08:00
Bob Lantz 8183cb6262 Handle libwireshark0/libwireshark1 2012-02-13 11:23:39 -08:00
Bob Lantz 46cffb3bcf Fixed arch detection - should be i686 rather than just 686 2012-02-13 11:10:11 -08:00
Bob Lantz 738ae1f3fa Make sure bc is installed. 2012-02-13 11:05:00 -08:00
Bob Lantz fb25ee0200 Disable automatic openvswitch-controller startup. 2012-02-13 01:36:02 -08:00
Bob Lantz 2a4cbe2f57 Fix OVS 1.4.0 switch and controller package build/remove/install. 2012-02-13 01:29:01 -08:00
Bob Lantz 0e3cb791b5 Add gross depends for ovsdbmonitor. 2012-02-10 23:43:30 -08:00
Bob Lantz ae5ac257dd Build tar archive of relevant OVS packages in correct order. 2012-02-10 22:25:01 -08:00
Bob Lantz 3cd2e1a6aa Add $install + various cleanup. 2012-02-10 16:16:16 -08:00
Bob Lantz 08773f8fe9 Script to build .deb packages for Open vSwitch. 2012-02-10 14:59:39 -08:00
Bob Lantz 8a7d42db0b Update OVS switch to use ovs-vsctl rather than deprecated ovs-openflowd. 2012-02-10 14:59:39 -08:00
Bob Lantz daa576c47a Add errRun to run a command with stderr, stdout, return code and monitoring. 2012-02-10 14:59:39 -08:00
Brandon Heller b80f4aeb85 install.sh: Copy Wireshark dissector to global plugin dir 2012-02-10 14:59:39 -08:00
Bob Lantz 60b5864e1d Change to not fail if OS not detected, and to print detected OS. 2012-02-10 14:57:32 -08:00
32 changed files with 2620 additions and 1116 deletions
+5
View File
@@ -1,3 +1,8 @@
mnexec
*.pyc
*~
\#*\#
mininet.egg-info
build/*
dist/*
+11 -23
View File
@@ -25,9 +25,6 @@ ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# Set the cache size for astng objects.
cache-size=500
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
@@ -35,32 +32,23 @@ load-plugins=
[MESSAGES CONTROL]
# Enable only checker(s) with the given id(s). This option conflicts with the
# disable-checker option
#enable-checker=
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=
# Enable all checker(s) except those with the given id(s). This option
# conflicts with the enable-checker option
#disable-checker=
# Enable all messages in the listed categories (IRCWEF).
#enable-msg-cat=
# Disable all messages in the listed categories (IRCWEF).
disable-msg-cat=IR
# Enable the message(s) with the given id(s).
#enable-msg=
# Disable the message(s) with the given id(s).
disable-msg=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=text
output-format=colorized
# Include message's id in outpu
include-ids=yes
@@ -276,7 +264,7 @@ int-import-graph=
max-line-length=80
# Maximum number of lines in a module
max-module-lines=1000
max-module-lines=1500
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
+17 -8
View File
@@ -1,30 +1,39 @@
all: codecheck test
clean:
rm -rf build dist *.egg-info *.pyc mnexec bin/mnexec
MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = examples/*.py
BIN = bin/mn
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
P8IGN = E251,E201,E302,E202
all: codecheck test
clean:
rm -rf build dist *.egg-info *.pyc $(MNEXEC)
codecheck: $(PYSRC)
-echo "Running code check"
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
errcheck: $(PYSRC)
-echo "Running check for errors only"
pyflakes $(PYSRC)
pylint -E --rcfile=.pylint $(PYSRC)
test: $(MININET) $(TEST)
-echo "Running tests"
mininet/test/test_nets.py
install: mnexec
cp mnexec bin/
install: $(MNEXEC)
install $(MNEXEC) /usr/local/bin/
python setup.py install
develop: $(MNEXEC)
install $(MNEXEC) /usr/local/bin/
python setup.py develop
doc:
doxygen doxygen.cfg
+109 -78
View File
@@ -18,13 +18,38 @@ import time
from mininet.clean import cleanup
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info
from mininet.net import Mininet, init
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
from mininet.log import lg, LEVELS, info, warn
from mininet.net import Mininet, MininetWithControlNet
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
OVSLegacyKernelSwitch )
from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
from mininet.util import makeNumeric
from mininet.util import makeNumeric, custom
def customNode( constructors, argStr ):
"Return custom Node constructor based on argStr"
cname, newargs, kwargs = splitArgs( argStr )
constructor = constructors.get( cname, None )
if not constructor:
raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, constructors.keys() ) )
def customized( name, *args, **params ):
"Customized Node constructor"
params.update( kwargs )
if not newargs:
return constructor( name, *args, **params )
if args:
warn( 'warning: %s replacing %s with %s\n' % (
constructor, args, newargs ) )
return constructor( name, *newargs, **params )
return customized
# built in topologies, created only when run
TOPODEF = 'minimal'
@@ -35,20 +60,26 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'tree': TreeTopo }
SWITCHDEF = 'ovsk'
SWITCHES = { 'kernel': KernelSwitch,
'user': UserSwitch,
'ovsk': OVSKernelSwitch }
SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch,
'ovsl': OVSLegacyKernelSwitch }
HOSTDEF = 'process'
HOSTS = { 'process': Host }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'ref'
# a and b are the name and inNamespace params.
CONTROLLERS = { 'ref': Controller,
'nox_dump': lambda name: NOX( name, 'packetdump' ),
'nox_pysw': lambda name: NOX( name, 'pyswitch' ),
'remote': lambda name: None,
'none': lambda name: None }
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'none': lambda name: None }
LINKDEF = 'default'
LINKS = { 'default': Link,
'tc': TCLink }
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
@@ -57,24 +88,30 @@ TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
def buildTopo( topo ):
"Create topology from string with format (object, arg1, arg2,...)."
topo_split = topo.split( ',' )
topo_name = topo_split[ 0 ]
topo_params = topo_split[ 1: ]
# Convert int and float args; removes the need for every topology to
# be flexible with input arg formats.
topo_seq_params = [ s for s in topo_params if '=' not in s ]
topo_seq_params = [ makeNumeric( s ) for s in topo_seq_params ]
topo_kw_params = {}
for s in [ p for p in topo_params if '=' in p ]:
def splitArgs( argstr ):
"""Split argument string into usable python arguments
argstr: argument string with format fn,arg2,kw1=arg3...
returns: fn, args, kwargs"""
split = argstr.split( ',' )
fn = split[ 0 ]
params = split[ 1: ]
# Convert int and float args; removes the need for function
# to be flexible with input arg formats.
args = [ makeNumeric( s ) for s in params if '=' not in s ]
kwargs = {}
for s in [ p for p in params if '=' in p ]:
key, val = s.split( '=' )
topo_kw_params[ key ] = makeNumeric( val )
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
if topo_name not in TOPOS.keys():
raise Exception( 'Invalid topo_name %s' % topo_name )
return TOPOS[ topo_name ]( *topo_seq_params, **topo_kw_params )
def buildTopo( topoStr ):
"Create topology from string with format (object, arg1, arg2,...)."
topo, args, kwargs = splitArgs( topoStr )
if topo not in TOPOS:
raise Exception( 'Invalid topo name %s' % topo )
return TOPOS[ topo ]( *args, **kwargs )
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
@@ -88,10 +125,10 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
if not helpStr:
helpStr = '[' + ' '.join( choicesDict.keys() ) + ']'
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name,
type='choice',
choices=choicesDict.keys(),
type='string',
default = default,
help = helpStr )
@@ -124,11 +161,11 @@ class MininetRunner( object ):
def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options."
custom = {}
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, custom, custom )
for name in custom:
self.setCustom( name, custom[ name ] )
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
@@ -136,11 +173,10 @@ class MininetRunner( object ):
"""Parse command-line args and return options object.
returns: opts parse options dict"""
if '--custom' in sys.argv:
print "custom in sys.argv"
index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1:
custom = sys.argv[ index + 1 ]
self.parseCustomFile( custom )
filename = sys.argv[ index + 1 ]
self.parseCustomFile( filename )
else:
raise Exception( 'Custom file name not found' )
@@ -148,46 +184,46 @@ class MininetRunner( object ):
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
addDictOption( opts, LINKS, LINKDEF, 'link' )
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
opts.add_option( '--topo', type='string', default=TOPODEF,
help='[' + ' '.join( TOPOS.keys() ) + '],arg1,arg2,'
'...argN')
opts.add_option( '--clean', '-c', action='store_true',
default=False, help='clean and exit' )
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py file' )
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='[' + ' '.join( TESTS ) + ']' )
help='|'.join( TESTS ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
help='base IP address for hosts' )
opts.add_option( '--mac', action='store_true',
default=False, help='set MACs equal to DPIDs' )
default=False, help='automatically set host MACs' )
opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice',
choices=LEVELS.keys(), default = 'info',
help = '[' + ' '.join( LEVELS.keys() ) + ']' )
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--ip', type='string', default='127.0.0.1',
help='[ip address as a dotted decimal string for a'
'remote controller]' )
opts.add_option( '--port', type='int', default=6633,
help='[port integer for a listening remote'
' controller]' )
help='ip address as a dotted decimal string for a'
'remote controller' )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6634,
help='[base port for passive switch listening'
' controller]' )
opts.add_option( '--listenport', type='int', default=6635,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening port")
opts.add_option( '--pre', type='string', default=None,
help='[CLI script to run before tests]' )
help='CLI script to run before tests' )
opts.add_option( '--post', type='string', default=None,
help='[CLI script to run after tests]' )
help='CLI script to run after tests' )
opts.add_option( '--prefixlen', type='int', default=8,
help='[prefix length (e.g. /8) for automatic '
'network configuration]' )
help='prefix length (e.g. /8) for automatic '
'network configuration' )
opts.add_option( '--pin', action='store_true',
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
self.options, self.args = opts.parse_args()
@@ -202,9 +238,6 @@ class MininetRunner( object ):
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# validate environment setup
init()
def begin( self ):
"Create and run mininet."
@@ -215,34 +248,32 @@ class MininetRunner( object ):
start = time.time()
topo = buildTopo( self.options.topo )
switch = SWITCHES[ self.options.switch ]
host = HOSTS[ self.options.host ]
controller = CONTROLLERS[ self.options.controller ]
if self.options.controller == 'remote':
controller = lambda a: RemoteController( a,
defaultIP=self.options.ip,
port=self.options.port )
switch = customNode( SWITCHES, self.options.switch )
host = customNode( HOSTS, self.options.host )
controller = customNode( CONTROLLERS, self.options.controller )
link = customNode( LINKS, self.options.link )
if self.validate:
self.validate( self.options )
# We should clarify what this is actually for...
# It seems like it should be default values for the
# *data* network, so it may be misnamed.
controllerParams = ControllerParams( '10.0.0.0',
self.options.prefixlen)
inNamespace = self.options.innamespace
Net = MininetWithControlNet if inNamespace else Mininet
ipBase = self.options.ipbase
xterms = self.options.xterms
mac = self.options.mac
arp = self.options.arp
pin = self.options.pin
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
mn = Mininet( topo, switch, host, controller, controllerParams,
inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, listenPort=listenPort )
mn = Net( topo=topo,
switch=switch, host=host, controller=controller,
link=link,
ipBase=ipBase,
inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
if self.options.pre:
CLI( mn, script=self.options.pre )
+24 -6
View File
@@ -6,12 +6,21 @@ 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.
emptynet.py:
This example demonstrates creating an empty network (i.e. with no
@@ -26,6 +35,15 @@ 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.
@@ -36,6 +54,10 @@ 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
@@ -44,10 +66,10 @@ 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:
treeping64.py:
This example creates a 64-host tree network, and attempts to check full
connectivity using ping, for three different switch/datapath types.
connectivity using ping, for different switch/datapath types.
tree1024.py:
@@ -55,7 +77,3 @@ 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.)
udpbwtest.py:
This example shows how to run a test across an entire network, and monitor
the output of a set of hosts in real time.
+5 -2
View File
@@ -6,14 +6,17 @@ from mininet.node import Host
print "*** Creating nodes"
h1 = Host( 'h1' )
root = Host( 'root', inNamespace=False )
print "*** Creating links"
h1.linkTo( root )
print h1
print "*** Configuring nodes"
h1.setIP( h1.intfs[ 0 ], '10.0.0.1', 8 )
root.setIP( root.intfs[ 0 ], '10.0.0.2', 8 )
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' )
+6 -12
View File
@@ -107,8 +107,10 @@ class Console( Frame ):
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:
self.outputHook( self, text )
outputHook = self.outputHook
outputHook( self, text )
def handleKey( self, event ):
"If it's an interactive command, send it to the node."
@@ -130,27 +132,22 @@ class Console( Frame ):
self.sendCmd( cmd )
# Callback ignores event
# pylint: disable-msg=W0613
def handleInt( self, event=None ):
def handleInt( self, _event=None ):
"Handle control-c."
self.node.sendInt()
# pylint: enable-msg=W0613
def sendCmd( self, cmd ):
"Send a command to our node."
if not self.node.waiting:
self.node.sendCmd( cmd )
# Callback ignores fds
# pylint: disable-msg=W0613
def handleReadable( self, fds, timeoutms=None ):
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 )
# pylint: enable-msg=W0613
def waiting( self ):
"Are we waiting for output?"
@@ -319,9 +316,7 @@ class ConsoleApp( Frame ):
self.pack( expand=True, fill='both' )
# Update callback doesn't use console arg
# pylint: disable-msg=W0613
def updateGraph( self, console, output ):
def updateGraph( self, _console, output ):
"Update our graph."
m = re.search( r'(\d+) Mbits/sec', output )
if not m:
@@ -332,7 +327,6 @@ class ConsoleApp( Frame ):
self.graph.addBar( self.bw )
self.bw = 0
self.updates = 0
# pylint: enable-msg=W0613
def setOutputHook( self, fn=None, consoles=None ):
"Register fn as output hook [on specific consoles.]"
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/python
"""
cpu.py: test iperf bandwidth for varying cpu limtis
"""
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 )
+69
View File
@@ -0,0 +1,69 @@
#!/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, quietRun
from mininet.log import setLogLevel
from time import sleep
def testLinkLimit( net, bw ):
"Run bandwidth limit test"
print '*** Testing network %.2f Mbps bandwidth limit' % bw
net.iperf( )
def testCpuLimit( net, cpu ):
"run CPU limit test"
pct = cpu * 100
print '*** Testing CPU %.0f%% bandwidth limit' % pct
h1, h2 = net.hosts
h1.cmd( 'while true; do a=1; done &' )
h2.cmd( 'while true; do a=1; done &' )
pid1 = h1.cmd( 'echo $!' ).strip()
pid2 = h2.cmd( 'echo $!' ).strip()
cmd = 'ps -p %s,%s -o pid,%%cpu,args' % ( pid1, pid2 )
# It's a shame that this is what pylint prefers
for _ in range( 5 ):
sleep( 1 )
print quietRun( cmd ).strip()
h1.cmd( 'kill %1')
h2.cmd( 'kill %1')
def limit( bw=10, cpu=.4 ):
"""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 )
testCpuLimit( net, 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()
+29 -31
View File
@@ -6,9 +6,9 @@ using both kernel and user datapaths.
We construct a network of N hosts and N-1 switches, connected as follows:
h1 <-> sN+1 <-> sN+2 .. sN+N-1
| | |
h2 h3 hN
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
@@ -23,42 +23,40 @@ of switches, this example demonstrates:
"""
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
from mininet.net import init, Mininet
# from mininet.node import KernelSwitch
from mininet.node import UserSwitch, OVSKernelSwitch
from mininet.topo import Topo, Node
from mininet.log import lg
class LinearTestTopo( Topo ):
"Topology for a string of N hosts and N-1 switches."
def __init__( self, N ):
def __init__( self, N, **params ):
# Add default members to class.
super( LinearTestTopo, self ).__init__()
# Initialize topology
Topo.__init__( self, **params )
# Create switch and host nodes
hosts = range( 1, N + 1 )
switches = range( N + 1 , N + N )
for h in hosts:
self.add_node( h, Node( is_switch=False ) )
for s in switches:
self.add_node( s, Node( is_switch=True ) )
# Create switches and hosts
hosts = [ self.add_host( 'h%s' % h )
for h in irange( 1, N ) ]
switches = [ self.add_switch( 's%s' % s )
for s in irange( 1, N - 1 ) ]
# Wire up switches
for s in switches[ :-1 ]:
self.add_edge( s, s + 1 )
last = None
for switch in switches:
if last:
self.add_link( last, switch )
last = switch
# Wire up hosts
self.add_edge( hosts[ 0 ], switches[ 0 ] )
for h in hosts[ 1: ]:
self.add_edge( h, h + N - 1 )
# Consider all switches and hosts 'on'
self.enable_all()
self.add_link( hosts[ 0 ], switches[ 0 ] )
for host, switch in zip( hosts[ 1: ], switches ):
self.add_link( host, switch )
def linearBandwidthTest( lengths ):
@@ -69,15 +67,16 @@ def linearBandwidthTest( lengths ):
switchCount = max( lengths )
hostCount = switchCount + 1
switches = { # 'reference kernel': KernelSwitch,
'reference user': UserSwitch,
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=LinearTestTopo( hostCount ), switch=Switch )
net = Mininet( topo=topo, switch=Switch )
net.start()
print "*** testing basic connectivity"
for n in lengths:
@@ -106,7 +105,6 @@ def linearBandwidthTest( lengths ):
if __name__ == '__main__':
lg.setLogLevel( 'info' )
init()
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
print "*** Running linearBandwidthTest", sizes
linearBandwidthTest( sizes )
+7 -18
View File
@@ -299,14 +299,11 @@ class MiniEdit( Frame ):
# Delete from view
self.canvas.delete( item )
# Callback ignores event
# pylint: disable-msg=W0613
def deleteSelection( self, event ):
def deleteSelection( self, _event ):
"Delete the selected item."
if self.selection is not None:
self.deleteItem( self.selection )
self.selectItem( None )
# pylint: enable-msg=W0613
def nodeIcon( self, node, name ):
"Create a new node icon."
@@ -350,14 +347,11 @@ class MiniEdit( Frame ):
c = self.canvas
c.coords( self.link, self.linkx, self.linky, x, y )
# Callback ignores event
# pylint: disable-msg=W0613
def releaseLink( self, event ):
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
# pylint: enable-msg=W0613
# Generic node handlers
@@ -385,12 +379,9 @@ class MiniEdit( Frame ):
"Select node on entry."
self.selectNode( event )
# Callback ignores event
# pylint: disable-msg=W0613
def leaveNode( self, event ):
def leaveNode( self, _event ):
"Restore old selection on exit."
self.selectItem( self.lastSelection )
# pylint: enable-msg=W0613
def clickNode( self, event ):
"Node click handler."
@@ -454,23 +445,21 @@ class MiniEdit( Frame ):
# Link bindings
# Selection still needs a bit of work overall
# Callbacks ignore event
# pylint: disable-msg=W0613
def select( event, link=self.link ):
def select( _event, link=self.link ):
"Select item on mouse entry."
self.selectItem( link )
def highlight( event, link=self.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 ):
def unhighlight( _event, link=self.link ):
"Unhighlight item on mouse exit."
self.canvas.itemconfig( link, fill='blue' )
# self.selectItem( None )
# pylint: disable-msg=W0613
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 )
@@ -602,7 +591,7 @@ class MiniEdit( Frame ):
cleanUpScreens()
self.net = None
def xterm( self, ignore=None ):
def xterm( self, _=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or
self.net is None or
+86
View File
@@ -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 )
+81
View File
@@ -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()
+28 -18
View File
@@ -8,13 +8,16 @@ but it exposes the configuration details and allows customization.
For most tasks, the higher-level API will be preferable.
"""
from mininet.net import init
from mininet.node import Node, OVSKernelSwitch
from mininet.util import createLink
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
def scratchNet( cname='controller', cargs='ptcp:' ):
"Create network from scratch using kernel switch."
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 )
@@ -23,36 +26,43 @@ def scratchNet( cname='controller', cargs='ptcp:' ):
h1 = Node( 'h1' )
info( "*** Creating links\n" )
createLink( node1=h0, node2=switch, port1=0, port2=0 )
createLink( node1=h1, node2=switch, port1=0, port2=1 )
Link( h0, switch )
Link( h1, switch )
info( "*** Configuring hosts\n" )
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', 24 )
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
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 kernel datapath\n" )
info( "*** Starting network using Open vSwitch\n" )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ovs-dpctl del-dp dp0' )
switch.cmd( 'ovs-dpctl add-dp dp0' )
switch.cmd( 'ovs-vsctl del-br dp0' )
switch.cmd( 'ovs-vsctl add-br dp0' )
for intf in switch.intfs.values():
print switch.cmd( 'ovs-dpctl add-if dp0 ' + intf )
print switch.cmd( 'ovs-openflowd dp0 tcp:127.0.0.1 &' )
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-dpctl del-dp dp0' )
switch.cmd( 'kill %ovs-openflowd' )
switch.cmd( 'ovs-vsctl del-br dp0' )
switch.deleteIntfs()
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (kernel datapath)\n' )
OVSKernelSwitch.setup()
init()
Mininet.init()
scratchNet()
+16 -11
View File
@@ -10,11 +10,16 @@ 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 init
from mininet.net import Mininet
from mininet.node import Node
from mininet.util import createLink
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."
@@ -28,17 +33,17 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
switch = Node( 's0')
h0 = Node( 'h0' )
h1 = Node( 'h1' )
cintf, sintf = createLink( controller, switch )
h0intf, sintf1 = createLink( h0, switch )
h1intf, sintf2 = createLink( h1, switch )
cintf, sintf = linkIntfs( controller, switch )
h0intf, sintf1 = linkIntfs( h0, switch )
h1intf, sintf2 = linkIntfs( h1, switch )
info( '*** Configuring control network\n' )
controller.setIP( cintf, '10.0.123.1', 24 )
switch.setIP( sintf, '10.0.123.2', 24 )
controller.setIP( '10.0.123.1/24', cintf )
switch.setIP( '10.0.123.2/24', sintf)
info( '*** Configuring hosts\n' )
h0.setIP( h0intf, '192.168.123.1', 24 )
h1.setIP( h1intf, '192.168.123.2', 24 )
h0.setIP( '192.168.123.1/24', h0intf )
h1.setIP( '192.168.123.2/24', h1intf )
info( '*** Network state:\n' )
for node in controller, switch, h0, h1:
@@ -47,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' )
intfs = [ sintf1, sintf2 ]
intfs = [ str( i ) for i in sintf1, sintf2 ]
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
@@ -64,5 +69,5 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (user datapath)\n' )
init()
Mininet.init()
scratchNetUser()
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/python
"""
Simple example of setting network and CPU parameters
"""
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.add_switch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
host = self.add_host('h%s' % (h + 1),
cpu=.5 / n)
# 10 Mbps, 5ms delay, 10% loss
self.add_link(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()
+4 -4
View File
@@ -21,7 +21,7 @@ from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node, OVSKernelSwitch
from mininet.topolib import TreeTopo
from mininet.util import createLink
from mininet.link import Link
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
@@ -37,13 +37,13 @@ def connectToRootNS( network, switch, ip, prefixLen, routes ):
routes: host networks to route to"""
# Create a node in root namespace and link to switch 0
root = Node( 'root', inNamespace=False )
intf = createLink( root, switch )[ 0 ]
root.setIP( intf, ip, prefixLen )
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 ' + intf )
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."
+6
View File
@@ -45,6 +45,12 @@ def cleanup():
if dp != '':
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths" )
dps = sh("ovs-vsctl list-br").split( '\n' )
for dp in dps:
if dp:
sh( 'ovs-vsctl del-br ' + dp )
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
for link in links:
+21 -27
View File
@@ -33,7 +33,7 @@ import sys
from mininet.log import info, output, error
from mininet.term import makeTerms
from mininet.util import quietRun, isShellBuiltin
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
@@ -46,6 +46,9 @@ class CLI( Cmd ):
self.nodemap = {} # map names to Node objects
for node in self.nodelist:
self.nodemap[ node.name ] = node
# Local variable bindings for py command
self.locals = { 'net': mininet }
self.locals.update( self.nodemap )
# Attempt to handle input
self.stdin = stdin
self.inPoller = poll()
@@ -77,7 +80,7 @@ class CLI( Cmd ):
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=W0613,R0201
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
@@ -104,21 +107,14 @@ class CLI( Cmd ):
if line is '':
output( self.helpStr )
def do_nodes( self, line ):
def do_nodes( self, _line ):
"List all nodes."
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
output( 'available nodes are: \n%s\n' % nodes )
def do_net( self, line ):
def do_net( self, _line ):
"List network connections."
for switch in self.mn.switches:
output( switch.name, '<->' )
for intf in switch.intfs.values():
# Ugly, but pylint wants it
name = switch.connection.get( intf,
( None, 'Unknown ' ) )[ 1 ]
output( ' %s' % name )
output( '\n' )
dumpNodeConnections( self.nodelist )
def do_sh( self, line ):
"Run an external shell command"
@@ -131,7 +127,7 @@ class CLI( Cmd ):
"""Evaluate a Python expression.
Node names may be used, e.g.: h1.cmd('ls')"""
try:
result = eval( line, globals(), self.nodemap )
result = eval( line, globals(), self.locals )
if not result:
return
elif isinstance( result, str ):
@@ -143,11 +139,11 @@ class CLI( Cmd ):
# pylint: enable-msg=W0703
def do_pingall( self, line ):
def do_pingall( self, _line ):
"Ping between all hosts."
self.mn.pingAll()
def do_pingpair( self, line ):
def do_pingpair( self, _line ):
"Ping between first two hosts, useful for testing."
self.mn.pingPair()
@@ -191,16 +187,16 @@ class CLI( Cmd ):
error( 'invalid number of args: iperfudp bw src dst\n' +
'bw examples: 10M\n' )
def do_intfs( self, line ):
def do_intfs( self, _line ):
"List interfaces."
for node in self.nodelist:
output( '%s: %s\n' %
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
( node.name, ','.join( node.intfNames() ) ) )
def do_dump( self, line ):
def do_dump( self, _line ):
"Dump node info."
for node in self.nodelist:
output( '%s\n' % node )
output( '%s\n' % repr( node ) )
def do_link( self, line ):
"Bring link(s) between two nodes up or down."
@@ -229,7 +225,7 @@ class CLI( Cmd ):
"Spawn gnome-terminal(s) for the given node(s)."
self.do_xterm( line, term='gterm' )
def do_exit( self, line ):
def do_exit( self, _line ):
"Exit"
return 'exited by user command'
@@ -275,16 +271,12 @@ class CLI( Cmd ):
def do_dpctl( self, line ):
"Run dpctl command on all switches."
args = line.split()
if len(args) == 0:
if len(args) < 1:
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
return
if not self.mn.listenPort:
error( "can't run dpctl w/no passive listening port\n")
return
for sw in self.mn.switches:
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
output( sw.cmd( 'dpctl ' + ' '.join(args) +
' tcp:127.0.0.1:%i' % sw.listenPort ) )
output( sw.dpctl( *args ) )
def default( self, line ):
"""Called on an input line when the command prefix is not recognized.
@@ -293,6 +285,8 @@ class CLI( Cmd ):
corresponding IP addrs."""
first, args, line = self.parseline( line )
if not args:
return
if args and len(args) > 0 and args[ -1 ] == '\n':
args = args[ :-1 ]
rest = args.split( ' ' )
@@ -311,7 +305,7 @@ class CLI( Cmd ):
else:
error( '*** Unknown command: %s\n' % first )
# pylint: enable-msg=W0613,R0201
# pylint: enable-msg=R0201
def waitForNode( self, node ):
"Wait for a node to finish, and print its output."
+391
View File
@@ -0,0 +1,391 @@
"""
link.py: interface and link abstractions for mininet
It seems useful to bundle functionality for interfaces into a single
class.
Also it seems useful to enable the possibility of multiple flavors of
links, including:
- simple veth pairs
- tunneled links
- patchable links (which can be disconnected and reconnected via a patchbay)
- link simulators (e.g. wireless)
Basic division of labor:
Nodes: know how to execute commands
Intfs: know how to configure themselves
Links: know how to connect nodes together
Intf: basic interface object that can configure itself
TCIntf: interface with bandwidth limiting and delay via tc
Link: basic link class for creating veth pairs
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
from time import sleep
import re
class Intf( object ):
"Basic interface object that can configure itself."
def __init__( self, name, node=None, port=None, link=None, **params ):
"""name: interface name (e.g. h1-eth0)
node: owning node (where this intf most likely lives)
link: parent link if we're part of a link
other arguments are passed to config()"""
self.node = node
self.name = name
self.link = link
self.mac, self.ip, self.prefixLen = None, None, None
# Add to node (and move ourselves if necessary )
node.addIntf( self, port=port )
# Save params for future reference
self.params = params
self.config( **params )
def cmd( self, *args, **kwargs ):
"Run a command in our owning node"
return self.node.cmd( *args, **kwargs )
def ifconfig( self, *args ):
"Configure ourselves using ifconfig"
return self.cmd( 'ifconfig', self.name, *args )
def setIP( self, ipstr, prefixLen=None ):
"""Set our IP address"""
# This is a sign that we should perhaps rethink our prefix
# mechanism and/or the way we specify IP addresses
if '/' in ipstr:
self.ip, self.prefixLen = ipstr.split( '/' )
return self.ifconfig( ipstr, 'up' )
else:
self.ip, self.prefixLen = ipstr, prefixLen
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
def setMAC( self, macstr ):
"""Set the MAC address for an interface.
macstr: MAC address as string"""
self.mac = macstr
return ( self.ifconfig( 'down' ) +
self.ifconfig( 'hw', 'ether', macstr ) +
self.ifconfig( 'up' ) )
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
_macMatchRegex = re.compile( r'..:..:..:..:..:..' )
def updateIP( self ):
"Return updated IP address based on ifconfig"
ifconfig = self.ifconfig()
ips = self._ipMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
return self.ip
def updateMAC( self ):
"Return updated MAC address based on ifconfig"
ifconfig = self.ifconfig()
macs = self._macMatchRegex.findall( ifconfig )
self.mac = macs[ 0 ] if macs else None
return self.mac
def IP( self ):
"Return IP address"
return self.ip
def MAC( self ):
"Return MAC address"
return self.mac
def isUp( self, setUp=False ):
"Return whether interface is up"
if setUp:
self.ifconfig( 'up' )
return "UP" in self.ifconfig()
def rename( self, newname ):
"Rename interface"
self.ifconfig( 'down' )
result = self.cmd( 'ip link set', self.name, 'name', newname )
self.name = newname
self.ifconfig( 'up' )
return result
# The reason why we configure things in this way is so
# That the parameters can be listed and documented in
# the config method.
# Dealing with subclasses and superclasses is slightly
# annoying, but at least the information is there!
def setParam( self, results, method, **param ):
"""Internal method: configure a *single* parameter
results: dict of results to update
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
f = getattr( self, method, None )
if not f or value is None:
return
if type( value ) is list:
result = f( *value )
elif type( value ) is dict:
result = f( **value )
else:
result = f( value )
results[ name ] = result
return result
def config( self, mac=None, ip=None, ifconfig=None,
up=True, **_params ):
"""Configure Node according to (optional) parameters:
mac: MAC address
ip: IP address
ifconfig: arbitrary interface configuration
Subclasses should override this method and call
the parent class's config(**params)"""
# If we were overriding this method, we would call
# the superclass config method here as follows:
# r = Parent.config( **params )
r = {}
self.setParam( r, 'setMAC', mac=mac )
self.setParam( r, 'setIP', ip=ip )
self.setParam( r, 'isUp', up=up )
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
self.updateIP()
self.updateMAC()
return r
def delete( self ):
"Delete interface"
self.cmd( 'ip link del ' + self.name )
# Does it help to sleep to let things run?
sleep( 0.001 )
def __repr__( self ):
return '<%s %s>' % ( self.__class__.__name__, self.name )
def __str__( self ):
return self.name
class TCIntf( Intf ):
"""Interface customized by tc (traffic control) utility
Allows specification of bandwidth limits (various methods)
as well as delay, loss and max queue length"""
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
elif bw is not None:
# BL: this seems a bit brittle...
if ( speedup > 0 and
self.node.name[0:1] == 's' ):
bw = speedup
# This may not be correct - we should look more closely
# at the semantics of burst (and cburst) to make sure we
# are specifying the correct sizes. For now I have used
# the same settings we had in the mininet-hifi code.
if use_hfsc:
cmds = [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
'%s class add dev %s parent 1:0 classid 1:1 hfsc sc '
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
latency_us = 10 * 1500 * 8 / bw
cmds = ['%s qdisc add dev %s root handle 1: tbf ' +
'rate %fMbit burst 15000 latency %fus' %
( bw, latency_us ) ]
else:
cmds = [ '%s qdisc add dev %s root handle 1:0 htb default 1',
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 1:1 '
# ECN or RED
if enable_ecn:
cmds = [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'min 20000 max 25000 avpkt 1000 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1 ecn' % bw ]
parent = ' parent 10: '
elif enable_red:
cmds = [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'min 20000 max 25000 avpkt 1000 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1' % bw ]
parent = ' parent 10: '
return cmds, parent
@staticmethod
def delayCmds( parent, delay=None, loss=None,
max_queue_size=None ):
"Internal method: return tc commands for delay and loss"
cmds = []
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
elif loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# Delay/loss/max queue size
netemargs = '%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'loss %d ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
if netemargs:
cmds = [ '%s qdisc add dev %s ' + parent +
' handle 10: netem ' +
netemargs ]
return cmds
def tc( self, cmd, tc='tc' ):
"Execute tc command for our interface"
c = cmd % (tc, self) # Add in tc command and our name
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
def config( self, bw=None, delay=None, loss=None, disable_gro=True,
speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False,
enable_red=False, max_queue_size=None, **params ):
"Configure the port and set its properties."
result = Intf.config( self, **params)
# Disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
# Optimization: return if nothing else to configure
# Question: what happens if we want to reset things?
if ( bw is None and not delay and not loss
and max_queue_size is None ):
return
# Clear existing configuration
cmds = [ '%s qdisc del dev %s root' ]
# Bandwidth limits via various methods
bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
use_hfsc=use_hfsc, use_tbf=use_tbf,
enable_ecn=enable_ecn,
enable_red=enable_red )
cmds += bwcmds
# Delay/loss/max_queue_size using netem
cmds += self.delayCmds( delay=delay, loss=loss,
max_queue_size=max_queue_size,
parent=parent )
# Ugly but functional: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
# Execute all the commands in our node
debug("at map stage w/cmds: %s\n" % cmds)
tcoutputs = [ self.tc(cmd) for cmd in cmds ]
debug( "cmds:", cmds, '\n' )
debug( "outputs:", tcoutputs, '\n' )
result[ 'tcoutputs'] = tcoutputs
return result
class Link( object ):
"""A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc.."""
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
port1: node1 port number (optional)
port2: node2 port number (optional)
intf: default interface class/constructor
cls1, cls2: optional interface-specific constructors
intfName1: node1 interface name (optional)
intfName2: node2 interface name (optional)
params1: parameters for interface 1
params2: parameters for interface 2"""
# This is a bit awkward; it seems that having everything in
# params would be more orthogonal, but being able to specify
# in-line arguments is more convenient!
if port1 is None:
port1 = node1.newPort()
if port2 is None:
port2 = node2.newPort()
if not intfName1:
intfName1 = self.intfName( node1, port1 )
if not intfName2:
intfName2 = self.intfName( node2, port2 )
self.makeIntfPair( intfName1, intfName2 )
if not cls1:
cls1 = intf
if not cls2:
cls2 = intf
if not params1:
params1 = {}
if not params2:
params2 = {}
intf1 = cls1( name=intfName1, node=node1, port=port1,
link=self, **params1 )
intf2 = cls2( name=intfName2, node=node2, port=port2,
link=self, **params2 )
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
@classmethod
def intfName( cls, node, n ):
"Construct a canonical interface name node-ethN for interface n."
return node.name + '-eth' + repr( n )
@classmethod
def makeIntfPair( cls, intf1, intf2 ):
"""Create pair of interfaces
intf1: name of interface 1
intf2: name of interface 2
(override this class method [and possibly delete()]
to change link type)"""
makeIntfPair( intf1, intf2 )
def delete( self ):
"Delete this link"
self.intf1.delete()
self.intf2.delete()
def __str__( self ):
return '%s<->%s' % ( self.intf1, self.intf2 )
class TCLink( Link ):
"Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, **params ):
Link.__init__( self, node1, node2, port1=port1, port2=port2,
intfName1=intfName1, intfName2=intfName2,
cls1=TCIntf,
cls2=TCIntf,
params1=params,
params2=params)
+145
View File
@@ -0,0 +1,145 @@
from time import sleep, time
from subprocess import Popen, PIPE
from multiprocessing import Process
import re
import os
from mininet.log import info, error, debug, output
from mininet.util import quietRun
class Monitor(object):
def __init__(self, output_dir='/tmp'):
self.monitors = []
self.output_dir = output_dir
# Add general process monitors
# Bandwidth monitor
self.monitors.append(Process(target=self.monitor_devs_ng,
args=('%s/bwm.txt' % self.output_dir, 1.0)))
# CPU monitor
self.monitors.append(Process(target=self.monitor_cpu,
args=('%s/cpu.txt' % self.output_dir, )))
# cwnd monitor: tcp_probe
self.monitors.append(Process(target=self.monitor_cwnd,
args=('%s/tcp_probe.txt' % self.output_dir, )))
def start(self):
'''Start all the system monitors'''
# Set output directory
self.set_output_dir(self.output_dir)
# Start the monitors
for m in self.monitors:
m.start()
def stop(self):
'''Terminate all the system monitors'''
# Stop the monitors
for m in self.monitors:
m.terminate()
self.monitors = []
Popen("killall -9 bwm-ng top", shell=True).wait()
Popen("killall -9 cat; rmmod tcp_probe > /dev/null 2>&1;", shell=True).wait()
def set_output_dir(self, output_dir):
# Create output directory if it doesn't exist already
self.output_dir = output_dir
debug('Monitoring output dir: %s' % self.output_dir)
if not os.path.isdir(self.output_dir):
os.makedirs(self.output_dir)
def monitor_qlen(self, iface, interval_sec = 0.01, fname='%s/qlen.txt' % '.'):
pat_queued = re.compile(r'backlog\s[^\s]+\s([\d]+)p')
cmd = "tc -s qdisc show dev %s" % (iface)
ret = []
open(fname, 'w').write('')
while 1:
p = Popen(cmd, shell=True, stdout=PIPE)
output = p.stdout.read()
# Not quite right, but will do for now
matches = pat_queued.findall(output)
if matches and len(matches) > 1:
ret.append(matches[1])
t = "%f" % time()
open(fname, 'a').write(t + ',' + matches[1] + '\n')
sleep(interval_sec)
return
def monitor_cwnd(self, fname='%s/tcp_probe.txt' % '.'):
Popen("rmmod tcp_probe > /dev/null 2>&1; modprobe tcp_probe;", shell=True).wait()
Popen("cat /proc/net/tcpprobe > %s" % fname, shell=True).wait()
def monitor_devs_ng(self, fname="%s/txrate.txt" % '.', interval_sec=0.01):
"""Uses bwm-ng tool to collect iface tx rate stats. Very reliable."""
cmd = "sleep 1; bwm-ng -t %s -o csv -u bits -T rate -C ',' > %s" % (interval_sec * 1000, fname)
Popen(cmd, shell=True).wait()
def monitor_cpu(self, fname="%s/cpu.txt" % '.'):
cmd = "(top -b -p 1 -d 1 | grep --line-buffered \"^Cpu\") > %s" % fname
Popen(cmd, shell=True).wait()
def monitor_cpuacct(self, hosts, fname="%s/cpuacct.txt" % '.', interval_sec=1.0):
prereqs = ['cgget']
for p in prereqs:
if not quietRun('which ' + p):
error('Could not find %s... not monitoring cpuacct' % p)
return
hnames = ' '.join([h.name for h in hosts])
cpuacct_cmd = 'cgget -g cpuacct %s >> %s' % (hnames, fname)
prev_time = time()
while 1:
sleep(interval_sec - (time() - prev_time))
prev_time = time()
cpu_usage = Popen(cpuacct_cmd, shell=True).wait()
return
# Obsolete: Use bwm-ng instead (monitor_devs_ng), for reliable stats
def monitor_count(self, ipt_args="--src 10.0.0.0/8", interval_sec=0.01, fname='%s/bytes_sent.txt' % '.', chain="OUTPUT"):
cmd = "iptables -I %(chain)s 1 %(filter)s -j RETURN" % {
"filter": ipt_args,
"chain": chain,
}
# We always erase the first rule; will fix this later
Popen("iptables -D %s 1" % chain, shell=True).wait()
# Add our rule
Popen(cmd, shell=True).wait()
open(fname, 'w').write('')
cmd = "iptables -vnL %s 1 -Z" % (chain)
while 1:
p = Popen(cmd, shell=True, stdout=PIPE)
output = p.stdout.read().strip()
values = output.split(' ')
if len(values) > 2:
t = "%f" % time()
pkts, bytes = values[0], values[1]
open(fname, 'a').write(','.join([t, pkts, bytes]) + '\n')
sleep(interval_sec)
return
# Obsolete: Use bwm-ng instead (monitor_devs_ng), for reliable stats
def monitor_devs(self, dev_pattern='^sw', fname="%s/bytes_sent.txt" % '.', interval_sec=0.01):
"""Aggregates (sums) all txed bytes and rate (in Mbps) from devices whose name
matches @dev_pattern and writes to @fname"""
pat = re.compile(dev_pattern)
spaces = re.compile('\s+')
open(fname, 'w').write('')
prev_tx = {}
while 1:
lines = open('/proc/net/dev').read().split('\n')
t = str(time())
total = 0
for line in lines:
line = spaces.split(line.strip())
iface = line[0]
if pat.match(iface) and len(line) > 9:
tx_bytes = int(line[9])
total += tx_bytes - prev_tx.get(iface, tx_bytes)
prev_tx[iface] = tx_bytes
open(fname, 'a').write(','.join([t, str(total * 8 / interval_sec / 1e6), str(total)]) + "\n")
sleep(interval_sec)
return
+244 -165
View File
@@ -1,6 +1,6 @@
"""
Mininet: A simple networking testbed for OpenFlow!
Mininet: A simple networking testbed for OpenFlow/SDN!
author: Bob Lantz (rlantz@cs.stanford.edu)
author: Brandon Heller (brandonh@stanford.edu)
@@ -91,228 +91,228 @@ import re
import select
import signal
from time import sleep
from datetime import datetime
from multiprocessing import Process
from mininet.cli import CLI
from mininet.log import info, error, debug, output
from mininet.node import Host, UserSwitch, OVSKernelSwitch, Controller
from mininet.node import ControllerParams
from mininet.util import quietRun, fixLimits
from mininet.util import createLink, macColonHex, ipStr, ipParse
from mininet.node import Host, OVSKernelSwitch, Controller
from mininet.link import Link, Intf
from mininet.util import quietRun, fixLimits, numCores
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.term import cleanUpScreens, makeTerms
from mininet.monitor import Monitor
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
controller=Controller,
cparams=ControllerParams( '10.0.0.0', 8 ),
build=True, xterms=False, cleanup=False,
controller=Controller, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, listenPort=None ):
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None ):
"""Create Mininet object.
topo: Topo (topology) object or None
switch: Switch class
host: Host class
controller: Controller class
cparams: ControllerParams object
switch: default Switch class
host: default Host class/constructor
controller: default Controller class/constructor
link: default Link class/constructor
intf: default Intf class/constructor
ipBase: base IP address for hosts,
build: build now from topo?
xterms: if build now, spawn xterms?
cleanup: if build now, cleanup before creating?
inNamespace: spawn switches and controller in net namespaces?
autoSetMacs: set MAC addrs from topo?
autoSetMacs: set MAC addrs automatically like IP addresses?
autoStaticArp: set all-pairs static MAC addrs?
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
listenPort: base listening port to open; will be incremented for
each additional switch in the net if inNamespace=False"""
self.topo = topo
self.switch = switch
self.host = host
self.controller = controller
self.cparams = cparams
self.topo = topo
self.link = link
self.intf = intf
self.ipBase = ipBase
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
self.nextIP = 1 # start for address allocation
self.inNamespace = inNamespace
self.xterms = xterms
self.cleanup = cleanup
self.autoSetMacs = autoSetMacs
self.autoStaticArp = autoStaticArp
self.autoPinCpus = autoPinCpus
self.numCores = numCores()
self.nextCore = 0 # next core for pinning hosts to CPUs
self.listenPort = listenPort
self.monitoring = None
self.hosts = []
self.switches = []
self.controllers = []
self.nameToNode = {} # name to Node (Host/Switch) objects
self.idToNode = {} # dpid to Node (Host/Switch) objects
self.dps = 0 # number of created kernel datapaths
self.terms = [] # list of spawned xterm processes
init()
switch.setup()
Mininet.init() # Initialize Mininet if necessary
self.built = False
if topo and build:
self.build()
def addHost( self, name, mac=None, ip=None ):
def set_debug(self, output_dir=None):
'''Enable debugging, with output to the specified directory'''
dt = datetime.now()
if not output_dir:
output_dir = '/tmp/mininet-%s-%s' % (str(dt.date()), str(dt.time()))
self.monitoring = Monitor(output_dir=output_dir)
def addHost( self, name, cls=None, **params ):
"""Add host.
name: name of host to add
mac: default MAC address for intf 0
ip: default IP address for intf 0
cls: custom host class/constructor (optional)
params: parameters for host
returns: added host"""
host = self.host( name, defaultMAC=mac, defaultIP=ip )
self.hosts.append( host )
self.nameToNode[ name ] = host
return host
# Default IP and MAC addresses
defaults = { 'ip': ipAdd( self.nextIP,
ipBaseNum=self.ipBaseNum,
prefixLen=self.prefixLen ) }
if self.autoSetMacs:
defaults[ 'mac'] = macColonHex( self.nextIP )
if self.autoPinCpus:
defaults[ 'cores' ] = self.nextCore
self.nextCore = ( self.nextCore + 1 ) % self.numCores
self.nextIP += 1
defaults.update( params )
if not cls:
cls = self.host
h = cls( name, **defaults )
self.hosts.append( h )
self.nameToNode[ name ] = h
return h
def addSwitch( self, name, mac=None, ip=None ):
def addSwitch( self, name, cls=None, **params ):
"""Add switch.
name: name of switch to add
mac: default MAC address for kernel/OVS switch intf 0
cls: custom switch class/constructor (optional)
returns: added switch
side effect: increments the listenPort member variable."""
if self.switch == UserSwitch:
sw = self.switch( name, listenPort=self.listenPort,
defaultMAC=mac, defaultIP=ip, inNamespace=self.inNamespace )
else:
sw = self.switch( name, listenPort=self.listenPort,
defaultMAC=mac, defaultIP=ip, dp=self.dps,
inNamespace=self.inNamespace )
side effect: increments listenPort ivar ."""
defaults = { 'listenPort': self.listenPort,
'inNamespace': self.inNamespace }
defaults.update( params )
if not cls:
cls = self.switch
sw = cls( name, **defaults )
if not self.inNamespace and self.listenPort:
self.listenPort += 1
self.dps += 1
self.switches.append( sw )
self.nameToNode[ name ] = sw
return sw
def addController( self, name='c0', controller=None, **kwargs ):
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
if not controller:
controller = self.controller
controller_new = controller( name, **kwargs )
controller_new = controller( name, **params )
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
return controller_new
# Control network support:
#
# Create an explicit control network. Currently this is only
# used by the user datapath configuration.
#
# Notes:
#
# 1. If the controller and switches are in the same (e.g. root)
# namespace, they can just use the loopback connection.
#
# 2. If we can get unix domain sockets to work, we can use them
# instead of an explicit control network.
#
# 3. Instead of routing, we could bridge or use 'in-band' control.
#
# 4. Even if we dispense with this in general, it could still be
# useful for people who wish to simulate a separate control
# network (since real networks may need one!)
# BL: is this better than just using nameToNode[] ?
# Should it have a better name?
def getNodeByName( self, *args ):
"Return node(s) with given name(s)"
if len( args ) == 1:
return self.nameToNode[ args[ 0 ] ]
return [ self.nameToNode[ n ] for n in args ]
def configureControlNetwork( self ):
"Configure control network."
self.configureRoutedControlNetwork()
# We still need to figure out the right way to pass
# in the control network location.
def configureRoutedControlNetwork( self, ip='192.168.123.1',
prefixLen=16 ):
"""Configure a routed control network on controller and switches.
For use with the user datapath only right now.
"""
controller = self.controllers[ 0 ]
info( controller.name + ' <->' )
cip = ip
snum = ipParse( ip )
for switch in self.switches:
info( ' ' + switch.name )
sintf, cintf = createLink( switch, controller )
snum += 1
while snum & 0xff in [ 0, 255 ]:
snum += 1
sip = ipStr( snum )
controller.setIP( cintf, cip, prefixLen )
switch.setIP( sintf, sip, prefixLen )
controller.setHostRoute( sip, cintf )
switch.setHostRoute( cip, sintf )
info( '\n' )
info( '*** Testing control network\n' )
while not controller.intfIsUp( cintf ):
info( '*** Waiting for', cintf, 'to come up\n' )
sleep( 1 )
for switch in self.switches:
while not switch.intfIsUp( sintf ):
info( '*** Waiting for', sintf, 'to come up\n' )
sleep( 1 )
if self.ping( hosts=[ switch, controller ] ) != 0:
error( '*** Error: control network test failed\n' )
exit( 1 )
info( '\n' )
def addLink( self, node1, node2, port1=None, port2=None,
cls=None, **params ):
""""Add a link from node1 to node2
node1: source node
node2: dest node
port1: source port
port2: dest port
returns: link object"""
defaults = { 'port1': port1,
'port2': port2,
'intf': self.intf }
defaults.update( params )
if not cls:
cls = self.link
return cls( node1, node2, **defaults )
def configHosts( self ):
"Configure a set of hosts."
# params were: hosts, ips
for host in self.hosts:
hintf = host.intfs[ 0 ]
host.setIP( hintf, host.defaultIP, self.cparams.prefixLen )
host.setDefaultRoute( hintf )
# You're low priority, dude!
quietRun( 'renice +18 -p ' + repr( host.pid ) )
info( host.name + ' ' )
host.configDefault( defaultRoute=host.defaultIntf() )
# You're low priority, dude!
# BL: do we want to do this here or not?
# May not make sense if we have CPU lmiting...
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
# This may not be the right place to do this, but
# it needs to be done somewhere.
host.cmd( 'ifconfig lo up' )
info( '\n' )
def buildFromTopo( self, topo ):
def buildFromTopo( self, topo=None ):
"""Build mininet from a topology object
At the end of this function, everything should be connected
and up."""
def addNode( prefix, addMethod, nodeId ):
"Add a host or a switch."
name = prefix + topo.name( nodeId )
mac = macColonHex( nodeId ) if self.setMacs else None
ip = topo.ip( nodeId )
node = addMethod( name, mac=mac, ip=ip )
self.idToNode[ nodeId ] = node
info( name + ' ' )
# Possibly we should clean up here and/or validate
# the topo
if self.cleanup:
pass
info( '*** Adding controller\n' )
self.addController( 'c0' )
info( '*** Creating network\n' )
if not self.controllers:
# Add a default controller
info( '*** Adding controller\n' )
self.addController( 'c0' )
info( '*** Adding hosts:\n' )
for hostId in sorted( topo.hosts() ):
addNode( 'h', self.addHost, hostId )
for hostName in topo.hosts():
self.addHost( hostName, **topo.nodeInfo( hostName ) )
info( hostName + ' ' )
info( '\n*** Adding switches:\n' )
for switchId in sorted( topo.switches() ):
addNode( 's', self.addSwitch, switchId )
for switchName in topo.switches():
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
info( switchName + ' ' )
info( '\n*** Adding links:\n' )
for srcId, dstId in sorted( topo.edges() ):
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
srcPort, dstPort = topo.port( srcId, dstId )
createLink( src, dst, srcPort, dstPort )
for srcName, dstName in topo.links(sort=True):
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
params = topo.linkInfo( srcName, dstName )
srcPort, dstPort = topo.port( srcName, dstName )
self.addLink( src, dst, srcPort, dstPort, **params )
info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' )
def configureControlNetwork( self ):
"Control net config hook: override in subclass"
raise Exception( 'configureControlNetwork: '
'should be overriden in subclass', self )
def build( self ):
"Build mininet."
if self.topo:
self.buildFromTopo( self.topo )
if self.inNamespace:
info( '*** Configuring control network\n' )
if ( self.inNamespace ):
self.configureControlNetwork()
info( '*** Configuring hosts\n' )
self.configHosts()
if self.xterms:
self.startTerms()
if self.autoSetMacs:
self.setMacs()
if self.autoStaticArp:
self.staticArp()
self.built = True
@@ -327,17 +327,10 @@ class Mininet( object ):
def stopXterms( self ):
"Kill each xterm."
# Kill xterms
for term in self.terms:
os.kill( term.pid, signal.SIGKILL )
cleanUpScreens()
def setMacs( self ):
"""Set MAC addrs to correspond to default MACs on hosts.
Assume that the host only has one interface."""
for host in self.hosts:
host.setMAC( host.intfs[ 0 ], host.defaultMAC )
def staticArp( self ):
"Add all-pairs ARP entries to remove the need to handle broadcast."
for src in self.hosts:
@@ -357,26 +350,38 @@ class Mininet( object ):
info( switch.name + ' ')
switch.start( self.controllers )
info( '\n' )
info( '*** Starting system monitor\n' )
if self.monitoring:
m = self.monitoring
m.monitors.append(Process(target=m.monitor_cpuacct,
args=(self.hosts, '%s/cpuacct.txt' % m.output_dir)))
m.start()
info( 'Logging monitoring info in: %s\n' % m.output_dir )
def stop( self ):
"Stop the controller(s), switches and hosts"
info( '*** Stopping system monitor\n' )
if self.monitoring:
self.monitoring.stop()
if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( '%s ' % host.name )
info( host.name + ' ' )
host.terminate()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name )
info( switch.name + ' ' )
switch.stop()
info( '\n' )
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
info( controller.name + ' ' )
controller.stop()
info( '*** Done\n' )
info( '\n' )
info( '\n*** Done\n' )
def run( self, test, *args, **kwargs ):
"Perform a complete start/test/stop cycle."
@@ -410,6 +415,9 @@ class Mininet( object ):
if not ready and timeoutms >= 0:
yield None, None
# XXX These test methods should be moved out of this class.
# Probably we should create a tests.py for them
@staticmethod
def _parsePing( pingOutput ):
"Parse ping output and return packets sent, received."
@@ -481,6 +489,8 @@ class Mininet( object ):
error( 'could not parse iperf output: ' + iperfOutput )
return ''
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses opposite hosts
@@ -508,10 +518,11 @@ class Mininet( object ):
servout = ''
while server.lastPid is None:
servout += server.monitor()
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
output('waiting for iperf to start up')
sleep(.5)
if l4Type == 'TCP':
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
output('waiting for iperf to start up...')
sleep(.5)
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs )
debug( 'Client output: %s\n' % cliout )
@@ -524,6 +535,8 @@ class Mininet( object ):
output( '*** Results: %s\n' % result )
return result
# BL: I think this can be rewritten now that we have
# a real link class.
def configLinkStatus( self, src, dst, status ):
"""Change status of src <-> dst links.
src: node name
@@ -534,15 +547,18 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
srcNode, dstNode = self.nameToNode[ src ], self.nameToNode[ dst ]
connections = srcNode.connectionsTo( dstNode )
if type( src ) is str:
src = self.nameToNode[ src ]
if type( dst ) is str:
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
for srcIntf, dstIntf in connections:
result = srcNode.cmd( 'ifconfig', srcIntf, status )
result = srcIntf.ifconfig( status )
if result:
error( 'link src status change failed: %s\n' % result )
result = dstNode.cmd( 'ifconfig', dstIntf, status )
result = dstIntf.ifconfig( status )
if result:
error( 'link dst status change failed: %s\n' % result )
@@ -553,26 +569,89 @@ class Mininet( object ):
self.stop()
return result
inited = False
# pylint thinks inited is unused
# pylint: disable-msg=W0612
@classmethod
def init( cls ):
"Initialize Mininet"
if cls.inited:
return
if os.getuid() != 0:
# Note: this script must be run as root
# Probably we should only sudo when we need
# to as per Big Switch's patch
print "*** Mininet must run as root."
exit( 1 )
fixLimits()
cls.inited = True
def init():
"Initialize Mininet."
if init.inited:
return
if os.getuid() != 0:
# Note: this script must be run as root
# Perhaps we should do so automatically!
print "*** Mininet must run as root."
exit( 1 )
# If which produces no output, then mnexec is not in the path.
# May want to loosen this to handle mnexec in the current dir.
if not quietRun( 'which mnexec' ):
raise Exception( "Could not find mnexec - check $PATH" )
fixLimits()
init.inited = True
init.inited = False
class MininetWithControlNet( Mininet ):
# pylint: enable-msg=W0612
"""Control network support:
Create an explicit control network. Currently this is only
used/usable with the user datapath.
Notes:
1. If the controller and switches are in the same (e.g. root)
namespace, they can just use the loopback connection.
2. If we can get unix domain sockets to work, we can use them
instead of an explicit control network.
3. Instead of routing, we could bridge or use 'in-band' control.
4. Even if we dispense with this in general, it could still be
useful for people who wish to simulate a separate control
network (since real networks may need one!)
5. Basically nobody ever used this code, so it has been moved
into its own class.
6. Ultimately we may wish to extend this to allow us to create a
control network which every node's control interface is
attached to."""
def configureControlNetwork( self ):
"Configure control network."
self.configureRoutedControlNetwork()
# We still need to figure out the right way to pass
# in the control network location.
def configureRoutedControlNetwork( self, ip='192.168.123.1',
prefixLen=16 ):
"""Configure a routed control network on controller and switches.
For use with the user datapath only right now."""
controller = self.controllers[ 0 ]
info( controller.name + ' <->' )
cip = ip
snum = ipParse( ip )
for switch in self.switches:
info( ' ' + switch.name )
link = self.link( switch, controller, port1=0 )
sintf, cintf = link.intf1, link.intf2
switch.controlIntf = sintf
snum += 1
while snum & 0xff in [ 0, 255 ]:
snum += 1
sip = ipStr( snum )
cintf.setIP( cip, prefixLen )
sintf.setIP( sip, prefixLen )
controller.setHostRoute( sip, cintf )
switch.setHostRoute( cip, sintf )
info( '\n' )
info( '*** Testing control network\n' )
while not cintf.isUp():
info( '*** Waiting for', cintf, 'to come up\n' )
sleep( 1 )
for switch in self.switches:
while not sintf.isUp():
info( '*** Waiting for', sintf, 'to come up\n' )
sleep( 1 )
if self.ping( hosts=[ switch, controller ] ) != 0:
error( '*** Error: control network test failed\n' )
exit( 1 )
info( '\n' )
+557 -266
View File
File diff suppressed because it is too large Load Diff
+6 -17
View File
@@ -5,16 +5,14 @@
import unittest
from mininet.net import init, Mininet
from mininet.node import Host, Controller, ControllerParams
# from mininet.node import KernelSwitch
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import UserSwitch, OVSKernelSwitch
from mininet.topo import SingleSwitchTopo, LinearTopo
from mininet.log import setLogLevel
SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch,
# 'kernel': KernelSwitch
}
@@ -23,21 +21,15 @@ class testSingleSwitch( unittest.TestCase ):
def testMinimal( self ):
"Ping test with both datapaths on minimal topology"
init()
for switch in SWITCHES.values():
controllerParams = ControllerParams( '10.0.0.0', 8 )
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller,
controllerParams )
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
def testSingle5( self ):
"Ping test with both datapaths on 5-host single-switch topology"
init()
for switch in SWITCHES.values():
controllerParams = ControllerParams( '10.0.0.0', 8 )
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller,
controllerParams )
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
@@ -47,15 +39,12 @@ class testLinear( unittest.TestCase ):
def testLinear5( self ):
"Ping test with both datapaths on a 5-switch topology"
init()
for switch in SWITCHES.values():
controllerParams = ControllerParams( '10.0.0.0', 8 )
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller,
controllerParams )
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
if __name__ == '__main__':
setLogLevel('warning')
setLogLevel( 'warning' )
unittest.main()
+137 -317
View File
@@ -16,258 +16,125 @@ setup for testing, and can even be emulated with the Mininet package.
# from networkx.classes.graph import Graph
from networkx import Graph
from mininet.node import SWITCH_PORT_BASE
class NodeID(object):
'''Topo node identifier.'''
def __init__(self, dpid = None):
'''Init.
@param dpid dpid
'''
# DPID-compatible hashable identifier: opaque 64-bit unsigned int
self.dpid = dpid
def __str__(self):
'''String conversion.
@return str dpid as string
'''
return str(self.dpid)
def name_str(self):
'''Name conversion.
@return name name as string
'''
return str(self.dpid)
def ip_str(self):
'''Name conversion.
@return ip ip as string
'''
hi = (self.dpid & 0xff0000) >> 16
mid = (self.dpid & 0xff00) >> 8
lo = self.dpid & 0xff
return "10.%i.%i.%i" % (hi, mid, lo)
class Node(object):
'''Node-specific vertex metadata for a Topo object.'''
def __init__(self, connected = False, admin_on = True,
power_on = True, fault = False, is_switch = True):
'''Init.
@param connected actively connected to controller
@param admin_on administratively on or off
@param power_on powered on or off
@param fault fault seen on node
@param is_switch switch or host
'''
self.connected = connected
self.admin_on = admin_on
self.power_on = power_on
self.fault = fault
self.is_switch = is_switch
class Edge(object):
'''Edge-specific metadata for a StructuredTopo graph.'''
def __init__(self, admin_on = True, power_on = True, fault = False):
'''Init.
@param admin_on administratively on or off; defaults to True
@param power_on powered on or off; defaults to True
@param fault fault seen on edge; defaults to False
'''
self.admin_on = admin_on
self.power_on = power_on
self.fault = fault
from mininet.util import irange, natural, naturalSeq
class Topo(object):
'''Data center network representation for structured multi-trees.'''
"Data center network representation for structured multi-trees."
def __init__(self):
'''Create Topo object.
'''
def __init__(self, hopts=None, sopts=None, lopts=None):
"""Topo object:
hinfo: default host options
sopts: default switch options
lopts: default link options"""
self.g = Graph()
self.node_info = {} # dpids hash to Node objects
self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = {} if hopts is None else hopts
self.sopts = {} if sopts is None else sopts
self.lopts = {} if lopts is None else lopts
self.ports = {} # ports[src][dst] is port on src that connects to dst
self.id_gen = NodeID # class used to generate dpid
def add_node(self, dpid, node):
'''Add Node to graph.
def add_node(self, name, **opts):
"""Add Node to graph.
name: name
opts: node options
returns: node name"""
self.g.add_node(name)
self.node_info[name] = opts
return name
@param dpid dpid
@param node Node object
'''
self.g.add_node(dpid)
self.node_info[dpid] = node
def add_host(self, name, **opts):
"""Convenience method: Add host to graph.
name: host name
opts: host options
returns: host name"""
if not opts and self.hopts:
opts = self.hopts
return self.add_node(name, **opts)
def add_edge(self, src, dst, edge = None):
'''Add edge (Node, Node) to graph.
def add_switch(self, name, **opts):
"""Convenience method: Add switch to graph.
name: switch name
opts: switch options
returns: switch name"""
if not opts and self.sopts:
opts = self.sopts
result = self.add_node(name, is_switch=True, **opts)
return result
@param src src dpid
@param dst dst dpid
@param edge Edge object
'''
src, dst = tuple(sorted([src, dst]))
self.g.add_edge(src, dst)
if not edge:
edge = Edge()
self.edge_info[(src, dst)] = edge
self.add_port(src, dst)
def add_link(self, node1, node2, port1=None, port2=None,
**opts):
"""node1, node2: nodes to link together
port1, port2: ports (optional)
opts: link options (optional)
returns: link info key"""
if not opts and self.lopts:
opts = self.lopts
self.add_port(node1, node2, port1, port2)
key = tuple(self.sorted([node1, node2]))
self.link_info[key] = opts
self.g.add_edge(*key)
return key
def add_port(self, src, dst):
def add_port(self, src, dst, sport=None, dport=None):
'''Generate port mapping for new edge.
@param src source switch DPID
@param dst destination switch DPID
@param src source switch name
@param dst destination switch name
'''
src_base = SWITCH_PORT_BASE if self.is_switch(src) else 0
dst_base = SWITCH_PORT_BASE if self.is_switch(dst) else 0
if src not in self.ports:
self.ports[src] = {}
if dst not in self.ports[src]:
# num outlinks
self.ports[src][dst] = len(self.ports[src]) + src_base
if dst not in self.ports:
self.ports[dst] = {}
if src not in self.ports[dst]:
# num outlinks
self.ports[dst][src] = len(self.ports[dst]) + dst_base
self.ports.setdefault(src, {})
self.ports.setdefault(dst, {})
# New port: number of outlinks + base
src_base = 1 if self.is_switch(src) else 0
dst_base = 1 if self.is_switch(dst) else 0
if sport is None:
sport = len(self.ports[src]) + src_base
if dport is None:
dport = len(self.ports[dst]) + dst_base
self.ports[src][dst] = sport
self.ports[dst][src] = dport
def node_enabled(self, dpid):
'''Is node connected, admin on, powered on, and fault-free?
@param dpid dpid
@return bool node is enabled
'''
ni = self.node_info[dpid]
return ni.connected and ni.admin_on and ni.power_on and not ni.fault
def nodes_enabled(self, dpids, enabled = True):
'''Return subset of enabled nodes
@param dpids list of dpids
@param enabled only return enabled nodes?
@return dpids filtered list of dpids
'''
if enabled:
return [n for n in dpids if self.node_enabled(n)]
def nodes(self, sort=True):
"Return nodes in graph"
if sort:
return self.sorted( self.g.nodes() )
else:
return dpids
def nodes(self, enabled = True):
'''Return graph nodes.
@param enabled only return enabled nodes?
@return dpids list of dpids
'''
return self.nodes_enabled(self.g.nodes(), enabled)
def nodes_str(self, dpids):
'''Return string of custom-encoded nodes.
@param dpids list of dpids
@return str string
'''
return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
return self.g.nodes()
def is_switch(self, n):
'''Returns true if node is a switch.'''
return self.node_info[n].is_switch
info = self.node_info[n]
return info and info.get('is_switch', False)
def switches(self, enabled = True):
def switches(self, sort=True):
'''Return switches.
@param enabled only return enabled nodes?
sort: sort switches alphabetically
@return dpids list of dpids
'''
nodes = [n for n in self.g.nodes() if self.is_switch(n)]
return self.nodes_enabled(nodes, enabled)
return [n for n in self.nodes(sort) if self.is_switch(n)]
def hosts(self, enabled = True):
def hosts(self, sort=True):
'''Return hosts.
@param enabled only return enabled nodes?
sort: sort hosts alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if not self.is_switch(n)]
def is_host(n):
'''Returns true if node is a host.'''
return not self.node_info[n].is_switch
nodes = [n for n in self.g.nodes() if is_host(n)]
return self.nodes_enabled(nodes, enabled)
def edge_enabled(self, edge):
'''Is edge admin on, powered on, and fault-free?
@param edge (src, dst) dpid tuple
@return bool edge is enabled
def links(self, sort=True):
'''Return links.
sort: sort links alphabetically
@return links list of name pairs
'''
src, dst = edge
src, dst = tuple(sorted([src, dst]))
ei = self.edge_info[tuple(sorted([src, dst]))]
return ei.admin_on and ei.power_on and not ei.fault
def edges_enabled(self, edges, enabled = True):
'''Return subset of enabled edges
@param edges list of edges
@param enabled only return enabled edges?
@return edges filtered list of edges
'''
if enabled:
return [e for e in edges if self.edge_enabled(e)]
if not sort:
return self.g.edges()
else:
return edges
def edges(self, enabled = True):
'''Return edges.
@param enabled only return enabled edges?
@return edges list of dpid pairs
'''
return self.edges_enabled(self.g.edges(), enabled)
def edges_str(self, dpid_pairs):
'''Return string of custom-encoded node pairs.
@param dpid_pairs list of dpid pairs (src, dst)
@return str string
'''
edges = []
for pair in dpid_pairs:
src, dst = pair
src = str(self.id_gen(dpid = src))
dst = str(self.id_gen(dpid = dst))
edges.append((src, dst))
return edges
links = [tuple(self.sorted(e)) for e in self.g.edges()]
return sorted( links, key=naturalSeq )
def port(self, src, dst):
'''Get port number.
@param src source switch DPID
@param dst destination switch DPID
@param src source switch name
@param dst destination switch name
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
@@ -276,130 +143,83 @@ class Topo(object):
assert dst in self.ports and src in self.ports[dst]
return (self.ports[src][dst], self.ports[dst][src])
def enable_edges(self):
'''Enable all edges in the network graph.
def linkInfo( self, src, dst ):
"Return link metadata"
src, dst = self.sorted([src, dst])
return self.link_info[(src, dst)]
Set admin on, power on, and fault off.
'''
for e in self.g.edges():
src, dst = e
ei = self.edge_info[tuple(sorted([src, dst]))]
ei.admin_on = True
ei.power_on = True
ei.fault = False
def nodeInfo( self, name ):
"Return metadata (dict) for node"
info = self.node_info[ name ]
return info if info is not None else {}
def enable_nodes(self):
'''Enable all nodes in the network graph.
Set connected on, admin on, power on, and fault off.
'''
for node in self.g.nodes():
ni = self.node_info[node]
ni.connected = True
ni.admin_on = True
ni.power_on = True
ni.fault = False
def enable_all(self):
'''Enable all nodes and edges in the network graph.'''
self.enable_nodes()
self.enable_edges()
def name(self, dpid):
'''Get string name of node ID.
@param dpid DPID of host or switch
@return name_str string name with no dashes
'''
return self.id_gen(dpid = dpid).name_str()
def ip(self, dpid):
'''Get IP dotted-decimal string of node ID.
@param dpid DPID of host or switch
@return ip_str
'''
return self.id_gen(dpid = dpid).ip_str()
def setNodeInfo( self, name, info ):
"Set metadata (dict) for node"
self.node_info[ name ] = info
@staticmethod
def sorted( items ):
"Items sorted in natural (i.e. alphabetical) order"
return sorted(items, key=natural)
class SingleSwitchTopo(Topo):
'''Single switch connected to k hosts.'''
def __init__(self, k = 2, enable_all = True):
def __init__(self, k=2, **opts):
'''Init.
@param k number of hosts
@param enable_all enables all nodes and switches?
'''
super(SingleSwitchTopo, self).__init__()
super(SingleSwitchTopo, self).__init__(**opts)
self.k = k
self.add_node(1, Node())
hosts = range(2, k + 2)
for h in hosts:
self.add_node(h, Node(is_switch = False))
self.add_edge(h, 1, Edge())
if enable_all:
self.enable_all()
switch = self.add_switch('s1')
for h in irange(1, k):
host = self.add_host('h%s' % h)
self.add_link(host, switch)
class SingleSwitchReversedTopo(SingleSwitchTopo):
class SingleSwitchReversedTopo(Topo):
'''Single switch connected to k hosts, with reversed ports.
The lowest-numbered host is connected to the highest-numbered port.
Useful to verify that Mininet properly handles custom port numberings.
'''
def port(self, src, dst):
'''Get port number.
@param src source switch DPID
@param dst destination switch DPID
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
if src == 1:
if dst in range(2, self.k + 2):
dst_index = dst - 2
highest = self.k - 1
return (highest - dst_index, 0)
else:
raise Exception('unexpected dst: %i' % dst)
elif src in range(2, self.k + 2):
if dst == 1:
raise Exception('unexpected dst: %i' % dst)
else:
src_index = src - 2
highest = self.k - 1
return (0, highest - src_index)
class LinearTopo(Topo):
'''Linear topology of k switches, with one host per switch.'''
def __init__(self, k = 2, enable_all = True):
def __init__(self, k=2, **opts):
'''Init.
@param k number of switches (and hosts too)
@param k number of hosts
@param enable_all enables all nodes and switches?
'''
super(LinearTopo, self).__init__()
super(SingleSwitchReversedTopo, self).__init__(**opts)
self.k = k
switch = self.add_switch('s1')
for h in irange(1, k):
host = self.add_host('h%s' % h)
self.add_link(host, switch,
port1=0, port2=(k - h + 1))
class LinearTopo(Topo):
"Linear topology of k switches, with one host per switch."
def __init__(self, k=2, **opts):
"""Init.
k: number of switches (and hosts)
hconf: host configuration options
lconf: link configuration options"""
super(LinearTopo, self).__init__(**opts)
self.k = k
switches = range(1, k + 1)
for s in switches:
h = s + k
self.add_node(s, Node())
self.add_node(h, Node(is_switch = False))
self.add_edge(s, h, Edge())
for s in switches:
if s != k:
self.add_edge(s, s + 1, Edge())
if enable_all:
self.enable_all()
lastSwitch = None
for i in irange(1, k):
host = self.add_host('h%s' % i)
switch = self.add_switch('s%s' % i)
self.add_link( host, switch)
if lastSwitch:
self.add_link( switch, lastSwitch)
lastSwitch = switch
+10 -19
View File
@@ -1,6 +1,6 @@
"Library of potentially useful topologies for Mininet"
from mininet.topo import Topo, Node
from mininet.topo import Topo
from mininet.net import Mininet
class TreeTopo( Topo ):
@@ -8,36 +8,27 @@ class TreeTopo( Topo ):
def __init__( self, depth=1, fanout=2 ):
super( TreeTopo, self ).__init__()
# Numbering: h1..N, sN+1..M
hostCount = fanout ** depth
# Numbering: h1..N, s1..M
self.hostNum = 1
self.switchNum = hostCount + 1
self.switchNum = 1
# Build topology
self.addTree( depth, fanout )
# Consider all switches and hosts 'on'
self.enable_all()
# It is OK that i is "unused" in the for loop.
# pylint: disable-msg=W0612
def addTree( self, depth, fanout ):
"""Add a subtree starting with node n.
returns: last node added"""
isSwitch = depth > 0
if isSwitch:
num = self.switchNum
node = self.add_switch( 's%s' % self.switchNum )
self.switchNum += 1
else:
num = self.hostNum
self.hostNum += 1
self.add_node( num, Node( is_switch=isSwitch ) )
if isSwitch:
for i in range( 0, fanout ):
for _ in range( fanout ):
child = self.addTree( depth - 1, fanout )
self.add_edge( num, child )
return num
self.add_link( node, child )
else:
node = self.add_host( 'h%s' % self.hostNum )
self.hostNum += 1
return node
# pylint: enable-msg=W0612
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
+170 -25
View File
@@ -2,10 +2,10 @@
from time import sleep
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
import select
from select import poll, POLLIN
from subprocess import call, check_call, Popen, PIPE, STDOUT
from mininet.log import error
from mininet.log import output, info, error
import re
# Command execution support
@@ -22,7 +22,7 @@ def checkRun( cmd ):
# pylint doesn't understand explicit type checking
# pylint: disable-msg=E1103
def quietRun( *cmd ):
def oldQuietRun( *cmd ):
"""Run a command, routing stderr to stdout, and return the output.
cmd: list of command params"""
if len( cmd ) == 1:
@@ -33,19 +33,82 @@ def quietRun( *cmd ):
# 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()
out = ''
readable = poll()
readable.register( popen.stdout )
while True:
while readable.poll():
data = popen.stdout.read( 1024 )
if len( data ) == 0:
break
output += data
out += data
popen.poll()
if popen.returncode != None:
break
return output
return out
# This is a bit complicated, but it enables us to
# monitor commount output as it is happening
def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code
cmd: string or list of command and args
stderr: STDOUT to merge stderr with stdout
shell: run command using shell
echo: monitor output to console"""
# Allow passing in a list or a string
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, str ):
cmd = cmd.split( ' ' )
cmd = [ str( arg ) for arg in cmd ]
# By default we separate stderr, don't run in a shell, and don't echo
stderr = kwargs.get( 'stderr', PIPE )
shell = kwargs.get( 'shell', False )
echo = kwargs.get( 'echo', False )
if echo:
# cmd goes to stderr, output goes to stdout
info( cmd, '\n' )
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
# We use poll() because select() doesn't work with large fd numbers
out, err = '', ''
poller = poll()
poller.register( popen.stdout, POLLIN )
fdtofile = { popen.stdout.fileno(): popen.stdout }
if popen.stderr:
fdtofile[ popen.stderr.fileno() ] = popen.stderr
poller.register( popen.stderr, POLLIN )
while True:
readable = poller.poll()
# Tell pylint to ignore unused variable event
# pylint: disable-msg=W0612
for fd, event in readable:
# pylint: enable-msg=W0612
f = fdtofile[ fd ]
data = f.read( 1024 )
if echo:
output( data )
if f == popen.stdout:
out += data
elif f == popen.stderr:
err += data
returncode = popen.poll()
if returncode is not None:
break
return out, err, returncode
def errFail( *cmd, **kwargs ):
"Run a command using errRun and raise exception on nonzero exit"
out, err, ret = errRun( *cmd, **kwargs )
if ret:
raise Exception( "errFail: %s failed with return code %s: %s"
% ( cmd, ret, err ) )
return out, err, ret
def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
# pylint: enable-msg=E1103
# pylint: disable-msg=E1101,W0612
@@ -124,25 +187,41 @@ def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
printError: if true, print error"""
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
def createLink( node1, node2, port1=None, port2=None ):
"""Create a link between nodes, making an interface for each.
node1: Node object
node2: Node object
port1: node1 port number (optional)
port2: node2 port number (optional)
returns: intf1 name, intf2 name"""
return node1.linkTo( node2, port1, port2 )
# Support for dumping network
def dumpNodeConnections( nodes ):
"Dump connections to/from nodes."
def dumpConnections( node ):
"Helper function: dump connections to node"
for intf in node.intfList():
output( ' %s:' % intf )
if intf.link:
intfs = [ intf.link.intf1, intf.link.intf2 ]
intfs.remove( intf )
output( intfs[ 0 ] )
else:
output( ' ' )
for node in nodes:
output( node.name )
dumpConnections( node )
output( '\n' )
def dumpNetConnections( net ):
"Dump connections in network"
nodes = net.controllers + net.switches + net.hosts
dumpNodeConnections( nodes )
# IP and Mac address formatting and parsing
def _colonHex( val, bytes ):
def _colonHex( val, bytecount ):
"""Generate colon-hex string.
val: input as unsigned int
bytes: number of bytes to convert
bytescount: number of bytes to convert
returns: chStr colon-hex string"""
pieces = []
for i in range( bytes - 1, -1, -1 ):
for i in range( bytecount - 1, -1, -1 ):
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
pieces.append( '%02x' % piece )
chStr = ':'.join( pieces )
@@ -158,23 +237,44 @@ def ipStr( ip ):
"""Generate IP address string from an unsigned int.
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
w = ( ip & 0xff000000 ) >> 24
w = ( ip >> 24 ) & 0xff
w = 10 if w == 0 else w
x = ( ip & 0xff0000 ) >> 16
y = ( ip & 0xff00 ) >> 8
x = ( ip >> 16 ) & 0xff
y = ( ip >> 8 ) & 0xff
z = ip & 0xff
return "%i.%i.%i.%i" % ( w, x, y, z )
def ipNum( w, x, y, z ):
"""Generate unsigned int from components ofIP address
"""Generate unsigned int from components of IP address
returns: w << 24 | x << 16 | y << 8 | z"""
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
"""Return IP address string from ints
i: int to be added to ipbase
prefixLen: optional IP prefix length
ipBaseNum: option base IP address as int
returns IP address as string"""
# Ugly but functional
assert i < ( 1 << ( 32 - prefixLen ) )
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
ipnum = i + ( ipBaseNum & mask )
return ipStr( ipnum )
def ipParse( ip ):
"Parse an IP address and return an unsigned int."
args = [ int( arg ) for arg in ip.split( '.' ) ]
return ipNum( *args )
def netParse( ipstr ):
"""Parse an IP network specification, returning
address and prefix len as unsigned ints"""
prefixLen = 0
if '/' in ipstr:
ip, pf = ipstr.split( '/' )
prefixLen = int( pf )
return ipParse( ip ), prefixLen
def checkInt( s ):
"Check if input string is an int"
try:
@@ -205,5 +305,50 @@ def makeNumeric( s ):
def fixLimits():
"Fix ridiculously small resource limits."
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
setrlimit( RLIMIT_NPROC, ( 8192, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 16384 ) )
def mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'mount' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if 'cgroups on %s' % cgdir not in mounts:
raise Exception( "cgroups not mounted on " + cgdir )
if 'cpuset on %s' % csdir not in mounts:
errRun( 'mkdir -p ' + csdir )
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
def natural( text ):
"To sort sanely/alphabetically: sorted( l, key=natural )"
def num( s ):
"Convert text segment to int if necessary"
return int( s ) if s.isdigit() else s
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
def naturalSeq( t ):
"Natural sort key function for sequences"
return [ natural( x ) for x in t ]
def numCores():
"Returns number of CPU cores based on /proc/cpuinfo"
if hasattr( numCores, 'ncores' ):
return numCores.ncores
try:
numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
except ValueError:
return 0
return numCores.ncores
def irange(start, end):
"""Inclusive range from start to end (vs. Python insanity.)
irange(1,5) -> 1, 2, 3, 4, 5"""
return range( start, end + 1 )
def custom( cls, **params ):
"Returns customized constructor for class cls."
def customized( *args, **kwargs):
"Customized constructor"
kwargs.update( params )
return cls( *args, **kwargs )
return customized
+1 -2
View File
@@ -5,8 +5,7 @@
from setuptools import setup, find_packages
from os.path import join
scripts = [ join( 'bin', filename ) for filename in [
'mn', 'mnexec' ] ]
scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ]
modname = distname = 'mininet'
+94
View File
@@ -0,0 +1,94 @@
#!/bin/bash
# Attempt to build debian packages for OVS
set -e # exit on error
set -u # exit on undefined variable
kvers=`uname -r`
ksrc=/lib/modules/$kvers/build
dist=`lsb_release -is | tr [A-Z] [a-z]`
release=`lsb_release -rs`
arch=`uname -m`
buildsuffix=-2
if [ "$arch" = "i686" ]; then arch=i386; fi
if [ "$arch" = "x86_64" ]; then arch=amd64; fi
overs=1.4.0
ovs=openvswitch-$overs
ovstgz=$ovs.tar.gz
ovsurl=http://openvswitch.org/releases/$ovstgz
install='sudo apt-get install -y'
echo "*** Installing debian/ubuntu build system"
$install build-essential devscripts ubuntu-dev-tools debhelper dh-make
$install diff patch cdbs quilt gnupg fakeroot lintian pbuilder piuparts
$install module-assistant
echo "*** Installing OVS dependencies"
$install pkg-config gcc make python-dev libssl-dev libtool
$install dkms ipsec-tools
echo "*** Installing headers for $kvers"
$install linux-headers-$kvers
echo "*** Retrieving OVS source"
wget -c $ovsurl
tar xzf $ovstgz
cd $ovs
echo "*** Patching OVS source"
# Not sure why this fails, but off it goes!
sed -i -e 's/dh_strip/# dh_strip/' debian/rules
if [ "$release" = "10.04" ]; then
# Lucid doesn't seem to have all the packages for ovsdbmonitor
echo "*** Patching debian/rules to remove dh_python2"
sed -i -e 's/dh_python2/dh_pysupport/' debian/rules
echo "*** Not building ovsdbmonitor since it's too hard on 10.04"
mv debian/ovsdbmonitor.install debian/ovsdbmonitor.install.backup
sed -i -e 's/ovsdbmonitor.install/ovsdbmonitor.install.backup/' Makefile.in
else
# Install a bag of hurt for ovsdbmonitor
$install python-pyside.qtcore pyqt4-dev-tools python-twisted python-twisted-bin \
python-twisted-core python-twisted-conch python-anyjson python-zope.interface
fi
# init script was written to assume that commands complete
sed -i -e 's/^set -e/#set -e/' debian/openvswitch-controller.init
echo "*** Building OVS user packages"
opts=--with-linux=/lib/modules/`uname -r`/build
fakeroot make -f debian/rules DATAPATH_CONFIGURE_OPTS=$opts binary
echo "*** Building OVS datapath kernel module package"
# Still looking for the "right" way to do this...
sudo mkdir -p /usr/src/linux
ln -sf _debian/openvswitch.tar.gz .
sudo make -f debian/rules.modules KSRC=$ksrc KVERS=$kvers binary-modules
echo "*** Built the following packages:"
cd ~
ls -l *deb
archive=ovs-$overs-core-$dist-$release-$arch$buildsuffix.tar
ovsbase='common pki switch brcompat controller datapath-dkms'
echo "*** Packing up $ovsbase .debs into:"
echo " $archive"
pkgs=""
for component in $ovsbase; do
if echo $component | egrep 'dkms|pki'; then
# Architecture-independent packages
deb=(openvswitch-${component}_$overs*all.deb)
else
deb=(openvswitch-${component}_$overs*$arch.deb)
fi
pkgs="$pkgs $deb"
done
rm -rf $archive
tar cf $archive $pkgs
echo "*** Contents of archive $archive:"
tar tf $archive
echo "*** Done (hopefully)"
+1 -1
View File
@@ -82,7 +82,7 @@ if __name__ == '__main__':
fixLines( infile.readlines(), outfid )
infile.close()
os.close( outfid )
call( [ 'doxypy.py', outname ] )
call( [ 'doxypy', outname ] )
+179 -66
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
# Mininet install script for Ubuntu (and Debian Lenny)
# Brandon Heller (brandonh@stanford.edu)
@@ -16,57 +17,78 @@ KERNEL_LOC=http://www.openflow.org/downloads/mininet
DIST=Unknown
RELEASE=Unknown
CODENAME=Unknown
ARCH=`uname -m`
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi
if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi
test -e /etc/debian_version && DIST="Debian"
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
sudo apt-get install -y lsb-release
install='sudo apt-get -y install'
remove='sudo apt-get -y remove'
pkginst='sudo dpkg -i'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install lsb-release
fi
if ! which bc &> /dev/null; then
$install bc
fi
fi
if which lsb_release &> /dev/null; then
DIST=`lsb_release -is`
RELEASE=`lsb_release -rs`
CODENAME=`lsb_release -cs`
fi
echo "Detected Linux distribution: $DIST $RELEASE $CODENAME"
echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
# Kernel params
if [ "$DIST" = "Debian" ]; then
KERNEL_NAME=2.6.33.1-mininet
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
elif [ "$DIST" = "Ubuntu" ]; then
if [ "$DIST" = "Ubuntu" ]; then
if [ "$RELEASE" = "10.04" ]; then
KERNEL_NAME='3.0.0-15-generic'
else
KERNEL_NAME=`uname -r`
fi
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}
elif [ "$DIST" = "Debian" ] && [ "$ARCH" = "i386" ] && [ "$CODENAME" = "lenny" ]; then
KERNEL_NAME=2.6.33.1-mininet
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
else
echo "Install.sh currently only supports Ubuntu and Debian."
echo "Install.sh currently only supports Ubuntu and Debian Lenny i386."
exit 1
fi
# More distribution info
DIST_LC=`echo $DIST | tr [A-Z] [a-z]` # as lower case
# Kernel Deb pkg to be removed:
KERNEL_IMAGE_OLD=linux-image-2.6.26-2-686
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
DRIVERS_DIR=/lib/modules/${KERNEL_NAME}/kernel/drivers/net
OVS_RELEASE=v1.2.2
OVS_RELEASE=1.4.0
OVS_PACKAGE_LOC=https://github.com/downloads/mininet/mininet
OVS_BUILDSUFFIX=-ignore # was -2
OVS_PACKAGE_NAME=ovs-$OVS_RELEASE-core-$DIST_LC-$RELEASE-$ARCH$OVS_BUILDSUFFIX.tar
OVS_SRC=~/openvswitch
OVS_TAG=v$OVS_RELEASE
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
function kernel {
echo "Install Mininet-compatible kernel if necessary"
sudo apt-get update
if [ "$DIST" = "Debian" ]; then
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
$install linux-image-$KERNEL_NAME
elif [ "$DIST" = "Debian" ]; then
# The easy approach: download pre-built linux-image and linux-headers packages:
wget -c $KERNEL_LOC/$KERNEL_HEADERS
wget -c $KERNEL_LOC/$KERNEL_IMAGE
# Install custom linux headers and image:
sudo dpkg -i $KERNEL_IMAGE $KERNEL_HEADERS
$pkginst $KERNEL_IMAGE $KERNEL_HEADERS
# The next two steps are to work around a bug in newer versions of
# kernel-package, which fails to add initrd images with the latest kernels.
@@ -83,16 +105,15 @@ function kernel {
# /boot/grub/menu.lst to set the default to the entry corresponding to the
# kernel you just installed.
fi
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
sudo apt-get -y install linux-image-$KERNEL_NAME
fi
}
function kernel_clean {
echo "Cleaning kernel..."
# To save disk space, remove previous kernel
sudo apt-get -y remove $KERNEL_IMAGE_OLD
if ! $remove $KERNEL_IMAGE_OLD; then
echo $KERNEL_IMAGE_OLD not installed.
fi
# Also remove downloaded packages:
rm -f ~/linux-headers-* ~/linux-image-*
@@ -101,8 +122,8 @@ function kernel_clean {
# Install Mininet deps
function mn_deps {
echo "Installing Mininet dependencies"
sudo aptitude install -y gcc make screen psmisc xterm ssh iperf iproute \
python-setuptools python-networkx
$install gcc make screen psmisc xterm ssh iperf iproute \
python-setuptools python-networkx cgroup-bin ethtool
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
echo "Upgrading networkx to avoid deprecation warning"
@@ -124,16 +145,14 @@ function mn_deps {
# The following will cause a full OF install, covering:
# -user switch
# -dissector
# The instructions below are an abbreviated version from
# http://www.openflowswitch.org/wk/index.php/Debian_Install
# ... modified to use Debian Lenny rather than unstable.
function of {
echo "Installing OpenFlow and its tools..."
echo "Installing OpenFlow reference implementation..."
cd ~/
sudo apt-get install -y git-core automake m4 pkg-config libtool \
make libc6-dev autoconf autotools-dev gcc
$install git-core autoconf automake autotools-dev pkg-config \
make gcc libtool libc6-dev
git clone git://openflowswitch.org/openflow.git
cd ~/openflow
@@ -146,19 +165,9 @@ function of {
make
sudo make install
# Install dissector:
sudo apt-get install -y wireshark libgtk2.0-dev
cd ~/openflow/utilities/wireshark_dissectors/openflow
make
sudo make install
# Copy coloring rules: OF is white-on-blue:
mkdir -p ~/.wireshark
cp ~/mininet/util/colorfilters ~/.wireshark
# Remove avahi-daemon, which may cause unwanted discovery packets to be
# sent during tests, near link status changes:
sudo apt-get remove -y avahi-daemon
# sent during tests, near link status changes:
$remove avahi-daemon
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
if [ "$DIST" = "Ubuntu" ]; then
@@ -167,46 +176,146 @@ function of {
BLACKLIST=/etc/modprobe.d/blacklist
fi
sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
cd ~
}
function wireshark {
echo "Installing Wireshark dissector..."
sudo apt-get install -y wireshark libgtk2.0-dev
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" != "10.04" ]; then
# Install newer version
sudo apt-get install -y scons mercurial libglib2.0-dev
sudo apt-get install -y libwiretap-dev libwireshark-dev
cd ~
hg clone https://bitbucket.org/onlab/of-dissector
cd of-dissector/src
export WIRESHARK=/usr/include/wireshark
scons
# libwireshark0/ on 11.04; libwireshark1/ on later
WSDIR=`ls -d /usr/lib/wireshark/libwireshark* | head -1`
WSPLUGDIR=$WSDIR/plugins/
sudo cp openflow.so $WSPLUGDIR
echo "Copied openflow plugin to $WSPLUGDIR"
else
# Install older version from reference source
cd ~/openflow/utilities/wireshark_dissectors/openflow
make
sudo make install
fi
# Copy coloring rules: OF is white-on-blue:
mkdir -p ~/.wireshark
cp ~/mininet/util/colorfilters ~/.wireshark
}
# Install Open vSwitch
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
function ovs {
echo "Installing Open vSwitch..."
if [ "$DIST" = "Debian" ] && [ "$CODENAME" == "lenny" ]; then
sudo aptitude -y install pkg-config gcc make git-core python-dev libssl-dev
# Install Autoconf 2.63+ backport from Debian Backports repo:
# Instructions from http://backports.org/dokuwiki/doku.php?id=instructions
sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list"
sudo apt-get update
sudo apt-get -y --force-yes install debian-backports-keyring
sudo apt-get -y --force-yes -t lenny-backports install autoconf
# Required for module build/dkms install
$install $KERNEL_HEADERS
# First see if we have packages
# XXX wget -c seems to fail from github/amazon s3
cd /tmp
if wget $OVS_PACKAGE_LOC/$OVS_PACKAGE_NAME; then
$install patch dkms fakeroot python-argparse
tar xf $OVS_PACKAGE_NAME
orig=`tar tf $OVS_PACKAGE_NAME`
# Now install packages in reasonable dependency order
order='dkms common pki openvswitch-switch brcompat controller'
pkgs=""
for p in $order; do
pkg=`echo "$orig" | grep $p`
# Annoyingly, things seem to be missing without this flag
$pkginst --force-confmiss $pkg
done
# Switch can run on its own, but
# Mininet should control the controller
if [ -e /etc/init.d/openvswitch-controller ]; then
if sudo service openvswitch-controller stop; then
echo "Stopped running controller"
fi
sudo update-rc.d openvswitch-controller disable
fi
echo "Done (hopefully) installing packages"
cd ~
return
fi
if [ "$DIST" = "Ubuntu" ]; then
sudo apt-get -y install $KERNEL_HEADERS
# Otherwise try distribution's OVS packages
if [ "$DIST" = "Ubuntu" ] && [ `echo "$RELEASE >= 11.10" | bc` = 1 ]; then
if ! dpkg --get-selections | grep openvswitch-datapath; then
# If you've already installed a datapath, assume you
# know what you're doing and don't need dkms datapath.
# Otherwise, install it.
$install openvswitch-datapath-dkms
fi
if $install openvswitch-switch openvswitch-controller; then
return
fi
fi
$install pkg-config gcc make python-dev libssl-dev libtool
if [ "$DIST" = "Debian" ]; then
if [ "$CODENAME" = "lenny" ]; then
$install git-core
# Install Autoconf 2.63+ backport from Debian Backports repo:
# Instructions from http://backports.org/dokuwiki/doku.php?id=instructions
sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list"
sudo apt-get update
sudo apt-get -y --force-yes install debian-backports-keyring
sudo apt-get -y --force-yes -t lenny-backports install autoconf
fi
else
$install git
fi
# Install OVS from release
cd ~/
git clone git://openvswitch.org/openvswitch
git clone git://openvswitch.org/openvswitch $OVS_SRC
cd $OVS_SRC
git checkout $OVS_RELEASE
git checkout $OVS_TAG
./boot.sh
BUILDDIR=/lib/modules/${KERNEL_NAME}/build
if [ ! -e $BUILDDIR ]; then
echo "Creating build sdirectory $BUILDDIR"
sudo mkdir -p $BUILDDIR
fi
opts="--with-linux=$BUILDDIR"
mkdir -p $OVS_BUILD
cd $OVS_BUILD
opts="--with-linux=$BUILDDIR"
mkdir -p $OVS_BUILD
cd $OVS_BUILD
../configure $opts
make
sudo make install
# openflowd is deprecated, but for now copy it in
sudo cp tests/test-openflowd /usr/local/bin/ovs-openflowd
modprobe
}
function remove_ovs {
pkgs=`dpkg --get-selections | grep openvswitch | awk '{ print $1;}'`
echo "Removing existing Open vSwitch packages:"
echo $pkgs
if ! $remove $pkgs; then
echo "Not all packages removed correctly"
fi
# For some reason this doesn't happen
if scripts=`ls /etc/init.d/*openvswitch* 2>/dev/null`; then
echo $scripts
for s in $scripts; do
s=$(basename $s)
echo SCRIPT $s
sudo service $s stop
sudo rm -f /etc/init.d/$s
sudo update-rc.d -f $s remove
done
fi
echo "Done removing OVS"
}
# Install NOX with tutorial files
@@ -214,17 +323,17 @@ function nox {
echo "Installing NOX w/tutorial files..."
# Install NOX deps:
sudo apt-get -y install autoconf automake g++ libtool python python-twisted \
$install autoconf automake g++ libtool python python-twisted \
swig libssl-dev make
if [ "$DIST" = "Debian" ]; then
sudo apt-get -y install libboost1.35-dev
$install libboost1.35-dev
elif [ "$DIST" = "Ubuntu" ]; then
sudo apt-get -y install python-dev libboost-dev
sudo apt-get -y install libboost-filesystem-dev
sudo apt-get -y install libboost-test-dev
$install python-dev libboost-dev
$install libboost-filesystem-dev
$install libboost-test-dev
fi
# Install NOX optional deps:
sudo apt-get install -y libsqlite3-dev python-simplejson
$install libsqlite3-dev python-simplejson
# Fetch NOX destiny
cd ~/
@@ -257,7 +366,7 @@ function oftest {
echo "Installing oftest..."
# Install deps:
sudo apt-get install -y tcpdump python-scapy
$install tcpdump python-scapy
# Install oftest:
cd ~/
@@ -271,7 +380,7 @@ function oftest {
function cbench {
echo "Installing cbench..."
sudo apt-get install -y libsnmp-dev libpcap-dev
$install libsnmp-dev libpcap-dev
cd ~/
git clone git://openflow.org/oflops.git
cd oflops
@@ -290,13 +399,13 @@ function other {
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
# a graphical git history viewer.
sudo apt-get install -y tcpdump tshark gitk
$install tcpdump tshark gitk
# Install common text editors
sudo apt-get install -y vim nano emacs
$install vim nano emacs
# Install NTP
sudo apt-get install -y ntp
$install ntp
# Set git to colorize everything.
git config --global color.diff auto
@@ -331,8 +440,8 @@ function all {
kernel
mn_deps
of
wireshark
ovs
modprobe
nox
oftest
cbench
@@ -386,10 +495,12 @@ function usage {
printf -- ' -f: install open(F)low\n' >&2
printf -- ' -h: print this (H)elp message\n' >&2
printf -- ' -k: install new (K)ernel\n' >&2
printf -- ' -m: install Open vSwitch kernel (M)odule\n' >&2
printf -- ' -m: install Open vSwitch kernel (M)odule from source dir\n' >&2
printf -- ' -n: install mini(N)et dependencies + core files\n' >&2
printf -- ' -r: remove existing Open vSwitch packages\n' >&2
printf -- ' -t: install o(T)her stuff\n' >&2
printf -- ' -v: install open (V)switch\n' >&2
printf -- ' -w: install OpenFlow (w)ireshark dissector\n' >&2
printf -- ' -x: install NO(X) OpenFlow controller\n' >&2
printf -- ' -y: install (A)ll packages\n' >&2
@@ -400,7 +511,7 @@ if [ $# -eq 0 ]
then
all
else
while getopts 'abcdfhkmntvx' OPTION
while getopts 'abcdfhkmnrtvwx' OPTION
do
case $OPTION in
a) all;;
@@ -412,8 +523,10 @@ else
k) kernel;;
m) modprobe;;
n) mn_deps;;
r) remove_ovs;;
t) other;;
v) ovs;;
w) wireshark;;
x) nox;;
?) usage;;
esac
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# This script is intended to install Mininet into
# a brand-new Ubuntu (10.04 or 11.10) virtual machine,
# to create a fully usable "tutorial" VM.
set -e
sudo sh -c 'cat >> /etc/sudoers' <<EOF
openflow ALL=NOPASSWD: ALL
EOF
sudo sed -i -e 's/Default/#Default/' /etc/sudoers
sudo sed -i -e 's/ubuntu/mininet-vm/' /etc/hostname
sudo sed -i -e 's/ubuntu/mininet-vm/g' /etc/hosts
sudo hostname `cat /etc/hostname`
sudo sed -i -e 's/quiet splash/text/' /etc/default/grub
sudo update-grub
sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
/etc/apt/sources.list
sudo apt-get update
sudo apt-get -y install git-core openssh-server
git clone git://github.com/mininet/mininet
cd mininet
# Check out branch for cs244
git checkout -b cs244 origin/class/cs244
cd
time mininet/util/install.sh
if ! grep NOX_CORE_DIR .bashrc; then
echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc
fi
echo <<EOF
You may need to reboot and then:
sudo dpkg-reconfigure openvswitch-datapath-dkms
sudo service openvswitch-switch start
EOF