Compare commits

..

439 Commits

Author SHA1 Message Date
Bob Lantz 433d83221a Fix typo 2013-09-19 04:12:04 -07:00
Bob Lantz 3151804cf8 Add option to set pexpect timeout 2013-09-19 02:02:03 -07:00
Bob Lantz f66e9af515 Update for 2.1.0 final packaging 2013-09-18 22:44:35 -07:00
Bob Lantz f92abd2ba5 Sync changelog (somewhat) with ubuntu packaging
Only differences are copyright and log message.
2013-09-18 22:41:17 -07:00
Bob Lantz 700c5bf5b5 Add date to ovf name 2013-09-18 21:39:09 -07:00
Bob Lantz 87c37bb182 Add jpoliv 2013-09-18 21:38:36 -07:00
Bob Lantz 9cc9a491a8 Avoid reverse DNS lookup 2013-09-18 20:53:54 -07:00
Bob Lantz 76a8a1637f Clean up sshd.py and its test so it works on VirtualBox 2013-09-18 20:48:22 -07:00
Bob Lantz 568f6424e5 Add --memory option for VM memory size (for large tests) 2013-09-18 18:49:21 -07:00
Bob f32a8f7ea0 Minor edits 2013-09-18 16:47:28 -07:00
Bob Lantz e0b50c8a70 Fix var name change 2013-09-18 00:04:20 -07:00
Bob Lantz 0294f5ec55 Add options to run commands before or after tests 2013-09-18 00:03:16 -07:00
Bob 53987e31aa Merge pull request #214 from zlim/mnexec-fix
mnexec: getopt bugfix
2013-09-18 13:06:58 -07:00
Bob Lantz f59858543c Fix walkthrough test path 2013-09-18 12:44:54 -07:00
Zi Shen Lim cae7be1a60 mnexec: getopt bugfix
getopt returns an 'int', so use change datatype of 'c' to match it.
Otherwise, 'c' may hold a value of 255 (0xff), and fail the comparison
with -1 (0xffffffff): while ((c = getopt(...)) != -1)

This bug was uncovered on Ubuntu 13.04 running on ARM, using
arm-linux-gnueabihf-gcc4.7.
2013-09-18 10:33:15 -07:00
Bob Lantz 4ccfe242e8 Remove extra expect (paste error) that broke bootAndRunTests() 2013-09-17 18:02:06 -07:00
Brian O'Connor 811963ad26 small additions to README 2013-09-17 15:51:46 -07:00
Brian O'Connor 41d932017b updated source install for Ubuntu in INSTALL 2013-09-17 15:51:46 -07:00
Bob Lantz 7a24407ecc Remove unneeded bc dependency
fixes #212
2013-09-17 17:31:21 -07:00
Brian O'Connor df68c211b5 Merge pull request #209 from jpoliv/master
Fedora 18+ installation notes
2013-09-17 16:38:24 -07:00
Bob Lantz 2ca1ea92ff Clarify log message 2013-09-17 15:56:09 -07:00
Bob Lantz 8f546d89b4 Backing out attempt to identify kernel binary format 2013-09-17 15:40:52 -07:00
Bob Lantz 6d3cb5bcd3 Add arch to vm name, and check kernel for boot arch 2013-09-17 14:50:41 -07:00
Bob Lantz ca5b0c56f0 setLogLevel('warning') to avoid test spam 2013-09-17 13:44:42 -07:00
Jose Pedro Oliveira 0d58b93a84 Fedora 18+ installation notes 2013-09-16 21:42:06 +01:00
Bob Lantz b7548e68a8 Add walkthrough test and log build exceptions 2013-09-16 11:22:26 -07:00
Bob Lantz 2f3e8c2bd3 Log failed test output. 2013-09-16 11:06:12 -07:00
Brian O'Connor fc2a8fd59a fixed git download for testRemoteController in test_walkthrough.py 2013-09-17 11:28:10 -07:00
Brian O'Connor 47b24beb6c Merge branch 'master' of github.com:mininet/mininet 2013-09-16 23:59:06 -07:00
Brian O'Connor 895ff6f452 adding runner to mininet/test, added test_walkthrough.py 2013-09-16 23:58:22 -07:00
Bob Lantz fce7f5c56b Add --zip option to archive VM. 2013-09-16 07:45:32 -07:00
Bob b5e8dd5edc Merge pull request #210 from rlane/ivs-opts
IVSSwitch: support opts constructor parameter
2013-09-16 14:22:18 -07:00
Rich Lane d4fabc0464 IVSSwitch: support opts constructor parameter
This parameter is already supported by the other switch classes.
2013-09-16 13:33:38 -07:00
Brian O'Connor 971d6aafd4 Merge pull request #208 from jpoliv/master
install.sh: improve support for the Fedora linux distribution
2013-09-13 18:32:29 -07:00
Jose Pedro Oliveira 0457824193 Fedora: update status messages 2013-09-14 02:22:07 +01:00
Jose Pedro Oliveira 40ad3a1bf9 Replaces a couple of 'sudo apt-get install -y' instances by '$install' 2013-09-14 02:21:24 +01:00
Jose Pedro Oliveira de2680152b Fedora: support the -w option (wireshark) 2013-09-14 02:19:39 +01:00
Jose Pedro Oliveira 541ba67daf Fedora: support the -v option (OVS) 2013-09-14 02:18:04 +01:00
Brian O'Connor 6576d894a3 Merge branch 'master' of github.com:mininet/mininet 2013-09-13 13:06:49 -07:00
Brian O'Connor 8e1dade106 removing assert from util.py 2013-09-13 13:06:21 -07:00
Bob 2ebdd028cf Merge pull request #194 from jpoliv/master
Make install.sh (-n) work in Fedora 19
2013-09-13 12:40:08 -07:00
Jose Pedro Oliveira b5a48f9f6c Merge branch 'master' of https://github.com/mininet/mininet
Conflicts:
	mininet/node.py -- revert our change; better solution upstream
	util/install.sh -- resolved the conflict (echo statement)
2013-09-13 04:05:47 +01:00
Bob d70ca981c9 Update README.md 2013-09-12 14:23:19 -07:00
Bob 4b719d7443 Minor cosmetic edits 2013-09-12 14:21:20 -07:00
Bob Lantz 5b9f6b2192 Added .md to README so that it displays nicely on github. 2013-09-12 14:16:58 -07:00
Bob Lantz 4e242e9211 Add -v so that we can see exampletest results 2013-09-12 14:14:55 -07:00
Bob 94f3c76039 Merge pull request #207 from mininet/devel/exampletest
Added tests for all non-X11 examples
2013-09-12 14:14:14 -07:00
Brian O'Connor 67a7c5601a Merge branch 'master' of github.com:mininet/mininet 2013-09-12 13:49:45 -07:00
Brian O'Connor 4ea0c0936d Updated mininet/util.py to support better resource setting semantics and protected with try block 2013-09-12 13:49:40 -07:00
Bob Lantz 7bd9a79b12 Add --test {test} and --branch {branch} options, and exampletest 2013-09-11 22:59:50 -07:00
Brian O'Connor bfb560045c add rm to sshd tests 2013-09-11 14:45:49 -07:00
Brian O'Connor c5da46f125 added -quick options to skip long tests 2013-09-11 14:45:49 -07:00
Brian O'Connor 10fdd01dc8 fixed runner.py and added -v and -quick options 2013-09-11 14:45:48 -07:00
Brian O'Connor 1e9e781c12 changed 1% to 2% in test_limit.py 2013-09-11 14:45:48 -07:00
Brian O'Connor 24b38126ec cleaned up and commented test_simpleperf.py 2013-09-11 14:45:48 -07:00
Brian O'Connor 3577a6989d cleaned up and commented test_tree1024.py and test_treeping64.py 2013-09-11 14:45:48 -07:00
Brian O'Connor 48c49c54e2 cleaned up and commented test_sshd.py 2013-09-11 14:45:48 -07:00
Brian O'Connor d4993c0ba4 cleaned up and commented test_scratchnet.py 2013-09-11 14:45:48 -07:00
Brian O'Connor 213b7c57ee cleaned up and commented test_popen.py 2013-09-11 14:45:48 -07:00
Brian O'Connor b9b1f2e7f0 cleaned up and commented test_nat.py; added check for connectivity before running test 2013-09-11 14:45:48 -07:00
Brian O'Connor cdd5210bb7 cleaned up and commented test_multitest.py 2013-09-11 14:45:48 -07:00
Brian O'Connor bc90a79581 cleaned up and commented test_multipoll.py 2013-09-11 14:45:47 -07:00
Brian O'Connor e6e1260bc2 cleaned up and commented test_multiping.py 2013-09-11 14:45:47 -07:00
Brian O'Connor 1d555e724b cleaned up and commented test_linearbandwidth.py 2013-09-11 14:45:47 -07:00
Brian O'Connor e6fe480a30 cleaned up and commented test_limit.py 2013-09-11 14:45:47 -07:00
Brian O'Connor 91a06063b4 cleaned up and commented test_hwintf.py 2013-09-11 14:45:47 -07:00
Brian O'Connor 49fc496c12 cleaned up and commented test_emptynet.py 2013-09-11 14:45:47 -07:00
Brian O'Connor 94abeeabb9 cleaned up and commented test_cpu.py 2013-09-11 14:45:47 -07:00
Brian O'Connor b7e506341f cleaned up and commented test_controlnet.py 2013-09-11 14:45:47 -07:00
Brian O'Connor fba3fd81fa cleaned up and commented test_controllers.py 2013-09-11 14:45:47 -07:00
Brian O'Connor e875c0de26 minor test cleanup 2013-09-11 14:45:47 -07:00
Brian O'Connor 43f058df6d cleaned up and commented test_bind.py; added one new test 2013-09-11 14:45:46 -07:00
Brian O'Connor 01c0ef0013 added comments to test_baresshd.py 2013-09-11 14:45:46 -07:00
Brian O'Connor a46fae0687 adding first draft of tests for all examples, they need comments and clean up, some could be made more rebust 2013-09-11 14:45:46 -07:00
Brian O'Connor 5a646a0d20 sshd.py: allow sshd args to be passed via commandline 2013-09-11 14:45:46 -07:00
Brian O'Connor 9a73dcad53 fixed print format in popenpoll 2013-09-11 14:45:46 -07:00
Brian O'Connor b605cf74d2 style in multitest 2013-09-11 14:45:46 -07:00
Brian O'Connor 220376b6e2 hwintf.py: allow intf to be specified in cmd line 2013-09-11 14:45:46 -07:00
Brian O'Connor cfb6bf95a3 adding commandline args to UserSwitch in controlnet, examples of partial 2013-09-11 14:45:46 -07:00
Brian O'Connor 3905843257 accept command line args in baresshd.py 2013-09-11 14:45:45 -07:00
Bob Lantz 501a164eba Added --test option to boot and test a VM 2013-09-11 12:00:17 -07:00
Bob Lantz 9e725cb28a Add 'mininet' prefix to output files 2013-09-11 12:00:17 -07:00
Bob Lantz e02cdc0c54 XML file cannot begin with a newline :( 2013-09-11 12:00:17 -07:00
Bob Lantz 0038720c01 Add generateOVF to finally create the OVF descriptor file! 2013-09-11 12:00:17 -07:00
Bob Lantz d4279559fa Add options; generate virtimage file (in progress) 2013-09-11 12:00:17 -07:00
Bob Lantz 1ea9d7d4de Update copyright for 2013 2013-09-11 12:00:17 -07:00
Bob Lantz f7abd084c6 Added socat, iperf, cgroup-bin to dependencies 2013-09-11 12:00:17 -07:00
Bob Lantz d82e0ef5c2 Add mtools to dependencies 2013-09-11 12:00:17 -07:00
Bob Lantz 3027856c7b Find wireshark dir using find (fix for 13.10) 2013-09-11 12:00:17 -07:00
Bob Lantz 9de7bd666d Minor additions and edits 2013-09-11 12:00:16 -07:00
Bob Lantz 92b51563b5 Remove extra ` 2013-09-11 12:00:16 -07:00
Brian O'Connor 389c7aa5af install.sh: making BUILD_DIR more robust 2013-09-11 12:00:16 -07:00
Brian O'Connor bda54a9aed updating INSTALL 2013-09-11 12:00:16 -07:00
Brian O'Connor 09b1239131 fixing comment 2013-09-11 12:00:16 -07:00
Bob Lantz 662f2447e3 Flush log file output to avoid slow buffering 2013-09-11 12:00:16 -07:00
Bob Lantz 20005f5bbc Add a space 2013-09-11 12:00:16 -07:00
Bob Lantz 803a1a5489 Write build log to file, and detect installation failure 2013-09-11 12:00:16 -07:00
Bob Lantz 20ba29590a Add 13.10 (won't work until final) 2013-09-11 12:00:16 -07:00
Bob Lantz 13554a3d83 Minor cleanup 2013-09-11 12:00:16 -07:00
Bob Lantz 9bfc7c7768 Satisfy pylint 2013-09-11 12:00:16 -07:00
Bob Lantz 045ef7b801 Add docstring to satisfy pylint 2013-09-11 12:00:15 -07:00
Bob Lantz e69355f78f One last code check fix: line too long 2013-09-11 12:00:15 -07:00
Bob Lantz 9d14c841d7 Pass code check 2013-09-11 12:00:15 -07:00
Bob Lantz f796f01f38 add spaces to satisfy pylint ;-p 2013-09-11 12:00:15 -07:00
Bob Lantz 350299786d code check fixes & add comment spaces 2013-09-11 12:00:15 -07:00
Bob Lantz 445c0959b5 Pass code check (except bogus Popen error) 2013-09-11 12:00:15 -07:00
Bob Lantz 0c5aae157a examples -> mininet/examples for code check 2013-09-11 12:00:15 -07:00
Brian O'Connor 2e7d0d4934 fixed indent issue with examples/controllers.py 2013-09-11 12:00:15 -07:00
Brian O'Connor e9a835ac55 print usage message for unknown command 2013-09-11 12:00:15 -07:00
Bob Lantz b55806017a Check for Connected in checkListening() 2013-09-11 12:00:15 -07:00
Bob Lantz b79ce2a549 Clarify actual testing in module comment 2013-09-11 12:00:14 -07:00
Bob Lantz a155795837 quetzal -> quantal 2013-09-11 12:00:14 -07:00
Brian O'Connor e935da461a added comments and cleaned up controlnet.py 2013-09-11 12:00:14 -07:00
Brian O'Connor 92bf2cf105 codecheck: removed unused variable in topo.py 2013-09-11 12:00:14 -07:00
Brian O'Connor 0840af5277 removing todos in net.py 2013-09-11 12:00:14 -07:00
Brian O'Connor 967614f64a adding examples/__init__.py 2013-09-11 12:00:14 -07:00
Brian O'Connor 891d807137 fixed multiping example 2013-09-11 12:00:14 -07:00
Brian O'Connor ecddbcf240 updated emptynet to use addLink 2013-09-11 12:00:14 -07:00
Brian O'Connor aacf7c4613 fixing controllers.py to use api 2013-09-11 12:00:14 -07:00
Brian O'Connor 3a35480c7a removing test from controlnet 2013-09-11 12:00:14 -07:00
Brian O'Connor 7c962d2f61 Fixed MininetFacade and moved main logic into run. First shot at "test" function 2013-09-11 12:00:13 -07:00
Brian O'Connor dc882d6905 clean up controlnet 2013-09-11 12:00:13 -07:00
Brian O'Connor 15146d900c changed CLI to MininetFacade; a great deal of logic also changed 2013-09-11 12:00:13 -07:00
Bob Lantz ad5a0e42d0 Explicitly create a qcow2 image 2013-09-11 12:00:13 -07:00
Bob Lantz a56e29704e Make sure that /bin/bash exists before attempting to chroot. 2013-09-11 12:00:13 -07:00
Bob Lantz f344290368 Change API for more efficient remount and unmount. 2013-09-11 12:00:13 -07:00
Bob Lantz 5ae8c936e7 Prototype implementation of bind mounts. 2013-09-11 12:00:13 -07:00
Bob Lantz 5413d2e5a3 Check for chroot dir and chroot if necessary. 2013-09-11 12:00:13 -07:00
Bob Lantz 226a1dc391 Minor edits 2013-09-11 12:00:13 -07:00
Bob Lantz ec810dd6db minor edits 2013-09-11 12:00:13 -07:00
Bob Lantz 5493212578 Add tests for OVS user switch (skipping hifi test for now)
test_hifi.py currently fails for OVS when datapath=user -
we should look at this and fix it.
2013-09-11 12:00:13 -07:00
Bob Lantz 45d365f98b Need a few more skipUnless() checks. 2013-09-11 12:00:12 -07:00
Bob Lantz 94324e3f46 Skip IVS and UserSwitch tests if they are not installed 2013-09-11 12:00:12 -07:00
Bob Lantz d2762938b7 Increase timeout (for lengthy hifi test) 2013-09-11 12:00:12 -07:00
Bob Lantz 765d126ee9 Delete leftover TAP interface from OVS with datapath=user
fixes #199
2013-09-11 12:00:12 -07:00
Bob Lantz 896c4cbccc Edits for 2.1.0 2013-09-11 12:00:12 -07:00
Bob Lantz 325074981c Initial text and version updates for 2.1.0 2013-09-11 12:00:12 -07:00
Bob Lantz b26f38a6aa Added CONTRIBUTORS file 2013-09-11 12:00:12 -07:00
Brian O'Connor d13505b6c6 updating setup to include examples 2013-08-26 15:29:05 -07:00
Bob Lantz 67f9d8f655 Remove qcow2 post conversion; drop unused etree dep 2013-08-26 11:46:25 -07:00
Bob Lantz c353e60913 correction: pexpect.timeout -> TIMEOUT 2013-08-25 18:06:22 -07:00
Bob Lantz 662fb712bc Detect failed iso download; begin virt-image support 2013-08-25 17:42:15 -07:00
Bob Lantz 28165f7b4e Check make test results 2013-08-25 16:27:43 -07:00
Bob Lantz dbcfda77d9 Update release URLS - should probably clean this up. 2013-08-25 15:51:56 -07:00
Bob Lantz 1dfa7776e1 Change to extract kernel and initrd 2013-08-25 15:25:04 -07:00
Bob Lantz 40a9c15345 Remove gigantic doxypy/texlive/fonts from install.sh -a
fixes #192
2013-08-25 13:55:50 -07:00
Bob Lantz 3dc3e066aa Update build directory name to include date. 2013-08-24 13:57:36 -07:00
Bob Lantz bbf808c347 Get rid of unused pexpect import. 2013-08-23 21:27:01 -07:00
Bob Lantz 4556e06fcf Fix erroneous tab hit before commit. 2013-08-23 21:25:07 -07:00
Bob Lantz f605a4e430 Works, more or less. 2013-08-23 20:33:25 -07:00
Bob Lantz fa1758b950 First draft of new world order (create build image from iso) 2013-08-23 17:26:46 -07:00
Bob Lantz 14903d6a05 Final gasp of cloud image version. 2013-08-22 18:40:10 -07:00
Jose Pedro Oliveira 5b08af2e8d Add a small notice about the Fedora's support. 2013-08-22 18:13:31 +01:00
Jose Pedro Oliveira 6cb05c3762 Make options -f and -b work in Fedora 19 2013-08-22 17:42:17 +01:00
Bob Lantz 94954177e5 Added support for creating a volume rather than a raw partition. 2013-08-21 21:18:11 -07:00
Jose Pedro Oliveira 3d89df13ef Handle Fedora's telnet output:
----------
*** c0 : ('echo A | telnet -e A 127.0.0.1 6633',)
telnet: connect to address 127.0.0.1: Connection refused
Telnet escape character is 'A'.
Trying 127.0.0.1...
Unable to contact the remote controller at 127.0.0.1:6633
----------
2013-08-22 00:57:14 +01:00
Jose Pedro Oliveira 15f2d240d6 Make install.sh support Fedora 19 (install.sh -n) 2013-08-22 00:47:30 +01:00
Bob Lantz 85dfac5c06 Many more improvements. 2013-08-20 13:31:15 -07:00
Bob Lantz 860bcc02c6 Minor changes and script evolution. 2013-08-20 13:31:15 -07:00
Bob Lantz 4daeeff0e3 Created first revision of VM build script. 2013-08-20 13:31:15 -07:00
Brian O'Connor 32de4c9e7c reverted iperf output and fixed regex 2013-08-16 11:16:28 -07:00
Brian O'Connor f0c123d4c0 fixing console.py graph parser 2013-08-15 19:06:31 -07:00
Brian O'Connor 8c22fe84c2 fixed iperf output parsing, changed to csv format
fixes #191
2013-08-15 18:54:07 -07:00
Brian O'Connor 9ff453a50b Changed iperf -sD to iperf -s &
fixes #190
2013-08-15 18:22:21 -07:00
Brian O'Connor 4e6b03d668 Merge pull request #188 from mininet/devel/fixlimits
Set sysctl limits when net starts
2013-08-14 17:30:43 -07:00
Brian O'Connor b635fd9edd Some cleanup and style fixes for fixlimits 2013-08-14 17:29:23 -07:00
Brian O'Connor a387952493 fixed host ip assignment and shutdown with xterms
fixes #189
2013-08-14 17:24:32 -07:00
Brian O'Connor 867a6d6731 Cleaned up sysctl and rlimit test and set
Use files instead of sysctl to set limits
2013-08-14 15:04:11 -07:00
Brian O'Connor b20c9470c7 add sysctl test and set
fixes #184
2013-08-12 16:22:19 -07:00
Brian O'Connor 7523c420bd Merge pull request #186 from mininet/devel/clifix
Replaced nodelist and nodemap in CLI with mn
2013-08-12 14:03:33 -07:00
Brian O'Connor 98cb33599f fixed comment in cli.py 2013-08-09 17:10:50 -07:00
Brian O'Connor 9281719d74 Made net compliant with dict semantics and added function comments
Fixed locals bug (now they are persisent across calls)
2013-08-09 17:07:39 -07:00
Brian O'Connor 8e04a9f844 Replaced nodelist and nodemap in CLI with mn
Updated Mininet to be more compliant with dict
Fixes #182
2013-08-09 12:20:13 -07:00
Brian O'Connor 6df4371df6 Removing test prints from user switch merge 2013-08-06 15:16:24 -07:00
Brian O'Connor be13072f0c Merge branch 'adf-userswitch-tclink' of github.com:adferguson/mininet into adferguson-adf-userswitch-tclink
Only sleep and reapply if slicing is enabled

Conflicts:
	mininet/node.py
2013-08-06 15:13:45 -07:00
Brian O'Connor 989df7454c Merge branch 'patches/examplesdoc' of github.com:ryancox/mininet into ryancox-patches/examplesdoc
Conflicts:
	examples/README
2013-08-05 18:13:37 -07:00
Brian O'Connor 7e87dbaa39 Fixed modprobe install. Checks to ensure that OVS_KMODS is set 2013-08-05 14:00:37 -07:00
Brian O'Connor 1e5cdfd805 Merge branch 'master' of github.com:mininet/mininet 2013-08-02 13:36:18 -07:00
Brian O'Connor bb0006b683 Merging MurphyMc LinearTopo pull request 2013-08-02 13:35:38 -07:00
Bob Lantz 786117dd49 Merge branch 'ivs' of https://github.com/rlane/mininet into rlane-ivs
Conflicts:
	util/install.sh

fixes #159
2013-07-31 18:20:18 -07:00
Brian O'Connor 43ba774e52 Merge pull request #181 from mininet/devel/ping
pingall now reports fraction received
2013-07-30 18:31:12 -07:00
Brian O'Connor f7439671bc pingall now reports fraction received
fixes #168
2013-07-30 18:28:38 -07:00
Brian O'Connor 48df51aa3b Merge branch 'notrace'
fixes #166
2013-07-30 18:15:56 -07:00
Brian O'Connor 4a77702473 Merge branch 'master' of github.com:mininet/mininet 2013-07-30 18:12:15 -07:00
Brian O'Connor 15c1a0e5e7 install.sh automatically detects wireshark version and installs correct version of of-dissector
fixes #180
2013-07-30 18:11:20 -07:00
Bob Lantz 9c4b734361 Fix indentation in LinearTopo() 2013-07-30 11:52:53 -07:00
Bob Lantz 5b48a7d92c Reimplemented and corrected Graph as MultiGraph
fixes #172
2013-07-30 11:51:32 -07:00
Brian O'Connor 4316be95c2 Prevent Mininet from crashing when display not set
fixes #173
2013-07-26 14:43:27 -07:00
Brian O'Connor 19bc1df15d fixed style error 2013-07-26 13:45:56 -07:00
Brian O'Connor bda2317d8b Catching exceptions thrown in mn 2013-07-25 19:27:06 -07:00
Brandon Heller 1f07530a54 install.sh: Fix break from version overrides missing, + annotate them
D'oh!  Next time test presence _and_ absense of an added env var.

OTOH, when people notice this quickly, it means they're using install.sh :-)
2013-07-24 06:01:30 +00:00
Brian O'Connor d5f5778492 Merge branch 'master' of github.com:mininet/mininet 2013-07-23 17:05:23 -07:00
Brian O'Connor 1ecc63dfec improved check for downed link in parsePingFull
fixes #176
2013-07-23 17:03:51 -07:00
Brian O'Connor 1e4e8b70d9 Merge pull request #174 from pantuza/master
Dynamic Network with Remote Controller example; Extended LinearTopo
2013-07-22 22:14:03 -07:00
Gustavo Pantuza Coelho Pinto e45cc16a90 Merge pull request #1 from bocon13/master
Simplified and refactored examples/dynamicnet.py
2013-07-21 12:53:37 -07:00
Brian O'Connor c26b4525aa Moving developer dependencies to separate function in install.sh
Developer dependencies are ~1GB
2013-07-19 16:17:16 -07:00
Brian O'Connor ea29d2b2cd Merge pull request #171 from vitalivanov/dev
Cleanup and cosmetic changes.
2013-07-19 16:07:15 -07:00
Brian O'Connor 5559a93d8a Merge pull request #170 from piyushs/cgroup-fix
Fix issue with mountCgroups not recognizing mounted cgroup
2013-07-19 16:04:48 -07:00
Brian O'Connor 921123155a Simplified and refactored examples/dynamicnet.py
Extended LinearTopo to support mulitple hosts per switch
2013-07-19 15:56:06 -07:00
Brandon Heller 2a1f2d59ee Merge pull request #175 from daviderickson/extensibility
Extensibility Improvements
2013-07-19 13:55:33 -07:00
David Erickson 3e70b01ec5 Enable OF 1.3 switch revision choice 2013-07-19 10:32:20 -07:00
David Erickson b55527739a Enable OF dissector revision choice 2013-07-19 10:29:53 -07:00
Gustavo Pantuza Coelho Pinto bf97d21c03 Added dynamicnet.py to README file of the examples 2013-07-18 18:42:10 -07:00
Gustavo Pantuza Coelho Pinto c554987912 Merge branch 'master' of github.com:pantuza/mininet 2013-07-18 18:34:21 -07:00
Gustavo Pantuza Coelho Pinto 6f09dedfad Dynamic network creation using a remote controller
The script builds a network topology based on command line
arguments and uses a remote controller
2013-07-18 18:32:08 -07:00
Rich Lane 0a54360211 IVSSwitch: support running IVS in a namespace
This change uses the `Node.cmd` method instead of `Popen`. The `cmd` method
sends the input to a shell which may be in another namespace (if --innamespace
is in use), while `Popen` would always run in the root namespace.
2013-07-18 17:50:52 -07:00
Rich Lane 5c24263779 clean: send SIGTERM before SIGKILL
IVS needs to be sent SIGTERM so it has a chance to clean up the kernel
datapath.
2013-07-18 13:50:20 -07:00
Vitaliy Ivanov 5b609ef9e2 Adding mininet doxygen installation dependencies.
Right now after installing mininet using util/install.sh there is a problem when try to generate doc:
	make doc
	doxygen doc/doxygen.cfg
	make: doxygen: Command not found
	make: *** [doc] Error 127
Adding doxygen, doxypy and texlive-fonts-recommended packages to fix the problem.
2013-07-15 14:20:16 +02:00
Vitaliy Ivanov 36bf8ac911 mnexec.c: substituting tabs with spaces. 2013-07-15 13:17:56 +03:00
Vitaliy Ivanov efe3877e3c Fixed mininet.org url in README.md.
s/http:\/\/openflow.org\/mininet/http:\/\/mininet.org/g
2013-07-15 13:17:19 +03:00
Piyush Srivastava 433ca2ecb6 Fix issue with mountCgroups not recognizing mounted cgroup
Changed mountCgroups to use the file /proc/mounts (instead of the
current case where it uses the less reliable
(http://www.mail-archive.com/lxc-devel@lists.sourceforge.net/msg00455.html)
/etc/mtab thorugh the mount program
2013-07-13 13:31:05 -07:00
Brian O'Connor fec98e2798 Added output line to ping that is closer to ping program output 2013-07-11 11:30:12 -07:00
Brian O'Connor c188bee3e3 Fixed divide by 0 error in ping command when no packets are sent
fixes #143
2013-07-11 11:17:46 -07:00
Rich Lane 0e2cc609df tests: run all tests with OVS, IVS, and userspace switches
Each switch gets its own class so that the test results are listed separately.
2013-07-10 11:19:25 -07:00
Brian O'Connor adf391fdec Fixed install.sh script to allow Mininet to be installed from any directory (not just ~/)
Fixes #94
2013-07-09 19:23:29 -07:00
Brian O'Connor 5d529edfb1 Removing unused import in mininet/link.py 2013-07-09 16:34:02 -07:00
Brian O'Connor 59eeeadbac Style changes in mininet/node.py and removing used import in mininet/link.py 2013-07-09 16:30:29 -07:00
Brian O'Connor 7265e2e88c Merge branch 'master' of github.com:mininet/mininet 2013-07-09 15:49:57 -07:00
Brian O'Connor 5c019d2aef CPULimitedHost cleanup() calls superclass method
fixes #161
2013-07-09 15:48:48 -07:00
Bob Lantz 679a3f1915 Don't stop data network controllers twice. 2013-07-09 14:34:37 -07:00
Rich Lane 60abb34497 clean: kill ivs processes 2013-07-09 08:36:36 -07:00
Rich Lane 94ff77f2f2 clean: don't wait forever for ovsdb
This could happen if OVS was installed but not running.
2013-07-09 08:34:33 -07:00
Bob Lantz e2eb95a29f Add $ to avoid h1 matching h10 2013-07-06 14:14:25 -07:00
Bob Lantz 10be691b86 Clean up intfs in root NS, and avoid deleting HW intfs
It appears that under certain conditions, such as when a
namespace exits, both ends of a veth pair may get dumped
into the root namespace. We therefore now remove an interface
both from its home namespace and from the root namespace.
2013-07-04 19:27:57 -07:00
Bob Lantz fcdb6d8a54 Remove unneeded line. 2013-07-04 00:45:08 -07:00
Bob Lantz 32502bbd1a Add controlnet.py example. 2013-07-04 00:41:30 -07:00
Rich Lane 71ffb0028e IVSSwitch: remove namespace warnings
IVS works fine using --innamespace.
2013-07-01 16:05:52 -07:00
Rich Lane 803c0a6e22 IVSSwitch: use ovs-ofctl for dpctl functionality 2013-06-29 18:11:15 -07:00
Rich Lane 8ee4aa6de4 install.sh: add support for IVS 2013-06-28 15:01:58 -07:00
Rich Lane 91261b2757 IVSSwitch: add support for dpctl
dpctl is not included with IVS. The user will need to obtain it from the
OpenFlow reference repository.
2013-06-27 17:56:46 -07:00
Rich Lane 812c91cc9e test_hifi: use SWITCH to pick the switch class 2013-06-27 14:50:59 -07:00
Rich Lane 27da832d6d add support for the IVS virtual switch
IVS is an open source virtual switch available for download at
https://github.com/floodlight/ivs. It uses the openvswitch
kernel module.
2013-06-27 14:50:59 -07:00
Bob Lantz aa554d985d Workaround: add default dpopts='--no-slicing' to UserSwitch
This disables slicing by default but fixes bandwidth limits.
Eventually we want to enable both to work together, but for now
this enables one or the other depending on the dpopts setting.
2013-06-26 06:21:59 -07:00
Bob Lantz 804c4bbfa6 Workaround: add dpopts to UserSwitch
Currently, slicing on the user switch breaks bandwidth limits.
We don't yet have a good way of using both of them at the same time.
I'm inclined to turn off slicing by default, but I have to think
about it and also see if any one is using it...
2013-06-26 06:14:43 -07:00
Bob Lantz 1a658054ab Ensure dpid is a valid hex string in OVSSwitch 2013-06-26 05:00:30 -07:00
Bob Lantz 33e39a2471 Fix some pylint messages. 2013-06-25 20:26:06 -07:00
Bob Lantz 538a856c2f Add Switch.connected() and OVSSwitch.controllerUUIDs() 2013-06-25 20:16:25 -07:00
Bob Lantz 877e7efb5f Minor comment change and text change. 2013-06-25 19:39:53 -07:00
Bob Lantz bdd43beaf3 Clean up (and fix) OVSSwitch fast reconnect 2013-06-25 19:31:27 -07:00
Bob Lantz 307d60a0c8 Replace /etc/hostname entirely; clarify sources.list edit 2013-06-25 16:35:26 -07:00
Bob Lantz 12758046e1 Fix setDefaultRoute to work with passed parameters. 2013-06-25 12:51:43 -07:00
Bob 9aaf87c19b Fix typo. 2013-06-25 01:06:00 -06:00
Bob 400bbbac11 Update archive for 13.04 2013-06-25 01:04:45 -06:00
Bob 226eae97cb Add script completion message 2013-06-24 14:34:53 -06:00
Brandon Heller e5d7b3801d topo: make new minimal Graph object a Graph, not a DiGraph
Fixes another Graph regression relative to NetworkX.

RipL broke because the NetworkX Graph object that was used previously
for topologies is an undirected graph:

>>> import networkx as nx
>>> g=nx.Graph()
>>> g.add_edge(0,1)
>>> g[1]
{0: {}}
>>> g[0]
{1: {}}

There is a separate DiGraph object in NetworkX for directed behavior.

The minimal replacement previously implemented DiGraph behavior.

>>> from mininet.topo import Graph
>>> g2=Graph()
>>> g2.add_edge(0,1)
>>> g2[0]
[1]
>>> g2[1]
[]

This commit restores undirected graph behavior.
2013-06-20 18:51:09 -07:00
Brandon Heller 4e1630e126 topo: add __getitem__ for Graph
Commit 65c35b65 'Remove networkx dependency' broke this line from RipL:

    nodes = [n for n in self.g[name] if self.layer(n) == layer]

To work around this, RipL code would have to be changed to something
like this:

    nodes = [n for n in self.g.data[name] if self.layer(n) == layer]

...which would use an internal variable, data.

It seems cleaner to add this one little feature from NetworkX Graph objects.
2013-06-20 16:27:17 -07:00
Brandon Heller 40b13c28b8 install.sh: Make OF1.3 Netbee location cmd-line configurable
If NBEEURL is defined when running install.sh, use that location.

Enables use of a local mirror for netbee download.

Thanks to David Erickson for the code.
2013-06-20 15:57:39 -07:00
Bob Lantz 2de621cda0 "file blocks" -> "disk blocks" 2013-06-19 17:27:33 -07:00
Bob Lantz 5c5a1eae8a Use dd to zero out disk space (hopefully faster) 2013-06-19 17:26:39 -07:00
Bob Lantz 8f113b48cc vm_cleanup: add apt-get autoremove, and zero disk blocks 2013-06-19 16:58:00 -07:00
Bob Lantz 949e0b3aff Separate VM finalization as install.sh -tc 2013-06-19 16:32:54 -07:00
Bob 448ac9dcd4 Merge pull request #150 from MurphyMc/ovs_fast_reconnect
node: Make OVS switches reconnect quickly
2013-06-19 12:22:28 -07:00
Bob Lantz dd21df3ce4 Don't flush ALL routes and enable OPTIONAL default route/gw
fixes #152 hopefully
2013-06-17 17:09:54 -07:00
Bob Lantz 3484389dcd Add NAT script to examples. 2013-06-12 14:57:13 -07:00
Bob ace0977930 Merge pull request #149 from MurphyMc/ovs_userspace_v2
node: Allow OVSSwitch to run in userspace mode
2013-06-06 18:35:39 -07:00
Murphy McCauley 3df07feb11 node: Make OVS switches reconnect quickly
Ordinarily, OVS switches back off when they can't reach their controllers.
Under the type of scenarios where Mininet is used, I think this is
probably just inconvenient.  This patch set controllers to attempt to
reconnect every second.
2013-06-06 16:28:13 -07:00
Murphy McCauley 153d598df3 node: Allow OVSSwitch to run in userspace mode
This adds a datapath parameter to OVSSwitch which allows one to tell OVS to
run in userspace mode rather than kernel mode.  From the commandline, this
is --switch=ovsk,datapath=user.

Note that this makes "ovsk" and the OVSKernelSwitch alias misnomers.  Since
the default behavior is still kernel mode, this is hopefully harmless.

This is the second version of this patch, which changes the argument name
and values according to Bob's suggestion.
2013-06-06 16:23:56 -07:00
Murphy McCauley a22e2618a6 topo: Add host count param to LinearTopo
Previously, LinearTopo took one parameter (k), which controlled the number
of switches; each of these got one host.  This adds a second parameter (j),
which controls the number of hosts per switch, defaulting to 1 (as before).

This is the second version of this patch, which attempts to make the
host name generation more straightforward.
2013-06-06 16:18:04 -07:00
Brandon Heller 3582facd34 install.sh: Use newer Netbee location for OF1.3 install
The previous version (12-05-16) does not compile on Ubuntu 12.10.

Also use a local variable to reduce duplication.
2013-06-04 23:53:04 -07:00
Bob Lantz 2a079911b5 Clarify controllers.py and add RemoteController 2013-06-03 17:10:12 -07:00
Bob Lantz 5fae96eb6e Change to only split once so that cargs='--foo=1 --bar=2' works.
fixes #144
2013-06-03 15:37:56 -07:00
Bob Lantz e3d07bc1a0 Add 'x' command to open an X11 tunnel (and run a client)
fixes #142
2013-05-28 22:10:27 -07:00
Bob f53866d0c0 Merge pull request #140 from fasaxc/patch-1
Fix method name mismatch for setDefaultRoute. 
This seems to be logically correct according to the intention of the code, but it may cause some trouble because routes will now be flushed by Mininet.configHosts().
2013-05-23 15:38:41 -07:00
Shaun Crampton e5754ae96f Fix method name mismatch for setDefaultRoute. 2013-05-23 14:31:27 -06:00
Bob Lantz 294bbad407 Display usage information if there are extra arguments
Fixes #130
2013-05-22 18:04:35 -07:00
Bob 83e43a42d9 Merge pull request #135 from adferguson/adf-vm-script-fix
tiny fix to vm script
2013-05-22 16:02:43 -07:00
Bob Lantz 2485d57f66 Edits to pass code check and make style consistent. 2013-05-22 15:40:12 -07:00
ryanc 65c35b6595 Remove networkx dependency
Conflicts:

	util/install.sh

closes #100
2013-05-22 15:24:22 -07:00
Bob Lantz 1ea5c91fcc Update host's IP address during name-IP substitution
fixes #139
2013-05-22 15:06:10 -07:00
Bob Lantz 76c5b9d021 Add ovs-controller to shutdown list 2013-05-22 15:03:48 -07:00
Bob Lantz 229f112ff3 Change OVSSwitch.dpctl() to call ovs-ofctl
This should fix the problem of dump-flows not showing the OpenFlow
flow table flows and only showing cached kernel flow table flows.

fixes #136
2013-04-29 14:04:46 -07:00
Andrew Ferguson fae4365983 actually, drop comment as per Bob Lantz 2013-04-26 13:11:39 -04:00
Andrew Ferguson e7f45ca90e tiny fix to vm script 2013-04-25 13:22:46 -04:00
Andrew Ferguson 3236d33b6a allow user switch to be used with TCIntf's 2013-04-21 15:47:37 -04:00
Bob Lantz 6c22e057cc Avoid false matches and detect multiple host processes 2013-04-12 13:30:50 -07:00
Bob Lantz 7bc10ebc7a Fix X11 forwarding (broken by no IPv6) for real 2013-04-09 23:26:08 -07:00
Bob Lantz c3f975aef8 Move IPv6 disable to 'other', and fix X11 forwarding
should fix #128
2013-04-09 22:46:49 -07:00
Bob Lantz 0f6bf4ce84 start xterm with TERM=ansi to preserve title from bash.bashrc
fixes #128
2013-04-09 20:10:55 -07:00
Bob Lantz 33c7e46492 Use __NR_setns so that setns has the right syscall # in 32-bit mode
fixes #127
2013-04-09 19:38:36 -07:00
Bob 7c920edc29 Merge pull request #125 from adferguson/adf-tc-renumber
Adjust numbering to support tc-based switch QoS

This looks fine to me - probably we want to change the way this works eventually, but for now renumbering it is fine and we can change the numbering later if desired.
2013-04-09 14:59:41 -07:00
Andrew Ferguson e09254eea3 Adjust numbering to support tc-based switch QoS
both the reference switch and Open vSwitch assume ids with major 1
can be used for tc-based QoS
2013-03-30 17:50:28 -04:00
Bob Lantz e5a15ced01 Change popen() to detach from terminal/process group.
This may or may not be the right thing to do - an alternative
would be to ignore SIGINT, but that would make the popen()
job unkillable by normal means! So we'll try this and see
how well it works.
Fixes #124
2013-03-28 18:14:24 -07:00
Bob Lantz c771b2d75a Add source node option to moveIntf() (note: low-level API!!)
In the future we may wish to enable moving interfaces across
nodes which are not in the root NS, and this would provide
the low-level mechanism to do so.
closes #122
2013-03-24 16:14:04 -07:00
Bob Lantz 477e84adba More code check nits. 2013-03-24 15:58:48 -07:00
Bob Lantz 3482d941e1 Fix code check. 2013-03-24 14:48:20 -07:00
Bob Lantz 1bf1a4d5e9 Tag node bash processes and add attach script
Try invoking bash processes with -s mininet:host,
for easy identification of hosts. This enables
easy attachment using the util/m script.
closes #121
2013-03-22 18:38:33 -07:00
Bob Lantz a0f69d98df Change makeTerm() to tunnel X11 using socat if needed
For local display, allow local root access
For remote display, tunnel with socat/mnexec
This should enable *wireshark in hosts* and fix #119
2013-03-22 15:48:51 -07:00
Bob Lantz 4b8b4b73e1 Clean up options and fix of13 to use sudo for netbee 2013-03-21 16:44:44 -07:00
ederlf 5b14cc2937 Add options to install OpenFlow 1.3 switch and compatible NOX controller.
Signed-off-by: Bob Lantz <rlantz@cs.stanford.edu>
2013-03-21 16:08:05 -07:00
Bob Lantz eca5a151a4 Fix usage message to note correct options and OS compatibility.
closes #85
2013-03-21 14:38:46 -07:00
Bob 4efd372223 Merge pull request #116 from yamahata/install-test-mar-2013
tools/install.sh: catch up oftest change
Looks fine to me.
2013-03-21 14:12:50 -07:00
Isaku Yamahata 356e9d8a92 tools/install.sh: update oftest
Since oftest removed tools/munger directory, install.sh fails as follows
> mininet/util/install.sh: line 399: cd: tools/mnuger: No such file or directory

oftest change set
> commit be8503a69d609d0aee844a91f3f5d66f4e2666c7
> Author: Rich Lane <rlane@bigswitch.com>
> Date:   Tue Mar 12 10:16:33 2013 -0700
>
>     remove pylibopenflow tools

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
2013-03-15 10:40:51 +09:00
Bob Lantz 8838c30ea1 Remove non-functional --prefixlen (use --ipbase instead) 2013-03-13 16:18:04 -07:00
Bob Lantz 8204a1b694 fix --ipbase: fix ipAdd() and remove unused default from ipStr() 2013-03-13 16:16:20 -07:00
Bob Lantz bd558875f3 Add net['h1'] and for node in net support
thanks to Brian O'Connor for reminder + suggestion
closes #114
2013-03-08 18:28:21 -08:00
Ryan Cox 898efb69a3 Added markdown formatting to examples readme 2013-03-07 17:54:08 -08:00
Bob Lantz 31fe4f1bd0 Fix pmonitor() to not return blank lines on EOF
fixes #109 (hopefully)
2013-03-06 17:26:52 -08:00
Bob Lantz dcb3036b70 Change to use addLink() interface for symmetry/consistency. 2013-03-06 16:55:47 -08:00
Bob Lantz 9734d9d7fa Add configuration to disable IPv6, since old method no longer works.
fixes #108
2013-03-06 16:06:59 -08:00
Bob Lantz 23c70f609d Add px command which uses exec() vs. py's eval()
This is necessary since exec() isn't really a function, and
eval can't evaluate statements.
fixes #104
2013-02-28 19:14:37 -08:00
Bob Lantz f018137207 Updated to reflect new controllers.py and old controllers2.py 2013-02-28 17:59:14 -08:00
Bob Lantz ad09c1e086 Add new example of making a custom Switch() class. 2013-02-28 17:56:36 -08:00
Bob Lantz 31e1ff7154 Rename controllers.py to controllers2.py since it's less convenient. 2013-02-28 17:55:41 -08:00
Bob Lantz 8b8bb37598 Remove unnecessary addHost() function, and clarify things a bit.
Fixes #102
2013-02-28 17:40:48 -08:00
Bob Lantz 0aefb0e036 Fix Node.MAC(intf) to return intf.MAC() rather than intf.IP()
Fixes #89 ; Thanks to Nikhil S. Menon for the bug report
2012-12-19 16:11:57 -08:00
Bob Lantz 5879c492d8 Minor clarifications. 2012-12-02 15:39:17 -08:00
Bob Lantz ee66d53c54 Clean up INSTALL and make it markdown-friendly
Also fixed a few errors like the time it takes to install,
some references to NOX, etc..

Left in noxcore ref, since you can still install it.
2012-12-02 15:29:53 -08:00
Bob f95c4a4712 Update INSTALL: git clone from github.com
Thanks to @xdhanz for the catch, and pull request
closes #84
2012-12-02 15:00:46 -08:00
Bob Lantz c8b857465b pass code check 2012-11-30 21:00:23 -08:00
Bob Lantz 21366afc54 2.0.0rc1 -> 2.0.0 final 2012-11-30 20:57:53 -08:00
Brandon Heller b453e0068f node: verify telnet installation for controller checking
Reported-by: Julius Bachnick

https://mailman.stanford.edu/pipermail/mininet-discuss/2012-November/001349.html
2012-11-26 15:22:07 -08:00
Brandon Heller 1e9b5f61fc install.sh: add Telnet
Reported-by: Julius Bachnick

https://mailman.stanford.edu/pipermail/mininet-discuss/2012-November/001349.html
2012-11-26 15:22:07 -08:00
Bob Lantz 0ce366b5d2 Add sudoers entry for current user using whoami 2012-11-19 17:30:09 -08:00
Brandon Heller 3744638e5c node: add OVS passive listening support 2012-11-15 00:50:25 -08:00
Brandon Heller 915c6d4a60 node: Use space, rather than comma, to separate controllers
for OVS

Otherwise, OVS thinks the second controller is a parameter of the first, and
only creates one controller entry as seen in 'ovs-vsctl show'.
2012-11-15 00:50:25 -08:00
Brandon Heller bd964adb57 mn: change default passive listening port to 6634
Becomes consistent with the OpenFlow walkthrough & MN walkthrough web pages.
2012-11-15 00:50:25 -08:00
Brandon Heller dec1f402ed custom topo: remove author name
No need for this here, especially because this file is in the walkthrough
and will be found by web crawlers.
2012-11-15 00:50:25 -08:00
Bob Lantz b96e1596b2 Change to use Intf() class rather than string for interface.
Was broken in 2.0.0rc1
2012-11-19 15:02:38 -08:00
Bob Lantz 3569838542 Updated for new Topo API 2012-11-19 12:32:50 -08:00
Bob Lantz 8c7c4812b2 Remove vmware easy install leftovers 2012-11-19 12:31:50 -08:00
Brandon Heller 987fd7555e Fix custom topology example; outdated import
Reported-by: Julius Bachnick
2012-11-14 21:28:42 -08:00
Bob Lantz 63d669f712 Update to match launchpad. 2012-11-18 20:54:47 -08:00
Bob Lantz 078b982be1 Sync with packaging on launchpad. 2012-11-17 23:14:10 -08:00
Bob Lantz d402c66af2 Update debian copyright. ;-/ 2012-11-16 23:11:59 -08:00
Brandon Heller 03c3123bb8 test_hifi: Fix occasional test breakage
Source of error: regex to parse ps output had a single space, rather
than handling any amount of whitespace before the CPU percentage.  When
that percentage would drop below 10.0, an space would be printed,
confusing the regex.

Fixed by handling arbitrary whitespace before the percentage.
2012-11-14 10:47:00 -08:00
Bob Lantz 82c8bdaf0d Remove redundant wireshark install. 2012-11-15 22:59:43 -08:00
Bob Lantz 163a6cf3dd Fix whitespace error. 2012-11-15 22:56:24 -08:00
Bob Lantz ab594b6afe Merge branch '2.0dev' into 2.0merge
Conflicts:
	.gitignore
	.pylint
	examples/miniedit.py
	mininet/node.py
	mininet/util.py
	util/install.sh
	util/vm/install-mininet-vm.sh
2012-11-15 22:54:27 -08:00
Brandon Heller 1f1d590c7a test: Improve unit tests to verify basic functionality
Also a more complete ping test that parses all output to the CLI.

These tests expand the hifi-specific ones to not just cover whether
a topology can be created with options, but whether those options
are properly implemented within some tolerance, like CPU limits,
link bandwidth, delays, and even drops.
2012-11-14 07:57:17 -08:00
Brandon Heller fcd01592e1 Move CPU limit into net, to be reused in future unit tests 2012-11-14 07:55:10 -08:00
Brandon Heller e1205a8a49 Add a simple unit test for link/host creation with options 2012-11-14 00:43:46 -08:00
Brandon Heller d7768ab228 examples/simpleperf: Warn in docstring about effects of link settings
These include dropped pings and iperf hanging.
2012-11-13 23:28:24 -08:00
Brandon Heller bf208cdeb6 Fix SSHD example by generalizing input intf args
A number of functions in node.py look like this:
   return self.intf( intf ).<other stuff>

Previously, self.intf(...) in Node would expect a string name for an
interface and return None if an object was passed in instead of a
string name.

Now, be more permissive and assume that objects passed in are for Intf
objects.  This makes all such functions in node.py handle more flexible
input args, either name or actual Intf object.

An alternative and equally valid approach would be to raise an Exception
whenever a non-string, non-falsy value was passed in to Node.intf(), and
to modify the code in at least one place - examples/sshd.py - to pass
the interface name, rather than the interface object.

Also fix input args for examples/scratchnetuser.py - the interface name
was being passed in as the prefix len, which makes no sense.
2012-11-13 22:44:47 -08:00
Brandon Heller 12fea0f6d5 examples/baresshd: ensure root permissions
Prevent idiots like me from getting confused by non-obvious 'broken pipe'
errors when they forget to put 'sudo' in front  :-)
2012-11-13 21:33:44 -08:00
Brandon Heller bcfb3009c0 small refactor: put function to ensure root in util
Two benefits:
- One place to change if in the future, a more granular method of
  root access is used (like the BigSwitch patch).
- Makes this reusable by stuff like examples/baresshd.py that use
  the low-level Mininet API.
2012-11-13 21:32:41 -08:00
Brandon Heller 8a1264e266 Fix 'cgroups not mounted' error in U12.10
'mount' shows something slightly different in Ubuntu 12.10:

cgroup on /sys/fs/cgroup type tmpfs (rw,uid=0,gid=0,mode=0755)

Note the lack of a plural on first word cgroup, which has changed.

Still mounted at /sys/fs/cgroup, so check for both possibilities
when instantiated CPU-limited hosts.
2012-11-13 20:53:13 -08:00
Brandon Heller 36c9b040ca examples: Add new tests to README 2012-11-13 18:33:10 -08:00
Brandon Heller 2eb0593cd2 examples/cpu: Fix typo, note existence in README 2012-11-13 18:06:19 -08:00
Brandon Heller d40003e0cd examples: Make simpleperf.py executable 2012-11-13 18:05:10 -08:00
Brandon Heller 9330a33fe5 pep8: Fix E501 line-too-long errors incurred fixing other pep8 stuff :-) 2012-11-13 17:21:36 -08:00
Brandon Heller 2e089b5e4a pep8: Fix E127 continuation line over-indented
There are a bunch of these remaining, but I don't think the right course is
to 'fix' all of them to make pep8 happy, but instead to either change
the test in pep8 to consider that a continuation line may itself
be continued halfway, OR, to change the code in these lines to be more
readable by removing the need for all those nested continuations.

Personally, I find multiply-broken lines (aka nested continuations) really
hard to read.
2012-11-13 17:17:51 -08:00
Brandon Heller edf6003217 pep8: fix E128 continuation line under-indented errors
I wasn't sure this was worth fixing at first, but it does look more readable
now.
2012-11-13 16:59:10 -08:00
Brandon Heller c0095746af pep8: Fix E121/126, continuation line indention 2012-11-13 16:08:26 -08:00
Brandon Heller 33d548b412 pep8: Fix E121 continuation line indentation is not a multiple of four 2012-11-13 15:01:11 -08:00
Brandon Heller 615ebb7afa pep8: Fix E125 continuation line does not distinguish itself from next logical line 2012-11-13 14:56:49 -08:00
Brandon Heller 7a5060478b pep8: Fix E711, comparisons to None should use 'is' or 'is not'
Lengthy discussion of why this is a good thing (I didn't know) at SO:

http://stackoverflow.com/questions/2209755/python-operation-vs-is-not
2012-11-13 14:46:58 -08:00
Brandon Heller 0bd5c6519c pep8: Fix E203 whitespace before punctutation 2012-11-13 14:39:31 -08:00
Brandon Heller 1052f8a0d4 pep8: Fix E271/E272, spaces before/after keyword 2012-11-13 14:36:56 -08:00
Brandon Heller 03d211f2a9 codecheck: Make codecheck happy with a consistent version num 2012-11-12 18:08:59 -08:00
Brandon Heller f6c4239409 install: Add 'make codecheck' deps
These total to only ~200KB.
2012-11-12 18:07:19 -08:00
Brandon Heller 59897168d4 install: Update OFTest repo location
This repo has moved to git://github.com/floodlight/oftest.git
2012-11-12 16:46:29 -08:00
Brandon Heller b597ef5d55 install: Add help2man to MN deps
'make install' calls help2man, so apt-get install it beforehand.
2012-11-12 16:46:22 -08:00
Bob Lantz e3c8066a9e OK, github is broken - reverting to old spacing. 2012-11-09 00:09:41 -08:00
Bob Lantz 78e3e18c42 trying to fix list spacing 2012-11-09 00:09:11 -08:00
Bob Lantz 01e028c19f Add Mininet-HiFi comment 2012-11-09 00:07:56 -08:00
Bob Lantz d49aaf0f70 Hmm, maybe this is better. 2012-11-09 00:04:40 -08:00
Bob Lantz f7d6c9e7a1 Another attempt. 2012-11-09 00:03:36 -08:00
Bob Lantz 015aaa2107 That didn't work... 2012-11-09 00:02:54 -08:00
Bob Lantz bad8656361 More header madness. 2012-11-09 00:02:17 -08:00
Bob Lantz eb3b74ea34 sudo mn seems lonely if it takes up the whole line 2012-11-08 23:58:54 -08:00
Bob Lantz dabc0b812a Bash in tt, abolish forward single quotes. 2012-11-08 23:57:39 -08:00
Bob Lantz dd1a450b50 Mars needs commas! 2012-11-08 23:55:31 -08:00
Bob Lantz ead9f83050 Reflow text and try crazy idea for heading. 2012-11-08 23:52:07 -08:00
Bob Lantz 0fb91f186e More minor tweaks.... 2012-11-08 23:47:48 -08:00
Bob Lantz 3e38a959c3 Moved installation instructions and prereqs into INSTALL. 2012-11-08 23:31:31 -08:00
Bob Lantz 535e61d2be Still trying to fix the typeface and spacing... 2012-11-08 23:18:56 -08:00
Bob 4885cb24ed Update README.md
File and command names in typewriter text.
2012-11-08 22:05:52 -08:00
Brandon Heller 73da7204e6 Update README 2012-11-08 15:31:10 -08:00
Bob Lantz ff4b41439a Minor changes. 2012-11-03 21:59:04 -07:00
Bob Lantz 21b2c2c4aa VERSION -> 2.0.0rc1 2012-11-03 21:38:59 -07:00
Bob Lantz e2b799b815 Fix/work around setuptools' evil PYTHONPATH madness. 2012-11-03 21:36:03 -07:00
Bob Lantz 4744aa2b78 Updated README to reflect Mininet 2.0. 2012-11-03 16:53:26 -07:00
Bob Lantz c04ef88e5c Add note regarding removing old OVS junk. 2012-11-03 16:10:57 -07:00
Bob Lantz 4b1dc93bcc Avoid modifying keyword parameter dictionary in customized() 2012-10-30 16:58:51 -07:00
Bob Lantz f58f83c043 Allow controller to optionally be a list of constructors/classes 2012-10-30 16:50:12 -07:00
Bob Lantz 22b8e5e427 Add custom name to customized functions.
Note: we could probably use functools.partial.
2012-10-30 16:45:11 -07:00
Angad Singh cec4476303 Merge pull request - closes #36 2012-10-29 16:10:00 -07:00
Bob Lantz 31015ef5d8 Make doc a real subdirectory so we can put other things there. 2012-10-29 15:36:19 -07:00
Bob Lantz 600dad2498 Added package installation "instructions" 2012-10-04 19:08:37 -07:00
Bob Lantz 4ff6243fd6 Update version number to 2.0.0d3 (and pass version check) 2012-10-04 19:02:44 -07:00
Bob Lantz 9c0ed88c72 Create version check utility and add to code check.
Fixes #70
2012-10-04 19:02:27 -07:00
Bob Lantz 93f9b956e4 Updated copyright to fix #68 2012-10-01 18:01:29 -07:00
Bob Lantz b69ef234ac Fix multi-controller/failover support on User, OVSLegacy switches 2012-09-14 18:23:30 -07:00
Bob Lantz 2988429747 show method names in git diff output 2012-09-14 15:18:17 -07:00
Bob Lantz d4ece25ba6 Deprecate NOX-classic; "install" POX. Fixes #61 2012-09-11 06:49:11 -07:00
Bob ad85e44d78 Update util/install.sh
Update to reflect new NOX classic repo on github and default branch.
2012-08-29 16:00:33 -07:00
Bob 655194d3e1 Update util/install.sh
Update to reflect new NOX classic repo on github and default branch.
2012-08-29 15:10:36 -07:00
Bob Lantz 6eb01d7923 Minor formatting changes. 2012-08-23 18:36:50 -07:00
Bob Lantz ae6475598f Change README to README.md for github presentation.
This isn't the final README text, of course, but it should
show up formatted now on github.
2012-08-23 18:28:15 -07:00
Bob Lantz 62499d96df Adjust README slightly to be markdown-compatible. 2012-08-23 18:27:48 -07:00
Bob Lantz 4f33cad025 Ignore more stuff, notably generated docs and man pages. 2012-08-17 18:20:22 -07:00
Bob Lantz 2aafefc2fa Fix typo in RemoteController.__init__ comment.
Thanks to Isaku Yamahata.
2012-08-17 15:26:19 -07:00
Bob Lantz 9d5a21a799 Fix typo MANPAGE->MANPAGES
Thanks to Isaku Yamahata.
2012-08-17 15:25:06 -07:00
Bob Lantz ce15c4f67d rename Topo() methods for consistency: add_node() -> addNode() 2012-08-16 18:48:41 -07:00
Bob Lantz 01e0758e5d Add 'type mn -h for details' to usage message. 2012-08-16 16:12:55 -07:00
Bob Lantz e8238d185d Use Mininet version number from mininet.net
Contributing toward issue #46.
2012-08-10 12:47:35 -07:00
Bob Lantz 54c51c0299 Fix whitespace and change no controller exception to warning. 2012-08-06 11:41:00 -07:00
James Page 2b35a2caeb Override remote controller check to ensure that remote controller is contactable 2012-08-06 03:08:41 -07:00
James Page 9b11238467 Observe build environment flags and use PYTHONPATH when generating version number 2012-08-06 03:08:41 -07:00
Bob Lantz fa24f22d4d Re-enable slicing in UserSwitch since it's fixed with newer kernels. 2012-07-31 17:23:08 -07:00
Bob Lantz ce823507cd Fix man page install. 2012-07-31 17:22:13 -07:00
Bob Lantz cd580debb6 Removed redundant debian/mininet.manpages 2012-07-07 22:29:46 -07:00
Bob Lantz 1e9106badb Add option to mangle github tarball filename. 2012-07-07 22:23:53 -07:00
Bob Lantz 232acc8261 Track tagged versions on github directly. 2012-07-07 19:07:20 -07:00
Bob Lantz 55179737f9 Change version to 2.0.0d2 - there has to be a better way. 2012-07-06 23:44:27 -07:00
Bob Lantz 28c2cdc2c4 Workaround for openvswitch_mod rename and pass code check. 2012-07-06 23:30:14 -07:00
Bob Lantz d85a58feeb Autogenerate man page for mnexec. 2012-07-06 23:18:24 -07:00
Bob Lantz 89a6dea7f9 Remove unnecessary copy to bin/ 2012-07-06 22:43:11 -07:00
Bob Lantz 9a518b1eee Add missing packaging files from launchpad. 2012-07-06 22:30:19 -07:00
Bob Lantz d54cde46e0 Add PYTHONPATH=. to allow "make man" to work if Mininet is not installed. 2012-07-06 19:54:42 -07:00
Bob Lantz 320df7fe28 Merging in James Page's packaging tweaks for quantal. 2012-07-06 16:06:19 -07:00
Bob Lantz 0ab282400e update maintainer for ppa submission to work 2012-07-04 10:46:25 -07:00
Bob Lantz 8aa7e05d83 Added missing help2man to build deps. 2012-07-04 00:23:06 -07:00
Bob Lantz b43a67edbd Pass lintian. This is still annoyingly redundant. ;-(
y
2012-07-03 23:53:47 -07:00
Bob Lantz 9c4d047462 Ugh, it looks like lintian wants a duplication of the license in debian/copyright.... 2012-07-03 23:19:08 -07:00
Bob Lantz ccc0b1a1cf Fixed debian/copyright 2012-07-03 23:09:37 -07:00
Bob Lantz 78b2f585ae Fixed support for adding man page to debian package - to pass lintian! 2012-07-03 22:50:53 -07:00
Bob Lantz 39128f8cf8 Add VERSION string. 2012-07-03 22:08:33 -07:00
Bob Lantz 7fe847967d clean up "make man" slightly 2012-07-03 22:08:25 -07:00
Bob Lantz f2e7884ade Add support for generating man page from mn --help. 2012-07-01 23:23:56 -07:00
Bob Lantz 0809105bed Fixing some lintian problems. 2012-07-01 23:22:44 -07:00
Bob Lantz 93bf7793cb Debian copyright wants a real file, so change LICENSE to symlink. 2012-07-01 23:22:05 -07:00
Bob Lantz ec969b7f99 Change default controller for mn to ovsc (ovs-controller.)
Also add check to see if another controller is running -
eventually we should really detect errors from starting the
controller!!
2012-07-01 20:31:30 -07:00
Bob Lantz cb859243a5 Added Debian/Ubuntu packaging - thanks to James Page. 2012-07-01 19:16:41 -07:00
Bob Lantz e04c207c3c Update for Mininet 2.0.0 development. 2012-07-01 19:08:20 -07:00
Brandon Heller 496cd25d37 Update oftest repo location
Fixes Issue 41: https://github.com/mininet/mininet/issues/41

Reported-by: sublinhado on GitHub
2012-06-14 11:10:39 -07:00
Bob Lantz d9fd4ea245 Add VM install script 2012-05-23 22:37:53 -07:00
Bob Lantz 88dd4f7329 Change wireshark install to reflect new repository location. 2012-05-13 14:37:45 -07:00
Bob Lantz 49d84f7cfc Fix poller to only check if stdin and node are readable.
Fixes busy waiting and excessive CPU usage in CLI.
Thanks to James Zeng for pointing this out!
2012-05-10 17:35:34 -07:00
Bob Lantz 7676c17f27 Add missing libconfig-dev dependency for oflops. 2012-04-25 17:39:55 -07:00
Brandon Heller 6143bb1395 Merge pull request #31 from mininet/devel/wireshark
Devel/wireshark
2012-04-12 10:12:53 -07:00
Bob Lantz a8c1965b90 Fix typo. 2012-04-11 13:21:07 -07:00
Bob Lantz 4bd1a61353 Add install message for wireshark dissector. 2012-04-10 21:47:29 -07:00
Bob Lantz edd1d0f3a4 Support libwireshark[0,1] for 11.04 and 11.10 2012-04-10 21:34:07 -07:00
Bob Lantz 0919b7ca67 Use correct non-authenticated clone for of-dissector. 2012-04-10 21:23:26 -07:00
Bob Lantz 1902dd2e3a Add support for updated wireshark plugin.
Need to verify this works with 11.04.
2012-04-10 21:14:11 -07:00
Brandon Heller 99222e70f7 Merge pull request #26 from mininet/devel/install-oneiric
Devel/install oneiric
2012-03-13 15:11:29 -07:00
Bob Lantz 5562e66a2d Ignore build, dist and emacs backup~ files. 2012-03-12 16:14:00 -07:00
Bob Lantz 28f46c8d2d Pass code check. 2012-03-12 16:12:38 -07:00
90 changed files with 5018 additions and 1173 deletions
+1
View File
@@ -0,0 +1 @@
*.py diff=python
+8 -2
View File
@@ -1,8 +1,14 @@
mnexec
*.pyc
*~
*.1
*.xcodeproj
*.xcworkspace
\#*\#
mininet.egg-info
build/*
dist/*
build
dist
doc/html
doc/latex
trunk
+39
View File
@@ -0,0 +1,39 @@
Mininet Contributors
Mininet is an open source project and we gratefully acknowledge
the many contributions to the project! If you have contributed
code into the project and are not on this list, please let us know
or send a pull request.
Contributors include:
Mininet Core Team
Bob Lantz
Brandon Heller
Nikhil Handigol
Vimal Jeyakumar
Brian O'Connor
Additional Mininet Contributors
Gustavo Pantuza Coelho Pinto
Ryan Cox
Shaun Crampton
David Erickson
Glen Gibb
Andrew Ferguson
Eder Leao Fernandes
Vitaly Ivanov
Rich Lane
Murphy McCauley
José Pedro Oliveira
James Page
Angad Singh
Piyush Srivastava
Ed Swierk
Isaku Yamahata
Thanks also to everyone who has submitted issues and pull
requests on github, and to our friendly mininet-discuss
mailing list!
+105 -212
View File
@@ -1,273 +1,166 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 1.0.0
Mininet 2.1.0
---
The supported installation methods for Mininet are 1) using
a pre-built VM image, and 2) native installation on Ubuntu or Debian.
The supported installation methods for Mininet are 1) using a
pre-built VM image, and 2) native installation on Ubuntu. You can also
easily create your own Mininet VM image (4).
(Other distributions may be supported in the future - if you would
like to contribute an installation script, we would welcome it!)
1. Easiest "install" - use our pre-built VM image!
1. Easiest "installation" - use our pre-built VM image!
The easiest way to get Mininet running is to start with one of our pre-built
virtual machine images from http://openflow.org/mininet
The easiest way to get Mininet running is to start with one of our
pre-built virtual machine images from <http://mininet.org/>
Boot up the VM image, log in, and follow the instructions on the wiki page.
Boot up the VM image, log in, and follow the instructions on the
Mininet web site.
An additional advantage of using the VM image is that it doesn't mess with
your native OS installation or damage it in any way.
One advantage of using the VM image is that it doesn't mess with
your native OS installation or damage it in any way.
2. Native installation (experimental!) for Ubuntu 10.04 LTS
Although a single Mininet instance can simulate multiple networks
with multiple controllers, only one Mininet instance may currently
be run at a time, and Mininet requires root access in the machine
it's running on. Therefore, if you have a multiuser system, you
may wish to consider running Mininet in a VM.
If you are running Ubuntu 10.04 LTS (or possibly Debian 5), you may be
able to use our handy install.sh script, which is in mininet/util.
2. Next-easiest option: use our Ubuntu package!
WARNING: USE AT YOUR OWN RISK!
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
12.10+:
install.sh is a bit intrusive and may possibly damage your OS and/or
home directory, by creating/modifying several directories such as
mininet, openflow, openvswitch and noxcore. Although we hope it won't
do anything completely terrible, you may want to look at the script
before you run it, and you should make sure your system and home
directory are backed up just in case!
sudo apt-get install mininet
To install ALL of the software which we use for OpenFlow tutorials,
you may use
Note: if you are upgrading from an older version of Mininet, make
sure you remove the old OVS from `/usr/local`:
$ mininet/util/install.sh
sudo rm /usr/local/bin/ovs*
sudo rm /usr/local/sbin/ovs*
This takes about 20-30 minutes.
3. Native installation from source
Alternately, you can install just the pieces you need.
3.1. Native installation from source on Ubuntu 12.04+
We recommend the following steps, in order:
If you're reading this, you've probably already done so, but the
command to download the Mininet source code is:
[a) On Debian 5, first install a Mininet-compatible kernel:
$ mininet/util/install.sh -k
Reboot and run 'uname -r' to make sure you're running the new kernel.]
git clone git://github.com/mininet/mininet.git
b) Install mininet and its dependencies:
$ mininet/util/install.sh -n
c) Install OpenFlow 1.0 and associated useful software
$ mininet/util/install.sh -f
If you are running Ubuntu, you may be able to use our handy
`install.sh` script, which is in `mininet/util`.
d) Install Open vSwitch and its kernel module
$ mininet/util/install.sh -vm
*WARNING: USE AT YOUR OWN RISK!*
e) If you wish to install the version of NOX we use in the tutorial:
$ mininet/util/install.sh -x
`install.sh` is a bit intrusive and may possibly damage your OS
and/or home directory, by creating/modifying several directories
such as `mininet`, `openflow`, `oftest`, `pox`, etc..
Although we hope it won't do anything completely terrible, you may
want to look at the script before you run it, and you should make
sure your system and home directory are backed up just in case!
Note: NOX development is progressing over time, so after you complete
the tutorial you may wish to install the latest and greatest NOX from
noxrepo.org.
To install Mininet itself, the OpenFlow reference implementation, and
Open vSwitch, you may use:
Good luck! Some additional installation notes are provided below, for
the brave and/or Linux-savvy, or those who are trying to understand what
is installed and why.
mininet/util/install.sh -fnv
p.s. Note that only one instance of Mininet is currently supported on a single
machine - that's one reason we recommend using a VM to run it.
This should be reasonably quick, and the following command should
work after the installation:
---
sudo mn --test pingall
Mininet Manual Installation Notes
To install ALL of the software which we use for OpenFlow tutorials,
including POX, the OpenFlow WireShark dissector, the `oftest`
framework, and other potentially useful software, you may use:
These installation notes assume you understand how to do things like
compile kernels, apply patches, configure networks, write code, etc.. If
this is unfamiliar territory, or if you run into trouble, we recommend
using one of our pre-built virtual machine images (see above.)
mininet/util/install.sh -a
If you wish to try to create a VM to run Mininet, you may also wish
to look at the Wiki page:
This takes about 4 minutes on our test system.
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
You can change the directory where the dependencies are installed using
the -s <directory> flag.
0. Obtaining Mininet
mininet/util/install.sh -s <directory> -a
If you're reading this, you've already done it, but the command to
download mininet is:
git clone git://openflow.org/mininet.git
1. Core Mininet installation
3.2. Native installation from source on Fedora 18+.
The core Mininet installation requires gcc, make, python,
and setuptools. On Ubuntu and Debian you may install them with:
# aptitude install gcc make python setuptools
As root execute the following operations:
To install Mininet itself, with root privileges:
# cd mininet
# make install
* install git
This places the mininet package in /usr/lib/python-*/site-packages/,
so that 'import mininet' will work, and installs the primary mn
script (mn) as well as its helper utility (mnexec.)
On Ubuntu and Debian, Mininet's dependencies and core files may also be
installed using mininet/util/install.sh -n
yum install git
2. Installation script for Ubuntu/Debian Lenny
* create an user account (e.g. mininet) and add it to the wheel group
If you are running Ubuntu 10.04 or Debian Lenny, you may be able to use the
util/install.sh script to install a compatible Linux kernel as well as
other software including the OpenFlow reference implementation, the Open
vSwitch switch implementation, and the NOX OpenFlow controller.
useradd [...] mininet
usermod -a -G wheel mininet
Many different installation options are possible by passing different
options to install.sh; install.sh -h lists them all.
Assuming the mininet source tree is installed in ~/mininet, the steps to run
install.sh to install EVERYTHING we use for OpenFlow tutorials are:
% cd
% time ~/mininet/util/install.sh # installs tons of stuff
% sudo reboot # to load new kernel
% ~/mininet/util/install.sh -c # to clean out unneeded kernel stuff
* change the SElinux setting to permissive. It can be done
temporarily with:
This installs a lot of useful software, but it will take a while (30
minutes or more, depending on your network connection, computer, etc..)
Probably the minimal semi-useful configuration would be to install
Mininet itself, kernel support if necessary, and either the
reference OpenFlow switch or Open vSwitch. This could be installed
as follows:
% sudo ~/mininet/util/install.sh -knvm
Respectively, this installs kernel support, core mininet dependencies,
Open vSwitch, and the Open vSwitch kernel module. If a new kernel was
installed, then a reboot may be required.
setenforce 0
If install.sh cannot be used for some reason (e.g. you're on Fedora
or some other Linux - please don't say CentOS) or if you don't want to
install all of these components (they're useful!), the kernel and
OpenFlow software requirements are described in steps [3] and [4],
which follow.
then login with the new account (e.g. mininet) and do the following:
If you successfully used install.sh, congratulations! You're basically
done. Proceed to step [6] for additional advice.
3. Linux Kernel requirements
* clone the Mininet repository
Mininet requires a kernel built with network namespace support enabled,
i.e. with CONFIG_NET_NS=Y, such as the kernel shipped with
Ubuntu 10.04 LTS, currently 2.6.32. On Ubuntu 10.04, you should not need
to install or build a custom kernel, although 2.6.33+ is faster at
tearing down virtual ethernet pairs.
git clone git://github.com/mininet/mininet.git
For Ubuntu and Debian, we provide a 2.6.33 kernel package which you may be
able to install using "util/install.sh -k". Note our kernel package
requires an ext2 or ext3 root file system, so it won't work if you have
a default Ubuntu install, which uses ext4.
If your kernel wasn't compiled with CONFIG_NET_NS=Y, you will need to
build and install a kernel that does! >= 2.6.33 works better, but may
be harder to get working, depending on your Linux distribution.
A script for building Debian packages for 2.6.33.1 is provided in
mininet/util/kbuild. You may wish to read it, as it applies patches
to enable 2.6.33.1 to build under debian-stable, and to enable the
tun driver to work correctly with Mininet.
Earlier kernels (e.g. 2.6.29) work with CONFIG_NET_NS enabled and no
additional patches, but are much slower at removing veth interfaces,
resulting in much slower switch shutdown.
* install Mininet, the OpenFlow reference implementation, and
Open vSwitch
For scalable configurations, you might need to increase some of your
kernel limits. Sample params are in util/sysctl_addon, which can be
appended to /etc/sysctl.conf (and modified as necessary for your
desired configuration):
mininet/util/install.sh -fnv
sudo su -c "cat sysctl_addon >> /etc/sysctl.conf"
* enable and start openvswitch
To save the config change, run:
sudo systemctl enable openvswitch
sudo systemctl start openvswitch
sudo sysctl -p
4. OpenFlow software and configuration requirements
* test the mininet installation
Mininet requires either the reference OpenFlow switch implementation
(from openflowswitch.org) or Open vSwitch (openvswitch.org) to be
installed. "make test" requires the reference user space
implementations as well as Open vSwitch. Note the reference kernel
implementation is not currently included in OpenFlow 1.0.
On Ubuntu and Debian, the install.sh script may be used with the '-f'
option to install the OpenFlow reference implementation, the '-v' option
to build Open vSwitch, and the '-m' option to install the Open vSwitch
kernel module into /lib/modules (note: you must build Open vSwitch first!)
Mininet will automatically load and remove kernel module dependencies
for supported switch types, using modprobe and rmmod - but these
modules must be in a location where modprobe can find them (e.g.
something like /lib/modules/`uname -r`/kernel/drivers/net/)
sudo mn --test pingall
The reference OpenFlow controller (controller(8)) only supports 16
switches by default! If you wish to run a network with more than 16
switches, please recompile controller(8) with larger limits, or use a
different controller such as nox. A patch to controller(8) is included
as util/openflow-patches/controller.patch.
5. Other software dependencies
4. Creating your own Mininet/OpenFlow tutorial VM
On Ubuntu and Debian, other Mininet dependencies may be installed using
the '-n' option of the install.sh script.
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
To run the iperf test, you need to install iperf:
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
time install-mininet-vm.sh
sudo aptitude/yum install iperf
Finally, verify that Mininet is installed and working in the VM:
We assume you already have ping installed. ;-)
To use xterm or sshd with Mininet, you need the following:
sudo mn --test pingall
sudo aptitude/yum install sshd xterm screen
Some examples may have additional requirements - consult the specific
example file for details.
The install.sh script has an '-x' option to install the version of
NOX from the OpenFlow tutorial.
6. Other notes and recommendations
5. Installation on other Linux distributions
Although we don't support other Linux distributions directly, it
should be possible to install and run Mininet with some degree of
manual effort.
In general, you must have:
* A Linux kernel compiled with network namespace support enabled
* An OpenFlow implementation (either the reference user or kernel
space implementations, or Open vSwitch.) Appropriate kernel
modules (e.g. tun and ofdatapath for the reference kernel
implementation) must be loaded.
* Python, `bash`, `ping`, `iperf`, etc.
* Root privileges (required for network device access)
We encourage contribution of patches to the `install.sh` script to
support other Linux distributions.
If you did not install certain useful packages and you wish to later,
it may be possible to install them using install.sh.
Mininet should be run either on a machine with
no other important processes, or on a virtual machine (recommended!)
Multiple concurrent Mininet instances are not supported!
Good luck!
Mininet Team
---
Historical information on OpenFlow 0.8.9 and the reference kernel module:
The kernel reference implementation has been deprecated, but it may
be possible to get it work with Mininet.
To switch to the most recent OpenFlow 0.8.9 release branch (the most
recent one with full NOX support and kernel datapath support) in your
OpenFlow git tree:
git checkout -b release/0.8.9 remotes/origin/release/0.8.9
A patch to enable datapath.c to compile with recent kernels
is included in util/openflow-patches/datapath.patch.
In OpenFlow 1.0, switch port numbering starts at 1 (for better or for worse.)
To run with previous versions of OpenFlow, it may be necessary
to change SWITCH_PORT_BASE from 1 to 0 in node.py.
+6 -2
View File
@@ -1,6 +1,10 @@
Mininet 1.0.0 License
Mininet 2.1.0 License
Copyright (c) 2009-2011 Bob Lantz and Brandon Heller
Copyright (c) 2013 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
Original authors: Bob Lantz and Brandon Heller
We are making Mininet available for public use and benefit with the
expectation that others will use, modify and enhance the Software and
+35 -9
View File
@@ -1,18 +1,25 @@
MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = examples/*.py
BIN = bin/mn
EXAMPLES = mininet/examples/*.py
MN = bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
DOCDIRS = doc/html doc/latex
PDF = doc/latex/refman.pdf
all: codecheck test
clean:
rm -rf build dist *.egg-info *.pyc $(MNEXEC)
rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS)
codecheck: $(PYSRC)
-echo "Running code check"
util/versioncheck.py
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
@@ -25,15 +32,34 @@ errcheck: $(PYSRC)
test: $(MININET) $(TEST)
-echo "Running tests"
mininet/test/test_nets.py
mininet/test/test_hifi.py
install: $(MNEXEC)
install $(MNEXEC) /usr/local/bin/
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py install
develop: $(MNEXEC)
install $(MNEXEC) /usr/local/bin/
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py develop
doc:
doxygen doxygen.cfg
man: $(MANPAGES)
mn.1: $(MN)
PYTHONPATH=. help2man -N -n "create a Mininet network." \
--no-discard-stderr $< -o $@
mnexec.1: mnexec
help2man -N -n "execution utility for Mininet." \
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
.PHONY: doc
doc: man
doxygen doc/doxygen.cfg
make -C doc/latex
-86
View File
@@ -1,86 +0,0 @@
Mininet: A Simple Virtual Testbed for OpenFlow/SDN
or
How to Squeeze a 1024-node OpenFlow Network onto your Laptop
Mininet 1.0.0
---
Welcome to Mininet!
Mininet creates OpenFlow test networks by using process-based
virtualization and network namespaces.
Simulated hosts (as well as switches and controllers with the user
datapath) are created as processes in separate network namespaces. This
allows a complete OpenFlow network to be simulated on top of a single
Linux kernel.
Mininet may be invoked directly from the command line, and also provides a
handy Python API for creating networks of varying sizes and topologies.
Mininet is currently in *limited alpha release*. We encourage you to
experiment with it and hope that you will provide us with feedback on
features, documentation, and how you're using it. We plan to make it
available publicly via a GPL or BSD license (probably in April), but please
don't distribute the code or URLs yet! The feedback you provide will help
us improve Mininet for general release.
In order to run Mininet, you must have:
* A Linux 2.6.26 or greater kernel compiled with network namespace support
enabled (see INSTALL for additional information.)
* An OpenFlow implementation (either the reference user or kernel
space implementations, or Open vSwitch.) Appropriate kernel modules
(e.g. tun and ofdatapath for the reference kernel implementation) must
be loaded.
* Python, bash, ping, iperf, etc.
* Root privileges (required for network device access)
Currently Mininet includes:
- A simple node infrastructure (Host, Switch, Controller classes) for
creating virtual OpenFlow networks
- A simple network infrastructure (Mininet class) supporting parametrized
topologies (Topo subclasses.) For example, a tree network may be created
with the command
# mn --topo tree,depth=2,fanout=3
- Basic tests, including connectivity (ping) and bandwidth (iperf)
- A command-line interface (CLI class) which provides useful
diagnostic commands, as well as the ability to send a command to a
node. For example,
mininet> h11 ifconfig -a
tells host h11 to run the command 'ifconfig -a'
- A 'cleanup' command to get rid of junk (interfaces, processes, files in
/tmp, etc.) which might be left around by Mininet or Linux. Try this if
things stop working!
# mn -c
- Examples (in the examples/ directory) to help you get started.
Batteries are not included (yet!)
However, some preliminary installation notes are included in the INSTALL
file.
Additionally, much useful information, including a Mininet tutorial,
is available on the Mininet wiki:
http://openflow.org/mininet
Enjoy, and good luck!
---
Bob Lantz
rlantz@cs.stanford.edu
+133
View File
@@ -0,0 +1,133 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Version 2.1.0
### What is Mininet?
Mininet emulates a complete network of hosts, links, and switches
on a single machine. To create a sample two-host, one-switch network,
just run:
`sudo mn`
Mininet is useful for interactive development, testing, and demos,
especially those using OpenFlow and SDN. OpenFlow-based network
controllers prototyped in Mininet can usually be transferred to
hardware with minimal changes for full line-rate execution.
### How does it work?
Mininet creates virtual networks using process-based virtualization
and network namespaces - features that are available in recent Linux
kernels. In Mininet, hosts are emulated as `bash` processes running in
a network namespace, so any code that would normally run on a Linux
server (like a web server or client program) should run just fine
within a Mininet "Host". The Mininet "Host" will have its own private
network interface and can only see its own processes. Switches in
Mininet are software-based switches like Open vSwitch or the OpenFlow
reference switch. Links are virtual ethernet pairs, which live in the
Linux kernel and connect our emulated switches to emulated hosts
(processes).
### Features
Mininet includes:
* A command-line launcher (`mn`) to instantiate networks.
* A handy Python API for creating networks of varying sizes and
topologies.
* Examples (in the `examples/` directory) to help you get started.
* Full API documentation via Python `help()` docstrings, as well as
the ability to generate PDF/HTML documentation with `make doc`.
* Parametrized topologies (`Topo` subclasses) using the Mininet
object. For example, a tree network may be created with the
command:
`mn --topo tree,depth=2,fanout=3`
* A command-line interface (`CLI` class) which provides useful
diagnostic commands (like `iperf` and `ping`), as well as the
ability to run a command to a node. For example,
`mininet> h11 ifconfig -a`
tells host h11 to run the command `ifconfig -a`
* A "cleanup" command to get rid of junk (interfaces, processes, files
in /tmp, etc.) which might be left around by Mininet or Linux. Try
this if things stop working!
`mn -c`
### New features in 2.1.0
Mininet 2.1.0 provides a number of bug fixes as well as
several new features, including:
* Convenient access to `Mininet()` as a dict of nodes
* X11 tunneling (wireshark in Mininet hosts, finally!)
* Accurate reflection of the `Mininet()` object in the CLI
* Automatically detecting and adjusting resource limits
* Automatic cleanup on failure of the `mn` command
* Preliminary support for running OVS in user space mode
* Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch
* support for installing the OpenFlow 1.3 versions of the reference
user switch and NOX from CPqD
* The ability to import modules from `mininet.examples`
We have provided several new examples (which can easily be
imported to provide useful functionality) including:
* Modeling separate control and data networks: `mininet.examples.controlnet`
* Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat`
* Creating per-host custom directories using bind mounts: `mininet.examples.bind`
Note that examples contain experimental features which might
"graduate" into mainline Mininet in the future, but they should
not be considered a stable part of the Mininet API!
### Installation
See `INSTALL` for installation instructions and details.
### Documentation
In addition to the API documentation (`make doc`), much useful
information, including a Mininet walkthrough and an introduction
to the Python API, is available on the
[Mininet Web Site](http://mininet.org).
There is also a wiki which you are encouraged to read and to
contribute to, particularly the Frequently Asked Questions (FAQ.)
### Support
Mininet is community-supported. We encourage you to join the
Mininet mailing list, `mininet-discuss` at:
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
### Contributing
Mininet is an open source project and is currently hosted
at <https://github.com/mininet>. You are encouraged to download
the code, examine it, modify it, and submit bug reports, bug fixes,
feature requests, new features and other issues and pull requests.
Thanks to everyone who has contributed to the project
(see CONTRIBUTORS for more info!)
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
### Credits
The Mininet 2.1.0 Team:
* Bob Lantz
* Brian O'Connor
+80 -39
View File
@@ -12,43 +12,48 @@ Example to pull custom params (topo, switch, etc.) from a file:
"""
from optparse import OptionParser
import os.path
import os
import sys
import time
# Fix setuptools' evil madness, and open up (more?) security holes
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
from mininet.clean import cleanup
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, warn
from mininet.net import Mininet, MininetWithControlNet
from mininet.log import lg, LEVELS, info, debug, error
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
OVSLegacyKernelSwitch )
OVSLegacyKernelSwitch, IVSSwitch )
from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
from mininet.util import makeNumeric, custom, customConstructor, splitArgs
from mininet.util import custom, customConstructor
from mininet.util import buildTopo
# built in topologies, created only when run
TOPODEF = 'minimal'
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo }
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo }
SWITCHDEF = 'ovsk'
SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch,
'ovsl': OVSLegacyKernelSwitch }
'ovsk': OVSKernelSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'ref'
CONTROLLERDEF = 'ovsc'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
@@ -62,10 +67,12 @@ LINKS = { 'default': Link,
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
'none' ]
'none' ]
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
ALTSPELLING = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
@@ -77,16 +84,21 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
help: string"""
if default not in choicesDict:
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
( default, name ) )
if not helpStr:
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
help = helpStr )
type='string',
default = default,
help = helpStr )
def version( *_args ):
"Print Mininet version and exit"
print "%s" % VERSION
exit()
class MininetRunner( object ):
"Build, setup, and run Mininet."
@@ -134,7 +146,14 @@ class MininetRunner( object ):
else:
raise Exception( 'Custom file name not found' )
opts = OptionParser()
desc = ( "The %prog utility creates Mininet network from the\n"
"command line. It can create parametrized topologies,\n"
"invoke the Mininet CLI, and run tests." )
usage = ( '%prog [options]\n'
'(type %prog -h for details)' )
opts = OptionParser( description=desc, usage=usage )
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
@@ -142,42 +161,47 @@ class MininetRunner( object ):
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
opts.add_option( '--clean', '-c', action='store_true',
default=False, help='clean and exit' )
default=False, help='clean and exit' )
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py file' )
help='read custom topo and node params from .py' +
'file' )
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
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='automatically set host MACs' )
default=False, help='automatically set host MACs' )
opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' )
default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice',
choices=LEVELS.keys(), default = 'info',
help = '|'.join( LEVELS.keys() ) )
choices=LEVELS.keys(), default = 'info',
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6635,
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6634,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening port")
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' )
opts.add_option( '--prefixlen', type='int', default=8,
help='prefix length (e.g. /8) for automatic '
'network configuration' )
help='CLI script to run after tests' )
opts.add_option( '--pin', action='store_true',
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
opts.add_option( '--version', action='callback', callback=version )
self.options, self.args = opts.parse_args()
# We don't accept extra arguments after the options
if self.args:
opts.print_help()
exit()
def setup( self ):
"Setup and validate environment."
@@ -255,4 +279,21 @@ class MininetRunner( object ):
if __name__ == "__main__":
MininetRunner()
try:
MininetRunner()
except KeyboardInterrupt:
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
cleanup()
except Exception:
# Print exception
type_, val_, trace_ = sys.exc_info()
errorMsg = ( "-"*80 + "\n" +
"Caught exception. Cleaning up...\n\n" +
"%s: %s\n" % ( type_.__name__, val_ ) +
"-"*80 + "\n" )
error( errorMsg )
# Print stack trace to debug log
import traceback
stackTrace = traceback.format_exc()
debug( stackTrace + "\n" )
cleanup()
+13 -24
View File
@@ -1,7 +1,5 @@
"""Custom topology example
author: Brandon Heller (brandonh@stanford.edu)
Two directly connected switches plus a host for each switch:
host --- switch --- switch --- host
@@ -10,36 +8,27 @@ Adding the 'topos' dict with a key/value pair to generate our newly defined
topology enables one to pass in '--topo=mytopo' from the command line.
"""
from mininet.topo import Topo, Node
from mininet.topo import Topo
class MyTopo( Topo ):
"Simple topology example."
def __init__( self, enable_all = True ):
def __init__( self ):
"Create custom topo."
# Add default members to class.
super( MyTopo, self ).__init__()
# Initialize topology
Topo.__init__( self )
# Set Node IDs for hosts and switches
leftHost = 1
leftSwitch = 2
rightSwitch = 3
rightHost = 4
# Add hosts and switches
leftHost = self.addHost( 'h1' )
rightHost = self.addHost( 'h2' )
leftSwitch = self.addSwitch( 's3' )
rightSwitch = self.addSwitch( 's4' )
# Add nodes
self.add_node( leftSwitch, Node( is_switch=True ) )
self.add_node( rightSwitch, Node( is_switch=True ) )
self.add_node( leftHost, Node( is_switch=False ) )
self.add_node( rightHost, Node( is_switch=False ) )
# Add edges
self.add_edge( leftHost, leftSwitch )
self.add_edge( leftSwitch, rightSwitch )
self.add_edge( rightSwitch, rightHost )
# Consider all switches and hosts 'on'
self.enable_all()
# Add links
self.addLink( leftHost, leftSwitch )
self.addLink( leftSwitch, rightSwitch )
self.addLink( rightSwitch, rightHost )
topos = { 'mytopo': ( lambda: MyTopo() ) }
+33
View File
@@ -0,0 +1,33 @@
mininet (2.1.0-0ubuntu1) saucy; urgency=low
* Add 2.1.0 final packaging
-- Bob Lantz <rlantz@cs.stanford.edu> Wed, 18 Sep 2013 22:43:47 -0700
mininet (2.1.0~rc1-0ubuntu1) saucy; urgency=low
* New upstream release candidate:
- d/control: Drop dependency on python-networkx, add iperf, socat
and cgroup-bin to Depends.
-- James Page <james.page@ubuntu.com> Wed, 28 Aug 2013 10:10:20 +0100
mininet (2.0.0-0ubuntu1) raring; urgency=low
* New upstream release.
-- James Page <james.page@ubuntu.com> Wed, 19 Dec 2012 15:48:01 +0000
mininet (2.0.0~rc1-0ubuntu1) quantal; urgency=low
* New upstream release.
* Update copyright to match upstream release
* Fix this message
-- Bob Lantz <rlantz@cs.stanford.edu> Sun, 18 Nov 2012 00:15:09 -0800
mininet (2.0.0~d4-0ubuntu1) quantal; urgency=low
* Initial release.
-- Bob Lantz <rlantz@cs.stanford.edu> Tue, 07 Aug 2012 14:11:27 -0700
+1
View File
@@ -0,0 +1 @@
9
+31
View File
@@ -0,0 +1,31 @@
Source: mininet
Section: net
Priority: extra
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
XSBC-Original-Maintainer: Bob Lantz <rlantz@cs.stanford.edu>
Standards-Version: 3.9.3
Build-Depends:
debhelper (>= 9~),
help2man,
python-dev,
python-pkg-resources,
python-setuptools
Homepage: http://openflow.org/mininet
Package: mininet
Architecture: any
Depends:
openvswitch-switch,
telnet,
socat,
iperf,
cgroup-bin,
${misc:Depends},
${python:Depends},
${shlibs:Depends}
Recommends: openvswitch-controller
Description: Process-based network emulator
Mininet is a network emulator which uses lightweight
virtualization to create virtual networks for rapid
prototyping of Software-Defined Network (SDN) designs
using OpenFlow.
+37
View File
@@ -0,0 +1,37 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
Upstream-Name: mininet
Source: https://github.com/mininet/mininet
Files: *
Copyright: 2012-2013 Open Networking Laboratory,
2009-2012 Bob Lantz,
2009-2012 The Board of Trustees of the Leland Stanford Junior
University
License:
Original authors: Bob Lantz and Brandon Heller
.
We are making Mininet available for public use and benefit with the
expectation that others will use, modify and enhance the Software and
contribute those enhancements back to the community. However, since we
would like to make the Software available for broadest use, with as few
restrictions as possible permission is hereby granted, free of charge, to
any person obtaining a copy of this Software to deal in the Software
under the copyrights without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
.
The name and trademarks of copyright holder(s) may NOT be used in
advertising or publicity pertaining to the Software or any derivatives
without specific, written prior permission.
Vendored
+1
View File
@@ -0,0 +1 @@
README.md
+1
View File
@@ -0,0 +1 @@
examples/*
+1
View File
@@ -0,0 +1 @@
mnexec /usr/bin
+1
View File
@@ -0,0 +1 @@
*.1
Vendored Executable
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/make -f
%:
dh $@ --buildsystem=python_distutils --with=python2
override_dh_auto_build:
make man
make mnexec
dh_auto_build
get-orig-source:
uscan --force-download --rename
+1
View File
@@ -0,0 +1 @@
3.0 (quilt)
+3
View File
@@ -0,0 +1,3 @@
version=3
opts=filenamemangle=s/(.*)\/archive/$1/,uversionmangle=s/([abdr].*)\.tar\.gz/~$1/ \
https://github.com/mininet/mininet/tags .*/archive\/(\d.*\.tar\.gz)
+3 -3
View File
@@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = Mininet
PROJECT_NAME = "Mininet Python API Reference Manual"
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
@@ -114,7 +114,7 @@ FULL_PATH_NAMES = YES
# If left blank the directory from which doxygen is run is used as the
# path to strip.
STRIP_FROM_PATH =
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
# the path mentioned in the documentation of a class, which tells
@@ -919,7 +919,7 @@ COMPACT_LATEX = NO
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
PAPER_TYPE = letter
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
-84
View File
@@ -1,84 +0,0 @@
Mininet Examples
These examples are intended to help you get started using
Mininet's Python API.
---
baresshd.py:
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
consoles.py:
This example creates a grid of console windows, one for each node,
and allows interaction with and monitoring of each console, including
graphical monitoring.
controllers.py:
This example creates a network and adds multiple controllers to it.
emptynet.py:
This example demonstrates creating an empty network (i.e. with no
topology object) and adding nodes to it.
linearbandwidth.py:
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
miniedit.py:
This example demonstrates creating a network via a graphical editor.
multiping.py:
This example demonstrates one method for
monitoring output from multiple hosts, using node.monitor().
multipoll.py:
This example demonstrates monitoring output files from multiple hosts.
multitest.py:
This example creates a network and runs multiple tests on it.
popenpoll.py:
This example demonstrates monitoring output from multiple hosts using
the node.popen() interface (which returns Popen objects) and pmonitor().
scratchnet.py, scratchnetuser.py:
These two examples demonstrate how to create a network by using the lowest-
level Mininet functions. Generally the higher-level API is easier to use,
but scratchnet shows what is going on behind the scenes.
simpleperf.py:
A simple example of configuring network and CPU bandwidth limits.
sshd.py:
This example shows how to run an sshd process in each host, allowing
you to log in via ssh. This requires connecting the Mininet data network
to an interface in the root namespace (generaly the control network
already lives in the root namespace, so it does not need to be explicitly
connected.)
treeping64.py:
This example creates a 64-host tree network, and attempts to check full
connectivity using ping, for different switch/datapath types.
tree1024.py:
This example attempts to create a 1024-host network, and then runs the
CLI on it. It may run into scalability limits, depending on available
memory and sysctl configuration (see INSTALL.)
+119
View File
@@ -0,0 +1,119 @@
Mininet Examples
========================================================
These examples are intended to help you get started using
Mininet's Python API.
========================================================
#### baresshd.py:
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
#### consoles.py:
This example creates a grid of console windows, one for each node,
and allows interaction with and monitoring of each console, including
graphical monitoring.
#### controllers.py:
This example creates a network with multiple controllers, by
using a custom `Switch()` subclass.
#### controllers2.py:
This example creates a network with multiple controllers by
creating an empty network, adding nodes to it, and manually
starting the switches.
#### controlnet.py:
This examples shows how you can model the control network as well
as the data network, by actually creating two Mininet objects.
#### cpu.py:
This example tests iperf bandwidth for varying CPU limits.
#### emptynet.py:
This example demonstrates creating an empty network (i.e. with no
topology object) and adding nodes to it.
#### hwintf.py:
This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
#### limit.py:
This example shows how to use link and CPU limits.
#### linearbandwidth.py:
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
#### miniedit.py:
This example demonstrates creating a network via a graphical editor.
#### multiping.py:
This example demonstrates one method for
monitoring output from multiple hosts, using `node.monitor()`.
#### multipoll.py:
This example demonstrates monitoring output files from multiple hosts.
#### multitest.py:
This example creates a network and runs multiple tests on it.
#### nat.py:
This example shows how to connect a Mininet network to the Internet
using NAT. It also answers the eternal question "why can't I ping
`google.com`?"
#### popen.py:
This example monitors a number of hosts using `host.popen()` and
`pmonitor()`.
#### popenpoll.py:
This example demonstrates monitoring output from multiple hosts using
the `node.popen()` interface (which returns `Popen` objects) and `pmonitor()`.
#### scratchnet.py, scratchnetuser.py:
These two examples demonstrate how to create a network by using the lowest-
level Mininet functions. Generally the higher-level API is easier to use,
but scratchnet shows what is going on behind the scenes.
#### simpleperf.py:
A simple example of configuring network and CPU bandwidth limits.
#### sshd.py:
This example shows how to run an `sshd` process in each host, allowing
you to log in via `ssh`. This requires connecting the Mininet data network
to an interface in the root namespace (generaly the control network
already lives in the root namespace, so it does not need to be explicitly
connected.)
#### tree1024.py:
This example attempts to create a 1024-host network, and then runs the
CLI on it. It may run into scalability limits, depending on available
memory and `sysctl` configuration (see `INSTALL`.)
#### treeping64.py:
This example creates a 64-host tree network, and attempts to check full
connectivity using `ping`, for different switch/datapath types.
+4
View File
@@ -0,0 +1,4 @@
"""
Mininet Examples
See README for details
"""
+9 -1
View File
@@ -2,7 +2,11 @@
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
import sys
from mininet.node import Host
from mininet.util import ensureRoot
ensureRoot()
print "*** Creating nodes"
h1 = Host( 'h1' )
@@ -24,6 +28,10 @@ f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
f.close()
print "*** Running sshd"
h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
# add arguments from the command line
if len( sys.argv ) > 1:
cmd += ' ' + ' '.join( sys.argv[ 1: ] )
h1.cmd( cmd )
print "*** You may now ssh into", h1.name, "at", h1.IP()
+197
View File
@@ -0,0 +1,197 @@
#!/usr/bin/python
"""
bind.py: Bind mount prototype
This creates hosts with private directories as desired.
"""
from mininet.net import Mininet
from mininet.node import Host
from mininet.cli import CLI
from mininet.util import errFail, quietRun, errRun
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info, debug
from os.path import realpath
from functools import partial
# Utility functions for unmounting a tree
MNRUNDIR = realpath( '/var/run/mn' )
def mountPoints():
"Return list of mounted file systems"
mtab, _err, _ret = errFail( 'cat /proc/mounts' )
lines = mtab.split( '\n' )
mounts = []
for line in lines:
if not line:
continue
fields = line.split( ' ')
mount = fields[ 1 ]
mounts.append( mount )
return mounts
def unmountAll( rootdir=MNRUNDIR ):
"Unmount all mounts under a directory tree"
rootdir = realpath( rootdir )
# Find all mounts below rootdir
# This is subtle because /foo is not
# a parent of /foot
dirslash = rootdir + '/'
mounts = [ m for m in mountPoints()
if m == dir or m.find( dirslash ) == 0 ]
# Unmount them from bottom to top
mounts.sort( reverse=True )
for mount in mounts:
debug( 'Unmounting', mount, '\n' )
_out, err, code = errRun( 'umount', mount )
if code != 0:
info( '*** Warning: failed to umount', mount, '\n' )
info( err )
class HostWithPrivateDirs( Host ):
"Host with private directories"
mnRunDir = MNRUNDIR
def __init__(self, name, *args, **kwargs ):
"""privateDirs: list of private directories
remounts: dirs to remount
unmount: unmount dirs in cleanup? (True)
Note: if unmount is False, you must call unmountAll()
manually."""
self.privateDirs = kwargs.pop( 'privateDirs', [] )
self.remounts = kwargs.pop( 'remounts', [] )
self.unmount = kwargs.pop( 'unmount', True )
Host.__init__( self, name, *args, **kwargs )
self.rundir = '%s/%s' % ( self.mnRunDir, name )
self.root, self.private = None, None # set in createBindMounts
if self.privateDirs:
self.privateDirs = [ realpath( d ) for d in self.privateDirs ]
self.createBindMounts()
# These should run in the namespace before we chroot,
# in order to put the right entries in /etc/mtab
# Eventually this will allow a local pid space
# Now we chroot and cd to wherever we were before.
pwd = self.cmd( 'pwd' ).strip()
self.sendCmd( 'exec chroot', self.root, 'bash -ms mininet:'
+ self.name )
self.waiting = False
self.cmd( 'cd', pwd )
# In order for many utilities to work,
# we need to remount /proc and /sys
self.cmd( 'mount /proc' )
self.cmd( 'mount /sys' )
def mountPrivateDirs( self ):
"Create and bind mount private dirs"
for dir_ in self.privateDirs:
privateDir = self.private + dir_
errFail( 'mkdir -p ' + privateDir )
mountPoint = self.root + dir_
errFail( 'mount -B %s %s' %
( privateDir, mountPoint) )
def mountDirs( self, dirs ):
"Mount a list of directories"
for dir_ in dirs:
mountpoint = self.root + dir_
errFail( 'mount -B %s %s' %
( dir_, mountpoint ) )
@classmethod
def findRemounts( cls, fstypes=None ):
"""Identify mount points in /proc/mounts to remount
fstypes: file system types to match"""
if fstypes is None:
fstypes = [ 'nfs' ]
dirs = quietRun( 'cat /proc/mounts' ).strip().split( '\n' )
remounts = []
for dir_ in dirs:
line = dir_.split()
mountpoint, fstype = line[ 1 ], line[ 2 ]
# Don't re-remount directories!!!
if mountpoint.find( cls.mnRunDir ) == 0:
continue
if fstype in fstypes:
remounts.append( mountpoint )
return remounts
def createBindMounts( self ):
"""Create a chroot directory structure,
with self.privateDirs as private dirs"""
errFail( 'mkdir -p '+ self.rundir )
unmountAll( self.rundir )
# Create /root and /private directories
self.root = self.rundir + '/root'
self.private = self.rundir + '/private'
errFail( 'mkdir -p ' + self.root )
errFail( 'mkdir -p ' + self.private )
# Recursively mount / in private doort
# note we'll remount /sys and /proc later
errFail( 'mount -B / ' + self.root )
self.mountDirs( self.remounts )
self.mountPrivateDirs()
def unmountBindMounts( self ):
"Unmount all of our bind mounts"
unmountAll( self.rundir )
def popen( self, *args, **kwargs ):
"Popen with chroot support"
chroot = kwargs.pop( 'chroot', True )
mncmd = kwargs.get( 'mncmd',
[ 'mnexec', '-a', str( self.pid ) ] )
if chroot:
mncmd = [ 'chroot', self.root ] + mncmd
kwargs[ 'mncmd' ] = mncmd
return Host.popen( self, *args, **kwargs )
def cleanup( self ):
"""Clean up, then unmount bind mounts
unmount: actually unmount bind mounts?"""
# Wait for process to actually terminate
self.shell.wait()
Host.cleanup( self )
if self.unmount:
self.unmountBindMounts()
errFail( 'rmdir ' + self.root )
# Convenience aliases
findRemounts = HostWithPrivateDirs.findRemounts
# Sample usage
def testHostWithPrivateDirs():
"Test bind mounts"
topo = SingleSwitchTopo( 10 )
remounts = findRemounts( fstypes=[ 'nfs' ] )
privateDirs = [ '/var/log', '/var/run' ]
host = partial( HostWithPrivateDirs, remounts=remounts,
privateDirs=privateDirs, unmount=False )
net = Mininet( topo=topo, host=host )
net.start()
info( 'Private Directories:', privateDirs, '\n' )
CLI( net )
net.stop()
# We do this all at once to save a bit of time
info( 'Unmounting host bind mounts...\n' )
unmountAll()
if __name__ == '__main__':
unmountAll()
setLogLevel( 'info' )
testHostWithPrivateDirs()
info( 'Done.\n')
+22 -15
View File
@@ -74,11 +74,11 @@ class Console( Frame ):
"Pop up a new terminal window for a node."
net.terms += makeTerms( [ node ], title )
label = Button( self, text=self.node.name, command=newTerm,
**self.buttonStyle )
**self.buttonStyle )
label.pack( side='top', fill='x' )
text = Text( self, wrap='word', **self.textStyle )
ybar = Scrollbar( self, orient='vertical', width=7,
command=text.yview )
command=text.yview )
text.configure( yscrollcommand=ybar.set )
text.pack( side='left', expand=True, fill='both' )
ybar.pack( side='right', fill='y' )
@@ -95,7 +95,7 @@ class Console( Frame ):
# way to trigger a file event handler from Tk's
# event loop!
self.tk.createfilehandler( self.node.stdout, READABLE,
self.handleReadable )
self.handleReadable )
# We're not a terminal (yet?), so we ignore the following
# control characters other than [\b\n\r]
@@ -169,11 +169,8 @@ class Graph( Frame ):
"Graph that we can add bars to over time."
def __init__( self, parent=None,
bg = 'white',
gheight=200, gwidth=500,
barwidth=10,
ymax=3.5,):
def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
barwidth=10, ymax=3.5,):
Frame.__init__( self, parent )
@@ -195,7 +192,7 @@ class Graph( Frame ):
width = 25
ymax = self.ymax
scale = Canvas( self, width=width, height=height,
background=self.bg )
background=self.bg )
opts = { 'fill': 'red' }
# Draw scale line
scale.create_line( width - 1, height, width - 1, 0, **opts )
@@ -211,7 +208,7 @@ class Graph( Frame ):
ofs = 20
height = self.gheight + ofs
self.graph.configure( scrollregion=( 0, -ofs,
self.xpos * self.barwidth, height ) )
self.xpos * self.barwidth, height ) )
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
def yview( self, *args ):
@@ -231,7 +228,7 @@ class Graph( Frame ):
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
ybar = Scrollbar( self, orient='vertical', command=self.yview )
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
scrollregion=(0, 0, width, height ) )
scrollregion=(0, 0, width, height ) )
scale.configure( yscrollcommand=ybar.set )
# Layout
@@ -252,7 +249,7 @@ class Graph( Frame ):
x1 = x0 + self.barwidth
y0 = self.gheight
y1 = ( 1 - percent ) * self.gheight
c.create_rectangle( x0 , y0, x1, y1, fill='green' )
c.create_rectangle( x0, y0, x1, y1, fill='green' )
self.xpos += 1
self.updateScrollRegions()
self.graph.xview( 'moveto', '1.0' )
@@ -318,11 +315,19 @@ class ConsoleApp( Frame ):
def updateGraph( self, _console, output ):
"Update our graph."
m = re.search( r'(\d+) Mbits/sec', output )
m = re.search( r'(\d+.?\d*) ([KMG]?bits)/sec', output )
if not m:
return
val, units = float( m.group( 1 ) ), m.group( 2 )
#convert to Gbps
if units[0] == 'M':
val *= 10 ** -3
elif units[0] == 'K':
val *= 10 ** -6
elif units[0] == 'b':
val *= 10 ** -9
self.updates += 1
self.bw += .001 * float( m.group( 1 ) )
self.bw += val
if self.updates >= self.hostCount:
self.graph.addBar( self.bw )
self.bw = 0
@@ -413,7 +418,9 @@ class ConsoleApp( Frame ):
count = len( consoles )
self.setOutputHook( self.updateGraph )
for console in consoles:
console.node.cmd( 'iperf -sD' )
# Sometimes iperf -sD doesn't return,
# so we run it in the background instead
console.node.cmd( 'iperf -s &' )
i = 0
for console in consoles:
i = ( i + 1 ) % count
+24 -52
View File
@@ -1,64 +1,36 @@
#!/usr/bin/python
"""
This example creates a multi-controller network from
semi-scratch; note a topo object could also be used and
would be passed into the Mininet() constructor.
Create a network where different switches are connected to
different controllers, by creating a custom Switch() subclass.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSKernelSwitch
from mininet.cli import CLI
from mininet.node import OVSSwitch, Controller, RemoteController
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.cli import CLI
Switch = OVSKernelSwitch
setLogLevel( 'info' )
def addHost( net, N ):
"Create host hN and add to net."
name = 'h%d' % N
ip = '10.0.0.%d' % N
return net.addHost( name, ip=ip )
# Two local and one "external" controller (which is actually c0)
# Ignore the warning message that the remote isn't (yet) running
c0 = Controller( 'c0', port=6633 )
c1 = Controller( 'c1', port=6634 )
c2 = RemoteController( 'c2', ip='127.0.0.1' )
def multiControllerNet():
"Create a network with multiple controllers."
cmap = { 's1': c0, 's2': c1, 's3': c2 }
net = Mininet( controller=Controller, switch=Switch)
class MultiSwitch( OVSSwitch ):
"Custom Switch() subclass that connects to different controllers"
def start( self, controllers ):
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
print "*** Creating controllers"
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
print "*** Creating switches"
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
print "*** Creating hosts"
hosts1 = [ addHost( net, n ) for n in 3, 4 ]
hosts2 = [ addHost( net, n ) for n in 5, 6 ]
print "*** Creating links"
for h in hosts1:
s1.linkTo( h )
for h in hosts2:
s2.linkTo( h )
s1.linkTo( s2 )
print "*** Starting network"
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
print "*** Testing network"
net.pingAll()
print "*** Running CLI"
CLI( net )
print "*** Stopping network"
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' ) # for CLI output
multiControllerNet()
topo = TreeTopo( depth=2, fanout=2 )
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
for c in [ c0, c1 ]:
net.addController(c)
net.build()
net.start()
CLI( net )
net.stop()
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/python
"""
This example creates a multi-controller network from semi-scratch by
using the net.add*() API and manually starting the switches and controllers.
This is the "mid-level" API, which is an alternative to the "high-level"
Topo() API which supports parametrized topology classes.
Note that one could also create a custom switch class and pass it into
the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel
def multiControllerNet():
"Create a network from semi-scratch with multiple controllers."
net = Mininet( controller=Controller, switch=OVSSwitch, build=False )
print "*** Creating (reference) controllers"
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
print "*** Creating switches"
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
print "*** Creating hosts"
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
print "*** Creating links"
for h in hosts1:
net.addLink( s1, h )
for h in hosts2:
net.addLink( s2, h )
net.addLink( s1, s2 )
print "*** Starting network"
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
print "*** Testing network"
net.pingAll()
print "*** Running CLI"
CLI( net )
print "*** Stopping network"
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' ) # for CLI output
multiControllerNet()
+151
View File
@@ -0,0 +1,151 @@
#!/usr/bin/python
"""
controlnet.py: Mininet with a custom control network
We create two Mininet() networks, a control network
and a data network, running four DataControllers on the
control network to control the data network.
Since we're using UserSwitch on the data network,
it should correctly fail over to a backup controller.
We also use a Mininet Facade to talk to both the
control and data networks from a single CLI.
"""
from functools import partial
from mininet.net import Mininet
from mininet.node import Controller, UserSwitch
from mininet.cli import CLI
from mininet.topo import Topo
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel, info
# Some minor hacks
class DataController( Controller ):
"""Data Network Controller.
patched to avoid checkListening error"""
def checkListening( self ):
"Ignore spurious error"
pass
class MininetFacade( object ):
"""Mininet object facade that allows a single CLI to
talk to one or more networks"""
def __init__( self, net, *args, **kwargs ):
"""Create MininetFacade object.
net: Primary Mininet object
args: unnamed networks passed as arguments
kwargs: named networks passed as arguments"""
self.net = net
self.nets = [ net ] + list( args ) + kwargs.values()
self.nameToNet = kwargs
self.nameToNet['net'] = net
def __getattr__( self, name ):
"returns attribute from Primary Mininet object"
return getattr( self.net, name )
def __getitem__( self, key ):
"returns primary/named networks or node from any net"
#search kwargs for net named key
if key in self.nameToNet:
return self.nameToNet[ key ]
#search each net for node named key
for net in self.nets:
if key in net:
return net[ key ]
def __iter__( self ):
"Iterate through all nodes in all Mininet objects"
for net in self.nets:
for node in net:
yield node
def __len__( self ):
"returns aggregate number of nodes in all nets"
count = 0
for net in self.nets:
count += len(net)
return count
def __contains__( self, key ):
"returns True if node is a member of any net"
return key in self.keys()
def keys( self ):
"returns a list of all node names in all networks"
return list( self )
def values( self ):
"returns a list of all nodes in all networks"
return [ self[ key ] for key in self ]
def items( self ):
"returns (key,value) tuple list for every node in all networks"
return zip( self.keys(), self.values() )
# A real control network!
class ControlNetwork( Topo ):
"Control Network Topology"
def __init__( self, n, dataController=DataController, **kwargs ):
"""n: number of data network controller nodes
dataController: class for data network controllers"""
Topo.__init__( self, **kwargs )
# Connect everything to a single switch
cs0 = self.addSwitch( 'cs0' )
# Add hosts which will serve as data network controllers
for i in range( 0, n ):
c = self.addHost( 'c%s' % i, cls=dataController,
inNamespace=True )
self.addLink( c, cs0 )
# Connect switch to root namespace so that data network
# switches will be able to talk to us
root = self.addHost( 'root', inNamespace=False )
self.addLink( root, cs0 )
# Make it Happen!!
def run():
"Create control and data networks, and invoke the CLI"
info( '* Creating Control Network\n' )
ctopo = ControlNetwork( n=4, dataController=DataController )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
info( '* Adding Control Network Controller\n')
cnet.addController( 'cc0', controller=Controller )
info( '* Starting Control Network\n')
cnet.start()
info( '* Creating Data Network\n' )
topo = TreeTopo( depth=2, fanout=2 )
# UserSwitch so we can easily test failover
sw = partial( UserSwitch, opts='--inactivity-probe=1 --max-backoff=1' )
net = Mininet( topo=topo, switch=sw, controller=None )
info( '* Adding Controllers to Data Network\n' )
for host in cnet.hosts:
if isinstance(host, Controller):
net.addController( host )
info( '* Starting Data Network\n')
net.start()
mn = MininetFacade( net, cnet=cnet )
CLI( mn )
info( '* Stopping Data Network\n' )
net.stop()
info( '* Stopping Control Network\n' )
cnet.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
run()
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python
"""
cpu.py: test iperf bandwidth for varying cpu limtis
cpu.py: test iperf bandwidth for varying cpu limits
"""
from mininet.net import Mininet
@@ -46,7 +46,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
server.cmd( 'iperf -s -p 5001 &' )
waitListening( client, server, 5001 )
result = client.cmd( 'iperf -yc -t %s -c %s' % (
seconds, server.IP() ) ).split( ',' )
seconds, server.IP() ) ).split( ',' )
bps = float( result[ -1 ] )
server.cmdPrint( 'kill %iperf' )
net.stop()
+2 -2
View File
@@ -27,8 +27,8 @@ def emptyNet():
s3 = net.addSwitch( 's3' )
info( '*** Creating links\n' )
h1.linkTo( s3 )
h2.linkTo( s3 )
net.addLink( h1, s3 )
net.addLink( h2, s3 )
info( '*** Starting network\n')
net.start()
+15 -7
View File
@@ -5,11 +5,12 @@ This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
"""
import re
import re, sys
from mininet.cli import CLI
from mininet.log import setLogLevel, info, error
from mininet.net import Mininet
from mininet.link import Intf
from mininet.topolib import TreeTopo
from mininet.util import quietRun
@@ -21,22 +22,29 @@ def checkIntf( intf ):
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
if ips:
error( 'Error:', intf, 'has an IP address,'
'and is probably in use!\n' )
'and is probably in use!\n' )
exit( 1 )
if __name__ == '__main__':
setLogLevel( 'info' )
newIntf = 'eth1'
info( '*** Checking', newIntf, '\n' )
checkIntf( newIntf )
# try to get hw intf from the command line; by default, use eth1
intfName = sys.argv[ 1 ] if len( sys.argv ) > 1 else 'eth1'
info( '*** Connecting to hw intf: %s' % intfName )
info( '*** Checking', intfName, '\n' )
checkIntf( intfName )
info( '*** Creating network\n' )
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
switch = net.switches[ 0 ]
info( '*** Adding', newIntf, 'to switch', switch.name, '\n' )
switch.addIntf( newIntf )
info( '*** Adding hardware interface', intfName, 'to switch',
switch.name, '\n' )
_intf = Intf( intfName, node=switch )
info( '*** Note: you may need to reconfigure the interfaces for '
'the Mininet hosts:\n', net.hosts, '\n' )
net.start()
CLI( net )
+4 -20
View File
@@ -8,33 +8,17 @@ 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.util import custom
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 ):
def limit( bw=10, cpu=.1 ):
"""Example/test of link and CPU bandwidth limits
bw: interface bandwidth limit in Mbps
cpu: cpu limit as fraction of overall CPU time"""
@@ -46,7 +30,7 @@ def limit( bw=10, cpu=.4 ):
net = Mininet( topo=myTopo, intf=intf, host=host )
net.start()
testLinkLimit( net, bw=bw )
testCpuLimit( net, cpu=cpu )
net.runCpuLimitTest( cpu=cpu )
net.stop()
def verySimpleLimit( bw=150 ):
+6 -6
View File
@@ -41,22 +41,22 @@ class LinearTestTopo( Topo ):
Topo.__init__( self, **params )
# Create switches and hosts
hosts = [ self.add_host( 'h%s' % h )
hosts = [ self.addHost( 'h%s' % h )
for h in irange( 1, N ) ]
switches = [ self.add_switch( 's%s' % s )
switches = [ self.addSwitch( 's%s' % s )
for s in irange( 1, N - 1 ) ]
# Wire up switches
last = None
for switch in switches:
if last:
self.add_link( last, switch )
self.addLink( last, switch )
last = switch
# Wire up hosts
self.add_link( hosts[ 0 ], switches[ 0 ] )
self.addLink( hosts[ 0 ], switches[ 0 ] )
for host, switch in zip( hosts[ 1: ], switches ):
self.add_link( host, switch )
self.addLink( host, switch )
def linearBandwidthTest( lengths ):
@@ -68,7 +68,7 @@ def linearBandwidthTest( lengths ):
hostCount = switchCount + 1
switches = { 'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
'Open vSwitch kernel': OVSKernelSwitch }
topo = LinearTestTopo( hostCount )
+16 -15
View File
@@ -112,7 +112,7 @@ class MiniEdit( Frame ):
appMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
appMenu.add_command( label='About MiniEdit', command=self.about,
font=font)
font=font)
appMenu.add_separator()
appMenu.add_command( label='Quit', command=self.quit, font=font )
@@ -127,7 +127,7 @@ class MiniEdit( Frame ):
editMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label="Edit", font=font, menu=editMenu )
editMenu.add_command( label="Cut", font=font,
command=lambda: self.deleteSelection( None ) )
command=lambda: self.deleteSelection( None ) )
runMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label="Run", font=font, menu=runMenu )
@@ -143,7 +143,7 @@ class MiniEdit( Frame ):
f = Frame( self )
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
bg=self.bg )
bg=self.bg )
# Scroll bars
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
@@ -177,7 +177,7 @@ class MiniEdit( Frame ):
bbox = self.canvas.bbox( 'all' )
if bbox is not None:
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
bbox[ 3 ] ) )
bbox[ 3 ] ) )
def canvasx( self, x_root ):
"Convert root x coordinate to canvas coordinate."
@@ -223,7 +223,7 @@ class MiniEdit( Frame ):
for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
doCmd = getattr( self, 'do' + cmd )
b = Button( toolbar, text=cmd, font=self.smallFont,
fg=color, command=doCmd )
fg=color, command=doCmd )
b.pack( fill='x', side='bottom' )
return toolbar
@@ -289,7 +289,7 @@ class MiniEdit( Frame ):
def deleteItem( self, item ):
"Delete an item."
# Don't delete while network is running
if self.buttons[ 'Select' ][ 'state' ] == 'disabled' :
if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
return
# Delete from model
if item in self.links:
@@ -308,7 +308,7 @@ class MiniEdit( Frame ):
def nodeIcon( self, node, name ):
"Create a new node icon."
icon = Button( self.canvas, image=self.images[ node ],
text=name, compound='top' )
text=name, compound='top' )
# Unfortunately bindtags wants a tuple
bindtags = [ str( self.nodeBindings ) ]
bindtags += list( icon.bindtags() )
@@ -322,8 +322,8 @@ class MiniEdit( Frame ):
self.nodeCount += 1
name = self.nodePrefixes[ node ] + str( self.nodeCount )
icon = self.nodeIcon( node, name )
item = self.canvas.create_window( x, y, anchor='c',
window=icon, tags=node )
item = self.canvas.create_window( x, y, anchor='c', window=icon,
tags=node )
self.widgetToItem[ icon ] = item
self.itemToWidget[ item ] = icon
self.selectItem( item )
@@ -437,7 +437,7 @@ class MiniEdit( Frame ):
item = self.widgetToItem[ w ]
x, y = self.canvas.coords( item )
self.link = self.canvas.create_line( x, y, x, y, width=4,
fill='blue', tag='link' )
fill='blue', tag='link' )
self.linkx, self.linky = x, y
self.linkWidget = w
self.linkItem = item
@@ -475,7 +475,7 @@ class MiniEdit( Frame ):
target = self.findItem( x, y )
dest = self.itemToWidget.get( target, None )
if ( source is None or dest is None or source == dest
or dest in source.links or source in dest.links ):
or dest in source.links or source in dest.links ):
self.releaseLink( event )
return
# For now, don't allow hosts to be directly linked
@@ -563,7 +563,9 @@ class MiniEdit( Frame ):
if 'Switch' in tags:
net.addSwitch( name )
elif 'Host' in tags:
net.addHost( name, ip=ipStr( nodeNum ) )
#Generate IP adddress in the 10.0/8 block
ipAddr = ( 10 << 24 ) + nodeNum
net.addHost( name, ip=ipStr( ipAddr ) )
else:
raise Exception( "Cannot create mystery node: " + name )
# Make links
@@ -591,7 +593,7 @@ class MiniEdit( Frame ):
cleanUpScreens()
self.net = None
def xterm( self, _=None ):
def xterm( self, _ignore=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or
self.net is None or
@@ -601,8 +603,7 @@ class MiniEdit( Frame ):
if name not in self.net.nameToNode:
return
term = makeTerm( self.net.nameToNode[ name ], 'Host' )
self.net.terms.append( term )
self.net.terms += term
def miniEditImages():
"Create and return images for MiniEdit."
+4 -7
View File
@@ -18,18 +18,13 @@ 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 ) ]
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 +
@@ -40,7 +35,7 @@ def startpings( host, targetips ):
'done &' )
print ( '*** Host %s (%s) will be pinging ips: %s' %
( host.name, host.IP(), targetips ) )
( host.name, host.IP(), targetips ) )
host.cmd( cmd )
@@ -63,6 +58,8 @@ def multiping( netsize, chunksize, seconds):
# Start pings
for subnet in subnets:
ips = [ host.IP() for host in subnet ]
#adding bogus to generate packet loss
ips.append( '10.0.0.200' )
for host in subnet:
startpings( host, ips )
+1 -1
View File
@@ -19,7 +19,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.iteritems():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
tails[ h ] = tail
fdToFile[ fd ] = tail.stdout
+1 -1
View File
@@ -22,7 +22,7 @@ if __name__ == '__main__':
info( "*** Initializing Mininet and kernel modules\n" )
OVSKernelSwitch.setup()
info( "*** Creating network\n" )
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch)
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
info( "*** Starting network\n" )
network.start()
info( "*** Running ping test\n" )
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/python
"""
Example to create a Mininet topology and connect it to the internet via NAT
through eth0 on the host.
Glen Gibb, February 2011
(slight modifications by BL, 5/13)
"""
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeNet
#################################
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
"""Start NAT/forwarding between Mininet and external network
root: node to access iptables from
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
# Identify the interface connecting to the mininet network
localIntf = root.defaultIntf()
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
root.cmd( 'iptables -P INPUT ACCEPT' )
root.cmd( 'iptables -P OUTPUT ACCEPT' )
root.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
def stopNAT( root ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
def fixNetworkManager( root, intf ):
"""Prevent network-manager from messing with our interface,
by specifying manual configuration in /etc/network/interfaces
root: a node in the root namespace (for running commands)
intf: interface name"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if ( line ) not in config:
print '*** Adding', line.strip(), 'to', cfile
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
root.cmd( 'service network-manager restart' )
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
"""Connect the network to the internet
switch: switch to connect to root namespace
rootip: address for interface in root namespace
subnet: Mininet subnet"""
switch = network.get( switch )
prefixLen = subnet.split( '/' )[ 1 ]
# Create a node in root namespace
root = Node( 'root', inNamespace=False )
# Prevent network-manager from interfering with our interface
fixNetworkManager( root, 'root-eth0' )
# Create link between root NS and switch
link = network.addLink( root, switch )
link.intf1.setIP( rootip, prefixLen )
# Start network that now includes link to root namespace
network.start()
# Start NAT and establish forwarding
startNAT( root )
# Establish routes from end hosts
for host in network.hosts:
host.cmd( 'ip route flush root 0/0' )
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
host.cmd( 'route add default gw', rootip )
return root
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
# Configure and start NATted connectivity
rootnode = connectToInternet( net )
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
CLI( net )
# Shut down NAT
stopNAT( rootnode )
net.stop()
+1 -1
View File
@@ -23,7 +23,7 @@ def pmonitorTest( N=3, seconds=10 ):
endTime = time() + seconds
for h, line in pmonitor( popens, timeoutms=500 ):
if h:
print '%s: %s' % ( h.name, line ),
print '<%s>: %s' % ( h.name, line ),
if time() >= endTime:
for p in popens.values():
p.send_signal( SIGINT )
+4 -4
View File
@@ -38,12 +38,12 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
h1intf, sintf2 = linkIntfs( h1, switch )
info( '*** Configuring control network\n' )
controller.setIP( '10.0.123.1/24', cintf )
switch.setIP( '10.0.123.2/24', sintf)
controller.setIP( '10.0.123.1/24', intf=cintf )
switch.setIP( '10.0.123.2/24', intf=sintf)
info( '*** Configuring hosts\n' )
h0.setIP( '192.168.123.1/24', h0intf )
h1.setIP( '192.168.123.2/24', h1intf )
h0.setIP( '192.168.123.1/24', intf=h0intf )
h1.setIP( '192.168.123.2/24', intf=h1intf )
info( '*** Network state:\n' )
for node in controller, switch, h0, h1:
Regular → Executable
+10 -5
View File
@@ -2,6 +2,11 @@
"""
Simple example of setting network and CPU parameters
NOTE: link params limit BW, add latency, and loss.
There is a high chance that pings WILL fail and that
iperf will hang indefinitely if the TCP handshake fails
to complete.
"""
from mininet.topo import Topo
@@ -15,14 +20,14 @@ class SingleSwitchTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
switch = self.add_switch('s1')
switch = self.addSwitch('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)
host = self.addHost('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)
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
def perfTest():
"Create network and run simple performance test"
+22 -12
View File
@@ -16,10 +16,12 @@ demonstrates:
- running server processes (sshd in this case) on hosts
"""
import sys
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node, OVSKernelSwitch
from mininet.node import Node
from mininet.topolib import TreeTopo
from mininet.link import Link
@@ -28,29 +30,33 @@ def TreeNet( depth=1, fanout=2, **kwargs ):
topo = TreeTopo( depth, fanout )
return Mininet( topo, **kwargs )
def connectToRootNS( network, switch, ip, prefixLen, routes ):
def connectToRootNS( network, switch, ip, routes ):
"""Connect hosts to root namespace via switch. Starts network.
network: Mininet() network object
switch: switch to connect to root namespace
ip: IP address for root namespace node
prefixLen: IP address prefix length (e.g. 8, 16, 24)
routes: host networks to route to"""
# Create a node in root namespace and link to switch 0
root = Node( 'root', inNamespace=False )
intf = Link( root, switch ).intf1
root.setIP( ip, prefixLen, intf )
root.setIP( ip, intf=intf )
# Start network that now includes link to root namespace
network.start()
# Add routes from root ns to hosts
for route in routes:
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
"Start a network, connect it to root ns, and run sshd on all hosts."
switch = network.switches[ 0 ] # switch to use
ip = '10.123.123.1' # our IP address on host network
routes = [ '10.0.0.0/8' ] # host networks to route to
connectToRootNS( network, switch, ip, 8, routes )
def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
ip='10.123.123.1/32', routes=None, switch=None ):
"""Start a network, connect it to root ns, and run sshd on all hosts.
ip: root-eth0 IP address in root namespace (10.123.123.1/32)
routes: Mininet host networks to route to (10.0/24)
switch: Mininet switch to connect to root namespace (s1)"""
if not switch:
switch = network[ 's1' ] # switch to use
if not routes:
routes = [ '10.0.0.0/24' ]
connectToRootNS( network, switch, ip, routes )
for host in network.hosts:
host.cmd( cmd + ' ' + opts + '&' )
print
@@ -67,5 +73,9 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4, switch=OVSKernelSwitch )
sshd( net )
net = TreeNet( depth=1, fanout=4 )
# get sshd args from the command line or use default args
# useDNS=no -u0 to avoid reverse DNS lookup timeout
opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
'-D -o UseDNS=no -u0' )
sshd( net, opts=opts )
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python
"""
Run all mininet.examples tests
-v : verbose output
-quick : skip tests that take more than ~30 seconds
"""
import unittest
import os
import sys
from mininet.util import ensureRoot
from mininet.clean import cleanup
def runTests( testDir, verbosity=1 ):
"discover and run all tests in testDir"
# ensure root and cleanup before starting tests
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
# get the directory containing example tests
testDir = os.path.dirname( os.path.realpath( __file__ ) )
verbosity = 2 if '-v' in sys.argv else 1
runTests( testDir, verbosity )
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python
"""
Tests for baresshd.py
"""
import unittest
import pexpect
from time import sleep
from mininet.clean import cleanup, sh
class testBareSSHD( unittest.TestCase ):
opts = [ '\(yes/no\)\?', 'Welcome to h1', 'refused', pexpect.EOF, pexpect.TIMEOUT ]
def connected( self ):
"Log into ssh server, check banner, then exit"
p = pexpect.spawn( 'ssh 10.0.0.1 -i /tmp/ssh/test_rsa exit' )
while True:
index = p.expect( self.opts )
if index == 0:
p.sendline( 'yes' )
elif index == 1:
return True
else:
return False
def setUp( self ):
# verify that sshd is not running
self.assertFalse( self.connected() )
# create public key pair for testing
sh( 'rm -rf /tmp/ssh' )
sh( 'mkdir /tmp/ssh' )
sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" )
sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' )
# run example with custom sshd args
cmd = ( 'python -m mininet.examples.baresshd '
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
'-o StrictModes=no' )
sh( cmd )
def testSSH( self ):
"Simple test to verify that we can ssh into h1"
result = False
# try to connect up to 3 times; sshd can take a while to start
for _ in range( 3 ):
result = self.connected()
if result:
break
else:
sleep( 1 )
self.assertTrue( result )
def tearDown( self ):
# kill the ssh process
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
cleanup()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
if __name__ == '__main__':
unittest.main()
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python
"""
Tests for bind.py
"""
import unittest
import pexpect
class testBind( unittest.TestCase ):
prompt = 'mininet>'
def setUp( self ):
self.net = pexpect.spawn( 'python -m mininet.examples.bind' )
self.net.expect( "Private Directories: \[([\w\s,'/]+)\]" )
self.directories = []
# parse directories from mn output
for d in self.net.match.group(1).split(', '):
self.directories.append( d.strip("'") )
self.net.expect( self.prompt )
self.assertTrue( len( self.directories ) > 0 )
def testCreateFile( self ):
"Create a file, a.txt, in the first private directory and verify"
fileName = 'a.txt'
directory = self.directories[ 0 ]
path = directory + '/' + fileName
self.net.sendline( 'h1 touch %s; ls %s' % ( path, directory ) )
index = self.net.expect( [ fileName, self.prompt ] )
self.assertTrue( index == 0 )
self.net.expect( self.prompt )
self.net.sendline( 'h1 rm %s' % path )
self.net.expect( self.prompt )
def testIsolation( self ):
"Create a file in two hosts and verify that contents are different"
fileName = 'b.txt'
directory = self.directories[ 0 ]
path = directory + '/' + fileName
contents = { 'h1' : '1', 'h2' : '2' }
# Verify file doesn't exist, then write private copy of file
for host in contents:
value = contents[ host ]
self.net.sendline( '%s cat %s' % ( host, path ) )
self.net.expect( 'No such file' )
self.net.expect( self.prompt )
self.net.sendline( '%s echo %s > %s' % ( host, value, path ) )
self.net.expect( self.prompt )
# Verify file contents
for host in contents:
value = contents[ host ]
self.net.sendline( '%s cat %s' % ( host, path ) )
self.net.expect( value )
self.net.expect( self.prompt )
self.net.sendline( '%s rm %s' % ( host, path ) )
self.net.expect( self.prompt )
# TODO: need more tests
def tearDown( self ):
self.net.sendline( 'exit' )
self.net.wait()
if __name__ == '__main__':
unittest.main()
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python
"""
Tests for controllers.py and controllers2.py
"""
import unittest
import pexpect
class testControllers( unittest.TestCase ):
prompt = 'mininet>'
def connectedTest( self, name, cmap ):
"Verify that switches are connected to the controller specified by cmap"
p = pexpect.spawn( 'python -m %s' % name )
p.expect( self.prompt )
# but first a simple ping test
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# verify connected controller
for switch in cmap:
p.sendline( 'sh ovs-vsctl get-controller %s' % switch )
p.expect( 'tcp:([\d.:]+)')
actual = p.match.group(1)
expected = cmap[ switch ]
self.assertEqual( actual, expected )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testControllers( self ):
c0 = '127.0.0.1:6633'
c1 = '127.0.0.1:6634'
cmap = { 's1': c0, 's2': c1, 's3': c0 }
self.connectedTest( 'mininet.examples.controllers', cmap )
def testControllers2( self ):
c0 = '127.0.0.1:6633'
c1 = '127.0.0.1:6634'
cmap = { 's1': c0, 's2': c1 }
self.connectedTest( 'mininet.examples.controllers2', cmap )
if __name__ == '__main__':
unittest.main()
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python
"""
Test for controlnet.py
"""
import unittest
import pexpect
class testControlNet( unittest.TestCase ):
prompt = 'mininet>'
def testPingall( self ):
"Simple pingall test that verifies 0% packet drop in data network"
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testFailover( self ):
"Kill controllers and verify that switch, s1, fails over properly"
count = 1
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p.expect( self.prompt )
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' )
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
ip = int( lp.match.group( 1 ) )
self.assertEqual( count, ip )
count += 1
for c in [ 'c0', 'c1' ]:
p.sendline( '%s ifconfig %s-eth0 down' % ( c, c) )
p.expect( self.prompt )
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
ip = int( lp.match.group( 1 ) )
self.assertEqual( count, ip )
count += 1
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python
"""
Test for cpu.py
"""
import unittest
import pexpect
import sys
class testCPU( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testCPU( self ):
"Verify that CPU utilization is monotonically decreasing for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)', pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
bw = float( p.match.group( 3 ) )
if sched not in scheds:
scheds.append( sched )
previous_bw = 10 ** 4 # 10 GB/s
self.assertTrue( bw < previous_bw )
previous_bw = bw
else:
break
self.assertTrue( len( scheds ) > 0 )
if __name__ == '__main__':
unittest.main()
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python
"""
Test for emptynet.py
"""
import unittest
import pexpect
class testEmptyNet( unittest.TestCase ):
prompt = 'mininet>'
def testEmptyNet( self ):
"Run simple CLI tests: pingall (verify 0% drop) and iperf (sanity)"
p = pexpect.spawn( 'python -m mininet.examples.emptynet' )
p.expect( self.prompt )
# pingall test
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# iperf test
p.sendline( 'iperf' )
p.expect( "Results: \['[\d.]+ .bits/sec', '[\d.]+ .bits/sec'\]" )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python
"""
Test for hwintf.py
"""
import unittest
import pexpect
import re
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.node import Node
from mininet.link import Link, Intf
class testHwintf( unittest.TestCase ):
prompt = 'mininet>'
def setUp( self ):
self.h3 = Node( 't0', ip='10.0.0.3/8' )
self.n0 = Node( 't1', inNamespace=False )
Link( self.h3, self.n0 )
self.h3.configDefault()
def testLocalPing( self ):
"Verify connectivity between virtual hosts using pingall"
p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testExternalPing( self ):
"Verify connnectivity between virtual host and virtual-physical 'external' host "
p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() )
p.expect( self.prompt )
# test ping external to internal
expectStr = '(\d+) packets transmitted, (\d+) received'
m = re.search( expectStr, self.h3.cmd( 'ping -v -c 1 10.0.0.1' ) )
tx = m.group( 1 )
rx = m.group( 2 )
self.assertEqual( tx, rx )
# test ping internal to external
p.sendline( 'h1 ping -c 1 10.0.0.3')
p.expect( expectStr )
tx = p.match.group( 1 )
rx = p.match.group( 2 )
self.assertEqual( tx, rx )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def tearDown( self ):
self.h3.terminate()
self.n0.terminate()
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python
"""
Test for limit.py
"""
import unittest
import pexpect
import sys
class testLimit( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testLimit( self ):
"Verify that CPU limits are within a 2% tolerance of limit for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.limit' )
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
pexpect.EOF ]
count = 0
bw = 0
tolerance = 2
while True:
index = p.expect( opts )
if index == 0:
bw = float( p.match.group( 1 ) )
count += 1
elif index == 1:
results = p.match.group( 1 )
for x in results.split( ',' ):
result = float( x )
self.assertTrue( result < bw + tolerance )
self.assertTrue( result > bw - tolerance )
else:
break
self.assertTrue( count > 0 )
if __name__ == '__main__':
unittest.main()
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python
"""
Test for linearbandwidth.py
"""
import unittest
import pexpect
import sys
class testLinearBandwidth( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testLinearBandwidth( self ):
"Verify that bandwidth is monotonically decreasing as # of hops increases"
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
count = 0
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
previous_bw = 10 ** 10 # 10 Gbits
count += 1
elif index == 1:
n = int( p.match.group( 1 ) )
bw = float( p.match.group( 2 ) )
unit = p.match.group( 3 )
if unit[ 0 ] == 'K':
bw *= 10 ** 3
elif unit[ 0 ] == 'M':
bw *= 10 ** 6
elif unit[ 0 ] == 'G':
bw *= 10 ** 9
self.assertTrue( bw < previous_bw )
previous_bw = bw
else:
break
self.assertTrue( count > 0 )
if __name__ == '__main__':
unittest.main()
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python
"""
Test for multiping.py
"""
import unittest
import pexpect
from collections import defaultdict
class testMultiPing( unittest.TestCase ):
def testMultiPing( self ):
"""Verify that each target is pinged at least once, and
that pings to 'real' targets are successful and unknown targets fail"""
p = pexpect.spawn( 'python -m mininet.examples.multiping' )
opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)",
"(h\d+): ([\d.]+) -> ([\d.]+) \d packets transmitted, (\d) received",
pexpect.EOF ]
pings = defaultdict( list )
while True:
index = p.expect( opts )
if index == 0:
name = p.match.group(1)
ip = p.match.group(2)
targets = p.match.group(3).split()
pings[ name ] += targets
elif index == 1:
name = p.match.group(1)
ip = p.match.group(2)
target = p.match.group(3)
received = int( p.match.group(4) )
if target == '10.0.0.200':
self.assertEqual( received, 0 )
else:
self.assertEqual( received, 1 )
try:
pings[ name ].remove( target )
except:
pass
else:
break
self.assertTrue( len( pings ) > 0 )
for t in pings.values():
self.assertEqual( len( t ), 0 )
if __name__ == '__main__':
unittest.main()
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python
"""
Test for multipoll.py
"""
import unittest
import pexpect
class testMultiPoll( unittest.TestCase ):
def testMultiPoll( self ):
"Verify that we receive one ping per second per host"
p = pexpect.spawn( 'python -m mininet.examples.multipoll' )
opts = [ "\*\*\* (h\d) :" ,
"(h\d+): \d+ bytes from",
"Monitoring output for (\d+) seconds",
pexpect.EOF ]
pings = {}
while True:
index = p.expect( opts )
if index == 0:
name = p.match.group( 1 )
pings[ name ] = 0
elif index == 1:
name = p.match.group( 1 )
pings[ name ] += 1
elif index == 2:
seconds = int( p.match.group( 1 ) )
else:
break
self.assertTrue( len( pings ) > 0 )
# make sure we have received at least one ping per second
for count in pings.values():
self.assertTrue( count >= seconds )
if __name__ == '__main__':
unittest.main()
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python
"""
Test for multitest.py
"""
import unittest
import pexpect
class testMultiTest( unittest.TestCase ):
prompt = 'mininet>'
def testMultiTest( self ):
"Verify pingall (0% dropped) and hX-eth0 interface for each host (ifconfig)"
p = pexpect.spawn( 'python -m mininet.examples.multitest' )
p.expect( '(\d+)% dropped' )
dropped = int( p.match.group( 1 ) )
self.assertEqual( dropped, 0 )
ifCount = 0
while True:
index = p.expect( [ 'h\d-eth0', self.prompt ] )
if index == 0:
ifCount += 1
elif index == 1:
p.sendline( 'exit' )
break
p.wait()
self.assertEqual( ifCount, 4 )
if __name__ == '__main__':
unittest.main()
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python
"""
Test for nat.py
"""
import unittest
import pexpect
from mininet.util import quietRun
destIP = '8.8.8.8' # Google DNS
class testNAT( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
'Destination IP is not reachable' )
def testNAT( self ):
"Attempt to ping an IP on the Internet and verify 0% packet loss"
p = pexpect.spawn( 'python -m mininet.examples.nat' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 %s' % destIP )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
if __name__ == '__main__':
unittest.main()
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python
"""
Test for popen.py and popenpoll.py
"""
import unittest
import pexpect
class testPopen( unittest.TestCase ):
def pingTest( self, name ):
"Verify that there are no dropped packets for each host"
p = pexpect.spawn( 'python -m %s' % name )
opts = [ "<(h\d+)>: PING ",
"<(h\d+)>: (\d+) packets transmitted, (\d+) received",
pexpect.EOF ]
pings = {}
while True:
index = p.expect( opts )
if index == 0:
name = p.match.group(1)
pings[ name ] = 0
elif index == 1:
name = p.match.group(1)
transmitted = p.match.group(2)
received = p.match.group(3)
# verify no dropped packets
self.assertEqual( received, transmitted )
pings[ name ] += 1
else:
break
self.assertTrue( len(pings) > 0 )
# verify that each host has gotten results
for count in pings.values():
self.assertEqual( count, 1 )
def testPopen( self ):
self.pingTest( 'mininet.examples.popen' )
def testPopenPoll( self ):
self.pingTest( 'mininet.examples.popenpoll' )
if __name__ == '__main__':
unittest.main()
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python
"""
Test for scratchnet.py
"""
import unittest
import pexpect
class testScratchNet( unittest.TestCase ):
opts = [ "1 packets transmitted, 1 received, 0% packet loss", pexpect.EOF ]
def pingTest( self, name ):
"Verify that no ping packets were dropped"
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts )
self.assertEqual( index, 0 )
def testPingKernel( self ):
self.pingTest( 'mininet.examples.scratchnet' )
def testPingUser( self ):
self.pingTest( 'mininet.examples.scratchnetuser' )
if __name__ == '__main__':
unittest.main()
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python
"""
Test for simpleperf.py
"""
import unittest
import pexpect
import re
import sys
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.examples.simpleperf import SingleSwitchTopo
class testSimplePerf( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testE2E( self ):
"Run the example and verify ping and iperf results"
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
# check ping results
p.expect( "Results: (\d+)% dropped", timeout=120 )
loss = int( p.match.group( 1 ) )
self.assertTrue( loss > 0 and loss < 100 )
# check iperf results
p.expect( "Results: \['([\d\.]+) .bits/sec", timeout=480 )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.wait()
def testTopo( self ):
"""Import SingleSwitchTopo from example and test connectivity between two hosts
Note: this test may fail very rarely because it is non-deterministic
i.e. links are configured with 10% packet loss, but if we get unlucky and
none or all of the packets are dropped, the test will fail"""
topo = SingleSwitchTopo( n=4 )
net = Mininet( topo=topo, host=CPULimitedHost, link=TCLink )
net.start()
h1, h4 = net.get( 'h1', 'h4' )
# have h1 ping h4 ten times
expectStr = '(\d+) packets transmitted, (\d+) received, (\d+)% packet loss'
output = h1.cmd( 'ping -c 10 %s' % h4.IP() )
m = re.search( expectStr, output )
loss = int( m.group( 3 ) )
net.stop()
self.assertTrue( loss > 0 and loss < 100 )
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python
"""
Test for sshd.py
"""
import unittest
import pexpect
from time import sleep
from mininet.clean import sh
class testSSHD( unittest.TestCase ):
opts = [ '\(yes/no\)\?', 'refused', 'Welcome|\$|#', pexpect.EOF, pexpect.TIMEOUT ]
def connected( self, ip ):
"Log into ssh server, check banner, then exit"
# Note: this test will fail if "Welcome" is not in the sshd banner
# and '#'' or '$'' are not in the prompt
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
while True:
index = p.expect( self.opts )
if index == 0:
print p.match.group(0)
p.sendline( 'yes' )
elif index == 1:
return False
elif index == 2:
p.sendline( 'exit' )
p.wait()
return True
else:
return False
def setUp( self ):
# create public key pair for testing
sh( 'rm -rf /tmp/ssh' )
sh( 'mkdir /tmp/ssh' )
sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" )
sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' )
cmd = ( 'python -m mininet.examples.sshd -D '
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
'-o StrictModes=no -o UseDNS=no -u0' )
# run example with custom sshd args
self.net = pexpect.spawn( cmd )
self.net.expect( 'mininet>' )
def testSSH( self ):
"Verify that we can ssh into all hosts (h1 to h4)"
for h in range( 1, 5 ):
self.assertTrue( self.connected( '10.0.0.%d' % h ) )
def tearDown( self ):
self.net.sendline( 'exit' )
self.net.wait()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
if __name__ == '__main__':
unittest.main()
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python
"""
Test for tree1024.py
"""
import unittest
import pexpect
import sys
class testTree1024( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testTree1024( self ):
"Run the example and do a simple ping test from h1 to h1024"
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
p.sendline( 'h1 ping -c 1 h1024' )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
if __name__ == '__main__':
unittest.main()
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python
"""
Test for treeping64.py
"""
import unittest
import pexpect
import sys
class testTreePing64( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testTreePing64( self ):
"Run the example and verify ping results"
p = pexpect.spawn( 'python -m mininet.examples.treeping64' )
p.expect( 'Tree network ping results:', timeout=6000 )
count = 0
while True:
index = p.expect( [ '(\d+)% packet loss', pexpect.EOF ] )
if index == 0:
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
count += 1
else:
break
self.assertTrue( count > 0 )
if __name__ == '__main__':
unittest.main()
+2 -2
View File
@@ -11,8 +11,8 @@ def treePing64():
results = {}
switches = { # 'reference kernel': KernelSwitch,
'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
for name in switches:
print "*** Testing", name, "datapath"
+11 -26
View File
@@ -11,35 +11,16 @@ nothing irreplaceable!
"""
from subprocess import Popen, PIPE
import time
from mininet.log import info
from mininet.term import cleanUpScreens
import os
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
CGROUPS_LOC='/mnt/cgroups'
def kill_cgroups(cgroups = None):
"""cgroups is a list of cgroup names."""
if not cgroups:
cpudir = os.path.join(CGROUPS_LOC, 'cpu')
if not os.path.exists(cpudir):
return
else:
cgroups = os.listdir(cpudir)
info( "killing cgroups: %s" % " ".join(cgroups) )
for g in cgroups:
if 'sysdefault' in g:
continue
for resource in os.listdir(CGROUPS_LOC):
cgdir = "%s/%s/%s" % (CGROUPS_LOC, resource, g)
if os.path.exists(cgdir):
sh( "sudo rmdir %s" % cgdir )
def cleanup():
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
@@ -47,16 +28,22 @@ def cleanup():
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
"\n")
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd udpbwtest'
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep(1)
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing old screen sessions\n" )
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
info( "*** Removing excess kernel datapaths\n" )
@@ -66,17 +53,15 @@ def cleanup():
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths" )
dps = sh("ovs-vsctl list-br").split( '\n' )
dps = sh("ovs-vsctl --timeout=1 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' )
links = sh( r"ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
for link in links:
if link != '':
sh( "ip link del " + link )
kill_cgroups()
info( "*** Cleanup complete.\n" )
+63 -34
View File
@@ -33,7 +33,7 @@ import sys
import time
from mininet.log import info, output, error
from mininet.term import makeTerms
from mininet.term import makeTerms, runX11
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
class CLI( Cmd ):
@@ -43,13 +43,8 @@ class CLI( Cmd ):
def __init__( self, mininet, stdin=sys.stdin, script=None ):
self.mn = mininet
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
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()
@@ -63,7 +58,7 @@ class CLI( Cmd ):
while True:
try:
# Make sure no nodes are still waiting
for node in self.nodelist:
for node in self.mn.values():
while node.waiting:
node.sendInt()
node.monitor()
@@ -78,6 +73,11 @@ class CLI( Cmd ):
"Don't repeat last command when you hit return."
pass
def getLocals( self ):
"Local variable bindings for py command"
self.locals.update( self.mn )
return self.locals
# 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
@@ -110,25 +110,25 @@ class CLI( Cmd ):
def do_nodes( self, _line ):
"List all nodes."
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
nodes = ' '.join( sorted( self.mn ) )
output( 'available nodes are: \n%s\n' % nodes )
def do_net( self, _line ):
"List network connections."
dumpNodeConnections( self.nodelist )
dumpNodeConnections( self.mn.values() )
def do_sh( self, line ):
"Run an external shell command"
call( line, shell=True )
# do_py() needs to catch any exception during eval()
# do_py() and do_px() need to catch any exception during eval()/exec()
# pylint: disable-msg=W0703
def do_py( self, line ):
"""Evaluate a Python expression.
Node names may be used, e.g.: h1.cmd('ls')"""
Node names may be used, e.g.: py h1.cmd('ls')"""
try:
result = eval( line, globals(), self.locals )
result = eval( line, globals(), self.getLocals() )
if not result:
return
elif isinstance( result, str ):
@@ -138,7 +138,18 @@ class CLI( Cmd ):
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable-msg=W0703
# We are in fact using the exec() pseudo-function
# pylint: disable-msg=W0122
def do_px( self, line ):
"""Execute a Python statement.
Node names may be used, e.g.: px print h1.cmd('ls')"""
try:
exec( line, globals(), self.getLocals() )
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable-msg=W0703,W0122
def do_pingall( self, _line ):
"Ping between all hosts."
@@ -148,6 +159,14 @@ class CLI( Cmd ):
"Ping between first two hosts, useful for testing."
self.mn.pingPair()
def do_pingallfull( self, _line ):
"Ping between first two hosts, returns all ping results."
self.mn.pingAllFull()
def do_pingpairfull( self, _line ):
"Ping between first two hosts, returns all ping results."
self.mn.pingPairFull()
def do_iperf( self, line ):
"Simple iperf TCP test between two (optionally specified) hosts."
args = line.split()
@@ -157,11 +176,11 @@ class CLI( Cmd ):
hosts = []
err = False
for arg in args:
if arg not in self.nodemap:
if arg not in self.mn:
err = True
error( "node '%s' not in network\n" % arg )
else:
hosts.append( self.nodemap[ arg ] )
hosts.append( self.mn[ arg ] )
if not err:
self.mn.iperf( hosts )
else:
@@ -177,11 +196,11 @@ class CLI( Cmd ):
hosts = []
err = False
for arg in args[ 1:3 ]:
if arg not in self.nodemap:
if arg not in self.mn:
err = True
error( "node '%s' not in network\n" % arg )
else:
hosts.append( self.nodemap[ arg ] )
hosts.append( self.mn[ arg ] )
if not err:
self.mn.iperf( hosts, l4Type='UDP', udpBw=udpBw )
else:
@@ -190,13 +209,13 @@ class CLI( Cmd ):
def do_intfs( self, _line ):
"List interfaces."
for node in self.nodelist:
for node in self.mn.values():
output( '%s: %s\n' %
( node.name, ','.join( node.intfNames() ) ) )
( node.name, ','.join( node.intfNames() ) ) )
def do_dump( self, _line ):
"Dump node info."
for node in self.nodelist:
for node in self.mn.values():
output( '%s\n' % repr( node ) )
def do_link( self, line ):
@@ -216,12 +235,23 @@ class CLI( Cmd ):
error( 'usage: %s node1 node2 ...\n' % term )
else:
for arg in args:
if arg not in self.nodemap:
if arg not in self.mn:
error( "node '%s' not in network\n" % arg )
else:
node = self.nodemap[ arg ]
node = self.mn[ arg ]
self.mn.terms += makeTerms( [ node ], term = term )
def do_x( self, line ):
"""Create an X11 tunnel to the given node,
optionally starting a client."""
args = line.split()
if not args:
error( 'usage: x node [cmd args]...\n' )
else:
node = self.mn[ args[ 0 ] ]
cmd = args[ 1: ]
self.mn.terms += runX11( node, cmd )
def do_gterm( self, line ):
"Spawn gnome-terminal(s) for the given node(s)."
self.do_xterm( line, term='gterm' )
@@ -270,7 +300,7 @@ class CLI( Cmd ):
self.inputFile = None
def do_dpctl( self, line ):
"Run dpctl command on all switches."
"Run dpctl (or ovs-ofctl) command on all switches."
args = line.split()
if len(args) < 1:
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
@@ -293,25 +323,24 @@ 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( ' ' )
if first in self.nodemap:
node = self.nodemap[ first ]
if first in self.mn:
if not args:
print "*** Enter a command for node: %s <cmd>" % first
return
node = self.mn[ first ]
rest = args.split( ' ' )
# Substitute IP addresses for node names in command
rest = [ self.nodemap[ arg ].IP()
if arg in self.nodemap else arg
for arg in rest ]
rest = [ self.mn[ arg ].defaultIntf().updateIP()
if arg in self.mn else arg
for arg in rest ]
rest = ' '.join( rest )
# Run cmd on node:
builtin = isShellBuiltin( first )
node.sendCmd( rest, printPid=( not builtin ) )
self.waitForNode( node )
else:
error( '*** Unknown command: %s\n' % first )
error( '*** Unknown command: %s\n' % line )
# pylint: enable-msg=R0201
+1
View File
@@ -0,0 +1 @@
../examples
+49 -39
View File
@@ -25,8 +25,7 @@ Link: basic link class for creating veth pairs
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
from time import sleep
from mininet.util import makeIntfPair, quietRun
import re
class Intf( object ):
@@ -162,8 +161,9 @@ class Intf( object ):
def delete( self ):
"Delete interface"
self.cmd( 'ip link del ' + self.name )
# Does it help to sleep to let things run?
sleep( 0.001 )
if self.node.inNamespace:
# Link may have been dumped into root NS
quietRun( 'ip link del ' + self.name )
def __repr__( self ):
return '<%s %s>' % ( self.__class__.__name__, self.name )
@@ -178,13 +178,13 @@ class TCIntf( Intf ):
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 ):
latency_ms=None, enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > 10000 ):
warn( 'Bandwidth', bw, 'is outside range 0..10000 Mbps\n' )
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...
@@ -196,58 +196,63 @@ class TCIntf( Intf ):
# 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 '
cmds += [ '%s qdisc add dev %s root handle 5:0 hfsc default 1',
'%s class add dev %s parent 5:0 classid 5: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 ) ]
if latency_ms is None:
latency_ms = 15 * 8 / bw
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
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 '
cmds += [ '%s qdisc add dev %s root handle 5:0 htb default 1',
'%s class add dev %s parent 5:0 classid 5:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 5:1 '
# ECN or RED
if enable_ecn:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'handle 6: red limit 1000000 ' +
'min 30000 max 35000 avpkt 1500 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1 ecn' % bw ]
parent = ' parent 10: '
parent = ' parent 6: '
elif enable_red:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'handle 6: red limit 1000000 ' +
'min 30000 max 35000 avpkt 1500 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1' % bw ]
parent = ' parent 10: '
parent = ' parent 6: '
return cmds, parent
@staticmethod
def delayCmds( parent, delay=None, loss=None,
max_queue_size=None ):
def delayCmds( parent, delay=None, jitter=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 jitter and jitter < 0:
error( 'Negative jitter', jitter, '\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/jitter/loss/max queue size
netemargs = '%s%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'%s ' % jitter if jitter 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 '' )
else '' )
if netemargs:
cmds = [ '%s qdisc add dev %s ' + parent +
' handle 10: netem ' +
netemargs ]
return cmds
netemargs ]
parent = ' parent 10:1 '
return cmds, parent
def tc( self, cmd, tc='tc' ):
"Execute tc command for our interface"
@@ -255,9 +260,10 @@ class TCIntf( Intf ):
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 ):
def config( self, bw=None, delay=None, jitter=None, loss=None,
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False,
max_queue_size=None, **params ):
"Configure the port and set its properties."
result = Intf.config( self, **params)
@@ -277,21 +283,24 @@ class TCIntf( Intf ):
# 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 )
use_hfsc=use_hfsc, use_tbf=use_tbf,
latency_ms=latency_ms,
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 )
# Delay/jitter/loss/max_queue_size using netem
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter,
loss=loss, max_queue_size=max_queue_size,
parent=parent )
cmds += delaycmds
# Ugly but functional: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
@@ -301,6 +310,7 @@ class TCIntf( Intf ):
debug( "cmds:", cmds, '\n' )
debug( "outputs:", tcoutputs, '\n' )
result[ 'tcoutputs'] = tcoutputs
result[ 'parent' ] = parent
return result
+14 -16
View File
@@ -11,11 +11,11 @@ import types
OUTPUT = 25
LEVELS = { 'debug': logging.DEBUG,
'info': logging.INFO,
'output': OUTPUT,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL }
'info': logging.INFO,
'output': OUTPUT,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL }
# change this to logging.INFO to get printouts when running unit tests
LOGLEVELDEFAULT = OUTPUT
@@ -60,18 +60,16 @@ class Singleton( type ):
See http://en.wikipedia.org/wiki/SingletonPattern#Python
Intended to be used as a __metaclass_ param, as shown for the class
below.
below."""
Changed cls first args to mcs to satisfy pylint."""
def __init__( cls, name, bases, dict_ ):
super( Singleton, cls ).__init__( name, bases, dict_ )
cls.instance = None
def __init__( mcs, name, bases, dict_ ):
super( Singleton, mcs ).__init__( name, bases, dict_ )
mcs.instance = None
def __call__( mcs, *args, **kw ):
if mcs.instance is None:
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
return mcs.instance
def __call__( cls, *args, **kw ):
if cls.instance is None:
cls.instance = super( Singleton, cls ).__call__( *args, **kw )
return cls.instance
class MininetLogger( Logger, object ):
@@ -117,7 +115,7 @@ class MininetLogger( Logger, object ):
Convenience function to support lowercase names.
levelName: level name from LEVELS"""
level = LOGLEVELDEFAULT
if levelname != None:
if levelname is not None:
if levelname not in LEVELS:
raise Exception( 'unknown levelname seen in setLogLevel' )
else:
+5 -5
View File
@@ -19,7 +19,7 @@ def modprobe( mod ):
return quietRun( [ 'modprobe', mod ] )
OF_KMOD = 'ofdatapath'
OVS_KMOD = 'openvswitch_mod'
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
TUN = 'tun'
def moduleDeps( subtract=None, add=None ):
@@ -48,8 +48,8 @@ def moduleDeps( subtract=None, add=None ):
modprobeOutput = modprobe( mod )
if modprobeOutput:
error( 'Error inserting ' + mod +
' - is it installed and available via modprobe?\n' +
'Error was: "%s"\n' % modprobeOutput )
' - is it installed and available via modprobe?\n' +
'Error was: "%s"\n' % modprobeOutput )
if mod not in lsmod():
error( 'Failed to insert ' + mod + ' - quitting.\n' )
exit( 1 )
@@ -63,6 +63,6 @@ def pathCheck( *args, **kwargs ):
for arg in args:
if not quietRun( 'which ' + arg ):
error( 'Cannot find required executable %s.\n' % arg +
'Please make sure that %s is installed ' % moduleName +
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
'Please make sure that %s is installed ' % moduleName +
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
exit( 1 )
+198 -33
View File
@@ -91,24 +91,28 @@ import re
import select
import signal
from time import sleep
from itertools import chain
from mininet.cli import CLI
from mininet.log import info, error, debug, output
from mininet.node import Host, OVSKernelSwitch, Controller
from mininet.link import Link, Intf
from mininet.util import quietRun, fixLimits, numCores
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.1.0"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
controller=Controller, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None ):
controller=Controller, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None ):
"""Create Mininet object.
topo: Topo (topology) object or None
switch: default Switch class
@@ -205,16 +209,26 @@ class Mininet( object ):
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
# Get controller class
if not controller:
controller = self.controller
controller_new = controller( name, **params )
# Construct new controller if one is not given
if isinstance(name, Controller):
controller_new = name
# Pylint thinks controller is a str()
# pylint: disable=E1103
name = controller_new.name
# pylint: enable=E1103
else:
controller_new = controller( name, **params )
# Add new controller to net
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
return controller_new
# BL: is this better than just using nameToNode[] ?
# Should it have a better name?
# BL: We now have four ways to look up nodes
# This may (should?) be cleaned up in the future.
def getNodeByName( self, *args ):
"Return node(s) with given name(s)"
if len( args ) == 1:
@@ -225,6 +239,37 @@ class Mininet( object ):
"Convenience alias for getNodeByName"
return self.getNodeByName( *args )
# Even more convenient syntax for node lookup and iteration
def __getitem__( self, key ):
"""net [ name ] operator: Return node(s) with given name(s)"""
return self.nameToNode[ key ]
def __iter__( self ):
"return iterator over node names"
for node in chain( self.hosts, self.switches, self.controllers ):
yield node.name
def __len__( self ):
"returns number of nodes in net"
return ( len( self.hosts ) + len( self.switches ) +
len( self.controllers ) )
def __contains__( self, item ):
"returns True if net contains named node"
return item in self.nameToNode
def keys( self ):
"return a list of all node names or net's keys"
return list( self )
def values( self ):
"return a list of all nodes or net's values"
return [ self[name] for name in self ]
def items( self ):
"return (key,value) tuple list for every node in net"
return zip( self.keys(), self.values() )
def addLink( self, node1, node2, port1=None, port2=None,
cls=None, **params ):
""""Add a link from node1 to node2
@@ -247,7 +292,7 @@ class Mininet( object ):
info( host.name + ' ' )
intf = host.defaultIntf()
if intf:
host.configDefault( defaultRoute=intf )
host.configDefault()
else:
# Don't configure nonexistent intf
host.configDefault( ip=None, mac=None )
@@ -272,10 +317,14 @@ class Mininet( object ):
info( '*** Creating network\n' )
if not self.controllers:
if not self.controllers and self.controller:
# Add a default controller
info( '*** Adding controller\n' )
self.addController( 'c0' )
classes = self.controller
if type( classes ) is not list:
classes = [ classes ]
for i, cls in enumerate( classes ):
self.addController( 'c%d' % i, cls )
info( '*** Adding hosts:\n' )
for hostName in topo.hosts():
@@ -300,7 +349,7 @@ class Mininet( object ):
def configureControlNetwork( self ):
"Control net config hook: override in subclass"
raise Exception( 'configureControlNetwork: '
'should be overriden in subclass', self )
'should be overriden in subclass', self )
def build( self ):
"Build mininet."
@@ -318,6 +367,9 @@ class Mininet( object ):
def startTerms( self ):
"Start a terminal for each node."
if 'DISPLAY' not in os.environ:
error( "Error starting terms: Cannot connect to display\n" )
return
info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
cleanUpScreens()
self.terms += makeTerms( self.controllers, 'controller' )
@@ -355,16 +407,16 @@ class Mininet( object ):
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( host.name + ' ' )
host.terminate()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ' )
switch.stop()
info( '\n' )
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( host.name + ' ' )
host.terminate()
info( '\n' )
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
info( controller.name + ' ' )
@@ -414,16 +466,17 @@ class Mininet( object ):
return (1, 0)
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m == None:
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
pingOutput )
return (1, 0)
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
return sent, received
def ping( self, hosts=None ):
def ping( self, hosts=None, timeout=None ):
"""Ping between all specified hosts.
hosts: list of hosts
timeout: time to wait for a response, as string
returns: ploss packet loss percentage"""
# should we check if running?
packets = 0
@@ -436,7 +489,10 @@ class Mininet( object ):
output( '%s -> ' % node.name )
for dest in hosts:
if node != dest:
result = node.cmd( 'ping -c1 ' + dest.IP() )
opts = ''
if timeout:
opts = '-W %s' % timeout
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
sent, received = self._parsePing( result )
packets += sent
if received > sent:
@@ -447,11 +503,78 @@ class Mininet( object ):
lost += sent - received
output( ( '%s ' % dest.name ) if received else 'X ' )
output( '\n' )
if packets > 0:
ploss = 100 * lost / packets
output( "*** Results: %i%% dropped (%d/%d lost)\n" %
( ploss, lost, packets ) )
received = packets - lost
output( "*** Results: %i%% dropped (%d/%d received)\n" %
( ploss, received, packets ) )
else:
ploss = 0
output( "*** Warning: No packets sent\n" )
return ploss
@staticmethod
def _parsePingFull( pingOutput ):
"Parse ping output and return all data."
errorTuple = (1, 0, 0, 0, 0, 0)
# Check for downed link
r = r'[uU]nreachable'
m = re.search( r, pingOutput )
if m is not None:
return errorTuple
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return errorTuple
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
r = r'rtt min/avg/max/mdev = '
r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return errorTuple
rttmin = float( m.group( 1 ) )
rttavg = float( m.group( 2 ) )
rttmax = float( m.group( 3 ) )
rttdev = float( m.group( 4 ) )
return sent, received, rttmin, rttavg, rttmax, rttdev
def pingFull( self, hosts=None, timeout=None ):
"""Ping between all specified hosts and return all data.
hosts: list of hosts
timeout: time to wait for a response, as string
returns: all ping data; see function body."""
# should we check if running?
# Each value is a tuple: (src, dsd, [all ping outputs])
all_outputs = []
if not hosts:
hosts = self.hosts
output( '*** Ping: testing ping reachability\n' )
for node in hosts:
output( '%s -> ' % node.name )
for dest in hosts:
if node != dest:
opts = ''
if timeout:
opts = '-W %s' % timeout
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
outputs = self._parsePingFull( result )
sent, received, rttmin, rttavg, rttmax, rttdev = outputs
all_outputs.append( (node, dest, outputs) )
output( ( '%s ' % dest.name ) if received else 'X ' )
output( '\n' )
output( "*** Results: \n" )
for outputs in all_outputs:
src, dest, ping_outputs = outputs
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
(rttmin, rttavg, rttmax, rttdev) )
return all_outputs
def pingAll( self ):
"""Ping between all hosts.
returns: ploss packet loss percentage"""
@@ -463,6 +586,17 @@ class Mininet( object ):
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
return self.ping( hosts=hosts )
def pingAllFull( self ):
"""Ping between all hosts.
returns: ploss packet loss percentage"""
return self.pingFull()
def pingPairFull( self ):
"""Ping between first two hosts, useful for testing.
returns: ploss packet loss percentage"""
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
return self.pingFull( hosts=hosts )
@staticmethod
def _parseIperf( iperfOutput ):
"""Parse iperf output and return bandwidth.
@@ -508,11 +642,11 @@ class Mininet( object ):
servout += server.monitor()
if l4Type == 'TCP':
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
'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 )
bwArgs )
debug( 'Client output: %s\n' % cliout )
server.sendInt()
servout += server.waitOutput()
@@ -523,6 +657,42 @@ class Mininet( object ):
output( '*** Results: %s\n' % result )
return result
def runCpuLimitTest( self, cpu, duration=5 ):
"""run CPU limit test with 'while true' processes.
cpu: desired CPU fraction of each host
duration: test duration in seconds
returns a single list of measured CPU fractions as floats.
"""
pct = cpu * 100
info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
hosts = self.hosts
for h in hosts:
h.cmd( 'while true; do a=1; done &' )
pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
pids_str = ",".join(["%s" % pid for pid in pids])
cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
# It's a shame that this is what pylint prefers
outputs = []
for _ in range( duration ):
sleep( 1 )
outputs.append( quietRun( cmd ).strip() )
for h in hosts:
h.cmd( 'kill %1' )
cpu_fractions = []
for test_output in outputs:
# Split by line. Ignore first line, which looks like this:
# PID %CPU COMMAND\n
for line in test_output.split('\n')[1:]:
r = r'\d+\s*(\d+\.\d+)'
m = re.search( r, line )
if m is None:
error( '*** Error: could not extract CPU fraction: %s\n' %
line )
return None
cpu_fractions.append( float( m.group( 1 ) ) )
output( '*** Results: %s\n' % cpu_fractions )
return cpu_fractions
# BL: I think this can be rewritten now that we have
# a real link class.
def configLinkStatus( self, src, dst, status ):
@@ -564,12 +734,7 @@ class Mininet( object ):
"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 )
ensureRoot()
fixLimits()
cls.inited = True
@@ -610,7 +775,7 @@ class MininetWithControlNet( Mininet ):
# in the control network location.
def configureRoutedControlNetwork( self, ip='192.168.123.1',
prefixLen=16 ):
prefixLen=16 ):
"""Configure a routed control network on controller and switches.
For use with the user datapath only right now."""
controller = self.controllers[ 0 ]
+267 -115
View File
@@ -49,15 +49,14 @@ import re
import signal
import select
from subprocess import Popen, PIPE, STDOUT
import time
import subprocess
from operator import or_
from time import sleep
from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
numCores, retry, mountCgroups, errFailTemp )
numCores, retry, mountCgroups )
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
from mininet.link import Link, Intf, TCIntf
from mininet.clean import kill_cgroups
class Node( object ):
"""A virtual network node is simply a shell in a network namespace.
@@ -86,8 +85,8 @@ class Node( object ):
# Make pylint happy
( self.shell, self.execed, self.pid, self.stdin, self.stdout,
self.lastPid, self.lastCmd, self.pollOut ) = (
None, None, None, None, None, None, None, None )
self.lastPid, self.lastCmd, self.pollOut ) = (
None, None, None, None, None, None, None, None )
self.waiting = False
self.readbuf = ''
@@ -121,9 +120,10 @@ class Node( object ):
if self.inNamespace:
opts += 'n'
# bash -m: enable job control
cmd = [ 'mnexec', opts, 'bash', '-m' ]
# -s: pass $* to shell, and make process easy to find in ps
cmd = [ 'mnexec', opts, 'bash', '-ms', 'mininet:' + self.name ]
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
close_fds=True )
close_fds=True )
self.stdin = self.shell.stdin
self.stdout = self.shell.stdout
self.pid = self.shell.pid
@@ -142,12 +142,14 @@ class Node( object ):
def cleanup( self ):
"Help python collect its garbage."
if not self.inNamespace:
for intfName in self.intfNames():
if self.name in intfName:
quietRun( 'ip link del ' + intfName )
# Intfs may end up in root NS
for intfName in self.intfNames():
if self.name in intfName:
quietRun( 'ip link del ' + intfName )
self.shell = None
# Subshell I/O, commands and control
def read( self, maxbytes=1024 ):
"""Buffered read from node, non-blocking.
maxbytes: maximum number of bytes to return"""
@@ -170,7 +172,7 @@ class Node( object ):
if '\n' not in self.readbuf:
return None
pos = self.readbuf.find( '\n' )
line = self.readbuf[ 0 : pos ]
line = self.readbuf[ 0: pos ]
self.readbuf = self.readbuf[ pos + 1: ]
return line
@@ -181,7 +183,8 @@ class Node( object ):
def terminate( self ):
"Send kill signal to Node and clean up after it."
os.kill( self.pid, signal.SIGKILL )
if self.shell:
os.kill( self.pid, signal.SIGKILL )
self.cleanup()
def stop( self ):
@@ -291,7 +294,7 @@ class Node( object ):
kwargs: Popen() keyword args"""
defaults = { 'stdout': PIPE, 'stderr': PIPE,
'mncmd':
[ 'mnexec', '-a', str( self.pid ) ] }
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
if len( args ) == 1:
if type( args[ 0 ] ) is list:
@@ -358,17 +361,23 @@ class Node( object ):
return self.intfs[ min( ports ) ]
else:
warn( '*** defaultIntf: warning:', self.name,
'has no interfaces\n' )
'has no interfaces\n' )
def intf( self, intf='' ):
"""Return our interface object with given name,
or default intf if name is empty"""
"""Return our interface object with given string name,
default intf if name is falsy (None, empty string, etc).
or the input intf arg.
Having this fcn return its arg for Intf objects makes it
easier to construct functions with flexible input args for
interfaces (those that accept both string names and Intf objects).
"""
if not intf:
return self.defaultIntf()
elif type( intf) is str:
elif type( intf ) is str:
return self.nameToIntf[ intf ]
else:
return None
return intf
def connectionsTo( self, node):
"Return [ intf1, intf2... ] for all intfs that connect self to node."
@@ -384,16 +393,19 @@ class Node( object ):
connections += [ ( intf, link.intf1 ) ]
return connections
def deleteIntfs( self ):
"Delete all of our interfaces."
def deleteIntfs( self, checkName=True ):
"""Delete all of our interfaces.
checkName: only delete interfaces that contain our name"""
# In theory the interfaces should go away after we shut down.
# However, this takes time, so we're better off removing them
# explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow,
# at least with Linux kernels before 2.6.33
for intf in self.intfs.values():
intf.delete()
info( '.' )
# Protect against deleting hardware interfaces
if ( self.name in intf.name ) or ( not checkName ):
intf.delete()
info( '.' )
# Routing support
@@ -412,11 +424,14 @@ class Node( object ):
def setDefaultRoute( self, intf=None ):
"""Set the default route to go through intf.
intf: string, interface name"""
if not intf:
intf = self.defaultIntf()
self.cmd( 'ip route flush root 0/0' )
return self.cmd( 'route add default %s' % intf )
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
# Note setParam won't call us if intf is none
if type( intf ) is str and ' ' in intf:
params = intf
else:
params = 'dev %s' % intf
self.cmd( 'ip route del default' )
return self.cmd( 'ip route add default', params )
# Convenience and configuration methods
@@ -428,7 +443,7 @@ class Node( object ):
def setIP( self, ip, prefixLen=8, intf=None ):
"""Set the IP address for an interface.
intf: interface name
intf: intf or intf name
ip: IP address as a string
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
# This should probably be rethought
@@ -442,7 +457,7 @@ class Node( object ):
def MAC( self, intf=None ):
"Return MAC address of a node or specific interface."
return self.intf( intf ).IP()
return self.intf( intf ).MAC()
def intfIsUp( self, intf=None ):
"Check if an interface is up."
@@ -487,7 +502,7 @@ class Node( object ):
r = {}
self.setParam( r, 'setMAC', mac=mac )
self.setParam( r, 'setIP', ip=ip )
self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute )
# This should be examined
self.cmd( 'ifconfig lo ' + lo )
return r
@@ -516,9 +531,9 @@ class Node( object ):
def __repr__( self ):
"More informative string representation"
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
for i in self.intfList() ] ) )
for i in self.intfList() ] ) )
return '<%s %s: %s pid=%s> ' % (
self.__class__.__name__, self.name, intfs, self.pid )
self.__class__.__name__, self.name, intfs, self.pid )
def __str__( self ):
"Abbreviated string representation"
@@ -559,18 +574,10 @@ class CPULimitedHost( Host ):
CPULimitedHost.init()
# Create a cgroup and move shell into it
self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name
cmd_to_run = 'cgcreate -g ' + self.cgroup
error("creating group using command: %s\n" % cmd_to_run)
errFail( cmd_to_run )
errFail( 'cgcreate -g ' + self.cgroup )
# We don't add ourselves to a cpuset because you must
# specify the cpu and memory placement first
#self.pid = 12345
#error("nap time!")
time.sleep(0.1)
cmd_to_run = 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid )
errFailTemp( cmd_to_run )
#error("nap time over.")
#exit
errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
# BL: Setting the correct period/quota is tricky, particularly
# for RT. RT allows very small quotas, but the overhead
# seems to be high. CFS has a mininimum quota of 1 ms, but
@@ -584,7 +591,7 @@ class CPULimitedHost( Host ):
cmd = 'cgset -r %s.%s=%s /%s' % (
resource, param, value, self.name )
quietRun( cmd )
nvalue = int( self.cgroupGet2( param, resource ) )
nvalue = int( self.cgroupGet( param, resource ) )
if nvalue != value:
error( '*** error: cgroupSet: %s set to %s instead of %s\n'
% ( param, nvalue, value ) )
@@ -594,49 +601,28 @@ class CPULimitedHost( Host ):
"Return value of cgroup parameter"
cmd = 'cgget -r %s.%s /%s' % (
resource, param, self.name )
cmd_output = quietRun( cmd )
return int( cmd_output.split()[ -1 ] )
def cgroupGet2( self, param, resource='cpu' ):
"Return value of cgroup parameter"
# TEMP: cgget not in 10.04 cgroup-bin. Oops!
# Can get same stuff by reading files.
filename = resource + '.' + param
filepath = os.path.join("/mnt/cgroups/", resource, self.name, filename)
#cmd = 'cgget -r %s.%s /%s' % (
# resource, param, self.name )
if not os.path.exists(filepath):
raise Exception("%s not found." % filename)
# TEMP below
f = open(filepath)
cmd_output = f.readlines()[0]
f.close()
#cmd_output = subprocess.check_output(cmd, shell=True)
#quietRun( cmd )
return int( cmd_output.split()[ -1 ] )
return int( quietRun( cmd ).split()[ -1 ] )
def cgroupDel( self ):
"Clean up our cgroup"
# info( '*** deleting cgroup', self.cgroup, '\n' )
# TEMP out
kill_cgroups([self.name])
#_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
#return exitcode != 0
return True
_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
return exitcode != 0
def popen( self, *args, **kwargs ):
"""Return a Popen() object in node's namespace
args: Popen() args, single list, or string
kwargs: Popen() keyword args"""
# Tell mnexec to execute command in our cgroup
mncmd = [ 'mnexec', '-a', str( self.pid ),
mncmd = [ 'mnexec', '-da', str( self.pid ),
'-g', self.name ]
if self.sched == 'rt':
mncmd += [ '-r', str( self.rtprio ) ]
return Host.popen( self, *args, mncmd=mncmd, **kwargs )
def cleanup( self ):
"Clean up our cgroup"
"Clean up Node, then clean up our cgroup"
super( CPULimitedHost, self ).cleanup()
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
def chrt( self ):
@@ -716,7 +702,7 @@ class CPULimitedHost( Host ):
# We have to do this here after we've specified
# cpus and mems
errFail( 'cgclassify -g cpuset:/%s %s' % (
self.name, self.pid ) )
self.name, self.pid ) )
def config( self, cpu=None, cores=None, **params ):
"""cpu: desired overall system CPU fraction
@@ -779,7 +765,7 @@ class Switch( Node ):
def defaultDpid( self ):
"Derive dpid from switch name, s1 -> 1"
try:
dpid = int( re.findall( '\d+', self.name )[ 0 ] )
dpid = int( re.findall( r'\d+', self.name )[ 0 ] )
dpid = hex( dpid )[ 2: ]
dpid = '0' * ( self.dpidLen - len( dpid ) ) + dpid
return dpid
@@ -803,28 +789,35 @@ class Switch( Node ):
return Node.sendCmd( self, *cmd, **kwargs )
else:
error( '*** Error: %s has execed and cannot accept commands' %
self.name )
self.name )
def connected( self ):
"Is the switch connected to a controller? (override this method)"
return False and self # satisfy pylint
def __repr__( self ):
"More informative string representation"
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
for i in self.intfList() ] ) )
for i in self.intfList() ] ) )
return '<%s %s: %s pid=%s> ' % (
self.__class__.__name__, self.name, intfs, self.pid )
self.__class__.__name__, self.name, intfs, self.pid )
class UserSwitch( Switch ):
"User-space switch."
dpidLen = 12
def __init__( self, name, **kwargs ):
def __init__( self, name, dpopts='--no-slicing', **kwargs ):
"""Init.
name: name for the switch"""
name: name for the switch
dpopts: additional arguments to ofdatapath (--no-slicing)"""
Switch.__init__( self, name, **kwargs )
pathCheck( 'ofdatapath', 'ofprotocol',
moduleName='the OpenFlow reference user switch (openflow.org)' )
moduleName='the OpenFlow reference user switch' +
'(openflow.org)' )
if self.listenPort:
self.opts += ' --listen=ptcp:%i ' % self.listenPort
self.dpopts = dpopts
@classmethod
def setup( cls ):
@@ -839,23 +832,57 @@ class UserSwitch( Switch ):
return self.cmd( 'dpctl ' + ' '.join( args ) +
' tcp:127.0.0.1:%i' % self.listenPort )
def connected( self ):
"Is the switch connected to a controller?"
return 'remote.is-connected=true' in self.dpctl( 'status' )
@staticmethod
def TCReapply( intf ):
"""Unfortunately user switch and Mininet are fighting
over tc queuing disciplines. To resolve the conflict,
we re-create the user switch's configuration, but as a
leaf of the TCIntf-created configuration."""
if type( intf ) is TCIntf:
ifspeed = 10000000000 # 10 Gbps
minspeed = ifspeed * 0.001
res = intf.config( **intf.params )
parent = res['parent']
# Re-add qdisc, root, and default classes user switch created, but
# with new parent, as setup by Mininet's TCIntf
intf.tc( "%s qdisc add dev %s " + parent +
" handle 1: htb default 0xfffe" )
intf.tc( "%s class add dev %s classid 1:0xffff parent 1: htb rate "
+ str(ifspeed) )
intf.tc( "%s class add dev %s classid 1:0xfffe parent 1:0xffff " +
"htb rate " + str(minspeed) + " ceil " + str(ifspeed) )
def start( self, controllers ):
"""Start OpenFlow reference user datapath.
Log to /tmp/sN-{ofd,ofp}.log.
controllers: list of controller objects"""
controller = controllers[ 0 ]
# Add controllers
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
for c in controllers ] )
ofdlog = '/tmp/' + self.name + '-ofd.log'
ofplog = '/tmp/' + self.name + '-ofp.log'
self.cmd( 'ifconfig lo up' )
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
' punix:/tmp/' + self.name + ' -d ' + self.dpid +
' --no-slicing ' +
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
' punix:/tmp/' + self.name + ' -d %s ' % self.dpid +
self.dpopts +
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
' --fail=closed ' + self.opts +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
' ' + clist +
' --fail=closed ' + self.opts +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
if "no-slicing" not in self.dpopts:
# Only TCReapply if slicing is enable
sleep(1) # Allow ofdatapath to start before re-arranging qdisc's
for intf in self.intfList():
if not intf.IP():
self.TCReapply( intf )
def stop( self ):
"Stop OpenFlow reference user datapath."
@@ -878,14 +905,14 @@ class OVSLegacyKernelSwitch( Switch ):
self.intf = self.dp
if self.inNamespace:
error( "OVSKernelSwitch currently only works"
" in the root namespace.\n" )
" in the root namespace.\n" )
exit( 1 )
@classmethod
def setup( cls ):
"Ensure any dependencies are loaded; if not, try to load them."
pathCheck( 'ovs-dpctl', 'ovs-openflowd',
moduleName='Open vSwitch (openvswitch.org)')
moduleName='Open vSwitch (openvswitch.org)')
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
def start( self, controllers ):
@@ -899,12 +926,13 @@ class OVSLegacyKernelSwitch( Switch ):
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
# Run protocol daemon
controller = controllers[ 0 ]
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
for c in controllers ] )
self.cmd( 'ovs-openflowd ' + self.dp +
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
' --fail=secure ' + self.opts +
' --datapath-id=' + self.dpid +
' 1>' + ofplog + ' 2>' + ofplog + '&' )
' ' + clist +
' --fail=secure ' + self.opts +
' --datapath-id=' + self.dpid +
' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False
def stop( self ):
@@ -917,19 +945,23 @@ class OVSLegacyKernelSwitch( Switch ):
class OVSSwitch( Switch ):
"Open vSwitch switch. Depends on ovs-vsctl."
def __init__( self, name, failMode='secure', **params ):
def __init__( self, name, failMode='secure', datapath='kernel', **params ):
"""Init.
name: name for switch
failMode: controller loss behavior (secure|open)"""
failMode: controller loss behavior (secure|open)
datapath: userspace or kernel mode (kernel|user)"""
Switch.__init__( self, name, **params )
self.failMode = failMode
self.datapath = datapath
@classmethod
def setup( cls ):
"Make sure Open vSwitch is installed and working"
pathCheck( 'ovs-vsctl',
moduleName='Open vSwitch (openvswitch.org)')
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
moduleName='Open vSwitch (openvswitch.org)')
# This should no longer be needed, and it breaks
# with OVS 1.7 which has renamed the kernel module:
# moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
if exitcode:
error( out + err +
@@ -943,8 +975,8 @@ class OVSSwitch( Switch ):
exit( 1 )
def dpctl( self, *args ):
"Run ovs-dpctl command"
return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
"Run ovs-ofctl command"
return self.cmd( 'ovs-ofctl', args[ 0 ], self, *args[ 1: ] )
@staticmethod
def TCReapply( intf ):
@@ -964,6 +996,23 @@ class OVSSwitch( Switch ):
"Disconnect a data port"
self.cmd( 'ovs-vsctl del-port', self, intf )
def controllerUUIDs( self ):
"Return ovsdb UUIDs for our controllers"
uuids = []
controllers = self.cmd( 'ovs-vsctl -- get Bridge', self,
'Controller' ).strip()
if controllers.startswith( '[' ) and controllers.endswith( ']' ):
controllers = controllers[ 1 : -1 ]
uuids = [ c.strip() for c in controllers.split( ',' ) ]
return uuids
def connected( self ):
"Are we connected to at least one of our controllers?"
results = [ 'true' in self.cmd( 'ovs-vsctl -- get Controller',
uuid, 'is_connected' )
for uuid in self.controllerUUIDs() ]
return reduce( or_, results, False )
def start( self, controllers ):
"Start up a new OVS OpenFlow switch using ovs-vsctl"
if self.inNamespace:
@@ -975,6 +1024,9 @@ class OVSSwitch( Switch ):
# Annoyingly, --if-exists option seems not to work
self.cmd( 'ovs-vsctl del-br', self )
self.cmd( 'ovs-vsctl add-br', self )
if self.datapath == 'user':
self.cmd( 'ovs-vsctl set bridge', self,'datapath_type=netdev' )
int( self.dpid, 16 ) # DPID must be a hex string
self.cmd( 'ovs-vsctl -- set Bridge', self,
'other_config:datapath-id=' + self.dpid )
self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode )
@@ -982,32 +1034,123 @@ class OVSSwitch( Switch ):
if not intf.IP():
self.attach( intf )
# Add controllers
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
clist = ' '.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
for c in controllers ] )
if self.listenPort:
clist += ' ptcp:%s' % self.listenPort
self.cmd( 'ovs-vsctl set-controller', self, clist )
# Reconnect quickly to controllers (1s vs. 15s max_backoff)
for uuid in self.controllerUUIDs():
if uuid.count( '-' ) != 4:
# Doesn't look like a UUID
continue
uuid = uuid.strip()
self.cmd( 'ovs-vsctl set Controller', uuid,
'max_backoff=1000' )
def stop( self ):
"Terminate OVS switch."
self.cmd( 'ovs-vsctl del-br', self )
if self.datapath == 'user':
self.cmd( 'ip link del', self )
self.deleteIntfs()
OVSKernelSwitch = OVSSwitch
class IVSSwitch(Switch):
"""IVS virtual switch"""
def __init__( self, name, **kwargs ):
Switch.__init__( self, name, **kwargs )
@classmethod
def setup( cls ):
"Make sure IVS is installed"
pathCheck( 'ivs-ctl', 'ivs',
moduleName="Indigo Virtual Switch (projectfloodlight.org)" )
out, err, exitcode = errRun( 'ivs-ctl show' )
if exitcode:
error( out + err +
'ivs-ctl exited with code %d\n' % exitcode +
'*** The openvswitch kernel module might '
'not be loaded. Try modprobe openvswitch.\n' )
exit( 1 )
def start( self, controllers ):
"Start up a new IVS switch"
args = ['ivs']
args.extend( ['--name', self.name] )
args.extend( ['--dpid', self.dpid] )
args.extend( ['--verbose'] )
for intf in self.intfs.values():
if not intf.IP():
args.extend( ['-i', intf.name] )
for c in controllers:
args.extend( ['-c', '%s:%d' % (c.IP(), c.port)] )
if self.listenPort:
args.extend( ['--listen', '127.0.0.1:%i' % self.listenPort] )
args.append( self.opts )
logfile = '/tmp/ivs.%s.log' % self.name
self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
def stop( self ):
"Terminate IVS switch."
self.cmd( 'kill %ivs' )
self.deleteIntfs()
def attach( self, intf ):
"Connect a data port"
self.cmd( 'ivs-ctl', 'add-port', '--datapath', self.name, intf )
def detach( self, intf ):
"Disconnect a data port"
self.cmd( 'ivs-ctl', 'del-port', '--datapath', self.name, intf )
def dpctl( self, *args ):
"Run dpctl command"
if not self.listenPort:
return "can't run dpctl without passive listening port"
return self.cmd( 'ovs-ofctl ' + ' '.join( args ) +
' tcp:127.0.0.1:%i' % self.listenPort )
class Controller( Node ):
"""A Controller is a Node that is running (or has execed?) an
OpenFlow controller."""
def __init__( self, name, inNamespace=False, command='controller',
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
port=6633, **params ):
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
port=6633, **params ):
self.command = command
self.cargs = cargs
self.cdir = cdir
self.ip = ip
self.port = port
Node.__init__( self, name, inNamespace=inNamespace,
ip=ip, **params )
ip=ip, **params )
self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary
self.checkListening()
def checkListening( self ):
"Make sure no controllers are running on our port"
# Verify that Telnet is installed first:
out, _err, returnCode = errRun( "which telnet" )
if 'telnet' not in out or returnCode != 0:
raise Exception( "Error running telnet to check for listening "
"controllers; please check that it is "
"installed." )
listening = self.cmd( "echo A | telnet -e A %s %d" %
( self.ip, self.port ) )
if 'Connected' in listening:
servers = self.cmd( 'netstat -atp' ).split( '\n' )
pstr = ':%d ' % self.port
clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
raise Exception( "Please shut down the controller which is"
" running on port %d:\n" % self.port +
'\n'.join( clist ) )
def start( self ):
"""Start <controller> <args> on controller.
@@ -1017,7 +1160,7 @@ class Controller( Node ):
if self.cdir is not None:
self.cmd( 'cd ' + self.cdir )
self.cmd( self.command + ' ' + self.cargs % self.port +
' 1>' + cout + ' 2>' + cout + '&' )
' 1>' + cout + ' 2>' + cout + '&' )
self.execed = False
def stop( self ):
@@ -1036,8 +1179,8 @@ class Controller( Node ):
def __repr__( self ):
"More informative string representation"
return '<%s %s: %s:%s pid=%s> ' % (
self.__class__.__name__, self.name,
self.IP(), self.port, self.pid )
self.__class__.__name__, self.name,
self.IP(), self.port, self.pid )
class OVSController( Controller ):
@@ -1065,24 +1208,24 @@ class NOX( Controller ):
noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
Controller.__init__( self, name,
command=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
' '.join( noxArgs ),
cdir=noxCoreDir, **kwargs )
command=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
' '.join( noxArgs ),
cdir=noxCoreDir,
**kwargs )
class RemoteController( Controller ):
"Controller running outside of Mininet's control."
def __init__( self, name, ip='127.0.0.1',
port=6633, **kwargs):
port=6633, **kwargs):
"""Init.
name: name to give controller
defaultIP: the IP address where the remote controller is
ip: the IP address where the remote controller is
listening
port: the port where the remote controller is listening"""
Controller.__init__( self, name, ip=ip, port=port,
**kwargs )
Controller.__init__( self, name, ip=ip, port=port, **kwargs )
def start( self ):
"Overridden to do nothing."
@@ -1091,3 +1234,12 @@ class RemoteController( Controller ):
def stop( self ):
"Overridden to do nothing."
return
def checkListening( self ):
"Warn if remote controller is not accessible"
listening = self.cmd( "echo A | telnet -e A %s %d" %
( self.ip, self.port ) )
if 'Connected' not in listening:
warn( "Unable to contact the remote controller"
" at %s:%d\n" % ( self.ip, self.port ) )
+52 -32
View File
@@ -1,60 +1,80 @@
"""
Terminal creation and cleanup.
Utility functions to run a term (connected via screen(1)) on each host.
Utility functions to run a terminal (connected via socat(1)) on each host.
Requires GNU screen(1) and xterm(1).
Requires socat(1) and xterm(1).
Optionally uses gnome-terminal.
"""
import re
from subprocess import Popen
from os import environ
from mininet.log import error
from mininet.util import quietRun
from mininet.util import quietRun, errRun
def quoteArg( arg ):
"Quote an argument if it contains spaces."
return repr( arg ) if ' ' in arg else arg
def tunnelX11( node, display=None):
"""Create an X11 tunnel from node:6000 to the root host
display: display on root host (optional)
returns: node $DISPLAY, Popen object for tunnel"""
if display is None and 'DISPLAY' in environ:
display = environ[ 'DISPLAY' ]
if display is None:
error( "Error: Cannot connect to display\n" )
return None, None
host, screen = display.split( ':' )
# Unix sockets should work
if not host or host == 'unix':
# GDM3 doesn't put credentials in .Xauthority,
# so allow root to just connect
quietRun( 'xhost +si:localuser:root' )
return display, None
else:
# Create a tunnel for the TCP connection
port = 6000 + int( float( screen ) )
connection = r'TCP\:%s\:%s' % ( host, port )
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm' ):
"""Run screen on a node, and hook up a terminal.
def makeTerm( node, title='Node', term='xterm', display=None ):
"""Create an X11 tunnel to the node and start up a terminal.
node: Node object
title: base title
term: 'xterm' or 'gterm'
returns: process created"""
returns: two Popen objects, tunnel and terminal"""
title += ': ' + node.name
if not node.inNamespace:
title += ' (root)'
cmds = {
'xterm': [ 'xterm', '-title', title, '-e' ],
'gterm': [ 'gnome-terminal', '--title', title, '-e' ]
'xterm': [ 'xterm', '-title', title, '-display' ],
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
return
if not node.execed:
node.cmd( 'screen -dmS ' + 'mininet.' + node.name)
args = [ 'screen', '-D', '-RR', '-S', 'mininet.' + node.name ]
else:
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
if term == 'gterm':
# Compress these for gnome-terminal, which expects one token
# to follow the -e option
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
return Popen( cmds[ term ] + args )
display, tunnel = tunnelX11( node, display )
if display is None:
return []
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
"Run an X11 client on a node"
_display, tunnel = tunnelX11( node )
if _display is None:
return []
popen = node.popen( cmd )
return [ tunnel, popen ]
def cleanUpScreens():
"Remove moldy old screen sessions."
r = r'(\d+\.mininet\.[hsc]\d+)'
output = quietRun( 'screen -ls' ).split( '\n' )
for line in output:
m = re.search( r, line )
if m:
quietRun( 'screen -S ' + m.group( 1 ) + ' -X quit' )
"Remove moldy socat X11 tunnels."
errRun( "pkill -9 -f mnexec.*socat" )
def makeTerms( nodes, title='Node', term='xterm' ):
"""Create terminals.
nodes: list of Node objects
title: base title for each
returns: list of created terminal processes"""
return [ makeTerm( node, title, term ) for node in nodes ]
returns: list of created tunnel/terminal processes"""
terms = []
for node in nodes:
terms += makeTerm( node, title, term )
return terms
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
"""
Run all mininet core tests
-v : verbose output
-quick : skip tests that take more than ~30 seconds
"""
import unittest
import os
import sys
from mininet.util import ensureRoot
from mininet.clean import cleanup
from mininet.log import setLogLevel
def runTests( testDir, verbosity=1 ):
"discover and run all tests in testDir"
# ensure root and cleanup before starting tests
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
setLogLevel( 'warning' )
# get the directory containing example tests
testDir = os.path.dirname( os.path.realpath( __file__ ) )
verbosity = 2 if '-v' in sys.argv else 1
runTests( testDir, verbosity )
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env python
"""Package: mininet
Test creation and pings for topologies with link and/or CPU options."""
import unittest
from functools import partial
from mininet.net import Mininet
from mininet.node import OVSSwitch, UserSwitch, IVSSwitch
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
# Number of hosts for each test
N = 2
class SingleSwitchOptionsTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, hopts=None, lopts=None):
if not hopts:
hopts = {}
if not lopts:
lopts = {}
Topo.__init__(self, hopts=hopts, lopts=lopts)
switch = self.addSwitch('s1')
for h in range(n):
host = self.addHost('h%s' % (h + 1))
self.addLink(host, switch)
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
class testOptionsTopoCommon( object ):
"""Verify ability to create networks with host and link options
(common code)."""
switchClass = None # overridden in subclasses
def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
"Generic topology-with-options test runner."
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
lopts=lopts ),
host=CPULimitedHost, link=TCLink,
switch=self.switchClass )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
def assertWithinTolerance(self, measured, expected, tolerance_frac):
"""Check that a given value is within a tolerance of expected
tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
"""
self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
def testCPULimits( self ):
"Verify topology creation with CPU limits set for both schedulers."
CPU_FRACTION = 0.1
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
hopts = { 'cpu': CPU_FRACTION }
#self.runOptionsTopoTest( N, hopts=hopts )
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
host=CPULimitedHost, switch=self.switchClass )
mn.start()
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop()
for cpu in results:
self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE )
def testLinkBandwidth( self ):
"Verify that link bandwidths are accurate within a bound."
BW = 5 # Mbps
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first;
lopts = { 'bw': BW, 'use_htb': True }
# Also verify correctness of limit limitng within a bound.
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass )
bw_strs = mn.run( mn.iperf )
for bw_str in bw_strs:
bw = float( bw_str.split(' ')[0] )
self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
def testLinkDelay( self ):
"Verify that link delays are accurate within a bound."
DELAY_MS = 15
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass )
ping_delays = mn.run( mn.pingFull )
test_outputs = ping_delays[0]
# Ignore unused variables below
# pylint: disable-msg=W0612
node, dest, ping_outputs = test_outputs
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
self.assertEqual( sent, received )
# pylint: enable-msg=W0612
for rttval in [rttmin, rttavg, rttmax]:
# Multiply delay by 4 to cover there & back on two links
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
DELAY_TOLERANCE)
def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate."
LOSS_PERCENT = 99
REPS = 1
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
host=CPULimitedHost, link=TCLink,
switch=self.switchClass )
# Drops are probabilistic, but the chance of no dropped packets is
# 1 in 100 million with 4 hops for a link w/99% loss.
dropped_total = 0
mn.start()
for _ in range(REPS):
dropped_total += mn.ping(timeout='1')
mn.stop()
self.assertTrue(dropped_total > 0)
def testMostOptions( self ):
"Verify topology creation with most link options and CPU limits."
lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
hopts = { 'cpu': 0.5 / N }
self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )
# pylint: enable=E1101
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(OVS kernel switch)."""
switchClass = OVSSwitch
@unittest.skip( 'Skipping OVS user switch test for now' )
class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(OVS user switch)."""
switchClass = partial( OVSSwitch, datapath='user' )
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
"Verify ability to create networks with host and link options (IVS)."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
"Verify ability to create networks with host and link options (UserSwitch)."
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+71 -23
View File
@@ -4,45 +4,93 @@
Test creation and all-pairs ping for each included mininet topo type."""
import unittest
from functools import partial
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import UserSwitch, OVSKernelSwitch
from mininet.node import UserSwitch, OVSSwitch, IVSSwitch
from mininet.topo import SingleSwitchTopo, LinearTopo
from mininet.log import setLogLevel
from mininet.util import quietRun
SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch,
}
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
class testSingleSwitchCommon( object ):
"Test ping with single switch topology (common code)."
class testSingleSwitch( unittest.TestCase ):
"For each datapath type, test ping with single switch topologies."
switchClass = None # overridden in subclasses
def testMinimal( self ):
"Ping test with both datapaths on minimal topology"
for switch in SWITCHES.values():
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
"Ping test on minimal topology"
mn = Mininet( SingleSwitchTopo(), self.switchClass, 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"
for switch in SWITCHES.values():
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
"Ping test on 5-host single-switch topology"
mn = Mininet( SingleSwitchTopo( k=5 ), self.switchClass, Host,
Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
# pylint: enable=E1101
class testSingleSwitchOVSKernel( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (OVS kernel switch)."
switchClass = OVSSwitch
class testSingleSwitchOVSUser( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (OVS user switch)."
switchClass = partial( OVSSwitch, datapath='user' )
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (IVS switch)."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (Userspace switch)."
switchClass = UserSwitch
class testLinear( unittest.TestCase ):
"For each datapath type, test all-pairs ping with LinearNet."
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
class testLinearCommon( object ):
"Test all-pairs ping with LinearNet (common code)."
switchClass = None # overridden in subclasses
def testLinear5( self ):
"Ping test with both datapaths on a 5-switch topology"
for switch in SWITCHES.values():
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
"Ping test on a 5-switch topology"
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
# pylint: enable=E1101
class testLinearOVSKernel( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (OVS kernel switch)."
switchClass = OVSSwitch
class testLinearOVSUser( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (OVS user switch)."
switchClass = partial( OVSSwitch, datapath='user' )
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
class testLinearIVS( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (IVS switch)."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testLinearUserspace( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (Userspace switch)."
switchClass = UserSwitch
if __name__ == '__main__':
+332
View File
@@ -0,0 +1,332 @@
#!/usr/bin/env python
"""
Tests for the Mininet Walkthrough
TODO: missing xterm test
"""
import unittest
import pexpect
import os
from time import sleep
from mininet.util import quietRun
class testWalkthrough( unittest.TestCase ):
prompt = 'mininet>'
# PART 1
def testHelp( self ):
"Check the usage message"
p = pexpect.spawn( 'mn -h' )
index = p.expect( [ 'Usage: mn', pexpect.EOF ] )
self.assertEqual( index, 0 )
def testWireshark( self ):
"Use tshark to test the of dissector"
tshark = pexpect.spawn( 'tshark -i lo -R of' )
tshark.expect( 'Capturing on lo' )
mn = pexpect.spawn( 'mn --test pingall' )
mn.expect( '0% dropped' )
tshark.expect( 'OFP 74 Hello' )
tshark.sendintr()
def testBasic( self ):
"Test basic CLI commands (help, nodes, net, dump)"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# help command
p.sendline( 'help' )
index = p.expect( [ 'commands', self.prompt ] )
self.assertEqual( index, 0, 'No output for "help" command')
# nodes command
p.sendline( 'nodes' )
p.expect( '([chs]\d ?){4}' )
nodes = p.match.group( 0 ).split()
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
p.expect( self.prompt )
# net command
p.sendline( 'net' )
expected = [ x for x in nodes ]
while len( expected ) > 0:
index = p.expect( expected )
node = p.match.group( 0 )
expected.remove( node )
p.expect( '\n' )
self.assertEqual( len( expected ), 0, '"nodes" and "net" differ')
p.expect( self.prompt )
# dump command
p.sendline( 'dump' )
expected = [ '<\w+ (%s)' % n for n in nodes ]
actual = []
for _ in nodes:
index = p.expect( expected )
node = p.match.group( 1 )
actual.append( node )
p.expect( '\n' )
self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testHostCommands( self ):
"Test ifconfig and ps on h1 and s1"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index == 0 or index == 3:
ifcount += 1
elif index == 1:
self.fail( 's1 interface displayed in "h1 ifconfig"' )
elif index == 2:
self.fail( 'eth0 displayed in "h1 ifconfig"' )
else:
break
self.assertEqual( ifcount, 2, 'Missing interfaces on h1')
# s1 ifconfig
p.sendline( 's1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index == 0:
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
elif index == 1 or index == 2 or index == 3:
ifcount += 1
else:
break
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( 'h1 ps -a' )
p.expect( self.prompt )
h1Output = p.before
# s1 ps
p.sendline( 's1 ps -a' )
p.expect( self.prompt )
s1Output = p.before
# strip command from ps output
h1Output = h1Output.split( '\n', 1 )[ 1 ]
s1Output = s1Output.split( '\n', 1 )[ 1 ]
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
p.sendline( 'exit' )
p.wait()
def testConnectivity( self ):
"Test ping and pingall"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect( '1 packets transmitted, 1 received' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect( '0% dropped' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testSimpleHTTP( self ):
"Start an HTTP server on h1 and wget from h2"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
p.expect( self.prompt )
p.sendline( ' h2 wget -O - h1' )
p.expect( '200 OK' )
p.expect( self.prompt )
p.sendline( 'h1 kill %python' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
# PART 2
def testRegressionRun( self ):
"Test pingpair (0% drop) and iperf (bw > 0) regression tests"
# test pingpair
p = pexpect.spawn( 'mn --test pingpair' )
p.expect( '0% dropped' )
p.expect( pexpect.EOF )
# test iperf
p = pexpect.spawn( 'mn --test iperf' )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
def testTopoChange( self ):
"Test pingall on single,3 and linear,4 topos"
# testing single,3
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
p.expect( '(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
self.assertEqual( sent, received, 'Dropped packets in single,3')
p.expect( pexpect.EOF )
# testing linear,4
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
p.expect( '(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
self.assertEqual( sent, received, 'Dropped packets in linear,4')
p.expect( pexpect.EOF )
def testLinkChange( self ):
"Test TCLink bw and delay"
p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
# test bw
p.expect( self.prompt )
p.sendline( 'iperf' )
p.expect( "Results: \['([\d\.]+) Mbits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
p.expect( self.prompt )
# test delay
p.sendline( 'h1 ping -c 4 h2' )
p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay > 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testVerbosity( self ):
"Test debug and output verbosity"
# test output
p = pexpect.spawn( 'mn -v output' )
p.expect( self.prompt )
self.assertEqual( len( p.before ), 0, 'Too much output for "output"' )
p.sendline( 'exit' )
p.wait()
# test debug
p = pexpect.spawn( 'mn -v debug --test none' )
p.expect( pexpect.EOF )
lines = p.before.split( '\n' )
self.assertTrue( len( lines ) > 100, "Debug output is too short" )
def testCustomTopo( self ):
"Start Mininet using a custom topo, then run pingall"
custom = os.path.dirname( os.path.realpath( __file__ ) )
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
custom = os.path.normpath( custom )
p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom )
p.expect( '0% dropped' )
p.expect( pexpect.EOF )
def testStaticMAC( self ):
"Verify that MACs are set to easy to read numbers"
p = pexpect.spawn( 'mn --mac' )
p.expect( self.prompt )
for i in range( 1, 3 ):
p.sendline( 'h%d ifconfig' % i )
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
p.expect( self.prompt )
def testSwitches( self ):
"Run iperf test using user and ovsk switches"
switches = [ 'user', 'ovsk' ]
for sw in switches:
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
def testBenchmark( self ):
"Run benchmark and verify that it takes less than 2 seconds"
p = pexpect.spawn( 'mn --test none' )
p.expect( 'completed in ([\d\.]+) seconds' )
time = float( p.match.group( 1 ) )
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
def testOwnNamespace( self ):
"Test running user switch in its own namespace"
p = pexpect.spawn( 'mn --innamespace --switch user' )
p.expect( self.prompt )
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
p.sendline( 's1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index == 1 or index == 3:
ifcount += 1
elif index == 0:
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
elif index == 2:
self.fail( 'eth0 displayed in "s1 ifconfig"' )
else:
break
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
# verify that all hosts a reachable
p.sendline( 'pingall' )
p.expect( '(\d+)% dropped' )
dropped = int( p.match.group( 1 ) )
self.assertEqual( dropped, 0, 'pingall failed')
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
# PART 3
def testPythonInterpreter( self ):
"Test py and px by checking IP for h1 and adding h3"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# test host IP
p.sendline( 'py h1.IP()' )
p.expect( '10.0.0.1' )
p.expect( self.prompt )
# test adding host
p.sendline( "px net.addHost('h3')" )
p.expect( self.prompt )
p.sendline( "px net.addLink(s1, h3)" )
p.expect( self.prompt )
p.sendline( 'net' )
p.expect( 'h3' )
p.expect( self.prompt )
p.sendline( 'py h3.MAC()' )
p.expect( '([a-f0-9]{2}:?){6}' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testLink( self ):
"Test link CLI command using ping"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'link s1 h1 down' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect( 'unreachable' )
p.expect( self.prompt )
p.sendline( 'link s1 h1 up' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect( '0% packet loss' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@unittest.skipUnless( os.path.exists( '/tmp/pox' ) or
'1 received' in quietRun( 'ping -c 1 github.com' ),
'Github is not reachable; cannot download Pox' )
def testRemoteController( self ):
"Test Mininet using Pox controller"
if not os.path.exists( '/tmp/pox' ):
p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p.expect( pexpect.EOF )
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net.expect( '0% dropped' )
net.expect( pexpect.EOF )
pox.sendintr()
pox.wait()
if __name__ == '__main__':
unittest.main()
+74 -35
View File
@@ -11,13 +11,40 @@ A Topo object can be a topology database for NOX, can represent a physical
setup for testing, and can even be emulated with the Mininet package.
'''
# BL: we may have to fix compatibility here.
# networkx is also a fairly heavyweight dependency
# from networkx.classes.graph import Graph
from networkx import Graph
from mininet.util import irange, natural, naturalSeq
class MultiGraph( object ):
"Utility class to track nodes and edges - replaces networkx.Graph"
def __init__( self ):
self.data = {}
def add_node( self, node ):
"Add node to graph"
self.data.setdefault( node, [] )
def add_edge( self, src, dest ):
"Add edge to graph"
src, dest = sorted( ( src, dest ) )
self.add_node( src )
self.add_node( dest )
self.data[ src ].append( dest )
def nodes( self ):
"Return list of graph nodes"
return self.data.keys()
def edges( self ):
"Iterator: return graph edges"
for src in self.data.keys():
for dest in self.data[ src ]:
yield ( src, dest )
def __getitem__( self, node ):
"Return link dict for the given node"
return self.data[node]
class Topo(object):
"Data center network representation for structured multi-trees."
@@ -26,7 +53,7 @@ class Topo(object):
hinfo: default host options
sopts: default switch options
lopts: default link options"""
self.g = Graph()
self.g = MultiGraph()
self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = {} if hopts is None else hopts
@@ -34,7 +61,7 @@ class Topo(object):
self.lopts = {} if lopts is None else lopts
self.ports = {} # ports[src][dst] is port on src that connects to dst
def add_node(self, name, **opts):
def addNode(self, name, **opts):
"""Add Node to graph.
name: name
opts: node options
@@ -43,40 +70,40 @@ class Topo(object):
self.node_info[name] = opts
return name
def add_host(self, name, **opts):
def addHost(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)
return self.addNode(name, **opts)
def add_switch(self, name, **opts):
def addSwitch(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)
result = self.addNode(name, isSwitch=True, **opts)
return result
def add_link(self, node1, node2, port1=None, port2=None,
**opts):
def addLink(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)
self.addPort(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, sport=None, dport=None):
def addPort(self, src, dst, sport=None, dport=None):
'''Generate port mapping for new edge.
@param src source switch name
@param dst destination switch name
@@ -84,8 +111,8 @@ class Topo(object):
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
src_base = 1 if self.isSwitch(src) else 0
dst_base = 1 if self.isSwitch(dst) else 0
if sport is None:
sport = len(self.ports[src]) + src_base
if dport is None:
@@ -100,24 +127,24 @@ class Topo(object):
else:
return self.g.nodes()
def is_switch(self, n):
def isSwitch(self, n):
'''Returns true if node is a switch.'''
info = self.node_info[n]
return info and info.get('is_switch', False)
return info and info.get('isSwitch', False)
def switches(self, sort=True):
'''Return switches.
sort: sort switches alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if self.is_switch(n)]
return [n for n in self.nodes(sort) if self.isSwitch(n)]
def hosts(self, sort=True):
'''Return hosts.
sort: sort hosts alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if not self.is_switch(n)]
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
def links(self, sort=True):
'''Return links.
@@ -180,10 +207,10 @@ class SingleSwitchTopo(Topo):
self.k = k
switch = self.add_switch('s1')
switch = self.addSwitch('s1')
for h in irange(1, k):
host = self.add_host('h%s' % h)
self.add_link(host, switch)
host = self.addHost('h%s' % h)
self.addLink(host, switch)
class SingleSwitchReversedTopo(Topo):
@@ -201,30 +228,42 @@ class SingleSwitchReversedTopo(Topo):
'''
super(SingleSwitchReversedTopo, self).__init__(**opts)
self.k = k
switch = self.add_switch('s1')
switch = self.addSwitch('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))
host = self.addHost('h%s' % h)
self.addLink(host, switch,
port1=0, port2=(k - h + 1))
class LinearTopo(Topo):
"Linear topology of k switches, with one host per switch."
"Linear topology of k switches, with n hosts per switch."
def __init__(self, k=2, **opts):
def __init__(self, k=2, n=1, **opts):
"""Init.
k: number of switches (and hosts)
k: number of switches
n: number of hosts per switch
hconf: host configuration options
lconf: link configuration options"""
super(LinearTopo, self).__init__(**opts)
self.k = k
self.n = n
if n == 1:
genHostName = lambda i, j: 'h%s' % i
else:
genHostName = lambda i, j: 'h%ss%d' % (j, i)
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)
# Add switch
switch = self.addSwitch('s%s' % i)
# Add hosts to switch
for j in irange(1, n):
host = self.addHost(genHostName(i, j))
self.addLink(host, switch)
# Connect switch to previous
if lastSwitch:
self.add_link( switch, lastSwitch)
self.addLink(switch, lastSwitch)
lastSwitch = switch
+3 -3
View File
@@ -19,13 +19,13 @@ class TreeTopo( Topo ):
returns: last node added"""
isSwitch = depth > 0
if isSwitch:
node = self.add_switch( 's%s' % self.switchNum )
node = self.addSwitch( 's%s' % self.switchNum )
self.switchNum += 1
for _ in range( fanout ):
child = self.addTree( depth - 1, fanout )
self.add_link( node, child )
self.addLink( node, child )
else:
node = self.add_host( 'h%s' % self.hostNum )
node = self.addHost( 'h%s' % self.hostNum )
self.hostNum += 1
return node
+112 -55
View File
@@ -1,10 +1,10 @@
"Utility functions for Mininet."
from mininet.log import output, info, error
from mininet.log import output, info, error, warn, debug
from time import sleep
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
from select import poll, POLLIN
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
from select import poll, POLLIN, POLLHUP
from subprocess import call, check_call, Popen, PIPE, STDOUT
import re
from fcntl import fcntl, F_GETFL, F_SETFL
@@ -47,7 +47,7 @@ def oldQuietRun( *cmd ):
break
out += data
popen.poll()
if popen.returncode != None:
if popen.returncode is not None:
break
return out
@@ -112,10 +112,6 @@ def errFail( *cmd, **kwargs ):
% ( cmd, ret, err ) )
return out, err, ret
def errFailTemp( *cmd, **kwargs ):
import os
os.system(" ".join(cmd))
def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
@@ -175,27 +171,35 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
error( "*** gave up after %i retries\n" % tries )
exit( 1 )
def moveIntfNoRetry( intf, node, printError=False ):
def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
"""Move interface to node, without retrying.
intf: string, interface
node: Node object
printError: if true, print error"""
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
quietRun( cmd )
links = node.cmd( 'ip link show' )
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
if srcNode:
srcNode.cmd( cmd )
else:
quietRun( cmd )
links = dstNode.cmd( 'ip link show' )
if not ( ' %s:' % intf ) in links:
if printError:
error( '*** Error: moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
' not successfully moved to ' + dstNode.name + '\n' )
return False
return True
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
def moveIntf( intf, dstNode, srcNode=None, printError=False,
retries=3, delaySecs=0.001 ):
"""Move interface to node, retrying on failure.
intf: string, interface
node: Node object
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
srcNode=srcNode, printError=printError )
# Support for dumping network
@@ -228,7 +232,7 @@ def dumpNetConnections( net ):
def _colonHex( val, bytecount ):
"""Generate colon-hex string.
val: input as unsigned int
bytescount: number of bytes to convert
bytecount: number of bytes to convert
returns: chStr colon-hex string"""
pieces = []
for i in range( bytecount - 1, -1, -1 ):
@@ -246,9 +250,8 @@ def macColonHex( mac ):
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"""
returns: ip address string w.x.y.z"""
w = ( ip >> 24 ) & 0xff
w = 10 if w == 0 else w
x = ( ip >> 16 ) & 0xff
y = ( ip >> 8 ) & 0xff
z = ip & 0xff
@@ -257,7 +260,7 @@ def ipStr( ip ):
def ipNum( w, x, y, z ):
"""Generate unsigned int from components of IP address
returns: w << 24 | x << 16 | y << 8 | z"""
return ( 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
@@ -265,10 +268,10 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
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 )
imax = 0xffffffff >> prefixLen
assert i <= imax
mask = 0xffffffff ^ imax
ipnum = ( ipBaseNum & mask ) + i
return ipStr( ipnum )
def ipParse( ip ):
@@ -330,48 +333,88 @@ def pmonitor(popens, timeoutms=500, readline=True,
# Use non-blocking reads
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
while True:
while popens:
fds = poller.poll( timeoutms )
if fds:
for fd, _event in fds:
for fd, event in fds:
host = fdToHost[ fd ]
popen = popens[ host ]
if readline:
# Attempt to read a line of output
# This blocks until we receive a newline!
line = popen.stdout.readline()
else:
line = popen.stdout.read( readmax )
yield host, line
if event & POLLIN:
if readline:
# Attempt to read a line of output
# This blocks until we receive a newline!
line = popen.stdout.readline()
else:
line = popen.stdout.read( readmax )
yield host, line
# Check for EOF
if not line:
popen.poll()
if popen.returncode is not None:
poller.unregister( fd )
del popens[ host ]
if not popens:
return
elif event & POLLHUP:
poller.unregister( fd )
del popens[ host ]
else:
yield None, ''
# Other stuff we use
def sysctlTestAndSet( name, limit ):
"Helper function to set sysctl limits"
#convert non-directory names into directory names
if '/' not in name:
name = '/proc/sys/' + name.replace( '.', '/' )
#read limit
with open( name, 'r' ) as readFile:
oldLimit = readFile.readline()
if type( limit ) is int:
#compare integer limits before overriding
if int( oldLimit ) < limit:
with open( name, 'w' ) as writeFile:
writeFile.write( "%d" % limit )
else:
#overwrite non-integer limits
with open( name, 'w' ) as writeFile:
writeFile.write( limit )
def rlimitTestAndSet( name, limit ):
"Helper function to set rlimits"
soft, hard = getrlimit( name )
if soft < limit:
hardLimit = hard if limit < hard else limit
setrlimit( name, ( limit, hardLimit ) )
def fixLimits():
"Fix ridiculously small resource limits."
setrlimit( RLIMIT_NPROC, ( 8192, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 16384 ) )
debug( "*** Setting resource limits\n" )
try:
rlimitTestAndSet( RLIMIT_NPROC, 8192 )
rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
#Increase open file limit
sysctlTestAndSet( 'fs.file-max', 10000 )
#Increase network buffer space
sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
#Increase arp cache size
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
#Increase routing table size
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
#Increase number of PTYs for nodes
sysctlTestAndSet( 'kernel.pty.max', 20000 )
except:
warn( "*** Error setting resource limits. "
"Mininet's performance may be affected.\n" )
def mountCgroups():
return
"Make sure cgroups file system is mounted"
mounts = quietRun( 'mount' )
#cgdir = '/sys/fs/cgroup'
# BDH temp for 10.04
cgdir = '/mnt/cgroups'
mounts = quietRun( 'cat /proc/mounts' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if 'cgroups on %s' % cgdir not in mounts:
if ('cgroup %s' % cgdir not in mounts and
'cgroups %s' % cgdir not in mounts):
raise Exception( "cgroups not mounted on " + cgdir )
if 'cpuset on %s' % csdir not in mounts:
if 'cpuset %s' % csdir not in mounts:
errRun( 'mkdir -p ' + csdir )
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
@@ -403,10 +446,14 @@ def irange(start, end):
def custom( cls, **params ):
"Returns customized constructor for class cls."
# Note: we may wish to see if we can use functools.partial() here
# and in customConstructor
def customized( *args, **kwargs):
"Customized constructor"
kwargs = kwargs.copy()
kwargs.update( params )
return cls( *args, **kwargs )
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
return customized
def splitArgs( argstr ):
@@ -421,13 +468,12 @@ def splitArgs( argstr ):
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( '=' )
key, val = s.split( '=', 1 )
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
def customConstructor( constructors, argStr ):
"""Return custom constructor based on argStr
The args and key/val pairs in argsStr will be automatically applied
when the generated constructor is later used.
"""
@@ -436,10 +482,11 @@ def customConstructor( constructors, argStr ):
if not constructor:
raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, constructors.keys() ) )
( cname, constructors.keys() ) )
def customized( name, *args, **params ):
"Customized constructor, useful for Node, Link, and other classes"
params = params.copy()
params.update( kwargs )
if not newargs:
return constructor( name, *args, **params )
@@ -448,14 +495,24 @@ def customConstructor( constructors, argStr ):
constructor, args, newargs ) )
return constructor( name, *newargs, **params )
customized.__name__ = 'customConstructor(%s)' % argStr
return customized
def buildTopo( topos, topoStr ):
"""Create topology from string with format (object, arg1, arg2,...).
input topos is a dict of topo names to constructors, possibly w/args.
"""
topo, args, kwargs = splitArgs( topoStr )
if topo not in topos:
raise Exception( 'Invalid topo name %s' % topo )
return topos[ topo ]( *args, **kwargs )
def ensureRoot():
"""Ensure that we are running as root.
Probably we should only sudo when needed as per Big Switch's patch.
"""
if os.getuid() != 0:
print "*** Mininet must run as root."
exit( 1 )
return
+27 -15
View File
@@ -23,24 +23,30 @@
#include <limits.h>
#include <sched.h>
#if !defined(VERSION)
#define VERSION "(devel)"
#endif
void usage(char *name)
{
printf("Execution utility for Mininet.\n"
"usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n"
"-c: close all file descriptors except stdin/out/error\n"
"-d: detach from tty by calling setsid()\n"
"-n: run in new network namespace\n"
"-p: print ^A + pid\n"
"-a pid: attach to pid's network namespace\n"
"-g group: add to cgroup\n"
"-r rtprio: run with SCHED_RR (usually requires -g)\n",
printf("Execution utility for Mininet\n\n"
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Options:\n"
" -c: close all file descriptors except stdin/out/error\n"
" -d: detach from tty by calling setsid()\n"
" -n: run in new network namespace\n"
" -p: print ^A + pid\n"
" -a pid: attach to pid's network namespace\n"
" -g group: add to cgroup\n"
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
" -v: print version\n",
name);
}
int setns(int fd, int nstype)
{
return syscall(308, fd, nstype);
return syscall(__NR_setns, fd, nstype);
}
/* Validate alphanumeric path foo1/bar2/baz */
@@ -86,13 +92,13 @@ int cgroup(char *gname)
int main(int argc, char *argv[])
{
char c;
int c;
int fd;
char path[PATH_MAX];
int nsid;
int pid;
static struct sched_param sp;
while ((c = getopt(argc, argv, "+cdnpa:g:r:")) != -1)
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
switch(c) {
case 'c':
/* close file descriptors except stdin/out/error */
@@ -106,9 +112,9 @@ int main(int argc, char *argv[])
case -1:
perror("fork");
return 1;
case 0: /* child */
case 0: /* child */
break;
default: /* parent */
default: /* parent */
return 0;
}
}
@@ -152,9 +158,15 @@ int main(int argc, char *argv[])
return 1;
}
break;
case 'v':
printf("%s\n", VERSION);
exit(0);
case 'h':
usage(argv[0]);
exit(0);
default:
usage(argv[0]);
break;
exit(1);
}
if (optind < argc) {
+18 -11
View File
@@ -5,32 +5,39 @@
from setuptools import setup, find_packages
from os.path import join
# Get version number from source tree
import sys
sys.path.append( '.' )
from mininet.net import VERSION
scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ]
modname = distname = 'mininet'
setup(
name=distname,
version='0.0.0',
version=VERSION,
description='Process-based OpenFlow emulator',
author='Bob Lantz',
author_email='rlantz@cs.stanford.edu',
packages=find_packages(exclude='test'),
packages=[ 'mininet', 'mininet.examples' ],
long_description="""
Insert longer description here.
""",
Mininet is a network emulator which uses lightweight
virtualization to create virtual networks for rapid
prototyping of Software-Defined Network (SDN) designs
using OpenFlow. http://mininet.org
""",
classifiers=[
"License :: OSI Approved :: GNU General Public License (GPL)",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python",
"Development Status :: 4 - Beta",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Topic :: Internet",
"Topic :: System :: Emulators",
],
keywords='networking protocol Internet OpenFlow',
license='unspecified',
keywords='networking emulator protocol Internet OpenFlow SDN',
license='BSD',
install_requires=[
'setuptools',
'networkx'
'setuptools'
],
scripts=scripts,
)
+335 -100
View File
@@ -5,10 +5,22 @@
# Fail on error
set -e
# Fail on unset var usage
set -o nounset
# Get directory containing mininet folder
MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )"
# Set up build directory, which by default is the working directory
# unless the working directory is a subdirectory of mininet,
# in which case we use the directory containing mininet
BUILD_DIR="$(pwd -P)"
case $BUILD_DIR in
$MININET_DIR/*) BUILD_DIR=$MININET_DIR;; # currect directory is a subdirectory
*) BUILD_DIR=$BUILD_DIR;;
esac
# Location of CONFIG_NET_NS-enabled kernel(s)
KERNEL_LOC=http://www.openflow.org/downloads/mininet
@@ -31,9 +43,16 @@ if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
if ! which lsb_release &> /dev/null; then
$install lsb-release
fi
if ! which bc &> /dev/null; then
$install bc
fi
fi
test -e /etc/fedora-release && DIST="Fedora"
if [ "$DIST" = "Fedora" ]; then
install='sudo yum -y install'
remove='sudo yum -y erase'
pkginst='sudo rpm -ivh'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install redhat-lsb-core
fi
fi
if which lsb_release &> /dev/null; then
DIST=`lsb_release -is`
@@ -55,8 +74,11 @@ elif [ "$DIST" = "Debian" ] && [ "$ARCH" = "i386" ] && [ "$CODENAME" = "lenny" ]
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" = "Fedora" ]; then
KERNEL_NAME=`uname -r`
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
else
echo "Install.sh currently only supports Ubuntu and Debian Lenny i386."
echo "Install.sh currently only supports Ubuntu, Debian Lenny i386 and Fedora."
exit 1
fi
@@ -72,10 +94,16 @@ 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})
IVS_TAG=v0.3
# Command-line versions overrides that simplify custom VM creation
# To use, pass in the vars on the cmd line before install.sh, e.g.
# WS_DISSECTOR_REV=pre-ws-1.10.0 install.sh -w
WS_DISSECTOR_REV=${WS_DISSECTOR_REV:-""}
OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
function kernel {
echo "Install Mininet-compatible kernel if necessary"
@@ -97,11 +125,11 @@ function kernel {
if ! test -e /boot/initrd.img-${KERNEL_NAME}; then
sudo update-initramfs -c -k ${KERNEL_NAME}
fi
# Ensure /boot/grub/menu.lst boots with initrd image:
sudo update-grub
# The default should be the new kernel. Otherwise, you may need to modify
# The default should be the new kernel. Otherwise, you may need to modify
# /boot/grub/menu.lst to set the default to the entry corresponding to the
# kernel you just installed.
fi
@@ -116,33 +144,34 @@ function kernel_clean {
fi
# Also remove downloaded packages:
rm -f ~/linux-headers-* ~/linux-image-*
rm -f $HOME/linux-headers-* $HOME/linux-image-*
}
# Install Mininet deps
function mn_deps {
echo "Installing Mininet dependencies"
$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"
sudo easy_install --upgrade networkx
if [ "$DIST" = "Fedora" ]; then
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8
else
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8
fi
# Add sysctl parameters as noted in the INSTALL file to increase kernel
# limits to support larger setups:
sudo su -c "cat $HOME/mininet/util/sysctl_addon >> /etc/sysctl.conf"
# Load new sysctl settings:
sudo sysctl -p
echo "Installing Mininet core"
pushd ~/mininet
pushd $MININET_DIR/mininet
sudo make install
popd
}
# Install Mininet developer dependencies
function mn_dev {
echo "Installing Mininet developer dependencies"
$install doxygen doxypy texlive-fonts-recommended
}
# The following will cause a full OF install, covering:
# -user switch
# The instructions below are an abbreviated version from
@@ -150,64 +179,124 @@ function mn_deps {
# ... modified to use Debian Lenny rather than unstable.
function of {
echo "Installing OpenFlow reference implementation..."
cd ~/
$install git-core autoconf automake autotools-dev pkg-config \
make gcc libtool libc6-dev
cd $BUILD_DIR/
$install autoconf automake libtool make gcc
if [ "$DIST" = "Fedora" ]; then
$install git pkgconfig glibc-devel
else
$install git-core autotools-dev pkg-config libc6-dev
fi
git clone git://openflowswitch.org/openflow.git
cd ~/openflow
cd $BUILD_DIR/openflow
# Patch controller to handle more than 16 switches
patch -p1 < ~/mininet/util/openflow-patches/controller.patch
patch -p1 < $MININET_DIR/mininet/util/openflow-patches/controller.patch
# Resume the install:
./boot.sh
./configure
make
sudo make install
cd $BUILD_DIR
}
# Remove avahi-daemon, which may cause unwanted discovery packets to be
# sent during tests, near link status changes:
$remove avahi-daemon
function of13 {
echo "Installing OpenFlow 1.3 soft switch implementation..."
cd $BUILD_DIR/
$install git-core autoconf automake autotools-dev pkg-config \
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
unzip libpcre3-dev flex bison libboost-dev
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
if [ "$DIST" = "Ubuntu" ]; then
BLACKLIST=/etc/modprobe.d/blacklist.conf
else
BLACKLIST=/etc/modprobe.d/blacklist
if [ ! -d "ofsoftswitch13" ]; then
git clone https://github.com/CPqD/ofsoftswitch13.git
if [[ -n "$OF13_SWITCH_REV" ]]; then
cd ofsoftswitch13
git checkout ${OF13_SWITCH_REV}
cd ..
fi
fi
# Install netbee
NBEESRC="nbeesrc-jan-10-2013"
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
wget -nc ${NBEEURL}${NBEESRC}.zip
unzip ${NBEESRC}.zip
cd ${NBEESRC}/src
cmake .
make
cd $BUILD_DIR/
sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib
sudo ldconfig
sudo cp -R ${NBEESRC}/include/ /usr/
# Resume the install:
cd $BUILD_DIR/ofsoftswitch13
./boot.sh
./configure
make
sudo make install
cd $BUILD_DIR
}
function wireshark_version_check {
# Check Wireshark version
WS=$(which wireshark)
WS_VER_PATCH=(1 10) # targetting wireshark 1.10.0
WS_VER=($($WS --version | sed 's/[a-z ]*\([0-9]*\).\([0-9]*\).\([0-9]*\).*/\1 \2 \3/'))
if [ "${WS_VER[0]}" -lt "${WS_VER_PATCH[0]}" ] ||
[[ "${WS_VER[0]}" -le "${WS_VER_PATCH[0]}" && "${WS_VER[1]}" -lt "${WS_VER_PATCH[1]}" ]]
then
# pre-1.10.0 wireshark
echo "Setting revision: pre-ws-1.10.0"
WS_DISSECTOR_REV="pre-ws-1.10.0"
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" = "Fedora" ]; then
# Just install Fedora's wireshark RPMS
# Fedora's wirehark >= 1.10.2-2 includes an OF dissector
# (it has been backported from the future Wireshark 1.12 code base)
$install wireshark wireshark-gnome
return
fi
$install wireshark tshark 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 ~
$install scons mercurial libglib2.0-dev
$install libwiretap-dev libwireshark-dev
cd $BUILD_DIR
hg clone https://bitbucket.org/barnstorm/of-dissector
cd of-dissector/src
if [[ -z "$WS_DISSECTOR_REV" ]]; then
wireshark_version_check
fi
cd of-dissector
if [[ -n "$WS_DISSECTOR_REV" ]]; then
hg checkout ${WS_DISSECTOR_REV}
fi
# Build dissector
cd src
export WIRESHARK=/usr/include/wireshark
scons
# libwireshark0/ on 11.04; libwireshark1/ on later
WSDIR=`ls -d /usr/lib/wireshark/libwireshark* | head -1`
WSDIR=`find /usr/lib -type d -name '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
cd $BUILD_DIR/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
mkdir -p $HOME/.wireshark
cp $MININET_DIR/mininet/util/colorfilters $HOME/.wireshark
}
@@ -217,6 +306,16 @@ function wireshark {
function ovs {
echo "Installing Open vSwitch..."
if [ "$DIST" = "Fedora" ]; then
# Just install Fedora's openvswitch RPMS
$install openvswitch openvswitch-controller
return
fi
OVS_SRC=$BUILD_DIR/openvswitch
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
# Required for module build/dkms install
$install $KERNEL_HEADERS
@@ -254,7 +353,7 @@ function ovs {
ovspresent=1
fi
# Switch can run on its own, but
# 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
@@ -265,7 +364,7 @@ function ovs {
if [ $ovspresent = 1 ]; then
echo "Done (hopefully) installing packages"
cd ~
cd $BUILD_DIR
return
fi
@@ -284,11 +383,11 @@ function ovs {
sudo apt-get -y --force-yes -t lenny-backports install autoconf
fi
else
$install git-core
fi
$install git
fi
# Install OVS from release
cd ~/
cd $BUILD_DIR/
git clone git://openvswitch.org/openvswitch $OVS_SRC
cd $OVS_SRC
git checkout $OVS_TAG
@@ -329,6 +428,22 @@ function remove_ovs {
echo "Done removing OVS"
}
function ivs {
echo "Installing Indigo Virtual Switch..."
IVS_SRC=$BUILD_DIR/ivs
# Install dependencies
$install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
# Install IVS from source
cd $BUILD_DIR
git clone git://github.com/floodlight/ivs $IVS_SRC -b $IVS_TAG --recursive
cd $IVS_SRC
make
sudo make install
}
# Install NOX with tutorial files
function nox {
echo "Installing NOX w/tutorial files..."
@@ -339,7 +454,7 @@ function nox {
if [ "$DIST" = "Debian" ]; then
$install libboost1.35-dev
elif [ "$DIST" = "Ubuntu" ]; then
$install python-dev libboost-dev
$install python-dev libboost-dev
$install libboost-filesystem-dev
$install libboost-test-dev
fi
@@ -347,16 +462,18 @@ function nox {
$install libsqlite3-dev python-simplejson
# Fetch NOX destiny
cd ~/
git clone git://noxrepo.org/nox noxcore
cd $BUILD_DIR/
git clone https://github.com/noxrepo/nox-classic.git noxcore
cd noxcore
git checkout -b destiny remotes/origin/destiny
if ! git checkout -b destiny remotes/origin/destiny ; then
echo "Did not check out a new destiny branch - assuming current branch is destiny"
fi
# Apply patches
git checkout -b tutorial-destiny
git am ~/mininet/util/nox-patches/*tutorial-port-nox-destiny*.patch
git am $MININET_DIR/mininet/util/nox-patches/*tutorial-port-nox-destiny*.patch
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
git am ~/mininet/util/nox-patches/*nox-ubuntu12-hacks.patch
git am $MININET_DIR/mininet/util/nox-patches/*nox-ubuntu12-hacks.patch
fi
# Build
@@ -368,13 +485,54 @@ function nox {
#make check
# Add NOX_CORE_DIR env var:
sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=~/noxcore/build/src|' ~/.bashrc
sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=$BUILD_DIR/noxcore/build/src|' ~/.bashrc
# To verify this install:
#cd ~/noxcore/build/src
#./nox_core -v -i ptcp:
}
# Install NOX Classic/Zaku for OpenFlow 1.3
function nox13 {
echo "Installing NOX w/tutorial files..."
# Install NOX deps:
$install autoconf automake g++ libtool python python-twisted \
swig libssl-dev make
if [ "$DIST" = "Debian" ]; then
$install libboost1.35-dev
elif [ "$DIST" = "Ubuntu" ]; then
$install python-dev libboost-dev
$install libboost-filesystem-dev
$install libboost-test-dev
fi
# Fetch NOX destiny
cd $BUILD_DIR/
git clone https://github.com/CPqD/nox13oflib.git
cd nox13oflib
# Build
./boot.sh
mkdir build
cd build
../configure
make -j3
#make check
# To verify this install:
#cd ~/nox13oflib/build/src
#./nox_core -v -i ptcp:
}
# "Install" POX
function pox {
echo "Installing POX into $BUILD_DIR/pox..."
cd $BUILD_DIR
git clone https://github.com/noxrepo/pox.git
}
# Install OFtest
function oftest {
echo "Installing oftest..."
@@ -383,37 +541,69 @@ function oftest {
$install tcpdump python-scapy
# Install oftest:
cd ~/
git clone git://openflow.org/oftest
cd oftest
cd tools/munger
sudo make install
cd $BUILD_DIR/
git clone git://github.com/floodlight/oftest
}
# Install cbench
function cbench {
echo "Installing cbench..."
$install libsnmp-dev libpcap-dev libconfig-dev
cd ~/
if [ "$DIST" = "Fedora" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
else
$install libsnmp-dev libpcap-dev libconfig-dev
fi
cd $BUILD_DIR/
git clone git://openflow.org/oflops.git
cd oflops
sh boot.sh || true # possible error in autoreconf, so run twice
sh boot.sh
./configure --with-openflow-src-dir=$HOME/openflow
./configure --with-openflow-src-dir=$BUILD_DIR/openflow
make
sudo make install || true # make install fails; force past this
}
function other {
echo "Doing other setup tasks..."
function vm_other {
echo "Doing other Mininet VM setup tasks..."
# Remove avahi-daemon, which may cause unwanted discovery packets to be
# sent during tests, near link status changes:
echo "Removing avahi-daemon"
$remove avahi-daemon
# was: Disable IPv6. Add to /etc/modprobe.d/blacklist:
#echo "Attempting to disable IPv6"
#if [ "$DIST" = "Ubuntu" ]; then
# BLACKLIST=/etc/modprobe.d/blacklist.conf
#else
# BLACKLIST=/etc/modprobe.d/blacklist
#fi
#sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
# Disable IPv6
if ! grep 'disable IPv6' /etc/sysctl.conf; then
echo 'Disabling IPv6'
echo '
# Mininet: disable IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
fi
# Disabling IPv6 breaks X11 forwarding via ssh
line='AddressFamily inet'
file='/etc/ssh/sshd_config'
echo "Adding $line to $file"
if ! grep "$line" $file > /dev/null; then
echo "$line" | sudo tee -a $file > /dev/null
fi
# Enable command auto completion using sudo; modify ~/.bashrc:
sed -i -e 's|# for examples$|&\ncomplete -cf sudo|' ~/.bashrc
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
# Install tcpdump, cmd-line packet dump tool. Also install gitk,
# a graphical git history viewer.
$install tcpdump tshark gitk
$install tcpdump gitk
# Install common text editors
$install vim nano emacs
@@ -444,23 +634,41 @@ function other {
# a good idea to use a symbolic link in place of the copy below.
function modprobe {
echo "Setting up modprobe for OVS kmod..."
sudo cp $OVS_KMODS $DRIVERS_DIR
sudo depmod -a ${KERNEL_NAME}
set +o nounset
if [ -z "$OVS_KMODS" ]; then
echo "OVS_KMODS not set. Aborting."
else
sudo cp $OVS_KMODS $DRIVERS_DIR
sudo depmod -a ${KERNEL_NAME}
fi
set -o nounset
}
function all {
echo "Running all commands..."
if [ "$DIST" = "Fedora" ]; then
printf "\nFedora 18+ support (still work in progress):\n"
printf " * Fedora 18+ has kernel 3.10 RPMS in the updates repositories\n"
printf " * Fedora 18+ has openvswitch 1.10 RPMS in the updates repositories\n"
printf " * the install.sh script options [-bfnpvw] should work.\n"
printf " * for a basic setup just try:\n"
printf " install.sh -fnpv\n\n"
exit 3
fi
echo "Installing all packages except for -eix (doxypy, ivs, nox-classic)..."
kernel
mn_deps
# Skip mn_dev (doxypy/texlive/fonts/etc.) because it's huge
# mn_dev
of
wireshark
ovs
nox
# We may add ivs once it's more mature
# ivs
# NOX-classic is deprecated, but you can install it manually if desired.
# nox
pox
oftest
cbench
other
echo "Please reboot, then run ./mininet/util/install.sh -c to remove unneeded packages."
echo "Enjoy Mininet!"
}
@@ -468,6 +676,7 @@ function all {
function vm_clean {
echo "Cleaning VM..."
sudo apt-get clean
sudo apt-get autoremove
sudo rm -rf /tmp/*
sudo rm -rf openvswitch*.tar.gz
@@ -488,16 +697,20 @@ function vm_clean {
git config --global user.name "None"
git config --global user.email "None"
# Remove mininet install script
rm -f install-mininet.sh
# Note: you can shrink the .vmdk in vmware using
# vmware-vdiskmanager -k *.vmdk
echo "Zeroing out disk blocks for efficient compaction..."
time sudo dd if=/dev/zero of=/tmp/zero bs=1M
sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero
}
function usage {
printf 'Usage: %s [-acdfhkmntvxy]\n\n' $(basename $0) >&2
printf '\nUsage: %s [-abcdfhikmnprtvwx03]\n\n' $(basename $0) >&2
printf 'This install script attempts to install useful packages\n' >&2
printf 'for Mininet. It should (hopefully) work on Ubuntu 10.04, 11.10\n' >&2
printf 'and Debian 5.0 (Lenny). If you run into trouble, try\n' >&2
printf 'for Mininet. It should (hopefully) work on Ubuntu 11.10+\n' >&2
printf 'If you run into trouble, try\n' >&2
printf 'installing one thing at a time, and looking at the \n' >&2
printf 'specific installation function in this script.\n\n' >&2
@@ -505,43 +718,65 @@ function usage {
printf -- ' -a: (default) install (A)ll packages - good luck!\n' >&2
printf -- ' -b: install controller (B)enchmark (oflops)\n' >&2
printf -- ' -c: (C)lean up after kernel install\n' >&2
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
printf -- ' -f: install open(F)low\n' >&2
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
printf -- ' -e: install Mininet d(E)veloper dependencies\n' >&2
printf -- ' -f: install Open(F)low\n' >&2
printf -- ' -h: print this (H)elp message\n' >&2
printf -- ' -i: install (I)ndigo Virtual Switch\n' >&2
printf -- ' -k: install new (K)ernel\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 -- ' -n: install Mini(N)et dependencies + core files\n' >&2
printf -- ' -p: install (P)OX OpenFlow Controller\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
printf -- ' -s <dir>: place dependency (S)ource/build trees in <dir>\n' >&2
printf -- ' -t: complete o(T)her Mininet VM setup tasks\n' >&2
printf -- ' -v: install Open (V)switch\n' >&2
printf -- ' -w: install OpenFlow (W)ireshark dissector\n' >&2
printf -- ' -x: install NO(X) Classic OpenFlow controller\n' >&2
printf -- ' -0: (default) -0[fx] installs OpenFlow 1.0 versions\n' >&2
printf -- ' -3: -3[fx] installs OpenFlow 1.3 versions\n' >&2
exit 2
}
OF_VERSION=1.0
if [ $# -eq 0 ]
then
all
else
while getopts 'abcdfhkmnrtvwx' OPTION
while getopts 'abcdefhikmnprs:tvwx03' OPTION
do
case $OPTION in
a) all;;
b) cbench;;
c) kernel_clean;;
d) vm_clean;;
f) of;;
e) mn_dev;;
f) case $OF_VERSION in
1.0) of;;
1.3) of13;;
*) echo "Invalid OpenFlow version $OF_VERSION";;
esac;;
h) usage;;
i) ivs;;
k) kernel;;
m) modprobe;;
n) mn_deps;;
p) pox;;
r) remove_ovs;;
t) other;;
s) mkdir -p $OPTARG; # ensure the directory is created
BUILD_DIR="$( cd -P "$OPTARG" && pwd )"; # get the full path
echo "Dependency installation directory: $BUILD_DIR";;
t) vm_other;;
v) ovs;;
w) wireshark;;
x) nox;;
x) case $OF_VERSION in
1.0) nox;;
1.3) nox13;;
*) echo "Invalid OpenFlow version $OF_VERSION";;
esac;;
0) OF_VERSION=1.0;;
3) OF_VERSION=1.3;;
?) usage;;
esac
done
Executable
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
# Attach to a Mininet host and run a command
if [ -z $1 ]; then
echo "usage: $0 host cmd [args...]"
exit 1
else
host=$1
fi
pid=`ps ax | grep "mininet:$host$" | grep bash | awk '{print $1};'`
if echo $pid | grep -q ' '; then
echo "Error: found multiple mininet:$host processes"
exit 2
fi
if [ "$pid" == "" ]; then
echo "Could not find Mininet host $host"
exit 3
fi
if [ -z $2 ]; then
cmd='bash'
else
shift
cmd=$*
fi
cgroup=/sys/fs/cgroup/cpu/$host
if [ -d "$cgroup" ]; then
cg="-g $host"
fi
# Check whether host should be running in a chroot dir
rootdir="/var/run/mn/$host/root"
if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
cmd="'cd `pwd`; exec $cmd'"
cmd="chroot $rootdir /bin/bash -c $cmd"
fi
cmd="exec sudo mnexec -a $pid $cg $cmd"
eval $cmd
+1
View File
@@ -15,3 +15,4 @@ net.ipv4.neigh.default.gc_thresh3 = 16384
# Mininet: increase routing table size
net.ipv4.route.max_size=32768
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/python
from subprocess import check_output as co
from sys import exit
# Actually run bin/mn rather than importing via python path
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
version = version.strip()
# Find all Mininet path references
lines = co( "grep -or 'Mininet \w\.\w\.\w\w*' *", shell=True )
error = False
for line in lines.split( '\n' ):
if line and 'Binary' not in line:
fname, fversion = line.split( ':' )
if version != fversion:
print "%s: incorrect version '%s' (should be '%s')" % (
fname, fversion, version )
error = True
if error:
exit( 1 )
+907
View File
@@ -0,0 +1,907 @@
#!/usr/bin/python
"""
build.py: build a Mininet VM
Basic idea:
prepare
-> create base install image if it's missing
- download iso if it's missing
- install from iso onto image
build
-> create cow disk for new VM, based on base image
-> boot it in qemu/kvm with text /serial console
-> install Mininet
test
-> sudo mn --test pingall
-> make test
release
-> shut down VM
-> shrink-wrap VM
-> upload to storage
"""
import os
from os import stat, path
from stat import ST_MODE, ST_SIZE
from os.path import abspath
from sys import exit, stdout, argv, modules
import re
from glob import glob
from subprocess import check_output, call, Popen
from tempfile import mkdtemp, NamedTemporaryFile
from time import time, strftime, localtime
import argparse
from distutils.spawn import find_executable
import inspect
pexpect = None # For code check - imported dynamically
# boot can be slooooow!!!! need to debug/optimize somehow
TIMEOUT=600
# Some configuration options
# Possibly change this to use the parsed arguments instead!
LogToConsole = False # VM output to console rather than log file
SaveQCOW2 = False # Save QCOW2 image rather than deleting it
NoKVM = False # Don't use kvm and use emulation instead
Branch = None # Branch to update and check out before testing
Zip = False # Archive .ovf and .vmdk into a .zip file
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
Prompt = '\$ ' # Shell prompt that pexpect will wait for
isoURLs = {
'precise32server':
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
'ubuntu-12.04.3-server-i386.iso',
'precise64server':
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
'ubuntu-12.04.3-server-amd64.iso',
'quantal32server':
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
'ubuntu-12.10-server-i386.iso',
'quantal64server':
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
'ubuntu-12.10-server-amd64.iso',
'raring32server':
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
'ubuntu-13.04-server-i386.iso',
'raring64server':
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
'ubuntu-13.04-server-amd64.iso',
'saucy32server':
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
'ubuntu-13.10-server-i386.iso',
'saucy64server':
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
'ubuntu-13.10-server-amd64.iso',
}
def OSVersion( flavor ):
"Return full OS version string for build flavor"
urlbase = path.basename( isoURLs.get( flavor, 'unknown' ) )
return path.splitext( urlbase )[ 0 ]
LogStartTime = time()
LogFile = None
def log( *args, **kwargs ):
"""Simple log function: log( message along with local and elapsed time
cr: False/0 for no CR"""
cr = kwargs.get( 'cr', True )
elapsed = time() - LogStartTime
clocktime = strftime( '%H:%M:%S', localtime() )
msg = ' '.join( str( arg ) for arg in args )
output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg )
if cr:
print output
else:
print output,
# Optionally mirror to LogFile
if type( LogFile ) is file:
if cr:
output += '\n'
LogFile.write( output )
LogFile.flush()
def run( cmd, **kwargs ):
"Convenient interface to check_output"
log( '-', cmd )
cmd = cmd.split()
arg0 = cmd[ 0 ]
if not find_executable( arg0 ):
raise Exception( 'Cannot find executable "%s";' % arg0 +
'you might try %s --depend' % argv[ 0 ] )
return check_output( cmd, **kwargs )
def srun( cmd, **kwargs ):
"Run + sudo"
return run( 'sudo ' + cmd, **kwargs )
# BL: we should probably have a "checkDepend()" which
# checks to make sure all dependencies are satisfied!
def depend():
"Install package dependencies"
log( '* Installing package dependencies' )
run( 'sudo apt-get -y update' )
run( 'sudo apt-get install -y'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs '
' landscape-client'
' python-setuptools mtools zip' )
run( 'sudo easy_install pexpect' )
def popen( cmd ):
"Convenient interface to popen"
log( cmd )
cmd = cmd.split()
return Popen( cmd )
def remove( fname ):
"Remove a file, ignoring errors"
try:
os.remove( fname )
except OSError:
pass
def findiso( flavor ):
"Find iso, fetching it if it's not there already"
url = isoURLs[ flavor ]
name = path.basename( url )
iso = path.join( VMImageDir, name )
if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0777 != 0444 ):
log( '* Retrieving', url )
run( 'curl -C - -o %s %s' % ( iso, url ) )
if 'ISO' not in run( 'file ' + iso ):
os.remove( iso )
raise Exception( 'findiso: could not download iso from ' + url )
# Write-protect iso, signaling it is complete
log( '* Write-protecting iso', iso)
os.chmod( iso, 0444 )
log( '* Using iso', iso )
return iso
def attachNBD( cow, flags='' ):
"""Attempt to attach a COW disk image and return its nbd device
flags: additional flags for qemu-nbd (e.g. -r for readonly)"""
# qemu-nbd requires an absolute path
cow = abspath( cow )
log( '* Checking for unused /dev/nbdX device ' )
for i in range ( 0, 63 ):
nbd = '/dev/nbd%d' % i
# Check whether someone's already messing with that device
if call( [ 'pgrep', '-f', nbd ] ) == 0:
continue
srun( 'modprobe nbd max-part=64' )
srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
print
return nbd
raise Exception( "Error: could not find unused /dev/nbdX device" )
def detachNBD( nbd ):
"Detatch an nbd device"
srun( 'qemu-nbd -d ' + nbd )
def extractKernel( image, flavor, imageDir=VMImageDir ):
"Extract kernel and initrd from base image"
kernel = path.join( imageDir, flavor + '-vmlinuz' )
initrd = path.join( imageDir, flavor + '-initrd' )
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444:
# If kernel is there, then initrd should also be there
return kernel, initrd
log( '* Extracting kernel to', kernel )
nbd = attachNBD( image, flags='-r' )
print srun( 'partx ' + nbd )
# Assume kernel is in partition 1/boot/vmlinuz*generic for now
part = nbd + 'p1'
mnt = mkdtemp()
srun( 'mount -o ro %s %s' % ( part, mnt ) )
kernsrc = glob( '%s/boot/vmlinuz*generic' % mnt )[ 0 ]
initrdsrc = glob( '%s/boot/initrd*generic' % mnt )[ 0 ]
srun( 'cp %s %s' % ( initrdsrc, initrd ) )
srun( 'chmod 0444 ' + initrd )
srun( 'cp %s %s' % ( kernsrc, kernel ) )
srun( 'chmod 0444 ' + kernel )
srun( 'umount ' + mnt )
run( 'rmdir ' + mnt )
detachNBD( nbd )
return kernel, initrd
def findBaseImage( flavor, size='8G' ):
"Return base VM image and kernel, creating them if needed"
image = path.join( VMImageDir, flavor + '-base.qcow2' )
if path.exists( image ):
# Detect race condition with multiple builds
perms = stat( image )[ ST_MODE ] & 0777
if perms != 0444:
raise Exception( 'Error - %s is writable ' % image +
'; are multiple builds running?' )
else:
# We create VMImageDir here since we are called first
run( 'mkdir -p %s' % VMImageDir )
iso = findiso( flavor )
log( '* Creating image file', image )
run( 'qemu-img create -f qcow2 %s %s' % ( image, size ) )
installUbuntu( iso, image )
# Write-protect image, also signaling it is complete
log( '* Write-protecting image', image)
os.chmod( image, 0444 )
kernel, initrd = extractKernel( image, flavor )
log( '* Using base image', image, 'and kernel', kernel )
return image, kernel, initrd
# Kickstart and Preseed files for Ubuntu/Debian installer
#
# Comments: this is really clunky and painful. If Ubuntu
# gets their act together and supports kickstart a bit better
# then we can get rid of preseed and even use this as a
# Fedora installer as well.
#
# Another annoying thing about Ubuntu is that it can't just
# install a normal system from the iso - it has to download
# junk from the internet, making this house of cards even
# more precarious.
KickstartText ="""
#Generated by Kickstart Configurator
#platform=x86
#System language
lang en_US
#Language modules to install
langsupport en_US
#System keyboard
keyboard us
#System mouse
mouse
#System timezone
timezone America/Los_Angeles
#Root password
rootpw --disabled
#Initial user
user mininet --fullname "mininet" --password "mininet"
#Use text mode install
text
#Install OS instead of upgrade
install
#Use CDROM installation media
cdrom
#System bootloader configuration
bootloader --location=mbr
#Clear the Master Boot Record
zerombr yes
#Partition clearing information
clearpart --all --initlabel
#Automatic partitioning
autopart
#System authorization infomation
auth --useshadow --enablemd5
#Firewall configuration
firewall --disabled
#Do not configure the X Window System
skipx
"""
# Tell the Ubuntu/Debian installer to stop asking stupid questions
PreseedText = """
d-i mirror/country string manual
d-i mirror/http/hostname string mirrors.kernel.org
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i user-setup/allow-password-weak boolean true
d-i finish-install/reboot_in_progress note
d-i debian-installer/exit/poweroff boolean true
"""
def makeKickstartFloppy():
"Create and return kickstart floppy, kickstart, preseed"
kickstart = 'ks.cfg'
with open( kickstart, 'w' ) as f:
f.write( KickstartText )
preseed = 'ks.preseed'
with open( preseed, 'w' ) as f:
f.write( PreseedText )
# Create floppy and copy files to it
floppy = 'ksfloppy.img'
run( 'qemu-img create %s 1440k' % floppy )
run( 'mkfs -t msdos ' + floppy )
run( 'mcopy -i %s %s ::/' % ( floppy, kickstart ) )
run( 'mcopy -i %s %s ::/' % ( floppy, preseed ) )
return floppy, kickstart, preseed
def archFor( filepath ):
"Guess architecture for file path"
name = path.basename( filepath )
if 'amd64' in name or 'x86_64' in name:
arch = 'x86_64'
# Beware of version 64 of a 32-bit OS
elif 'i386' in name or '32' in name or 'x86' in name:
arch = 'i386'
elif '64' in name:
arch = 'x86_64'
else:
log( "Error: can't discern CPU for name", name )
exit( 1 )
return arch
def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
"Install Ubuntu from iso onto image"
kvm = 'qemu-system-' + archFor( iso )
floppy, kickstart, preseed = makeKickstartFloppy()
# Mount iso so we can use its kernel
mnt = mkdtemp()
srun( 'mount %s %s' % ( iso, mnt ) )
kernel = path.join( mnt, 'install/vmlinuz' )
initrd = path.join( mnt, 'install/initrd.gz' )
if NoKVM:
accel = 'tcg'
else:
accel = 'kvm'
cmd = [ 'sudo', kvm,
'-machine', 'accel=%s' % accel,
'-nographic',
'-netdev', 'user,id=mnbuild',
'-device', 'virtio-net,netdev=mnbuild',
'-m', str( memory ),
'-k', 'en-us',
'-fda', floppy,
'-drive', 'file=%s,if=virtio' % image,
'-cdrom', iso,
'-kernel', kernel,
'-initrd', initrd,
'-append',
' ks=floppy:/' + kickstart +
' preseed/file=floppy://' + preseed +
' console=ttyS0' ]
ubuntuStart = time()
log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image )
log( ' '.join( cmd ) )
log( '* logging to', abspath( logfilename ) )
params = {}
if not LogToConsole:
logfile = open( logfilename, 'w' )
params = { 'stdout': logfile, 'stderr': logfile }
vm = Popen( cmd, **params )
log( '* Waiting for installation to complete')
vm.wait()
if not LogToConsole:
logfile.close()
elapsed = time() - ubuntuStart
# Unmount iso and clean up
srun( 'umount ' + mnt )
run( 'rmdir ' + mnt )
if vm.returncode != 0:
raise Exception( 'Ubuntu installation returned error %d' %
vm.returncode )
log( '* UBUNTU INSTALLATION COMPLETED FOR', image )
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
def boot( cow, kernel, initrd, logfile, memory=1024 ):
"""Boot qemu/kvm with a COW disk and local/user data store
cow: COW disk path
kernel: kernel path
logfile: log file for pexpect object
memory: memory size in MB
returns: pexpect object to qemu process"""
# pexpect might not be installed until after depend() is called
global pexpect
import pexpect
arch = archFor( kernel )
log( '* Detected kernel architecture', arch )
if NoKVM:
accel = 'tcg'
else:
accel = 'kvm'
cmd = [ 'sudo', 'qemu-system-' + arch,
'-machine accel=%s' % accel,
'-nographic',
'-netdev user,id=mnbuild',
'-device virtio-net,netdev=mnbuild',
'-m %s' % memory,
'-k en-us',
'-kernel', kernel,
'-initrd', initrd,
'-drive file=%s,if=virtio' % cow,
'-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
cmd = ' '.join( cmd )
log( '* BOOTING VM FROM', cow )
log( cmd )
vm = pexpect.spawn( cmd, timeout=TIMEOUT, logfile=logfile )
return vm
def login( vm ):
"Log in to vm (pexpect object)"
log( '* Waiting for login prompt' )
vm.expect( 'login: ' )
log( '* Logging in' )
vm.sendline( 'mininet' )
log( '* Waiting for password prompt' )
vm.expect( 'Password: ' )
log( '* Sending password' )
vm.sendline( 'mininet' )
log( '* Waiting for login...' )
def sanityTest( vm ):
"Run Mininet sanity test (pingall) in vm"
vm.sendline( 'sudo mn --test pingall' )
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0:
log( '* Sanity check OK' )
else:
log( '* Sanity check FAILED' )
log( '* Sanity check output:' )
log( vm.before )
def coreTest( vm, prompt=Prompt ):
"Run core tests (make test) in VM"
log( '* Making sure cgroups are mounted' )
vm.sendline( 'sudo service cgroup-lite restart' )
vm.expect( prompt )
vm.sendline( 'sudo cgroups-mount' )
vm.expect( prompt )
log( '* Running make test' )
vm.sendline( 'cd ~/mininet; sudo make test' )
# We should change "make test" to report the number of
# successful and failed tests. For now, we have to
# know the time for each test, which means that this
# script will have to change as we add more tests.
for test in range( 0, 2 ):
if vm.expect( [ 'OK', 'FAILED', pexpect.TIMEOUT ], timeout=180 ) == 0:
log( '* Test', test, 'OK' )
else:
log( '* Test', test, 'FAILED' )
log( '* Test', test, 'output:' )
log( vm.before )
def examplesquickTest( vm, prompt=Prompt ):
"Quick test of mininet examples"
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v -quick' )
def examplesfullTest( vm, prompt=Prompt ):
"Full (slow) test of mininet examples"
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v' )
def walkthroughTest( vm, prompt=Prompt ):
"Test mininet walkthrough"
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/mininet/test/test_walkthrough.py -v' )
def checkOutBranch( vm, branch, prompt=Prompt ):
vm.sendline( 'cd ~/mininet; git fetch; git pull --rebase; git checkout '
+ branch )
vm.expect( prompt )
vm.sendline( 'sudo make install' )
def interact( vm, tests, pre='', post='', prompt=Prompt ):
"Interact with vm, which is a pexpect object"
login( vm )
log( '* Waiting for login...' )
vm.expect( prompt )
log( '* Sending hostname command' )
vm.sendline( 'hostname' )
log( '* Waiting for output' )
vm.expect( prompt )
log( '* Fetching Mininet VM install script' )
vm.sendline( 'wget '
'https://raw.github.com/mininet/mininet/master/util/vm/'
'install-mininet-vm.sh' )
vm.expect( prompt )
log( '* Running VM install script' )
vm.sendline( 'bash install-mininet-vm.sh' )
vm.expect ( 'password for mininet: ' )
vm.sendline( 'mininet' )
log( '* Waiting for script to complete... ' )
# Gigantic timeout for now ;-(
vm.expect( 'Done preparing Mininet', timeout=3600 )
log( '* Completed successfully' )
vm.expect( prompt )
version = getMininetVersion( vm )
vm.expect( prompt )
log( '* Mininet version: ', version )
log( '* Testing Mininet' )
runTests( vm, tests=tests, pre=pre, post=post )
log( '* Shutting down' )
vm.sendline( 'sync; sudo shutdown -h now' )
log( '* Waiting for EOF/shutdown' )
vm.read()
log( '* Interaction complete' )
return version
def cleanup():
"Clean up leftover qemu-nbd processes and other junk"
call( [ 'sudo', 'pkill', '-9', 'qemu-nbd' ] )
def convert( cow, basename ):
"""Convert a qcow2 disk to a vmdk and put it a new directory
basename: base name for output vmdk file"""
vmdk = basename + '.vmdk'
log( '* Converting qcow2 to vmdk' )
run( 'qemu-img convert -f qcow2 -O vmdk %s %s' % ( cow, vmdk ) )
return vmdk
# Template for OVF - a very verbose format!
# In the best of all possible worlds, we might use an XML
# library to generate this, but a template is easier and
# possibly more concise!
# Warning: XML file cannot begin with a newline!
OVFTemplate = """<?xml version="1.0"?>
<Envelope ovf:version="1.0" xml:lang="en-US"
xmlns="http://schemas.dmtf.org/ovf/envelope/1"
xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<References>
<File ovf:href="%s" ovf:id="file1" ovf:size="%d"/>
</References>
<DiskSection>
<Info>Virtual disk information</Info>
<Disk ovf:capacity="%d" ovf:capacityAllocationUnits="byte"
ovf:diskId="vmdisk1" ovf:fileRef="file1"
ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html"/>
</DiskSection>
<NetworkSection>
<Info>The list of logical networks</Info>
<Network ovf:name="nat">
<Description>The nat network</Description>
</Network>
</NetworkSection>
<VirtualSystem ovf:id="Mininet-VM">
<Info>A Mininet Virtual Machine (%s)</Info>
<Name>mininet-vm</Name>
<VirtualHardwareSection>
<Info>Virtual hardware requirements</Info>
<Item>
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
<rasd:Description>Number of Virtual CPUs</rasd:Description>
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
<rasd:Description>Memory Size</rasd:Description>
<rasd:ElementName>%dMB of memory</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>%d</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Caption>scsiController0</rasd:Caption>
<rasd:Description>SCSI Controller</rasd:Description>
<rasd:ElementName>scsiController0</rasd:ElementName>
<rasd:InstanceID>4</rasd:InstanceID>
<rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
<rasd:ResourceType>6</rasd:ResourceType>
</Item>
<Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:ElementName>disk1</rasd:ElementName>
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
<rasd:InstanceID>11</rasd:InstanceID>
<rasd:Parent>4</rasd:Parent>
<rasd:ResourceType>17</rasd:ResourceType>
</Item>
<Item>
<rasd:AddressOnParent>2</rasd:AddressOnParent>
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
<rasd:Connection>nat</rasd:Connection>
<rasd:Description>E1000 ethernet adapter on nat</rasd:Description>
<rasd:ElementName>ethernet0</rasd:ElementName>
<rasd:InstanceID>12</rasd:InstanceID>
<rasd:ResourceSubType>E1000</rasd:ResourceSubType>
<rasd:ResourceType>10</rasd:ResourceType>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Caption>usb</rasd:Caption>
<rasd:Description>USB Controller</rasd:Description>
<rasd:ElementName>usb</rasd:ElementName>
<rasd:InstanceID>9</rasd:InstanceID>
<rasd:ResourceType>23</rasd:ResourceType>
</Item>
</VirtualHardwareSection>
</VirtualSystem>
</Envelope>
"""
def generateOVF( name, diskname, disksize, mem=1024 ):
"""Generate (and return) OVF file "name.ovf"
name: root name of OVF file to generate
diskname: name of disk file
disksize: size of virtual disk in bytes
mem: VM memory size in MB"""
ovf = name + '.ovf'
filesize = stat( diskname )[ ST_SIZE ]
# OVFTemplate uses the memory size twice in a row
xmltext = OVFTemplate % ( diskname, filesize, disksize, name, mem, mem )
with open( ovf, 'w+' ) as f:
f.write( xmltext )
return ovf
def qcow2size( qcow2 ):
"Return virtual disk size (in bytes) of qcow2 image"
output = check_output( [ 'file', qcow2 ] )
assert 'QCOW' in output
bytes = int( re.findall( '(\d+) bytes', output )[ 0 ] )
return bytes
def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
"""Build a Mininet VM; return vmdk and vdisk size
tests: tests to run
pre: command line to run in VM before tests
post: command line to run in VM after tests
prompt: shell prompt (default '$ ')
memory: memory size in MB"""
global LogFile, Zip
start = time()
lstart = localtime()
date = strftime( '%y%m%d-%H-%M-%S', lstart)
ovfdate = strftime( '%y%m%d', lstart )
dir = 'mn-%s-%s' % ( flavor, date )
try:
os.mkdir( dir )
except:
raise Exception( "Failed to create build directory %s" % dir )
os.chdir( dir )
LogFile = open( 'build.log', 'w' )
log( '* Logging to', abspath( LogFile.name ) )
log( '* Created working directory', dir )
image, kernel, initrd = findBaseImage( flavor )
basename = 'mininet-' + flavor
volume = basename + '.qcow2'
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, volume ) )
log( '* VM image for', flavor, 'created as', volume )
if LogToConsole:
logfile = stdout
else:
logfile = open( flavor + '.log', 'w+' )
log( '* Logging results to', abspath( logfile.name ) )
vm = boot( volume, kernel, initrd, logfile, memory=memory )
version = interact( vm, tests=tests, pre=pre, post=post )
size = qcow2size( volume )
vmdk = convert( volume, basename='mininet-vm-' + archFor( flavor ) )
if not SaveQCOW2:
log( '* Removing qcow2 volume', volume )
os.remove( volume )
log( '* Converted VM image stored as', abspath( vmdk ) )
ovfname = 'mininet-%s-%s-%s' % ( version, ovfdate, OSVersion( flavor ) )
ovf = generateOVF( diskname=vmdk, disksize=size, name=ovfname )
log( '* Generated OVF descriptor file', ovf )
if Zip:
log( '* Generating .zip file' )
run( 'zip %s-ovf.zip %s %s' % ( ovfname, ovf, vmdk ) )
end = time()
elapsed = end - start
log( '* Results logged to', abspath( logfile.name ) )
log( '* Completed in %.2f seconds' % elapsed )
log( '* %s VM build DONE!!!!! :D' % flavor )
os.chdir( '..' )
def runTests( vm, tests=None, pre='', post='', prompt=Prompt ):
"Run tests (list) in vm (pexpect object)"
if not tests:
tests = []
if pre:
log( '* Running command', pre )
vm.sendline( pre )
vm.expect( prompt )
testfns = testDict()
if tests:
log( '* Running tests' )
for test in tests:
if test not in testfns:
raise Exception( 'Unknown test: ' + test )
log( '* Running test', test )
fn = testfns[ test ]
fn( vm )
vm.expect( prompt )
if post:
log( '* Running post-test command', post )
vm.sendline( post )
vm.expect( prompt )
def getMininetVersion( vm ):
"Run mn to find Mininet version in VM"
vm.sendline( '~/mininet/bin/mn --version' )
# Eat command line echo, then read output line
vm.readline()
version = vm.readline().strip()
return version
def bootAndRunTests( image, tests=None, pre='', post='', prompt=Prompt,
memory=1024 ):
"""Boot and test VM
tests: list of tests to run
pre: command line to run in VM before tests
post: command line to run in VM after tests
prompt: shell prompt (default '$ ')
memory: VM memory size in MB"""
bootTestStart = time()
basename = path.basename( image )
image = abspath( image )
tmpdir = mkdtemp( prefix='test-' + basename )
log( '* Using tmpdir', tmpdir )
cow = path.join( tmpdir, basename + '.qcow2' )
log( '* Creating COW disk', cow )
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) )
log( '* Extracting kernel and initrd' )
kernel, initrd = extractKernel( image, flavor=basename, imageDir=tmpdir )
if LogToConsole:
logfile = stdout
else:
logfile = NamedTemporaryFile( prefix=basename,
suffix='.testlog', delete=False )
log( '* Logging VM output to', logfile.name )
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
memory=memory )
login( vm )
log( '* Waiting for prompt after login' )
vm.expect( prompt )
if Branch:
checkOutBranch( vm, branch=Branch )
vm.expect( prompt )
runTests( vm, tests=tests, pre=pre, post=post )
# runTests eats its last prompt, but maybe it shouldn't...
log( '* Shutting down' )
vm.sendline( 'sudo shutdown -h now ' )
log( '* Waiting for shutdown' )
vm.wait()
log( '* Removing temporary dir', tmpdir )
srun( 'rm -rf ' + tmpdir )
elapsed = time() - bootTestStart
log( '* Boot and test completed in %.2f seconds' % elapsed )
def buildFlavorString():
"Return string listing valid build flavors"
return 'valid build flavors: ( %s )' % ' '.join( sorted( isoURLs ) )
def testDict():
"Return dict of tests in this module"
suffix = 'Test'
trim = len( suffix )
fdict = dict( [ ( fname[ : -trim ], f ) for fname, f in
inspect.getmembers( modules[ __name__ ],
inspect.isfunction )
if fname.endswith( suffix ) ] )
return fdict
def testString():
"Return string listing valid tests"
return 'valid tests: ( %s )' % ' '.join( testDict().keys() )
def parseArgs():
"Parse command line arguments and run"
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT
parser = argparse.ArgumentParser( description='Mininet VM build script',
epilog=buildFlavorString() + ' ' +
testString() )
parser.add_argument( '-v', '--verbose', action='store_true',
help='send VM output to console rather than log file' )
parser.add_argument( '-d', '--depend', action='store_true',
help='install dependencies for this script' )
parser.add_argument( '-l', '--list', action='store_true',
help='list valid build flavors and tests' )
parser.add_argument( '-c', '--clean', action='store_true',
help='clean up leftover build junk (e.g. qemu-nbd)' )
parser.add_argument( '-q', '--qcow2', action='store_true',
help='save qcow2 image rather than deleting it' )
parser.add_argument( '-n', '--nokvm', action='store_true',
help="Don't use kvm - use tcg emulation instead" )
parser.add_argument( '-m', '--memory', metavar='MB', type=int,
default=1024, help='VM memory size in MB' )
parser.add_argument( '-i', '--image', metavar='image', default=[],
action='append',
help='Boot and test an existing VM image' )
parser.add_argument( '-t', '--test', metavar='test', default=[],
action='append',
help='specify a test to run' )
parser.add_argument( '-w', '--timeout', metavar='timeout', type=int,
default=0, help='set expect timeout' )
parser.add_argument( '-r', '--run', metavar='cmd', default='',
help='specify a command line to run before tests' )
parser.add_argument( '-p', '--post', metavar='cmd', default='',
help='specify a command line to run after tests' )
parser.add_argument( '-b', '--branch', metavar='branch',
help='For an existing VM image, check out and install'
' this branch before testing' )
parser.add_argument( 'flavor', nargs='*',
help='VM flavor(s) to build (e.g. raring32server)' )
parser.add_argument( '-z', '--zip', action='store_true',
help='archive .ovf and .vmdk into .zip file' )
args = parser.parse_args()
if args.depend:
depend()
if args.list:
print buildFlavorString()
if args.clean:
cleanup()
if args.verbose:
LogToConsole = True
if args.nokvm:
NoKVM = True
if args.branch:
Branch = args.branch
if args.zip:
Zip = True
if args.timeout:
TIMEOUT = args.timeout
if not args.test and not args.run and not args.post:
args.test = [ 'sanity', 'core' ]
for flavor in args.flavor:
if flavor not in isoURLs:
print "Unknown build flavor:", flavor
print buildFlavorString()
break
try:
build( flavor, tests=args.test, pre=args.run, post=args.post,
memory=args.memory )
except Exception as e:
log( '* BUILD FAILED with exception: ', e )
exit( 1 )
for image in args.image:
bootAndRunTests( image, tests=args.test, pre=args.run,
post=args.post, memory=args.memory)
if not ( args.depend or args.list or args.clean or args.flavor
or args.image ):
parser.print_help()
if __name__ == '__main__':
parseArgs()
Regular → Executable
+22 -17
View File
@@ -1,36 +1,41 @@
#!/bin/bash
# This script is intended to install Mininet into
# a brand-new Ubuntu (10.04 or 11.10) virtual machine,
# a brand-new Ubuntu virtual machine,
# to create a fully usable "tutorial" VM.
set -e
sudo sh -c 'cat >> /etc/sudoers' <<EOF
openflow ALL=NOPASSWD: ALL
EOF
echo `whoami` ALL=NOPASSWD: ALL | sudo tee -a /etc/sudoers > /dev/null
sudo sed -i -e 's/Default/#Default/' /etc/sudoers
sudo sed -i -e 's/ubuntu/mininet-vm/' /etc/hostname
echo mininet-vm | sudo tee /etc/hostname > /dev/null
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
# 12.10 and earlier
sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
/etc/apt/sources.list
# 13.04 and later
sudo sed -i -e 's/\/archive.ubuntu.com/\/mirrors.kernel.org/' \
/etc/apt/sources.list
sudo apt-get update
# Clean up vmware easy install junk if present
if [ -e /etc/issue.backup ]; then
sudo mv /etc/issue.backup /etc/issue
fi
if [ -e /etc/rc.local.backup ]; then
sudo mv /etc/rc.local.backup /etc/rc.local
fi
# Install Mininet
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
# Finalize VM
time mininet/util/install.sh -tc
# Ignoring this since NOX classic is deprecated
#if ! grep NOX_CORE_DIR .bashrc; then
# echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc
#fi
echo "Done preparing Mininet VM."