Compare commits

...
273 Commits
Author SHA1 Message Date
lantz 008f62f6ce Merge branch 'master' into rc-local 2021-02-06 15:55:55 -08:00
lantz 06dae1adc1 fix bootAndRun() (#1024)
* rc.local: /usr/bin/bash -> /bin/bash

bash is (and should be) located in /bin

this may have caused ubuntu 18.04 to not execute
/etc/rc.local and regenerate ssh keys

* fix bootAndRun()
2021-02-06 15:55:40 -08:00
lantz f0c726a42f Use /usr/bin/env python for virtualenv (#1025)
This helps with virtualenv although it can open
up another security hole if you end up using an
unexpected python interpreter.

Overall it seems to make sense to err on the side
of usability but it's good to be aware of security.

However, for the remaining utility scripts that require
python 2, we explicitly note this with #!/usr/bin/python2.
2021-02-05 07:42:50 -08:00
Bob Lantz f33a8f0eea rc.local: /usr/bin/bash -> /bin/bash
bash is (and should be) located in /bin

this may have caused ubuntu 18.04 to not execute
/etc/rc.local and regenerate ssh keys
2021-02-01 16:21:43 -08:00
lantz a882d68a5f Update README.md 2021-01-30 16:42:03 -08:00
lantz 9b69c99f4e Update INSTALL 2021-01-30 16:40:40 -08:00
lantz 904796436d Update README.md 2021-01-30 16:38:36 -08:00
lantz ec2d93d482 2.3.0b2 (#1022)
+ text file updates
2021-01-30 15:41:20 -08:00
lantz dffddc338f Install python3 mn last in install-mininet-vm.sh (#1021)
This means that 'mn' will run be default in python3.

For python2, run

    sudo python2 `which mn`

Also:

- change mn execution line to /usr/bin/python
  (install seems to change it to python2 or python3)

- don't install python-is-python3 package
2021-01-29 22:39:19 -08:00
lantz b6ad3a1619 Mininet 2.3.0b1 (#1019)
Also tweak README.md, INSTALL, and CONTRIBUTORS
2021-01-29 16:15:39 -08:00
lantz 96ac94a985 fix session typo
thanks to Vintas Avinash
closes #1011
2021-01-29 15:47:47 -08:00
lantz 715db45b9d more logging fixes (#1018)
- call getLogger( "mininet" ) to set lg properly
- support warning() as well as (deprecated) warn()
- rearrange initialization slightly
2021-01-28 18:48:45 -08:00
lantz e07a29b4e6 fix make develop: --no-binary :all: (#1017) 2021-01-28 16:04:39 -08:00
lantz 2255bc7da3 Fix/clean up setLogLevel (#1016)
Sometimes we got no output with python 3 on ubuntu 20
This simplifies setLogLevel slightly and should work
on python 2 and python 3.
2021-01-27 18:27:22 -08:00
lantz 12f4d5b8ae Fetch older version of get-pip.py for python 2 (#1015)
fixes #1014
2021-01-26 00:18:54 -08:00
lantz f9d5ef3461 remove .travis.yml and travis badge from README.md (#1013)
Thanks to travis.org for the CI support for many years.
2021-01-25 14:56:05 -08:00
lantz dad451bf8f Pass code check with pylint=2.4.4 (#1012) 2021-01-25 14:00:53 -08:00
lantz 77938e0a85 Increase timeout for emulated build (#1009) 2021-01-16 19:21:00 -08:00
lantz e4003290e0 Try to install python2 and python3 mininet (#1008) 2021-01-13 23:24:06 -08:00
lantz 9517f6c197 Build script for 20.04 (#1004)
Should be able to build 16.04, 18.04 and 20.04,
as well as branch builds.
2021-01-13 19:36:18 -08:00
lantz d8c30910cb ubuntu 14 code check fixes so travis can pass (#1006) 2021-01-09 21:15:27 -08:00
lantz 53975940de Test fixes and debugging (#1005)
test_controlnet: log to stdout for now
test_walkthrough: wait for http server connection
2021-01-09 18:53:50 -08:00
lantz 77c473687e Wait for exit to avoid leftover dp0 (#1002)
In certain cases, dp0 and its interfaces were not
being cleaned up, probably due to scratchnet.py being
killed before it terminated. This in turn caused the
natnet test to fail.
2021-01-05 17:01:35 -08:00
lantz 0847991030 Install pip for debian/ubuntu (#1001) 2021-01-04 22:14:10 -08:00
lantz 462929b3af Run full core test suite (#1000)
also remove unneeded rm -f pexpect.out since we are not
generating that at the moment
2021-01-04 19:12:43 -08:00
lantz 26c7c70024 use pip for make {install,develop} (#999) 2021-01-04 18:51:54 -08:00
lantz 942745e91f Fix tree1024.py (#995) 2021-01-04 18:35:42 -08:00
lantz dcc39a7b25 Loosen test_intfoptions tolerance to 25% for CI (#998)
Adjusting the tolerance for this test to compensate
for apparent performance variation when running as
part of github actions CI builds/tests.
2021-01-04 18:25:35 -08:00
Giuseppe Di Lena ebdb3a5107 Fix a small typo error in clusterdemo.py (#945) 2021-01-04 14:14:44 -08:00
lantz 6def304585 Remove long tests from travis (#993) 2021-01-04 13:53:30 -08:00
lantz e02e338e3c intfoptions and test adjustments (#992)
* 2.3.0a1

* intfoptions and test_intfoptions tweaks

also added cleanup() to test_walkthrough fixture
2021-01-03 22:24:28 -08:00
lantz b7c412073a CI matrix build + 20.04 fixes (#990)
Ubuntu 20.04 fixes:
- fixes sshd test
- speeds up examples/{treeping64,tree1024}.py
- debugging hacks/output for testLinkChange
- removes cfs from examples/popen.py
- improves nat in nodelib (netplan fixes?)
- makes some tests executable
- waits for switches to connect in tests to
  avoid race conditions
-- adds mn -w option and wait CLI command
Changes:
- REMOVES default "-v" argument for Controller()
  and adds verbose(=False) option; avoiding logging
  makes it faster
- CHANGES waitConnected to wait for 5 seconds
  as documented; we may wish to implement an argument
  to -w to set this timeout
Issues?
- There may still be an issue with the ovs-netplan-clean
  service causing the boot to hang ;-(
2021-01-03 20:00:33 -08:00
lantz afdf9fd571 Update for python 2.x and 3.x builds (#988) 2020-12-19 18:06:52 -08:00
lantz 336b01ae34 Add matrix build for ubuntu-16.04 and ubuntu-18.04 (#987) 2020-12-13 19:14:26 -08:00
Jeff R. Allen 537e8242fc Install correctly on Ubuntu 20.04 (#980)
* Install correctly on Ubuntu 20.04

Main changes are to avoid pulling in python2 by mistake, and then to make sure to install only python3 packages.

Also: Add net-tools which is needed to get ifconfig on newer Ubuntu releases.

(admin note: there are still a couple of remaining 20.04 issues but this is helpful)
2020-12-13 19:00:20 -08:00
lantz cab8970b0a Update run-tests.yaml 2020-12-13 18:13:27 -08:00
lantz 3625149356 Update run-tests.yaml 2020-12-13 18:00:44 -08:00
lantz fd9e011277 Merge pull request #986 from lantz/github-actions
Initial support for github actions CI builds
2020-12-13 17:57:48 -08:00
Bob Lantz 591a961762 Initial support for github actions CI builds 2020-12-13 17:15:11 -08:00
lantz bfc42f6d02 Merge pull request #921 from timgates42/bugfix/typo_updates
Fix simple typo: udpates -> updates
2020-04-15 18:22:28 -07:00
lantz 458d20e3fd Merge pull request #928 from JuFil/master
Fix install.sh on Ubuntu 19.10: substitute cgroup-bin with cgroup-too…
2020-04-15 18:18:06 -07:00
lantz 73b2ecc5fe Merge pull request #934 from ssikdar1/flake8-octal-number-positions
change to use 0o for octal numbers w/ file permissons for python2/3 compatibility
2020-04-15 18:17:19 -07:00
lantz 6a5adb48d2 Merge pull request #938 from JuFil/fix-poll
Fix usage of poll in mininet.utils.errRun
2020-04-15 18:13:39 -07:00
julian.filter 5f69bf0ade Add unit tests for util 2020-04-09 18:16:13 +02:00
julian.filter d5b4aa829b Improve util.pmonitor 2020-04-09 17:10:52 +02:00
julian.filter 1704541cc9 fix util.errRun 2020-04-09 17:05:06 +02:00
Shan Sikdar f56a12a1ab change permissons to use octal for rest of file 2020-04-05 10:50:58 -04:00
Shan Sikdar 70c643f8de change to use 0o for octal numbers w/ file permissons 2020-04-05 10:42:31 -04:00
lantz fa9fd770ca Merge pull request #932 from ssikdar1/flake8-F632
change to fix flake8 F632 issues
2020-04-03 19:34:45 -07:00
Shan Sikdar dd41ecaae4 change to fix flake8 F632 issues 2020-04-03 11:43:23 -04:00
julian.filter 287c1fb154 Fix install.sh on Ubuntu 19.10: substitute cgroup-bin with cgroup-tools for Debian/Ubuntu 2020-02-15 19:09:13 +01:00
Tim Gates 1c268e7667 Fix simple typo: udpates -> updates
Closes #920
2020-01-05 20:13:36 +11:00
lantz 2b8d254cc0 Merge pull request #873 from raingloom/patch-1
make it easier to set the compiler
2019-12-01 15:36:48 -08:00
lantz 8fc867b1f4 Merge pull request #897 from jadinm/fix-cli-stdin
Fix CLI constructor: stdin parameter was replaced
2019-12-01 15:34:08 -08:00
Mathieu Jadin 6af291c968 Fix CLI constructor: stdin parameter was replaced
This also enables CLI users to pass parameters to its superclass: Cmd.
2019-12-01 08:45:43 +01:00
lantz c0582b9c5e Merge pull request #895 from jadinm/fix-topo-addLink
Return the new link key in Topo.addLink()
2019-11-29 16:00:38 -08:00
lantz 023c53aec6 Merge pull request #912 from lantz/fix-1604-install
Check for non-functional 'python'
2019-11-29 15:59:55 -08:00
Mathieu Jadin bd1a442a17 Return the new link key in Topo.addLink() 2019-11-26 09:49:30 +01:00
lantz 6c17e1ade7 Update ISSUE_TEMPLATE 2019-11-24 18:11:28 -08:00
Bob Lantz e56f6839f1 Improve Python version detection
Ubuntu 16.04 no longer includes a 'python' executable.

If none of {$PYTHON,python,python2,python3} are usable, we advise
the user to either set PYTHON or install a usable python.
2019-11-22 17:14:10 -08:00
Bob Lantz e0436642ae 2.3.0d6 2019-07-11 17:16:46 -07:00
lantz c5f626ce27 Merge pull request #891 from benfrankel/master
Use incremental utf-8 decoder for buffered reading
2019-07-11 17:15:37 -07:00
Ben Frankel 10e758e80a Use incremental UTF-8 decoder for buffered reading 2019-07-11 16:57:09 -07:00
lantz de28f67a97 Merge pull request #882 from stephanreiter/master
Don't pass 'loss 0' to tc as it will cause an error on newer versions
2019-06-09 15:52:33 -07:00
Stephan Reiter 46242acdd4 Don't pass 'loss 0' to tc as it will cause an error on newer versions
Observed on Ubuntu 18.04.
2019-06-09 00:24:21 +02:00
lantz e203808a20 Merge pull request #872 from dsaucez/patch-2
change isShellBuiltin
2019-04-25 20:16:31 -07:00
rain 3b3a11b6dd make it easier to set the compiler
Hi, I'm trying to package Mininet for Guix which doesn't symlink cc to gcc and this would make it easier to package, as well as configure it for alternative compilers.
2019-04-25 23:02:27 +02:00
Damien Saucez 7c0d1506e7 change isShellBuiltin
change isShellBuiltin to have each builtin as a different token in a set and lookup cmd to be an element of the set instead of a sequence of characters in a string (tested only on Python 3.6.7). Corrected according to @lantz comments.
2019-04-23 08:42:07 +02:00
lantz c5f23b9f82 Merge pull request #815 from teto/fix_controller
fix "which" calls always returning true
2019-04-16 13:28:47 -07:00
Bob Lantz cfb0a6d3f0 Silence warning for failing to stop ovs-testcontroller 2019-04-09 12:17:35 -07:00
Bob Lantz 664ba39383 NAT warning if IPv4 loopback address in /etc/resolv.conf 2019-04-04 16:40:04 -07:00
Bob Lantz 598d694058 2.3.0d5 2019-03-13 21:56:32 -07:00
Bob Lantz f92e3d3432 2018 -> 2019 2019-03-13 21:52:13 -07:00
lantz ce4f1e0c92 Merge pull request #863 from mininet/devel/fix-tclink
Change Link/TCLink to accept universal parameters
2019-03-06 16:17:31 -08:00
Bob Lantz 31f44e1856 Change Link/TCLink to accept universal parameters
This also enables us to specify IP addresses for both sides
of a TCLink, which enables LinuxRouter to work with TCLink.

fixes #854
fixes #634
2019-03-06 15:01:27 -08:00
lantz 36d2b21187 Merge pull request #852 from gabay/patch/rename_init_to_build
Replaced example topologies __init__ with build
2019-02-06 13:22:44 -08:00
None 1888001555 fix kwargs names so they wont spark a warning 2019-01-17 13:06:53 -08:00
None fdc3156a91 Replaced example topologies __init__ with build 2019-01-17 12:01:09 -08:00
lantz cf6da391fa Merge pull request #847 from yousong/poll-eintr
cli: ignore EINTR caused by SIGWINCH when calling poll()
2018-12-20 11:36:42 -08:00
Yousong Zhou f170cc6f64 cli: ignore EINTR caused by SIGWINCH when calling poll()
This can happen when adjusting a tmux pane running mininet cli.  The
process will receive SIGWINCH causing the poll() call to raise
select.error

Below is a session dump with strace

	09:55:52.498106 poll([{fd=0, events=POLLIN}, {fd=28, events=POLLIN}], 2, -1) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
	09:55:52.606734 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
	09:55:52.606770 rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
	Traceback (most recent call last):
	  File "examples/topofac.py", line 527, in <module>
	    run()
	  File "examples/topofac.py", line 522, in run
	    CLI( net )
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 68, in __init__
	    self.run()
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 103, in run
	    self.cmdloop()
	  File "/home/yunion/.usr/lib/python2.7/cmd.py", line 142, in cmdloop
	    stop = self.onecmd(line)
	  File "/home/yunion/.usr/lib/python2.7/cmd.py", line 220, in onecmd
	    return self.default(line)
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 422, in default
	    self.waitForNode( node )
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 440, in waitForNode
	    bothPoller.poll()
	select.error: (4, 'Interrupted system call')
	09:55:52.622076 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7ff9a9b936d0}, {0x7ff9a9ef2230, [], SA_RESTORER, 0x7ff9a9b936d0}, 8) = 0
	09:55:52.631905 +++ exited with 1 +++
2018-12-14 02:46:08 +00:00
Bob Lantz fd96de6485 Add iproute back in for older dists 2018-12-07 17:08:41 -08:00
lantz 73f530b569 Merge pull request #841 from gabay/master
replaces isinstance(str) with isinstance(BaseString)
(note we define BaseString as str for python 3)
2018-11-15 06:40:28 -08:00
gabay c7a27b8876 replaces isinstance(str) with isinstance(BaseString) 2018-11-15 16:15:58 +02:00
Bob Lantz b7a6b8137f Fix undefined variable and code check. 2018-10-19 13:38:06 -07:00
lantz d072e531c2 Merge pull request #837 from theojepsen/master
intf.rename() also renames the intf name in the host
2018-10-11 15:51:48 -07:00
Bob Lantz 8139d7d1b4 Better IP address selection for controllers
Previously we looked for 'eth0', but linux has renamed interfaces.

Instead of looking by name, we now look for an interface which
is the appropriate gateway to a remote server address.

fixes #831
2018-10-11 15:46:57 -07:00
Theo 273b14b771 intf.rename() also renames the intf name in the host 2018-10-11 09:31:26 +02:00
Matthieu Coudron a73e776695 fix "which" calls always returning true
The redirection of stderr towards stdout means calls to "which program"
always return something even when "program" is not installed.

This patch checks for the return value instead before returning the
value.

See #814.
2018-09-25 22:29:30 +09:00
Bob Lantz bfda33544a code check 2018-08-28 18:48:50 -07:00
Bob Lantz 1f4525ed0a Test for Node()/Host() pty leakage 2018-08-28 18:48:45 -07:00
Bob Lantz 1969669f51 pass code check 2018-08-23 13:06:16 -07:00
Bob Lantz c639f342e5 2.3.0d4 2018-08-23 11:43:45 -07:00
Bob Lantz 6e887d0788 Close pty to avoid leak 2018-08-23 11:43:43 -07:00
Bob Lantz 092863f113 2.3.0d3 2018-08-15 23:13:20 -07:00
Bob Lantz 5100a38fcd pmonitor() fixes
pmonitor() is hard to get right, but this seems like
a reasonable improvement:

1. non-blocking I/O so we can drain buffer with impunity
2. drain buffer after poll
3. unregister fd on POLLHUP

Note we may have a unicode splitting problem since we're
decoding chunks of bytes, but that is a problem for another
day I think.
2018-08-02 12:45:35 -07:00
Bob Lantz 3bcecd8e08 Fix popen() to work properly with shell=True 2018-08-02 06:56:18 -07:00
Bob Lantz 486676d617 2.3.0d2 2018-07-26 14:04:08 -07:00
lantz 9e5280b4d1 Python 3 Compatibility (Merge pull request #817 from lantz/py3-compat)
Changes for compatibility with Python 3.

The approach is to make as few changes as possible to maintain compatibility with both Python 2 and Python 3.

We use whatever python is installed, and also support a PYTHON environment variable for installing another version.

For simplicity, we provide mininet.util.pexpect which works out of the box with Python 3 utf-8 strings.

Thanks to @cuihantao for looking at this as well and also for changes to MiniEdit.

Closes #794
2018-07-26 14:03:34 -07:00
Bob Lantz b70eed69d3 Fix typo (50s -> 50ms) 2018-07-26 13:52:15 -07:00
Bob Lantz d96b35694a Use appropriate python version 2018-07-26 13:40:12 -07:00
Bob Lantz c7deeae11c Add sanity check for python mn version 2018-07-26 13:40:12 -07:00
Bob Lantz 323989953b Try to fix pylint errors ;-p 2018-07-26 13:40:12 -07:00
Bob Lantz 17f9756e5c sudo interferes with travis python 3 environment 2018-07-26 13:40:12 -07:00
Bob Lantz 71f3931f2f Remove redundancies in miniedit; pass code check 2018-07-26 13:40:12 -07:00
Bob Lantz 1e9ca5f7fc Fix missing quote in .travis.yml 2018-07-26 13:40:12 -07:00
Bob Lantz 8933c996cb Add python-tk/python3-tk 2018-07-26 13:40:06 -07:00
Bob Lantz afa83cd5ae Restore python 2 compatibility 2018-07-26 13:40:03 -07:00
Hantao Cui 3e81ea7455 miniedit.py changes for Python 3 2018-07-26 13:39:49 -07:00
Bob Lantz 8102704726 Pass code check (14.04) 2018-07-25 21:30:05 -07:00
Bob Lantz f4490069ca Add PYTHON and python2/python3 support
Perhaps we should have an option for py2/p3, but for
now we detect the default python version and it can
also be specified via the PYTHON env var.
2018-07-25 19:44:46 -07:00
Bob Lantz 2ac4f92af3 Add PYTHON variable for python2/python3 make install 2018-07-25 19:44:46 -07:00
Bob Lantz f98154a323 Decode subprocess output for python3 2018-07-25 19:44:46 -07:00
Bob Lantz 7b48460e9e Change pexpect hackery to use a custom class
So the original pexpect is unperturbed
2018-07-25 19:44:46 -07:00
Bob Lantz 3a0ef258d1 grep cgroup /proc/mounts in mountCgroup 2018-07-25 19:44:45 -07:00
Bob Lantz 0d9a6796df Add Python 2.7 and Python 3.6 2018-07-25 19:44:45 -07:00
Bob Lantz 08a59783f4 Decode readline() and call cleanup() for python 3
Python 3.6 crashes if there are leftover processes, even
if they shut down later...
2018-07-25 19:44:45 -07:00
Bob Lantz e37afe996b First crack at Python 3 compatibility
Mostly we just use mininet.util.pexpect so that we can use
unicode strings without changing calls to pexpect.spawn()
2018-07-25 19:44:45 -07:00
Bob Lantz 6a387faa04 Fix whitespace error 2018-07-25 19:44:45 -07:00
Bob Lantz 1a134cb4d2 80 columns 2018-07-25 19:44:45 -07:00
Bob Lantz af4921adc5 Minor changes for Python 3
items() vs. iteritems() and decode() bytes in monitorFiles

Probably not strictly correct if bytes are split - we still
need to deal with this properly.
2018-07-25 19:44:45 -07:00
Bob Lantz f94ee8ec97 Redesign pmonitor()
This is tricky to get right, but I think it is correct
to drain the buffers like this.
2018-07-25 19:44:45 -07:00
Bob Lantz 356b024d6b Wrap reads in pmonitor() for python3 2018-07-25 19:44:45 -07:00
Bob Lantz 2283bb01e6 Python3 compat and add timeout for connect 2018-07-25 19:44:45 -07:00
Bob Lantz 3eef584c6b Python 3 compatibility
And some Ubuntu 18 compat:

- ifconfig's output has changed
- relaxing timing as it seems first ping is slower
2018-07-25 19:44:45 -07:00
Bob Lantz 2e00a7de97 Python 3 compatibility 2018-07-25 19:44:45 -07:00
Bob Lantz e28348f6cd Indent error. 2018-07-25 19:44:44 -07:00
Bob Lantz 4b744d1fb0 Fix dynamic Python items() in deleteIntfs() 2018-07-25 19:44:44 -07:00
Bob Lantz 2822998cad A couple of missed py3 items: execfile, iteritems 2018-07-25 19:44:44 -07:00
Bob Lantz 853815500d Create util.pexpect which is compatible with Python 3 strings
Unfortunately pexpect isn't compatible with Python 3 strings
unless you specify unicode encoding, which isn't the default.

With this helper code, you can import pexpect from mininet.util
and get default pexpect.spawn() behavior that works with Python 3
strings.
2018-07-25 19:44:44 -07:00
Bob Lantz 1a2925923f translate() -> replace() for Python 3 compatibility
str.translate() works differently in Python 3
2018-07-25 19:44:44 -07:00
Bob Lantz cac884a85e Wait for Node() shell to exit on Python 3
I'm not a fan of this, but Python 3's subprocess module
complains otherwise.

For now we're only doing this on Python 3. We should probably
quantify the slowdown however.
2018-07-25 19:44:44 -07:00
Bob Lantz f314a6626a Missed one instance of basestring 2018-07-25 19:44:44 -07:00
Bob Lantz bf83f4f343 Don't bother checking for negative delay/jitter
It's kind of pointless. However technically the argument should
be a number and not a string - it's always in ms.
2018-07-25 19:44:44 -07:00
Bob Lantz 1ef12e450a Apparently Python 3 types aren't ordered 2018-07-25 19:44:44 -07:00
Bob Lantz 56fc6c3955 Close popen stdout and stderr
They should get cleaned up anyway, but Python 3 gives
us an error message. This silences it.
2018-07-25 19:44:44 -07:00
Bob Lantz 88cbf4ec42 Kinder, gentler Python 3 compatibility
Looking at trying to minimize code changes for Python 3.

For now we are keeping byte str() in py2 and unicode str()
in py3. It's a pain to rewrite all string code to usee
u'' for py2 compatibility. However we may need to revisit
this for pexpect().

Created compatibility wrappers in util.py:

BaseString (replacement for basestring)
decode() (optionally decode utf-8 for py3)
encode() (optionally encode utf-8 for py3)

Also we've switched from the .iter*() flavors to their
static alternatives for py2 (note they are iterators in py3!)

Changed util.errRun and clean.sh to call decode()
2018-07-25 19:44:44 -07:00
Bob Lantz 26b3959968 Fix keys parameter in edges_iter
Fixes #813
2018-07-11 23:36:11 +00:00
Bob Lantz 966dd20b4b waitConnected: don't compare timeout if it is None 2018-07-11 23:29:19 +00:00
Bob Lantz 82998cac8b waitConnected and debug output for testMultiPing
This test fails periodically on Travis (#714)

Hopefully waiting for connection will make it more reliable,
and if not then the debug output will help.
2018-07-10 07:41:19 -07:00
lantz 34b1f4161a Merge pull request #803 from teto/nixos_clean
Merge in nixos changes:
1. More flexible makefile
2. Fix problems from setting vi-ins-mode-string in .inputrc (fixes #799)
2018-06-25 19:49:22 -07:00
lantz dfb297901f Update copyright date in LICENSE
It's 2018. ;-)
2018-06-18 09:59:11 -07:00
Bob Lantz 67236e9db3 Update ofsoftswitch build
Changed netbee URL.
Changed libxerces package for Ubuntu 18.04
2018-06-03 17:20:18 -07:00
Matthieu Coudron 8a00c3abf8 bash: run with --noediting
Having "set editing-mode vi" in ~/.inputrc can output characters that
may induce mininet into (wrongly) thinking a process has failed.
2018-06-03 09:54:50 +09:00
Matthieu Coudron 037f7f5921 Makefile: make it more flexible
- let the user choose where to install (via PREFIX)
- added targets install-mnexec/install-manpages
2018-06-03 09:54:13 +09:00
Bob Lantz 8af3f28b67 iproute -> iproute2 for Ubuntu 18.04
This should also be backward compatible to 14.04.
2018-04-28 01:48:36 +00:00
lantz c5b2efd839 Merge pull request #780 from nemethf/set_dp_desc
Set the datapath desciption (dp-desc) of OVS switches
2018-02-28 12:20:20 -08:00
Felician Nemeth a638e0b085 Set the datapath desciption (dp-desc) of OVS switches 2017-12-07 12:17:50 +01:00
lantz f65e1ab936 Merge pull request #767 from juangascon/travis_yml
Remove '- dist: xenial' until Travis gets their act together and supports the current LTS
2017-10-10 16:12:50 -07:00
juan 70d14db881 Remove '- dist: xenial' because both jobs are run in the same Ubuntu version (14.04) as I have seen in the build logs.
The travis-CI docs confirm the OS environment is 'Ubuntu 14.04 LTS trusty'.
Travis-CI only proposes 14.04 LTS Trusty and there is no plan to update to 16.04 xenial
(c.f. https://github.com/travis-ci/travis-ci/issues/5821)
It is useless to add a second job because it will run in the same environment.
2017-10-10 21:36:29 +02:00
Bob Lantz b142cf32ec Pass code check (14.04 LTS: pyflakes 0.8.1, pylint 1.1.0)
Was no longer passing due to pyflakes.
2017-09-25 16:07:47 -07:00
Bob Lantz 87e26ef931 Restore CLI=MyCLI customization in --custom files
fixes #746
2017-06-02 14:11:36 -07:00
Bob Lantz 7366645c81 Fix plot() command for newer networkx
Networkx moved graphviz_layout, so we look for it in two different
places.

Also plot() should be fixed for regular Mininet(), which doesn't have
a .servers list.
2017-05-30 15:37:10 -07:00
Bob Lantz 2437aba984 Change to if to avoid breaking travis build on 16.04 2017-05-24 17:17:25 -07:00
Bob Lantz a6627cd928 Don't call node.addIntf() if node is None
fixes #716
2017-05-24 15:11:03 -07:00
IWASE Yusuke aa8901268d Fix Ryu installation
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
2017-05-22 15:56:46 -07:00
Bob Lantz 9d306ad53a Allow passing mncmd to CPULimitedHost.popen()
This is not not really intended as an external API (and
it currently isn't documented) but this makes it consistent.

Fixes #743
2017-05-22 15:36:23 -07:00
lantz 69b6c05970 Merge pull request #736 from hwchiu/FIX_TBF_LINK
Modify the default latency type from int to float if tbf is enabled.
Fixes #729
2017-05-09 15:10:39 -07:00
Bob Lantz e55e18e4ce Treat Raspbian as Debian; add $DISTS.
Fixes #713
2017-05-09 14:41:10 -07:00
hwchiu f86db75b79 Modify the default latency from int to float if tbf is enabled.
- the original version will caluse the latency to 0 when bandwidth
  is bigger than 200;
- the TC command doesn't allow use the 0.00ms as input for latency field

Signed-off-by: hwchiu <sppsorrg@gmail.com>
2017-04-28 09:29:17 +08:00
lantz 5728fcbf40 Merge pull request #727 from leonardodavila/master
Fix ryu installation
2017-04-27 16:31:37 -07:00
lantz 23f4f4a998 Merge pull request #734 from akoshibe/upstream
Allow match on 'packets received' in ping results
2017-04-23 23:59:05 -07:00
akoshibe ba2d91b489 Allow match on 'packets received' in ping results
Fix typo and remove redundant call to nproc
2017-04-22 16:17:35 -08:00
Leonardo D'Avila 40e2c374d0 Merge pull request #1 from leonardodavila/fix-ryu-install
update install.sh fixing ryu install
2017-03-28 23:43:48 -03:00
Leonardo D'Avila ce8feb7bfa update install.sh fixing ryu install 2017-03-28 23:43:27 -03:00
lantz e823c4f93c Fix line break
Beware of emacs eating trailing spaces!
2017-03-22 14:34:23 -07:00
lantz 497a342c27 Update README.md 2017-03-22 14:32:44 -07:00
Bob Lantz 32565765c1 For now, install dependencies before make codecheck 2017-03-15 16:19:31 -07:00
Bob Lantz c1d07835bd Rearrange code check 2017-03-15 15:49:26 -07:00
Bob Lantz 65acef533a Run bash explicitly in codecheck conditional 2017-03-15 15:09:06 -07:00
Bob Lantz bf2bb4c88c Sadly pylint isn't backward compatible; comment out newer options
Also slightly adjust some valid name conventions
2017-03-15 15:03:47 -07:00
Bob Lantz bfc00c567c Add more ignores for plint 1.5.2 2017-03-15 14:50:01 -07:00
Bob Lantz 92d57db0d0 Run codecheck on 14.04 only and after install (for deps) 2017-03-14 15:47:46 -07:00
Bob Lantz 13331d0ffd Add "make codecheck" 2017-03-14 15:43:47 -07:00
Bob Lantz 359b1feb5a code check 2017-03-14 15:43:41 -07:00
Bob Lantz a77ddba607 code check 2017-03-14 15:43:28 -07:00
Bob Lantz caf23f078e code check 2017-03-13 19:09:24 -07:00
Bob Lantz d5cb865aaa code check 2017-03-13 19:08:39 -07:00
Bob Lantz 1c0aeb699f code check 2017-03-13 19:08:26 -07:00
Bob Lantz c458c9e2b4 code check 2017-03-13 18:56:21 -07:00
Bob Lantz 26a0e351f3 satisfy code check 2017-03-13 17:11:05 -07:00
Bob Lantz 742f5b9af9 satisfy code check 2017-03-13 17:10:14 -07:00
Bob Lantz 40e4546c48 satisfy code check 2017-03-13 17:09:19 -07:00
Bob Lantz 5479117584 pass code check 2017-03-13 17:06:53 -07:00
Bob Lantz 02453b02b0 Remove relative import 2017-03-13 17:05:50 -07:00
Bob Lantz d282c55831 pass code check; make GRE_KEY a class var 2017-03-13 17:03:11 -07:00
Bob Lantz 6977633de1 pass code check 2017-03-13 16:36:50 -07:00
Bob Lantz bc0a72e2f9 pass code check 2017-03-13 16:36:41 -07:00
Bob Lantz ef5c80abb3 pass code check 2017-03-13 16:34:38 -07:00
Bob Lantz 35a33fc617 print() -> error() 2017-03-13 16:31:17 -07:00
Bob Lantz 494840c908 satisfy codecheck 2017-03-13 13:57:41 -07:00
Bob Lantz b7d9fdd5bd Satisfy codecheck 2017-03-13 13:56:46 -07:00
Bob Lantz 32299a6808 Restore incorrectly placed docstring 2017-03-13 13:48:50 -07:00
Bob Lantz d7cfb729c3 Pass code check and add ovs-testcontroller to zombies 2017-03-13 13:46:51 -07:00
Bob Lantz 348b526212 Satisfy pylint 2017-02-15 18:35:45 -08:00
Bob Lantz 18bfa1d6b3 warn() was undefined 2017-02-15 18:30:09 -08:00
Bob Lantz 5d2dfa9f40 Handle version string sent to stderr
An unfortunate side effect of switching from print to output() is
that all output() goes to stderr. We should probably carefully
consider whether this is the right thing to do.
2017-02-15 18:26:18 -08:00
Bob Lantz 0b5d24148b default(): add CR to error(); also clean up docstring 2017-02-08 16:30:36 -08:00
Bob Lantz dfb2594793 testLinkChange: report bandwidth on error 2017-02-07 15:01:05 -08:00
lantz b5b6beacb0 Multiple builds: trusty and xenial 2017-01-31 15:34:27 -08:00
Brian O'Connor 213563583d Updating .travis.yml to suppress success notifications 2017-01-12 18:44:45 -08:00
Brian O'Connor 9c7638a015 Updating README
Moving Travis logo down and using image reference to declutter.
2017-01-12 17:01:26 -08:00
Brian O'Connor fef7e0521c Merge pull request #710 from bocon13/travisci
Setting up .travis.yml for automated builds on Travis CI
2017-01-12 16:29:54 -08:00
Brian O'Connor 335cb1bdf3 Adding TravisCI build static icon to README 2017-01-12 16:13:20 -08:00
Brian O'Connor 0b2d8461fd Updating hwintf test to call host.stop()
host.terminate() does not remove the interfaces
2017-01-12 16:11:24 -08:00
Brian O'Connor 9376ccbedd Updating runner to exit 1 on failure 2017-01-12 16:11:24 -08:00
Brian O'Connor 03b1cbb347 Update example runner to exit 1 on failure 2017-01-12 16:11:24 -08:00
Brian O'Connor 51f50e8b8f Adding .travis.yml for Travis CI 2017-01-12 16:11:13 -08:00
lantz 2e9a79a375 Merge pull request #618 from BhattiMarry/master
UPDATE command made generic. Obsolete kernel() function was breaking Fedora/RHEL install.
2017-01-11 15:50:53 -08:00
lantz 86a26e0baf Merge pull request #602 from youf3/patch-1
Added ryu to be clean up in clean.py
2017-01-10 14:08:16 -08:00
lantz 6f2981134d Merge pull request #606 from oliviertilmans/patch-2
link: Fix circular import
2017-01-06 15:26:47 -08:00
lantz e58cc37c49 Merge pull request #699 from NvanAdrichem/MiniEdit-OVSVersion
Changed verifying the OVS version to not require root in MiniEdit.
2017-01-06 14:57:59 -08:00
lantz baebce8b05 Merge pull request #672 from nirmoy/opensuse_installation
RFC: add installation support for SUSE based distros
2017-01-06 14:55:11 -08:00
lantz 2bfaec75e0 Merge pull request #685 from maufl/fix-ping-parse-regex
Fix regex for ping result parsing, supporting optional 'packets' word in ping output
2017-01-06 14:53:42 -08:00
lantz 36c2e7cd79 Added "Additional Information" section. 2017-01-06 14:37:11 -08:00
lantz 3284b04f2b Merge pull request #704 from mininet/devel/ipBase
Updating Mininet to start with host IP request in ipBase
2017-01-03 16:55:37 -08:00
Brian O'Connor 8c662d63bf Updating Mininet to start with host IP request in ipBase 2017-01-03 15:11:38 -08:00
Niels van Adrichem 3cb387ebd0 Changed verifying the OVS version to not require root in MiniEdit. 2016-12-21 11:59:55 +01:00
lantz e90a4a74cb Update ISSUE_TEMPLATE 2016-12-09 16:02:29 -08:00
Bob Lantz d338b4425b Added GitHub issue template. 2016-12-09 15:55:48 -08:00
Felix Konstantin Maurer c128712d4d Fix regex for ping result parsing 2016-11-14 14:16:07 +01:00
Bob Lantz 8b2881b0c2 Changes for OVS 2.6.0 + Ubuntu 16.04 + Debian 8.6 2016-10-28 18:16:25 -07:00
Bob Lantz 3a9081b5b1 Add small wait before wget in web test.
Although the walkthrough doesn't specify waiting, it is a race
condition that we occasionally hit in tests. Adding a 2 second
delay seems realistic (for when a human is doing the walkthrough)
and should reduce the likelihood of hitting the race condition.
2016-10-21 15:31:33 -07:00
Bob Lantz 54bd4e4d3a For 10Mb/s links, we should certainly be network-limited rather
than CPU-limited, which should expose the feature we are trying
to demonstrate (TCP data rate slowing down as latency increases
due to congestion control.)
2016-10-21 13:25:40 -07:00
lantz cc1cd9619f Merge pull request #671 from vik-y/master
Changes override method from __init__ to build
2016-09-28 20:45:35 -07:00
Nirmoy Das 007261d19e add installation support for SUSE based distros 2016-09-28 12:08:32 +02:00
vik_y dc22b0a877 Changes override method from __init__ to build 2016-09-27 13:19:08 +05:30
Bob Lantz 4178333174 Wait for exit in testWireshark()
If mn doesn't shut down cleanly, the subsequent test may fail.
2016-09-23 16:07:12 -07:00
Bob Lantz f012dac2cb Clean shutdown for testStaticMAC
Without waiting for mn to exit, OVS keeps listening on the
static listening port, which prevents UserSwitch from listening
on it (and starting up) in the next test.
2016-09-21 13:25:04 -07:00
Bob Lantz 8def6752f3 Add TCULink and make it the default for --switch user
For some time, UserSwitch has suffered from abysmal performance due
to rx and tx offload settings. In the latest Ubuntu 16.04/Linux 4.4
kernel, it simply doesn't work with the default settings, since
packets with bad TCP checksums are generated at one end, passed through
the user switch, and dropped at the other. It doesn't seem to be
something we can fix in UserSwitch itself although it may be possible
to do something in the C code of the Stanford reference switch and
CPqD switch.
2016-09-20 12:43:27 -07:00
Bob Lantz 0f9c1b33c7 Change default --listenport to 6654
Previously the standard OpenFlow poort (6653) that Controller()
may be listening at would conflict with the default listenPort
for switch 20 (usually UserSwitch 20), breaking waitConnected()
and disconnecting that switch from the network in general.

Fixes #668
2016-09-14 00:38:35 -07:00
Bob Lantz f530c99415 testHostCommands: accept both ethX and en.*X interfaces
systemd (Ubuntu 16) names interfaces like this:
enp1s3 for "ethernet, PCI bus 1, slot 3"

We now accept both in testHostCommands

Fixes #665
2016-09-09 14:54:38 -07:00
Bob Lantz 5470415bfc Fix mn -v by using output() rather than info()
info() doesn't produce any output if it's called at
this point; bug was introduced when we tried to eliminate
print().
2016-09-01 23:25:14 -07:00
Bob Lantz 7e5324c80e Add TESTS parameters, multiple tests with +
It's very useful to be able to pass parameters to tests.
Unfortunately, our previous --test argument syntax didn't
permit it, because , was used as a delimiter for multiple
tests. With this change, we change , to become a delimiter
for arguments, as it is with other options to mn.  For now,
we introduce + as a separator, as it is legal in filenames
and therefore shouldn't conflict with special characters
used by Unix shells.

We also reorganize mn a bit to make it hopefully slightly
clearer.
2016-08-30 02:44:38 -07:00
Bob Lantz e893fc9da4 Try specifying timeout in pexpect.spawn() + adjust error msg
Unfortunately pexpect() seems to be timing out with a 30 second
timeout rather than the 600 seconds we are passing in. How
could this even be working normally? It is puzzling. We are
going to try specifying the timeout in the spawn() call.

Also if the test fails, we use %e format for readability.
2016-08-29 23:57:17 -07:00
Bob Lantz b2e1907c3a Increase link delay to 2ms for more robust effect
With a 1 ms delay, the performance tests periodically fails
under nested virtualization, which is how we are testing it.
Increasing the delay should make the effect (lower TCP
data rates as latency increases, due to TCP's congestion
control algorithm) more pronounced and robust.
2016-08-25 03:55:28 -07:00
Bob Lantz 21086cd79e Reduce CPU so that iperf client is CPU bound
If we want to observe a monotonic affect, we should
make sure that we are in fact CPU limited where it
matters. In this case, we are CPU limiting the hosts,
and the iperf client uses a lot of CPU. We need to
reduce the CPU allocation so that iperf is in fact
CPU bound.

We also correct the CPU allocation so that the client
and server each receive 50% of the total. Previously
we were specifying the per-host CPU allocation, so
45% meant we were allocating 90% of the overall CPU,
which seems a bit confusing. On the other hand, now
at 40% each host gets 20% of the CPU, which could also
be considered slightly confusing!

Although the client transmit rate is going to be the
limiting factor, we still measure the received data
rate at the server, because that is more interesting
than the initial burst of buffering at the client.
Measuring at the server becomes more important as
we reduce the iperf time.

The output is also changed slightly, and the test has
been updated appropriately.
2016-08-24 23:59:02 -07:00
Bob Lantz ee15ce2243 print -> info; end the horror of print vs. print()
Although we could use print() from __future__, this messes
up scripts which use examples as modules.

The simple, if not nicest for 2.7, solution is to use
info(), output() and other mininet.log functions. The disadvantage
is that we may have to adjust things if we change info() to
add automatic newlines, but we can burn that bridge in Mininet
3.x.
2016-08-23 01:54:54 -07:00
Bob Lantz 1eb6f65e40 Increase scratchnet timeout to see if it's just slow. 2016-08-22 23:18:58 -07:00
lantz 238ffe64a7 Merge pull request #659 from jhall11/links_output
Restore links output format
2016-08-23 12:28:58 -07:00
Jon Hall cea176fa9f Restore links output format
In the change from print -> print() -> output() we lost the new lines
which makes it hard to read the output
2016-08-23 10:49:36 -07:00
Bob Lantz 3276e3fef5 Allow some diffs in h1 ps vs. h2 ps
Presumably daemons, race conditions, or ephemeral processes
can cause the ps output to vary slightly. We allow up to
two differing lines to account for this.

Fixes #651
2016-08-22 15:59:49 -07:00
Bob Lantz e40d9b8c0d print -> info; also clarify signalTest output 2016-08-15 22:54:21 -07:00
lantz 30d08cfbb2 Merge pull request #641 from xiaozhou/master
add a new type of RemoteLink in cluster edition that uses GRE tunnels
2016-08-16 15:05:59 -07:00
Bob Lantz 29d902918d Change tshark version check for compatibility
The version string changed in tshark 2.0.2 in Ubuntu 16.
Perhaps we should just check for the version string itself
to be more robust but this is probably OK for now.
2016-08-11 14:33:41 -07:00
Bob Lantz f89a5bcf70 Use output()/error() instead of print
We shouldn't be using print in the core API, with
the possible (?) exception of error/abort messages.
2016-08-08 15:32:31 -07:00
Bob Lantz 8ac077a721 Remove "from __future__ import print"
This was well-intentioned, but it causes more trouble
than it's worth.

Fixes #652
2016-08-08 15:28:52 -07:00
lantz 666635c969 Merge pull request #636 from mininet/devel/add-del
Add net.delNode(), net.delLink() and associated methods
2016-08-04 17:10:08 -07:00
Bob Lantz 4c1cfcf6f3 14.04.3 -> 14.04.4
We should probably do this in a more automatic way.
2016-08-03 15:17:08 -07:00
Bob Lantz 76c8afde8a Fixes for Unbuntu 16.04/Xenial
- add xenial
- partx returns an error (warn rather than fail)
- remove dnsmasq build.py dependency (not really needed, side effects)
2016-08-02 15:27:53 -07:00
lantz 5714495f8c Merge pull request #603 from oliviertilmans/patch-1
TCIntf: Don't delete a non-existing root qdisc
2016-08-01 15:24:20 -07:00
Xiaozhou Li d8371615ce fix typo 2016-07-29 09:57:30 -07:00
Xiaozhou Li 7630861fde fix typo 2016-07-11 21:15:04 -07:00
Xiaozhou Li da594dcecd add a new type of RemoteLink in cluster edition that uses GRE tunnels 2016-07-11 21:05:01 -07:00
lantz 4fe5a19221 Merge pull request #598 from croft/master
Set limit on command history length
2016-06-21 16:40:55 -07:00
lantz 330a466503 Merge pull request #611 from jue-jiang/master
modify loss type of tclink to simulate loss rate under 1%
2016-06-21 16:37:38 -07:00
lantz c0b8eddadb Merge pull request #623 from bradmwalker/ivs-install
Fix/speed up IVS install and build on Fedora/RH
2016-06-21 16:36:39 -07:00
Bob Lantz 76b54e3e44 Merge branch 'bradmwalker-py3-part1'
py3 low-hanging fruit

closes #621
2016-06-21 16:34:12 -07:00
Bob Lantz 36489c7470 Change xrange to range for py3 compatibility 2016-06-21 16:31:46 -07:00
Brad Walker dd8adda3ff Use "Exception as _" instead of "Exception, _"
Requires Python 2.6+
2016-06-21 16:27:04 -07:00
Brad Walker fb1f0dca2a Make some tuples explicit to pass parsing in py3 2016-06-21 16:27:04 -07:00
Brad Walker 70fcc45893 Use print function 2016-06-21 16:27:04 -07:00
Bob Lantz 511d71a110 Add net.delNode(), net.delLink() and associated methods
This makes the mid-level net.add*() API symmetric.
You can now delete hosts, switches, controllers and links using
a new net.del*() API, as well as del net[ 'nodename' ].
2016-06-03 23:59:27 -07:00
Bob Lantz 57abd9baef Use correct command name in OVSController 2016-05-24 17:40:00 -07:00
Bob Lantz e6b0430a87 Enable TESTS={'mytest':test} in --custom files 2016-05-11 19:30:18 -07:00
Brad Walker 6f286033c5 Checkout only first level of IVS submodules
The submodules have submodules with authenticated URLs which breaks anonymous,
automated builds. Excluding them also decreases build time.

git -C is not available in git 1.8 (CentOS 7)
2016-04-30 19:13:27 -04:00
Brad Walker 1600b10131 Specify RPM dependencies for IVS 2016-04-30 19:13:27 -04:00
Bob Lantz 475bb4d911 2.3.0d1 2016-04-26 15:13:14 -07:00
Muhammad Umair Bhatti 361beab0b0 UPDATE command made generic.
Update command was hard-coded for debian or ubuntu based systems which was 'apt-get'. Setting an update variable accordingly solves this problem.
2016-04-18 16:45:44 +05:00
zhuo 098b39d07a modify loss type of tclink to simulate loss rate under 1% 2016-04-09 19:27:52 +08:00
Olivier Tilmans 60e537f9bc link: Fix circular import
Having an 'import mininet.node' in the link module causes
a circular import as the node module also has a top-level
import for the link module.

In order to break the cycle, the import for the node module
is delayed and executed only if using the sole class that
actually needs it in the link module.
2016-03-25 00:32:52 +01:00
Olivier Tilmans 5cfc9f84b8 TCIntf: Don't delete a non-existing root qdisc
In recent kernels, virtual interfaces come without any associated
qdisc, resulting in errors when spawning the network.
Checking for "noqueue" in the tc output, enables to detect that
case and thus avoid deleting the non-existent qdisc.
2016-03-11 13:36:56 +01:00
youf3 6ddce0ac92 Added ryu to be clean up in clean.py
'ryu-manager' is included in zombies list so Ryu controller is cleaned up properly.
2016-03-11 16:47:35 +13:00
Jason Croft e87ee10f8b Set limit on command history length 2016-02-25 03:50:22 -06:00
98 changed files with 2173 additions and 1122 deletions

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
**Mininet uses GitHub issues for bug reports and feature requests only.**
These issues can be viewed at bugs.mininet.org
If you have a question that is not a **bug report** or a **feature request**,
please use the documentation at docs.mininet.org, the FAQ
at faq.mininet.org, and the mininet-discuss mailing list.
For bug reports, please fill in the following information in detail,
and also feel free to include additional information such as debug
output from mn -v debug, etc.
--- Cut Here ---
### Expected/Desired Behavior:
### Actual Behavior:
### Detailed Steps to Reproduce the Behavior:
### Additional Information:
+22
View File
@@ -0,0 +1,22 @@
name: code-check
on: [push, pull_request]
jobs:
code-check:
name: Mininet Code Check
runs-on: ubuntu-latest
steps:
- name: Set up Python 3.x
uses: actions/setup-python@v2
with:
python-version: 3.x
- name: Check out Mininet source
uses: actions/checkout@v2
- name: Install Mininet code check dependencies
run: |
PYTHON=`which python` util/install.sh -n
python -m pip install pylint==2.4.4
- name: Run code check
run: make codecheck
+51
View File
@@ -0,0 +1,51 @@
name: mininet-tests
on: [push, pull_request]
jobs:
test:
name: Mininet Tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
python-version: [3.x, 2.x]
steps:
- name: Check out Mininet source
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Mininet and base dependencies
run: |
sudo apt-get update -qq
# This seems too slow unfortunately:
# sudo apt-get upgrade -y -qq
PYTHON=`which python` util/install.sh -nv
- name: Sanity test
run: |
export sudo="sudo env PATH=$PATH"
# Verify major python-version number matches python and mininet
export PYVER=`echo ${{ matrix.python-version }} | cut -d . -f 1`
export CHKVER='px import platform; print(platform.python_version())'
python --version |& grep " $PYVER\."
echo $CHKVER | $sudo mn -v output | grep " $PYVER\."
# Newer OvS tries OpenFlow15 which crashes ovsc on ubuntu-20.04
$sudo mn --switch ovs,protocols=OpenFlow13 --test pingall
- name: Install test dependencies
run: |
sudo apt-get install -qq vlan
pip install pexpect
util/install.sh -fw
- name: Run core tests
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=`which python`
$sudo $PYTHON mininet/test/runner.py -v
- name: Run examples tests (quick)
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=`which python`
$sudo $PYTHON examples/test/runner.py -v -quick
+14 -6
View File
@@ -41,10 +41,18 @@ load-plugins=
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
#
# Note: we may want to re-enable some of these at some point, but many of them
# are just style issues rather than errors.
#
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
too-many-instance-attributes, too-few-public-methods,
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
locally-disabled
locally-disabled, locally-enabled, bad-continuation,
useless-object-inheritance, unnecessary-pass, no-else-return,
no-else-raise, no-else-continue, super-with-arguments
# bad-continuation, wrong-import-order
[REPORTS]
@@ -61,7 +69,7 @@ include-ids=yes
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells wether to display a full report or only the messages
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highes
@@ -111,10 +119,10 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
function-rgx=[a-z_][a-z0-9]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
method-rgx=[a-z_][a-z0-9]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
@@ -123,7 +131,7 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
variable-rgx=[a-z_][a-z0-9]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
+30 -1
View File
@@ -18,34 +18,63 @@ Cody Burkard
Additional Mininet Contributors
Joseph Beshay
M S Vishwanath Bhat
Muhammad Umair Bhatti
Arie Bregman
Tomasz Buchert
Gustavo Pantuza Coelho Pinto
Fernando Cappi
HW Chiu
Ryan Cox
Shaun Crampton
Jason Croft
Hantao Cui
Nirmoy Das
Lenoardo D'avila
Giuseppe Di Lena
David Erickson
Juan Gascon
Glen Gibb
Andrew Ferguson
Eder Leao Fernandes
Julian Filter
Ben Frankel
Tim Gates
Gregory Gee
Jon Hall
Roan Huang
Vitaly Ivanov
Theo Jepsen
Mathieu Jadin
Babis Kaidos
Rich Lane
Rémy Léone
Xiaozhou Li
Zi Shen Lim
David Mahler
Felix Maurer
Murphy McCauley
Alex Moijes
Felician Nemeth
José Pedro Oliveira
James Page
Gustavo Pantuza Coelho Pinto
Ramon Pujianto
Stempha Reiter
Damien Saucez
Shan Sikdar
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Olivier Tl]ilmans
Niels van Adrichem
Brad Walker
Andreas Wundsam
Vikas Yadav
Isaku Yamahata
Baohua Yang
Zhuo
Thanks also to everyone who has submitted issues and pull
requests on github, and to our friendly mininet-discuss
+67 -25
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.2.2b1
Mininet 2.3.0b2
---
The supported installation methods for Mininet are 1) using a
@@ -32,19 +32,21 @@ like to contribute an installation script, we would welcome it!)
2. Next-easiest option: use our Ubuntu package!
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
12.10+:
16.04+:
sudo apt-get install mininet
Note: if you are upgrading from an older version of Mininet, make
sure you remove the old OVS from `/usr/local`:
sudo rm /usr/local/bin/ovs*
sudo rm /usr/local/sbin/ovs*
Note: this may install an older version of Mininet which may not
support Python 3. If you would like the latest version of Mininet,
consider installing from source as described in the next section.
3. Native installation from source
3.1. Native installation from source on Ubuntu 12.04+
If you are running Ubuntu, Debian, or Fedora, you may be able to use
our handy `install.sh` script, which is in `util/`. Please read the
following sections first.
3.1. Obtaining the Mininet source code
If you're reading this, you've probably already done so, but the
command to download the Mininet source code is:
@@ -52,8 +54,9 @@ like to contribute an installation script, we would welcome it!)
git clone git://github.com/mininet/mininet.git
Note that the above git command will check out the latest and greatest
Mininet (which we recommend!) If you want to run the last tagged/released
version of Mininet, you can look at the release tags using
Mininet (which we recommend!) If you want to run the last
tagged/released version of Mininet, you can look at the release tags
using
cd mininet
git tag
@@ -64,21 +67,38 @@ like to contribute an installation script, we would welcome it!)
where <release tag> is the release you want to check out.
If you are running Ubuntu, Debian, or Fedora, you may be able to use
our handy `install.sh` script, which is in `util/`.
3.1.1 *CAUTION: USE AT YOUR OWN RISK!*
*WARNING: USE AT YOUR OWN RISK!*
`install.sh` is a bit intrusive and may possibly damage your OS
`install.sh` can be 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.. We recommend
trying it in a VM before trying it on a system you use from day to day.
trying it in a VM before trying it on a system you use from day to
day.
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!
To install Mininet itself, the OpenFlow reference implementation, and
You can change the directory where the dependencies are installed
using the -s <directory> flag.
util/install.sh -s <directory> ...
3.1.2 Running `install.sh`
Installing a "minimal" version of Mininet with Open vSwitch should
be reasonably non-perturbing since it should not create directories
for other tools:
util/install.sh -nv
Note this will not install a controller, so you will have to either
install your own controller, or use a switch such OVSBridge that does
not require a controller:
sudo mn --switch ovsbr --test pingall
To install Mininet itself, the OpenFlow reference controller, and
Open vSwitch, you may use:
util/install.sh -fnv
@@ -88,6 +108,27 @@ like to contribute an installation script, we would welcome it!)
sudo mn --test pingall
3.1.3 Python 3 and Python 2 support
Mininet supports Python 3 and Python 2. By default, `install.sh`
will use whatever `python` is on your system. To specify a
specific version of Pythonm, you can set the PYTHON environment
variable:
PYTHON=python3 util/install.sh -fnv
You can install Mininet for both Python 3 and Python 2:
PYTHON=python2 util/install.sh -fnv
PYTHON=python3 util/install.sh -n
Whichever version was installed last will be the default for `mn`.
As long as Mininet is installed for the appropriate version of
Python, you can run it using that versinon of Python:
python3 `which mn`
python2 `which mn`
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:
@@ -96,13 +137,10 @@ like to contribute an installation script, we would welcome it!)
This takes about 4 minutes on our test system.
You can change the directory where the dependencies are installed using
the -s <directory> flag.
util/install.sh -s <directory> -a
3.2. Native installation from source on Fedora 18+.
*This may be out of date.*
As root execute the following operations:
* install git
@@ -155,7 +193,8 @@ like to contribute an installation script, we would welcome it!)
Although we don't support other Linux distributions directly, it
should be possible to install and run Mininet with some degree of
manual effort.
manual effort. People have even gotten `mn --switch user` to run
in a ChromeOS container.
In general, you must have:
@@ -172,8 +211,11 @@ like to contribute an installation script, we would welcome it!)
support other Linux distributions.
Good luck!
As always, please feel free to submit issues or pull requests for
installation-related features.
Mininet Team
Good luck, and have fun!
Mininet Developers
---
+2 -2
View File
@@ -1,6 +1,6 @@
Mininet 2.2.2b1 License
Mininet 2.3.0b2 License
Copyright (c) 2013-2016 Open Networking Laboratory
Copyright (c) 2013-2020 Open Networking Foundation
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+17 -10
View File
@@ -2,16 +2,19 @@ MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
PYMN = python -B bin/mn
PYTHON ?= python
PYMN = $(PYTHON) -B bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226,E402,W504,W503,E731
PREFIX ?= /usr
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man/man1
DOCDIRS = doc/html doc/latex
PDF = doc/latex/refman.pdf
CC ?= cc
CFLAGS += -Wall -Wextra
@@ -44,18 +47,22 @@ slowtest: $(MININET)
mininet/examples/test/runner.py -v
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
$(CC) $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py install
install-mnexec: $(MNEXEC)
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
install-manpages: $(MANPAGES)
install -D -t $(MANDIR) $(MANPAGES)
install: install-mnexec install-manpages
$(PYTHON) -m pip install .
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py develop
$(PYTHON) -m pip install -e . --no-binary :all:
man: $(MANPAGES)
+30 -21
View File
@@ -1,9 +1,11 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.2.2b1
Mininet 2.3.0b2
[![Build Status][1]](https://github.com/mininet/mininet/actions)
### What is Mininet?
@@ -66,27 +68,31 @@ Mininet includes:
`mn -c`
### New features in this release
### Python 3 Support
This is primarily a performance improvement and bug fix release.
- Mininet 2.3.0b2 supports Python 3 and Python 2!
- Batch startup has been implemented for Open vSwitch, improving
startup performance.
- You can install both the Python 3 and Python 2 versions of
Mininet side by side, but the most recent installation will
determine which Python version is used by default by `mn`.
- OVS patch links have been implemented via OVSLink and --link ovs
- You can run `mn` directly with Python 2 or Python 3,
as long as the appropriate version of Mininet is installed,
e.g.
Warning! These links have *serious limitations* compared to
virtual Ethernet pairs: they are not attached to real Linux
interfaces so you cannot use tcpdump or wireshark with them;
they also cannot be used in long chains - we don't recommend more
than 64 OVSLinks, for example --linear,64. However, they can offer
significantly better performance than veth pairs, for certain
configurations.
$ sudo python2 `which mn`
- You can now easily install Mininet on a Raspberry Pi ;-)
- More information regarding Python 3 and Python 2 support
may be found in the release notes on http://docs.mininet.org.
### Other Enhancements and Information
- Additional information for this release and previous releases
may be found in the release notes on docs.mininet.org
- Support for Ubuntu 20.04 LTS (and 18.04 and 16.04)
- More reliable testing and CI via github actions
- Additional information about this release and previous releases
may be found in the release notes on http://docs.mininet.org.
### Installation
@@ -99,7 +105,8 @@ 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.)
contribute to, particularly the Frequently Asked Questions
(FAQ) at http://faq.mininet.org.
### Support
@@ -122,8 +129,10 @@ hard work that Mininet continues to grow and improve.
### Enjoy Mininet
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
Have fun! We look forward to seeing what you will do with Mininet
to change the networking world.
Bob Lantz
Mininet Core Team
on behalf of the Mininet Contributors
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
+133 -95
View File
@@ -11,18 +11,23 @@ Example to pull custom params (topo, switch, etc.) from a file:
sudo mn --custom ~/mininet/custom/custom_example.py
"""
from optparse import OptionParser
import os
import sys
import time
from functools import partial
from optparse import OptionParser # pylint: disable=deprecated-module
from sys import exit # pylint: disable=redefined-builtin
# Fix setuptools' evil madness, and open up (more?) security holes
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
# pylint: disable=wrong-import-position
from mininet.clean import cleanup
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, warn, error
import mininet.cli
from mininet.log import lg, LEVELS, info, debug, warn, error, output
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
Ryu, NOX, RemoteController, findController,
@@ -30,14 +35,11 @@ from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
UserSwitch, OVSSwitch, OVSBridge,
IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink, OVSLink
from mininet.link import Link, TCLink, TCULink, OVSLink
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
SingleSwitchReversedTopo, MinimalTopo )
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import customClass, specialClass, splitArgs
from mininet.util import buildTopo
from functools import partial
from mininet.util import customClass, specialClass, splitArgs, buildTopo
# Experimental! cluster edition prototype
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
@@ -46,6 +48,7 @@ from mininet.examples.cluster import ( MininetCluster, RemoteHost,
ClusterCleanup )
from mininet.examples.clustercli import ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
# built in topologies, created only when run
@@ -73,29 +76,66 @@ HOSTS = { 'proc': Host,
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
CONTROLLERDEF = 'default'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'ryu': Ryu,
'default': DefaultController, # Note: replaced below
'default': DefaultController, # Note: overridden below
'none': NullController }
LINKDEF = 'default'
LINKS = { 'default': Link,
LINKS = { 'default': Link, # Note: overridden below
'tc': TCLink,
'tcu': TCULink,
'ovs': OVSLink }
# TESTS dict can contain functions and/or Mininet() method names
# XXX: it would be nice if we could specify a default test, but
# this may be tricky
TESTS = { name: True
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
'none' ]
CLI = None # Set below if needed
ALTSPELLING = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
# Locally defined tests
def allTest( net ):
"Run ping and iperf tests"
net.waitConnected()
net.start()
net.ping()
net.iperf()
def nullTest( _net ):
"Null 'test' (does nothing)"
pass
TESTS.update( all=allTest, none=nullTest, build=nullTest )
# Map to alternate spellings of Mininet() methods
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp' }
def runTests( mn, options ):
"""Run tests
mn: Mininet object
option: list of test optinos """
# Split option into test name and parameters
for option in options:
# Multiple tests may be separated by '+' for now
for test in option.split( '+' ):
test, args, kwargs = splitArgs( test )
test = ALTSPELLING.get( test.lower(), test )
testfn = TESTS.get( test, test )
if callable( testfn ):
testfn( mn, *args, **kwargs )
elif hasattr( mn, test ):
mn.waitConnected()
getattr( mn, test )( *args, **kwargs )
else:
raise Exception( 'Test %s is unknown - please specify one of '
'%s ' % ( test, TESTS.keys() ) )
def addDictOption( opts, choicesDict, default, name, **kwargs ):
@@ -116,7 +156,7 @@ def addDictOption( opts, choicesDict, default, name, **kwargs ):
def version( *_args ):
"Print Mininet version and exit"
print "%s" % VERSION
output( "%s\n" % VERSION )
exit()
@@ -150,15 +190,18 @@ class MininetRunner( object ):
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
# pylint: disable=exec-used
exec( compile( open( fileName ).read(), fileName, 'exec' ),
customs, customs )
for name, val in customs.items():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
def setCustom( self, name, value ):
"Set custom parameters for MininetRunner."
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links' ):
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links'
'testnames', 'tests' ):
# Update dictionaries
param = name.upper()
globals()[ param ].update( value )
@@ -208,10 +251,8 @@ class MininetRunner( object ):
type='string',
help='read custom classes or params from .py file(s)'
)
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
opts.add_option( '--test', default=[], action='append',
dest='test', help='|'.join( TESTS.keys() ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
@@ -221,11 +262,11 @@ class MininetRunner( object ):
opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice',
choices=LEVELS.keys(), default = 'info',
choices=list( 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=6634,
opts.add_option( '--listenport', type='int', default=6654,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening " +
@@ -247,11 +288,13 @@ class MininetRunner( object ):
" Mininet's IP subnet, see the --ipbase option." )
opts.add_option( '--version', action='callback', callback=version,
help='prints the version and exits' )
opts.add_option( '--wait', '-w', action='store_true',
default=False, help='wait for switches to connect' )
opts.add_option( '--cluster', type='string', default=None,
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
opts.add_option( '--placement', type='choice',
choices=PLACEMENT.keys(), default='block',
choices=list( PLACEMENT.keys() ), default='block',
metavar='block|random',
help=( 'node placement for --cluster '
'(experimental!) ' ) )
@@ -268,116 +311,111 @@ class MininetRunner( object ):
# set logging verbosity
if LEVELS[self.options.verbosity] > LEVELS['output']:
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
'output!\n'
'Please restart Mininet with -v [debug, info, output].'
'Please restart Mininet with -v [debug, info, output].\n'
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# Maybe we'll reorganize this someday...
# pylint: disable=too-many-branches,too-many-statements
# pylint: disable=too-many-branches,too-many-statements,global-statement
def begin( self ):
"Create and run mininet."
if self.options.cluster:
servers = self.options.cluster.split( ',' )
global CLI
opts = self.options
if opts.cluster:
servers = opts.cluster.split( ',' )
for server in servers:
ClusterCleanup.add( server )
if self.options.clean:
if opts.clean:
cleanup()
exit()
start = time.time()
if not self.options.controller:
if not opts.controller:
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
self.options.controller = [ 'default' ]
opts.controller = [ 'default' ]
if not CONTROLLERS[ 'default' ]:
self.options.controller = [ 'none' ]
if self.options.switch == 'default':
opts.controller = [ 'none' ]
if opts.switch == 'default':
info( '*** No default OpenFlow controller found '
'for default switch!\n' )
info( '*** Falling back to OVS Bridge\n' )
self.options.switch = 'ovsbr'
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
opts.switch = 'ovsbr'
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
raise Exception( "Could not find a default controller "
"for switch %s" %
self.options.switch )
opts.switch )
topo = buildTopo( TOPOS, self.options.topo )
switch = customClass( SWITCHES, self.options.switch )
host = customClass( HOSTS, self.options.host )
topo = buildTopo( TOPOS, opts.topo )
switch = customClass( SWITCHES, opts.switch )
host = customClass( HOSTS, opts.host )
controller = [ customClass( CONTROLLERS, c )
for c in self.options.controller ]
link = customClass( LINKS, self.options.link )
for c in opts.controller ]
if opts.switch == 'user' and opts.link == 'default':
debug( '*** Using TCULink with UserSwitch\n' )
# Use link configured correctly for UserSwitch
opts.link = 'tcu'
link = customClass( LINKS, opts.link )
if self.validate:
self.validate( self.options )
self.validate( opts )
ipBase = self.options.ipbase
xterms = self.options.xterms
mac = self.options.mac
arp = self.options.arp
pin = self.options.pin
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
if opts.nolistenport:
opts.listenport = None
# Handle inNamespace, cluster options
inNamespace = self.options.innamespace
cluster = self.options.cluster
if inNamespace and cluster:
print "Please specify --innamespace OR --cluster"
# Handle innamespace, cluster options
if opts.innamespace and opts.cluster:
error( "Please specify --innamespace OR --cluster\n" )
exit()
Net = MininetWithControlNet if inNamespace else Mininet
cli = ClusterCLI if cluster else CLI
if cluster:
Net = MininetWithControlNet if opts.innamespace else Mininet
if opts.cluster:
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
Net = partial( MininetCluster, servers=servers,
placement=PLACEMENT[ self.options.placement ] )
placement=PLACEMENT[ opts.placement ] )
mininet.cli.CLI = ClusterCLI
mn = Net( topo=topo,
switch=switch, host=host, controller=controller,
link=link,
ipBase=ipBase,
inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
switch=switch, host=host, controller=controller, link=link,
ipBase=opts.ipbase, inNamespace=opts.innamespace,
xterms=opts.xterms, autoSetMacs=opts.mac,
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
waitConnected=opts.wait,
listenPort=opts.listenport )
if self.options.ensure_value( 'nat', False ):
nat = mn.addNAT( *self.options.nat_args,
**self.options.nat_kwargs )
nat.configDefault()
if opts.ensure_value( 'nat', False ):
with open( '/etc/resolv.conf' ) as f:
if 'nameserver 127.' in f.read():
warn( '*** Warning: loopback address in /etc/resolv.conf '
'may break host DNS over NAT\n')
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
if self.options.pre:
cli( mn, script=self.options.pre )
# --custom files can set CLI or change mininet.cli.CLI
CLI = mininet.cli.CLI if CLI is None else CLI
test = self.options.test
test = ALTSPELLING.get( test, test )
if opts.pre:
CLI( mn, script=opts.pre )
mn.start()
if test == 'none':
pass
elif test == 'all':
mn.waitConnected()
mn.start()
mn.ping()
mn.iperf()
elif test == 'cli':
cli( mn )
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
if opts.test:
runTests( mn, opts.test )
else:
CLI( mn )
if self.options.post:
cli( mn, script=self.options.post )
if opts.post:
CLI( mn, script=opts.post )
mn.stop()
@@ -391,7 +429,7 @@ if __name__ == "__main__":
except KeyboardInterrupt:
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
cleanup()
except Exception:
except Exception: # pylint: disable=broad-except
# Print exception
type_, val_, trace_ = sys.exc_info()
errorMsg = ( "-"*80 + "\n" +
+1 -4
View File
@@ -13,12 +13,9 @@ from mininet.topo import Topo
class MyTopo( Topo ):
"Simple topology example."
def __init__( self ):
def build( self ):
"Create custom topo."
# Initialize topology
Topo.__init__( self )
# Add hosts and switches
leftHost = self.addHost( 'h1' )
rightHost = self.addHost( 'h2' )
+13 -10
View File
@@ -1,34 +1,37 @@
#!/usr/bin/python
#!/usr/bin/env python
"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, waitListening
from mininet.log import info, warn, output
ensureRoot()
timeout = 5
print "*** Creating nodes"
info( "*** Creating nodes\n" )
h1 = Host( 'h1' )
root = Host( 'root', inNamespace=False )
print "*** Creating links"
info( "*** Creating link\n" )
h1.linkTo( root )
print h1
info( h1 )
print "*** Configuring nodes"
info( "*** Configuring nodes\n" )
h1.setIP( '10.0.0.1', 8 )
root.setIP( '10.0.0.2', 8 )
print "*** Creating banner file"
info( "*** Creating banner file\n" )
f = open( '/tmp/%s.banner' % h1.name, 'w' )
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
f.close()
print "*** Running sshd"
info( "*** Running sshd\n" )
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:
@@ -37,7 +40,7 @@ h1.cmd( cmd )
listening = waitListening( server=h1, port=22, timeout=timeout )
if listening:
print "*** You may now ssh into", h1.name, "at", h1.IP()
output( "*** You may now ssh into", h1.name, "at", h1.IP(), '\n' )
else:
print ( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ) )
warn( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ), '\n' )
+5 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
bind.py: Bind mount example
@@ -34,14 +34,14 @@ and '/var/run'. It also has a temporary private directory mounted
on '/var/mn'
"""
from functools import partial
from mininet.net import Mininet
from mininet.node import Host
from mininet.cli import CLI
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from functools import partial
# Sample usage
@@ -53,7 +53,7 @@ def testHostWithPrivateDirs():
'/var/mn' ]
host = partial( Host,
privateDirs=privateDirs )
net = Mininet( topo=topo, host=host )
net = Mininet( topo=topo, host=host, waitConnected=True )
net.start()
directories = [ directory[ 0 ] if isinstance( directory, tuple )
else directory for directory in privateDirs ]
@@ -61,6 +61,7 @@ def testHostWithPrivateDirs():
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
testHostWithPrivateDirs()
+190 -77
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
cluster.py: prototyping/experimentation for distributed Mininet,
@@ -74,6 +74,16 @@ Things to do:
- hifi support (e.g. delay compensation)
"""
from signal import signal, SIGINT, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
import os
from random import randrange
import sys
import re
from itertools import groupby
from operator import attrgetter
from distutils.version import StrictVersion
from mininet.node import Node, Host, OVSSwitch, Controller
from mininet.link import Link, Intf
from mininet.net import Mininet
@@ -84,15 +94,7 @@ from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from mininet.clean import addCleanupCallback
from signal import signal, SIGINT, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
import os
from random import randrange
import sys
import re
from itertools import groupby
from operator import attrgetter
from distutils.version import StrictVersion
# pylint: disable=too-many-arguments
def findUser():
@@ -125,7 +127,7 @@ class ClusterCleanup( object ):
def cleanup( cls ):
"Clean up"
info( '*** Cleaning up cluster\n' )
for server, user in cls.serveruser.iteritems():
for server, user in cls.serveruser.items():
if server == 'localhost':
# Handled by mininet.clean.cleanup()
continue
@@ -238,7 +240,7 @@ class RemoteMixin( object ):
args: string or list of strings
returns: stdout and stderr"""
popen = self.rpopen( *cmd, **opts )
# print 'RCMD: POPEN:', popen
# info( 'RCMD: POPEN:', popen, '\n' )
# These loops are tricky to get right.
# Once the process exits, we can read
# EOF twice if necessary.
@@ -260,7 +262,7 @@ class RemoteMixin( object ):
cmd: remote command to run (list)
**params: parameters to Popen()
returns: Popen() object"""
if type( cmd ) is str:
if isinstance( cmd, str):
cmd = cmd.split()
if self.isRemote:
if sudo:
@@ -287,7 +289,8 @@ class RemoteMixin( object ):
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
kwargs.update( moveIntfFn=RemoteLink.moveIntf )
# kwargs.update( moveIntfFn=RemoteLink.moveIntf )
# pylint: disable=useless-super-delegation
return super( RemoteMixin, self).addIntf( *args, **kwargs )
@@ -324,6 +327,7 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
StrictVersion( '1.10' ) )
@classmethod
# pylint: disable=arguments-differ
def batchStartup( cls, switches, **_kwargs ):
"Start up switches in per-server batches"
key = attrgetter( 'server' )
@@ -335,6 +339,7 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
return switches
@classmethod
# pylint: disable=arguments-differ
def batchShutdown( cls, switches, **_kwargs ):
"Stop switches in per-server batches"
key = attrgetter( 'server' )
@@ -392,33 +397,31 @@ class RemoteLink( Link ):
return self.tunnel
@staticmethod
def moveIntf( intf, node, printError=True ):
def moveIntf( intf, node ):
"""Move remote interface from root ns to node
intf: string, interface
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
srcNode: source Node or None (default) for root ns"""
intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
node.rcmd( cmd )
links = node.cmd( 'ip link show' )
if not ' %s:' % intf in links:
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
result = node.rcmd( cmd )
if result:
raise Exception('error executing command %s' % cmd)
return True
def makeTunnel( self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None ):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != 'localhost' or node2.server != 'localhost'
assert node1.server != node2.server
# And we can't ssh into this server remotely as 'localhost',
# so try again swappping node1 and node2
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
return self.makeTunnel( node1=node2, node2=node1,
intfname1=intfname2, intfname2=intfname1,
addr1=addr2, addr2=addr1 )
debug( '\n*** Make SSH tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
# 1. Create tap interfaces
for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename
@@ -474,6 +477,93 @@ class RemoteLink( Link ):
return result
class RemoteSSHLink( RemoteLink ):
"Remote link using SSH tunnels"
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
class RemoteGRELink( RemoteLink ):
"Remote link using GRE tunnels"
GRE_KEY = 0
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
def stop( self ):
"Stop this link"
if self.tunnel:
self.intf1.delete()
self.intf2.delete()
else:
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
node1 = self.node1 if node1 is None else node1
node2 = self.node2 if node2 is None else node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == server2:
# Link within same server
Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs )
# Need to reduce the MTU of all emulated hosts to 1450 for GRE
# tunneling, otherwise packets larger than 1400 bytes cannot be
# successfully transmitted through the tunnel.
node1.cmd('ip link set dev %s mtu 1450' % intfname1)
node2.cmd('ip link set dev %s mtu 1450' % intfname2)
else:
# Otherwise, make a tunnel
self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
self.tunnel = 1
def makeTunnel(self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != node2.server
if node2.server == 'localhost':
return self.makeTunnel( node1=node2, node2=node1,
intfname1=intfname2, intfname2=intfname1,
addr1=addr2, addr2=addr1 )
IP1, IP2 = node1.serverIP, node2.serverIP
# GRE tunnel needs to be set up with the IP of the local interface
# that connects the remote node, NOT '127.0.0.1' of localhost
if node1.server == 'localhost':
output = quietRun('ip route get %s' % node2.serverIP)
IP1 = output.split(' src ')[1].split()[0]
debug( '\n*** Make GRE tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
tun1 = 'local ' + IP1 + ' remote ' + IP2
tun2 = 'local ' + IP2 + ' remote ' + IP1
self.__class__.GRE_KEY += 1
for (node, intfname, addr, tun) in [(node1, intfname1, addr1, tun1),
(node2, intfname2, addr2, tun2)]:
node.rcmd('ip link delete ' + intfname)
result = node.rcmd('ip link add name ' + intfname + ' type gretap '
+ tun + ' ttl 64 key '
+ str( self.__class__.GRE_KEY) )
if result:
raise Exception('error creating gretap on %s: %s'
% (node, result))
if addr:
node.rcmd('ip link set %s address %s' % (intfname, addr))
node.rcmd('ip link set dev %s up' % intfname)
node.rcmd('ip link set dev %s mtu 1450' % intfname)
if not self.moveIntf(intfname, node):
raise Exception('interface move failed on node %s' % node)
return None # May want to return something useful here
# Some simple placement algorithms for MininetCluster
class Placer( object ):
@@ -506,10 +596,10 @@ class Placer( object ):
class RandomPlacer( Placer ):
"Random placement"
def place( self, nodename ):
def place( self, node ):
"""Random placement function
nodename: node name"""
assert nodename # please pylint
node: node"""
assert node # please pylint
# This may be slow with lots of servers
return self.servers[ randrange( 0, len( self.servers ) ) ]
@@ -523,10 +613,10 @@ class RoundRobinPlacer( Placer ):
Placer.__init__( self, *args, **kwargs )
self.next = 0
def place( self, nodename ):
def place( self, node ):
"""Round-robin placement function
nodename: node name"""
assert nodename # please pylint
node: node"""
assert node # please pylint
# This may be slow with lots of servers
server = self.servers[ self.next ]
self.next = ( self.next + 1 ) % len( self.servers )
@@ -564,7 +654,7 @@ class SwitchBinPlacer( Placer ):
tickets = sum( [ binsizes[ server ] * [ server ]
for server in servers ], [] )
# And assign one ticket to each node
return { node: ticket for node, ticket in zip( nodes, tickets ) }
return dict( zip( nodes, tickets ) )
def calculatePlacement( self ):
"Pre-calculate node placement"
@@ -621,21 +711,21 @@ class HostSwitchBinPlacer( Placer ):
self.cset = frozenset( self.controllers )
self.hind, self.sind, self.cind = 0, 0, 0
def place( self, nodename ):
def place( self, node ):
"""Simple placement algorithm:
place nodes into evenly sized bins"""
# Place nodes into bins
if nodename in self.hset:
if node in self.hset:
server = self.servdict[ self.hind / self.hbin ]
self.hind += 1
elif nodename in self.sset:
elif node in self.sset:
server = self.servdict[ self.sind / self.sbin ]
self.sind += 1
elif nodename in self.cset:
elif node in self.cset:
server = self.servdict[ self.cind / self.cbin ]
self.cind += 1
else:
info( 'warning: unknown node', nodename )
info( 'warning: unknown node', node )
server = self.servdict[ 0 ]
return server
@@ -681,6 +771,7 @@ class MininetCluster( Mininet ):
# Make sure control directory exists
self.cdir = os.environ[ 'HOME' ] + '/.ssh/mn'
errRun( [ 'mkdir', '-p', self.cdir ] )
# pylint: disable=unexpected-keyword-arg
Mininet.__init__( self, *args, **params )
def popen( self, cmd ):
@@ -757,16 +848,33 @@ class MininetCluster( Mininet ):
if cfile:
config.setdefault( 'controlPath', cfile )
# pylint: disable=arguments-differ,signature-differs
def addController( self, *args, **kwargs ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
# Update IP address for controller that may not be local
if ( isinstance( controller, Controller)
and controller.IP() == '127.0.0.1'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
loopback = '127.0.0.1'
if ( not isinstance( controller, Controller ) or
controller.IP() != loopback ):
return None
# Find route to a different server IP address
serverIPs = [ ip for ip in self.serverIP.values()
if ip is not controller.IP() ]
if not serverIPs:
return None # no remote servers - loopback is fine
remoteIP = serverIPs[ 0 ]
# Route should contain 'dev <intfname>'
route = controller.cmd( 'ip route get', remoteIP,
r'| egrep -o "dev\s[^[:space:]]+"' )
if not route:
raise Exception('addController: no route from', controller,
'to', remoteIP )
intf = route.split()[ 1 ].strip()
debug( 'adding', intf, 'to', controller )
Intf( intf, node=controller ).updateIP()
debug( controller, 'IP address updated to', controller.IP() )
return controller
# pylint: disable=arguments-differ,signature-differs
def buildFromTopo( self, *args, **kwargs ):
"Start network"
info( '*** Placing nodes\n' )
@@ -775,11 +883,11 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels():
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=RemoteLink )
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2', server='ubuntu2' )
net = Mininet( host=RemoteHost, link=link, waitConnected=True )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server=remote )
net.addLink( h1, h2 )
net.start()
net.pingAll()
@@ -790,30 +898,31 @@ def testNsTunnels():
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2' ):
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
"Test remote Node classes"
print '*** Remote Node Test'
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=RemoteLink )
info( '*** Remote Node Test\n' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link,
waitConnected=True )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
print "*** Creating local h1"
info( "*** Creating local h1\n" )
h1 = net.addHost( 'h1' )
print "*** Creating remote h2"
info( "*** Creating remote h2\n" )
h2 = net.addHost( 'h2', server=remote )
print "*** Creating local s1"
info( "*** Creating local s1\n" )
s1 = net.addSwitch( 's1' )
print "*** Creating remote s2"
info( "*** Creating remote s2\n" )
s2 = net.addSwitch( 's2', server=remote )
print "*** Adding links"
info( "*** Adding links\n" )
net.addLink( h1, s1 )
net.addLink( s1, s2 )
net.addLink( h2, s2 )
net.start()
print 'Mininet is running on', quietRun( 'hostname' ).strip()
info( 'Mininet is running on', quietRun( 'hostname' ).strip(), '\n' )
for node in c0, h1, h2, s1, s2:
print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip()
info( 'Node', node, 'is running on',
node.cmd( 'hostname' ).strip(), '\n' )
net.pingAll()
CLI( net )
net.stop()
@@ -851,11 +960,11 @@ def ClusterController( *args, **kwargs):
Intf( 'eth0', node=controller ).updateIP()
return controller
def testRemoteTopo():
def testRemoteTopo( link=RemoteGRELink ):
"Test remote Node classes using Mininet()/Topo() API"
topo = LinearTopo( 2 )
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
link=RemoteLink, controller=ClusterController )
link=link, controller=ClusterController )
net.start()
net.pingAll()
net.stop()
@@ -865,18 +974,17 @@ def testRemoteTopo():
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches():
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
"Test with local hosts and remote switches"
servers = [ 'localhost', 'ubuntu2']
servers = [ 'localhost', remote]
topo = TreeTopo( depth=4, fanout=2 )
net = MininetCluster( topo=topo, servers=servers,
net = MininetCluster( topo=topo, servers=servers, link=link,
placement=RoundRobinPlacer )
net.start()
net.pingAll()
net.stop()
#
# For testing and demo purposes it would be nice to draw the
# network graph and color it based on server.
@@ -884,31 +992,36 @@ def testRemoteSwitches():
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster():
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
"Test MininetCluster()"
servers = [ 'localhost', 'ubuntu2' ]
servers = [ 'localhost', remote ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers,
net = MininetCluster( topo=topo, servers=servers, link=link,
placement=SwitchBinPlacer )
net.start()
net.pingAll()
net.stop()
def signalTest():
def signalTest( remote='ubuntu2'):
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server='ubuntu1' )
h = RemoteHost( 'h0', server=remote )
h.shell.send_signal( SIGINT )
h.shell.poll()
if h.shell.returncode is None:
print 'OK: ', h, 'has not exited'
info( 'signalTest: SUCCESS: ', h, 'has not exited after SIGINT', '\n' )
else:
print 'FAILURE:', h, 'exited with code', h.shell.returncode
info( 'signalTest: FAILURE:', h, 'exited with code',
h.shell.returncode, '\n' )
h.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
# testRemoteTopo()
# testRemoteNet()
# testMininetCluster()
# testRemoteSwitches()
signalTest()
remoteServer = 'ubuntu2'
remoteLink = RemoteSSHLink
testRemoteTopo(link=remoteLink)
testNsTunnels( remote=remoteServer, link=remoteLink )
testRemoteNet( remote=remoteServer, link=remoteLink)
testMininetCluster( remote=remoteServer, link=remoteLink)
testRemoteSwitches( remote=remoteServer, link=remoteLink)
signalTest( remote=remoteServer )
+1
View File
@@ -17,6 +17,7 @@ def clusterSanity():
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
clusterSanity()
+14 -7
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"CLI for Mininet Cluster Edition prototype demo"
@@ -27,17 +27,23 @@ class ClusterCLI( CLI ):
def do_plot( self, _line ):
"Plot topology colored by node placement"
# Import networkx if needed
global nx, plt
global nx, plt, graphviz_layout
if not nx:
try:
# pylint: disable=import-error
# pylint: disable=import-error,no-member
# pylint: disable=import-outside-toplevel
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
plt = pyplot # satisfiy pylint
plt = pyplot # satisfy pylint
import pygraphviz
assert pygraphviz # silence pyflakes
# pylint: enable=import-error
# Networkx moved this around
if hasattr( nx, 'graphviz_layout' ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error,no-member
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
@@ -45,7 +51,8 @@ class ClusterCLI( CLI ):
# Make a networkx Graph
g = nx.Graph()
mn = self.mn
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
servers = getattr( mn, 'servers', [ 'localhost' ] )
hosts, switches = mn.hosts, mn.switches
nodes = hosts + switches
g.add_nodes_from( nodes )
links = [ ( link.intf1.node, link.intf2.node )
@@ -55,7 +62,7 @@ class ClusterCLI( CLI ):
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
color = dict( zip( servers, self.colorsFor( servers ) ) )
# Plot it!
pos = nx.graphviz_layout( g )
pos = graphviz_layout( g )
opts = { 'ax': None, 'font_weight': 'bold',
'width': 2, 'edge_color': 'darkblue' }
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
+6 -3
View File
@@ -1,8 +1,10 @@
#!/usr/bin/python
#!/usr/bin/env python
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
from mininet.examples.cluster import ( MininetCluster, SwitchBinPlacer,
RemoteLink )
# ^ Could also use: RemoteSSHLink, RemoteGRELink
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
@@ -11,12 +13,13 @@ def demo():
"Simple Demo of Cluster Mode"
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers,
net = MininetCluster( topo=topo, servers=servers, link=RemoteLink,
placement=SwitchBinPlacer )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
demo()
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
from mininet.net import Mininet
from mininet.log import setLogLevel
def perf(Link):
"Test connectivity nand performance over Link"
net = Mininet( host=RemoteHost, link=Link, waitConnected=True )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
net.iperf()
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perf( RemoteSSHLink )
perf( RemoteGRELink )
+6 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
consoles.py: bring up a bunch of miniature consoles on a virtual network
@@ -27,6 +27,7 @@ Bob Lantz, April 2010
import re
# pylint: disable=import-error
from Tkinter import Frame, Button, Label, Text, Scrollbar, Canvas, Wm, READABLE
from mininet.log import setLogLevel
@@ -34,6 +35,9 @@ from mininet.topolib import TreeNet
from mininet.term import makeTerms, cleanUpScreens
from mininet.util import quietRun
# pylint: disable=too-many-arguments
class Console( Frame ):
"A simple console on a host."
@@ -319,7 +323,7 @@ class ConsoleApp( Frame ):
if not m:
return
val, units = float( m.group( 1 ) ), m.group( 2 )
#convert to Gbps
# convert to Gbps
if units[0] == 'M':
val *= 10 ** -3
elif units[0] == 'K':
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network where different switches are connected to
@@ -26,8 +26,9 @@ class MultiSwitch( OVSSwitch ):
def start( self, controllers ):
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
topo = TreeTopo( depth=2, fanout=2 )
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
net = Mininet( topo=topo, switch=MultiSwitch, build=False, waitConnected=True )
for c in [ c0, c1 ]:
net.addController(c)
net.build()
+16 -13
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example creates a multi-controller network from semi-scratch by
@@ -11,51 +11,54 @@ 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
from mininet.log import setLogLevel, info
def multiControllerNet():
"Create a network from semi-scratch with multiple controllers."
net = Mininet( controller=Controller, switch=OVSSwitch )
net = Mininet( controller=Controller, switch=OVSSwitch,
waitConnected=True )
print "*** Creating (reference) controllers"
info( "*** Creating (reference) controllers\n" )
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
print "*** Creating switches"
info( "*** Creating switches\n" )
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 ]
info( "*** Creating hosts\n" )
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"
info( "*** Creating links\n" )
for h in hosts1:
net.addLink( s1, h )
for h in hosts2:
net.addLink( s2, h )
net.addLink( s1, s2 )
print "*** Starting network"
info( "*** Starting network\n" )
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
print "*** Testing network"
info( "*** Testing network\n" )
net.pingAll()
print "*** Running CLI"
info( "*** Running CLI\n" )
CLI( net )
print "*** Stopping network"
info( "*** Stopping network\n" )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' ) # for CLI output
multiControllerNet()
+11 -8
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
controlnet.py: Mininet with a custom control network
@@ -49,7 +49,7 @@ class MininetFacade( object ):
args: unnamed networks passed as arguments
kwargs: named networks passed as arguments"""
self.net = net
self.nets = [ net ] + list( args ) + kwargs.values()
self.nets = [ net ] + list( args ) + list( kwargs.values() )
self.nameToNet = kwargs
self.nameToNet['net'] = net
@@ -59,13 +59,14 @@ class MininetFacade( object ):
def __getitem__( self, key ):
"returns primary/named networks or node from any net"
#search kwargs for net named key
# search kwargs for net named key
if key in self.nameToNet:
return self.nameToNet[ key ]
#search each net for node named key
# search each net for node named key
for net in self.nets:
if key in net:
return net[ key ]
return None
def __iter__( self ):
"Iterate through all nodes in all Mininet objects"
@@ -100,10 +101,10 @@ class MininetFacade( object ):
class ControlNetwork( Topo ):
"Control Network Topology"
def __init__( self, n, dataController=DataController, **kwargs ):
# pylint: disable=arguments-differ
def build( 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
@@ -124,7 +125,8 @@ def run():
info( '* Creating Control Network\n' )
ctopo = ControlNetwork( n=4, dataController=DataController )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24',
controller=None, waitConnected=True )
info( '* Adding Control Network Controller\n')
cnet.addController( 'cc0', controller=Controller )
info( '* Starting Control Network\n')
@@ -134,7 +136,8 @@ def run():
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 )
net = Mininet( topo=topo, switch=sw, controller=None,
waitConnected=True )
info( '* Adding Controllers to Data Network\n' )
for host in cnet.hosts:
if isinstance(host, Controller):
+55 -21
View File
@@ -1,17 +1,41 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
cpu.py: test iperf bandwidth for varying cpu limits
Since we are limiting the hosts (only), we should expect the iperf
processes to be affected, as well as any system processing which is
billed to the hosts.
We reserve >50% of cycles for system processing; we assume that
this is enough for it not to affect results. Hosts are limited to
40% of total cycles, which we assume is enough to make them CPU
bound.
As CPU performance increases over time, we may have to reduce the
overall CPU allocation so that the host processing is still CPU bound.
This is perhaps an argument for specifying performance in a more
system-independent manner.
It would also be nice to have a better handle on limiting packet
processing cycles. It's not entirely clear to me how those are
billed to user or system processes if we are using OVS with a kernel
datapath. With a user datapath, they are easier to account for, but
overall performance is usually lower.
Although the iperf client uses more CPU and should be CPU bound (?),
we measure the received data at the server since the client transmit
rate includes buffering.
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, waitListening
from mininet.util import custom, waitListening, decode
from mininet.log import setLogLevel, info
from mininet.clean import cleanup
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
"""Example/test of link and CPU bandwidth limits
cpu: cpu limit as fraction of overall CPU time"""
@@ -20,27 +44,36 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
results = {}
for sched in 'rt', 'cfs':
print '*** Testing with', sched, 'bandwidth limiting'
info( '*** Testing with', sched, 'bandwidth limiting\n' )
for cpu in cpuLimits:
# cpu is the cpu fraction for all hosts, so we divide
# it across two hosts
host = custom( CPULimitedHost, sched=sched,
period_us=period_us,
cpu=cpu )
cpu=.5*cpu )
try:
net = Mininet( topo=topo, host=host )
net = Mininet( topo=topo, host=host, waitConnected=True )
# pylint: disable=bare-except
except:
info( '*** Skipping host %s\n' % sched )
except: # noqa
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
cleanup()
break
net.start()
net.pingAll()
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
client, server = hosts[ 0 ], hosts[ -1 ]
server.cmd( 'iperf -s -p 5001 &' )
info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' %
( 100.0 * cpu ) )
# We measure at the server because it doesn't include
# the client's buffer fill rate
popen = server.popen( 'iperf -yc -s -p 5001' )
waitListening( client, server, 5001 )
result = client.cmd( 'iperf -yc -t %s -c %s' % (
seconds, server.IP() ) ).split( ',' )
# ignore empty result from waitListening/telnet
popen.stdout.readline()
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
result = decode( popen.stdout.readline() ).split( ',' )
bps = float( result[ -1 ] )
server.cmdPrint( 'kill %iperf' )
popen.terminate()
net.stop()
updated = results.get( sched, [] )
updated += [ ( cpu, bps ) ]
@@ -52,22 +85,23 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
def dump( results ):
"Dump results"
fmt = '%s\t%s\t%s'
fmt = '%s\t%s\t%s\n'
print
print fmt % ( 'sched', 'cpu', 'client MB/s' )
print
info( '\n' )
info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) )
for sched in sorted( results.keys() ):
entries = results[ sched ]
for cpu, bps in entries:
pct = '%.2f%%' % ( cpu * 100 )
mbps = bps / 1e6
print fmt % ( sched, pct, mbps )
pct = '%d%%' % ( cpu * 100 )
mbps = '%.2e' % bps
info( fmt % ( sched, pct, mbps ) )
if __name__ == '__main__':
setLogLevel( 'info' )
limits = [ .45, .4, .3, .2, .1 ]
# These are the limits for the hosts/iperfs - the
# rest is for system processes
limits = [ .5, .4, .3, .2, .1 ]
out = bwtest( limits )
dump( out )
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example shows how to create an empty Mininet object
@@ -14,7 +14,7 @@ def emptyNet():
"Create an empty network and add nodes to it."
net = Mininet( controller=Controller )
net = Mininet( controller=Controller, waitConnected=True )
info( '*** Adding controller\n' )
net.addController( 'c0' )
@@ -39,6 +39,7 @@ def emptyNet():
info( '*** Stopping network' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
emptyNet()
+6 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example shows how to add an interface (for example a real
@@ -8,6 +8,8 @@ hardware interface) to a network after the network is created.
import re
import sys
from sys import exit # pylint: disable=redefined-builtin
from mininet.cli import CLI
from mininet.log import setLogLevel, info, error
from mininet.net import Mininet
@@ -15,6 +17,7 @@ from mininet.link import Intf
from mininet.topolib import TreeTopo
from mininet.util import quietRun
def checkIntf( intf ):
"Make sure intf exists and is not configured."
config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True )
@@ -27,6 +30,7 @@ def checkIntf( intf ):
'and is probably in use!\n' )
exit( 1 )
if __name__ == '__main__':
setLogLevel( 'info' )
@@ -38,7 +42,7 @@ if __name__ == '__main__':
checkIntf( intfName )
info( '*** Creating network\n' )
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ), waitConnected=True )
switch = net.switches[ 0 ]
info( '*** Adding hardware interface', intfName, 'to switch',
+6 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
'''
example of using various TCIntf options.
@@ -13,7 +13,7 @@ from mininet.link import TCLink
def intfOptions():
"run various traffic control commands on a single interface"
net = Mininet( autoStaticArp=True )
net = Mininet( autoStaticArp=True, waitConnected=True )
net.addController( 'c0' )
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2' )
@@ -25,10 +25,10 @@ def intfOptions():
# flush out latency from reactive forwarding delay
net.pingAll()
info( '\n*** Configuring one intf with bandwidth of 5 Mb\n' )
link1.intf1.config( bw=5 )
info( '\n*** Configuring one intf with bandwidth of 10 Mb\n' )
link1.intf1.config( bw=10 )
info( '\n*** Running iperf to test\n' )
net.iperf()
net.iperf( seconds=10 )
info( '\n*** Configuring one intf with loss of 50%\n' )
link1.intf1.config( loss=50 )
@@ -43,6 +43,7 @@ def intfOptions():
info( '\n*** Done testing\n' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
intfOptions()
+4 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
limit.py: example of using link and CPU limits
@@ -34,7 +34,7 @@ def limit( bw=10, cpu=.1 ):
'Skipping this test\n' )
continue
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
net = Mininet( topo=myTopo, intf=intf, host=host )
net = Mininet( topo=myTopo, intf=intf, host=host, waitConnected=True )
net.start()
testLinkLimit( net, bw=bw )
net.runCpuLimitTest( cpu=cpu )
@@ -43,7 +43,7 @@ def limit( bw=10, cpu=.1 ):
def verySimpleLimit( bw=150 ):
"Absurdly simple limiting test"
intf = custom( TCIntf, bw=bw )
net = Mininet( intf=intf )
net = Mininet( intf=intf, waitConnected=True )
h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' )
net.addLink( h1, h2 )
net.start()
@@ -55,6 +55,7 @@ def verySimpleLimit( bw=150 ):
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
limit()
+33 -32
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Test bandwidth (using iperf) on linear networks of varying size,
@@ -23,25 +23,26 @@ of switches, this example demonstrates:
"""
import sys
from functools import partial
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
from mininet.log import lg
from mininet.log import lg, info
from mininet.util import irange, quietRun
from mininet.link import TCLink
from functools import partial
import sys
flush = sys.stdout.flush
class LinearTestTopo( Topo ):
"Topology for a string of N hosts and N-1 switches."
def __init__( self, N, **params ):
# Initialize topology
Topo.__init__( self, **params )
# pylint: disable=arguments-differ
def build( self, N, **params ):
# Create switches and hosts
hosts = [ self.addHost( 'h%s' % h )
for h in irange( 1, N ) ]
@@ -82,45 +83,45 @@ def linearBandwidthTest( lengths ):
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath in switches.keys():
print "*** testing", datapath, "datapath"
for datapath in switches:
info( "*** testing", datapath, "datapath\n" )
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='1ms' )
link = partial( TCLink, delay='30ms', bw=100 )
net = Mininet( topo=topo, switch=Switch,
controller=Controller, waitConnected=True,
link=link )
controller=Controller, link=link,
waitConnected=True )
net.start()
print "*** testing basic connectivity"
info( "*** testing basic connectivity\n" )
for n in lengths:
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
print "*** testing bandwidth"
info( "*** testing bandwidth\n" )
for n in lengths:
src, dst = net.hosts[ 0 ], net.hosts[ n ]
# Try to prime the pump to reduce PACKET_INs during test
# since the reference controller is reactive
src.cmd( 'telnet', dst.IP(), '5001' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ], seconds=10 )
print bandwidth
info( "testing", src.name, "<->", dst.name, '\n' )
# serverbw = received; _clientbw = buffered
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=5 )
info( serverbw, '\n' )
flush()
results[ datapath ] += [ ( n, bandwidth ) ]
results[ datapath ] += [ ( n, serverbw ) ]
net.stop()
for datapath in switches.keys():
print
print "*** Linear network results for", datapath, "datapath:"
print
for datapath in switches:
info( "\n*** Linear network results for", datapath, "datapath:\n" )
result = results[ datapath ]
print "SwitchCount\tiperf Results"
for switchCount, bandwidth in result:
print switchCount, '\t\t',
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
print
print
info( "SwitchCount\tiperf Results\n" )
for switchCount, serverbw in result:
info( switchCount, '\t\t' )
info( serverbw, '\n' )
info( '\n')
info( '\n' )
if __name__ == '__main__':
lg.setLogLevel( 'info' )
sizes = [ 1, 10, 20, 40, 60, 80 ]
print "*** Running linearBandwidthTest", sizes
sizes = [ 1, 2, 3, 4 ]
info( "*** Running linearBandwidthTest", sizes, '\n' )
linearBandwidthTest( sizes )
+10 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
linuxrouter.py: Example network with Linux IP router
@@ -27,15 +27,18 @@ Additional routes may be added to the router or hosts by
executing 'ip route' or 'route' commands on the router or hosts.
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
class LinuxRouter( Node ):
"A Node with IP forwarding enabled."
# pylint: disable=arguments-differ
def config( self, **params ):
super( LinuxRouter, self).config( **params )
# Enable forwarding on the router
@@ -49,12 +52,13 @@ class LinuxRouter( Node ):
class NetworkTopo( Topo ):
"A LinuxRouter connecting three IP subnets"
# pylint: disable=arguments-differ
def build( self, **_opts ):
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
s1, s2, s3 = [ self.addSwitch( s ) for s in 's1', 's2', 's3' ]
s1, s2, s3 = [ self.addSwitch( s ) for s in ( 's1', 's2', 's3' ) ]
self.addLink( s1, router, intfName2='r0-eth1',
params2={ 'ip' : defaultIP } ) # for clarity
@@ -77,13 +81,15 @@ class NetworkTopo( Topo ):
def run():
"Test linux router"
topo = NetworkTopo()
net = Mininet( topo=topo ) # controller is used by s1-s3
net = Mininet( topo=topo,
waitConnected=True ) # controller is used by s1-s3
net.start()
info( '*** Routing Table on Router:\n' )
print net[ 'r0' ].cmd( 'route' )
info( net[ 'r0' ].cmd( 'route' ) )
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
run()
+139 -119
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
MiniEdit: a simple network editor for Mininet
@@ -13,57 +13,72 @@ Controller icon from http://semlabs.co.uk/
OpenFlow icon from https://www.opennetworking.org/
"""
# Miniedit needs some work in order to pass pylint...
# pylint: disable=line-too-long,too-many-branches
# pylint: disable=too-many-statements,attribute-defined-outside-init
# pylint: disable=missing-docstring
MINIEDIT_VERSION = '2.2.0.1'
from optparse import OptionParser
# from Tkinter import *
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton,
Menu, Toplevel, Button, BitmapImage, PhotoImage, Canvas,
Scrollbar, Wm, TclError, StringVar, IntVar,
E, W, EW, NW, Y, VERTICAL, SOLID, CENTER,
RIGHT, LEFT, BOTH, TRUE, FALSE )
from ttk import Notebook
from tkMessageBox import showerror
from subprocess import call
import tkFont
import tkFileDialog
import tkSimpleDialog
import re
import json
from distutils.version import StrictVersion
import os
import re
import sys
from distutils.version import StrictVersion
from functools import partial
from optparse import OptionParser
from subprocess import call
from sys import exit # pylint: disable=redefined-builtin
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
# someday: from ttk import *
from mininet.log import info, setLogLevel
from mininet.log import info, debug, warn, setLogLevel
from mininet.net import Mininet, VERSION
from mininet.util import netParse, ipAdd, quietRun
from mininet.util import buildTopo
from mininet.util import custom, customClass
from mininet.util import (netParse, ipAdd, quietRun,
buildTopo, custom, customClass )
from mininet.term import makeTerm, cleanUpScreens
from mininet.node import Controller, RemoteController, NOX, OVSController
from mininet.node import CPULimitedHost, Host, Node
from mininet.node import OVSSwitch, UserSwitch
from mininet.node import (Controller, RemoteController, NOX, OVSController,
CPULimitedHost, Host, Node,
OVSSwitch, UserSwitch, IVSSwitch )
from mininet.link import TCLink, Intf, Link
from mininet.cli import CLI
from mininet.moduledeps import moduleDeps
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
print 'MiniEdit running against Mininet '+VERSION
# pylint: disable=import-error
if sys.version_info[0] == 2:
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
Checkbutton, Menu, Toplevel, Button, BitmapImage,
PhotoImage, Canvas, Scrollbar, Wm, TclError,
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
from ttk import Notebook
from tkMessageBox import showerror
import tkFont
import tkFileDialog
import tkSimpleDialog
else:
from tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
Checkbutton, Menu, Toplevel, Button, BitmapImage,
PhotoImage, Canvas, Scrollbar, Wm, TclError,
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
from tkinter.ttk import Notebook
from tkinter.messagebox import showerror
from tkinter import font as tkFont
from tkinter import simpledialog as tkSimpleDialog
from tkinter import filedialog as tkFileDialog
# someday: from ttk import *
# pylint: enable=import-error
# Miniedit still needs work in order to pass pylint...
# pylint: disable=line-too-long,too-many-branches
# pylint: disable=too-many-statements,attribute-defined-outside-init
# pylint: disable=missing-docstring,too-many-ancestors
# pylint: disable=too-many-nested-blocks,too-many-arguments
MINIEDIT_VERSION = '2.2.0.1'
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
info( 'MiniEdit running against Mininet '+VERSION, '\n' )
MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
from mininet.node import IVSSwitch
TOPODEF = 'none'
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
@@ -123,6 +138,7 @@ class LegacyRouter( Node ):
def __init__( self, name, inNamespace=True, **params ):
Node.__init__( self, name, inNamespace, **params )
# pylint: disable=arguments-differ
def config( self, **_params ):
if self.intfs:
self.setParam( _params, 'setIP', ip='0.0.0.0' )
@@ -379,14 +395,14 @@ class PrefsDialog(tkSimpleDialog.Dialog):
@staticmethod
def getOvsVersion():
"Return OVS version"
outp = quietRun("ovs-vsctl show")
r = r'ovs_version: "(.*)"'
outp = quietRun("ovs-vsctl --version")
r = r'ovs-vsctl \(Open vSwitch\) (.*)'
m = re.search(r, outp)
if m is None:
print 'Version check failed'
warn( 'Version check failed' )
return None
else:
print 'Open vSwitch version is '+m.group(1)
info( 'Open vSwitch version is '+m.group(1), '\n' )
return m.group(1)
@@ -755,7 +771,7 @@ class SwitchDialog(CustomDialog):
def apply(self):
externalInterfaces = []
for row in range(self.tableFrame.rows):
#print 'Interface is ' + self.tableFrame.get(row, 0)
# debug( 'Interface is ' + self.tableFrame.get(row, 0), '\n' )
if len(self.tableFrame.get(row, 0)) > 0:
externalInterfaces.append(self.tableFrame.get(row, 0))
@@ -803,8 +819,8 @@ class VerticalScrolledTable(LabelFrame):
* This frame only allows vertical scrolling
"""
def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
def __init__(self, parent, rows=2, columns=2, title=None, **kw):
LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
@@ -840,8 +856,6 @@ class VerticalScrolledTable(LabelFrame):
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
return
class TableFrame(Frame):
def __init__(self, parent, rows=2, columns=2):
@@ -866,14 +880,14 @@ class TableFrame(Frame):
return widget.get()
def addRow( self, value=None, readonly=False ):
#print "Adding row " + str(self.rows +1)
# debug( "Adding row " + str(self.rows +1), '\n' )
current_row = []
for column in range(self.columns):
label = Entry(self, borderwidth=0)
label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
if value is not None:
label.insert(0, value[column])
if readonly == True:
if readonly:
label.configure(state='readonly')
current_row.append(label)
self._widgets.append(current_row)
@@ -1386,11 +1400,11 @@ class MiniEdit( Frame ):
def addNode( self, node, nodeNum, x, y, name=None):
"Add a new node to our canvas."
if 'Switch' == node:
if node == 'Switch':
self.switchCount += 1
if 'Host' == node:
if node == 'Host':
self.hostCount += 1
if 'Controller' == node:
if node == 'Controller':
self.controllerCount += 1
if name is None:
name = self.nodePrefixes[ node ] + nodeNum
@@ -1407,14 +1421,17 @@ class MiniEdit( Frame ):
def convertJsonUnicode(self, text):
"Some part of Mininet don't like Unicode"
if isinstance(text, dict):
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.iteritems()}
elif isinstance(text, list):
return [self.convertJsonUnicode(element) for element in text]
elif isinstance(text, unicode):
return text.encode('utf-8')
else:
try:
unicode
except NameError:
return text
if isinstance(text, dict):
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
if isinstance(text, list):
return [self.convertJsonUnicode(element) for element in text]
if isinstance(text, unicode): # pylint: disable=undefined-variable
return text.encode('utf-8')
return text
def loadTopology( self ):
"Load command."
@@ -1425,7 +1442,7 @@ class MiniEdit( Frame ):
('All Files','*'),
]
f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
if f == None:
if f is None:
return
self.newTopology()
loadedTopology = self.convertJsonUnicode(json.load(f))
@@ -1586,10 +1603,11 @@ class MiniEdit( Frame ):
for widget in self.widgetToItem:
if name == widget[ 'text' ]:
return widget
return None
def newTopology( self ):
"New command."
for widget in self.widgetToItem.keys():
for widget in self.widgetToItem:
self.deleteItem( self.widgetToItem[ widget ] )
self.hostCount = 0
self.switchCount = 0
@@ -1669,7 +1687,7 @@ class MiniEdit( Frame ):
f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
# pylint: disable=broad-except
except Exception as er:
print er
warn( er, '\n' )
# pylint: enable=broad-except
finally:
f.close()
@@ -1683,10 +1701,10 @@ class MiniEdit( Frame ):
fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
if len(fileName ) > 0:
#print "Now saving under %s" % fileName
# debug( "Now saving under %s\n" % fileName )
f = open(fileName, 'wb')
f.write("#!/usr/bin/python\n")
f.write("#!/usr/bin/env python\n")
f.write("\n")
f.write("from mininet.net import Mininet\n")
f.write("from mininet.node import Controller, RemoteController, OVSController\n")
@@ -1710,7 +1728,7 @@ class MiniEdit( Frame ):
if controllerType == 'inband':
inBandCtrl = True
if inBandCtrl == True:
if inBandCtrl:
f.write("\n")
f.write("class InbandController( RemoteController ):\n")
f.write("\n")
@@ -1835,7 +1853,7 @@ class MiniEdit( Frame ):
# Save Links
f.write(" info( '*** Add links\\n')\n")
for key,linkDetail in self.links.iteritems():
for key,linkDetail in self.links.items():
tags = self.canvas.gettags(key)
if 'data' in tags:
optsExist = False
@@ -2107,7 +2125,7 @@ class MiniEdit( Frame ):
c = self.canvas
x, y = c.canvasx( event.x ), c.canvasy( event.y )
name = self.nodePrefixes[ node ]
if 'Switch' == node:
if node == 'Switch':
self.switchCount += 1
name = self.nodePrefixes[ node ] + str( self.switchCount )
self.switchOpts[name] = {}
@@ -2115,14 +2133,14 @@ class MiniEdit( Frame ):
self.switchOpts[name]['hostname']=name
self.switchOpts[name]['switchType']='default'
self.switchOpts[name]['controllers']=[]
if 'LegacyRouter' == node:
if node == 'LegacyRouter':
self.switchCount += 1
name = self.nodePrefixes[ node ] + str( self.switchCount )
self.switchOpts[name] = {}
self.switchOpts[name]['nodeNum']=self.switchCount
self.switchOpts[name]['hostname']=name
self.switchOpts[name]['switchType']='legacyRouter'
if 'LegacySwitch' == node:
if node == 'LegacySwitch':
self.switchCount += 1
name = self.nodePrefixes[ node ] + str( self.switchCount )
self.switchOpts[name] = {}
@@ -2130,13 +2148,13 @@ class MiniEdit( Frame ):
self.switchOpts[name]['hostname']=name
self.switchOpts[name]['switchType']='legacySwitch'
self.switchOpts[name]['controllers']=[]
if 'Host' == node:
if node == 'Host':
self.hostCount += 1
name = self.nodePrefixes[ node ] + str( self.hostCount )
self.hostOpts[name] = {'sched':'host'}
self.hostOpts[name]['nodeNum']=self.hostCount
self.hostOpts[name]['hostname']=name
if 'Controller' == node:
if node == 'Controller':
name = self.nodePrefixes[ node ] + str( self.controllerCount )
ctrlr = { 'controllerType': 'ref',
'hostname': name,
@@ -2154,15 +2172,15 @@ class MiniEdit( Frame ):
self.itemToWidget[ item ] = icon
self.selectItem( item )
icon.links = {}
if 'Switch' == node:
if node == 'Switch':
icon.bind('<Button-3>', self.do_switchPopup )
if 'LegacyRouter' == node:
if node == 'LegacyRouter':
icon.bind('<Button-3>', self.do_legacyRouterPopup )
if 'LegacySwitch' == node:
if node == 'LegacySwitch':
icon.bind('<Button-3>', self.do_legacySwitchPopup )
if 'Host' == node:
if node == 'Host':
icon.bind('<Button-3>', self.do_hostPopup )
if 'Controller' == node:
if node == 'Controller':
icon.bind('<Button-3>', self.do_controllerPopup )
def clickController( self, event ):
@@ -2232,7 +2250,7 @@ class MiniEdit( Frame ):
def clickNode( self, event ):
"Node click handler."
if self.active is 'NetLink':
if self.active == 'NetLink':
self.startLink( event )
else:
self.selectNode( event )
@@ -2240,14 +2258,14 @@ class MiniEdit( Frame ):
def dragNode( self, event ):
"Node drag handler."
if self.active is 'NetLink':
if self.active == 'NetLink':
self.dragNetLink( event )
else:
self.dragNodeAround( event )
def releaseNode( self, event ):
"Node release handler."
if self.active is 'NetLink':
if self.active == 'NetLink':
self.finishLink( event )
# Specific node handlers
@@ -2359,6 +2377,8 @@ class MiniEdit( Frame ):
# For now, don't allow hosts to be directly linked
stags = self.canvas.gettags( self.widgetToItem[ source ] )
dtags = self.canvas.gettags( target )
# TODO: Make this less confusing
# pylint: disable=too-many-boolean-expressions
if (('Host' in stags and 'Host' in dtags) or
('Controller' in dtags and 'LegacyRouter' in stags) or
('Controller' in stags and 'LegacyRouter' in dtags) or
@@ -2489,7 +2509,7 @@ class MiniEdit( Frame ):
if len(hostBox.result['privateDirectory']) > 0:
newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
self.hostOpts[name] = newHostOpts
print 'New host details for ' + name + ' = ' + str(newHostOpts)
info( 'New host details for ' + name + ' = ' + str(newHostOpts), '\n' )
def switchDetails( self, _ignore=None ):
if ( self.selection is None or
@@ -2527,7 +2547,7 @@ class MiniEdit( Frame ):
newSwitchOpts['sflow'] = switchBox.result['sflow']
newSwitchOpts['netflow'] = switchBox.result['netflow']
self.switchOpts[name] = newSwitchOpts
print 'New switch details for ' + name + ' = ' + str(newSwitchOpts)
info( 'New switch details for ' + name + ' = ' + str(newSwitchOpts), '\n' )
def linkUp( self ):
if ( self.selection is None or
@@ -2566,12 +2586,12 @@ class MiniEdit( Frame ):
linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
if linkBox.result is not None:
linkDetail['linkOpts'] = linkBox.result
print 'New link details = ' + str(linkBox.result)
info( 'New link details = ' + str(linkBox.result), '\n' )
def prefDetails( self ):
prefDefaults = self.appPrefs
prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
print 'New Prefs = ' + str(prefBox.result)
info( 'New Prefs = ' + str(prefBox.result), '\n' )
if prefBox.result:
self.appPrefs = prefBox.result
@@ -2590,14 +2610,14 @@ class MiniEdit( Frame ):
ctrlrBox = ControllerDialog(self, title='Controller Details', ctrlrDefaults=self.controllers[name])
if ctrlrBox.result:
#print 'Controller is ' + ctrlrBox.result[0]
# debug( 'Controller is ' + ctrlrBox.result[0], '\n' )
if len(ctrlrBox.result['hostname']) > 0:
name = ctrlrBox.result['hostname']
widget[ 'text' ] = name
else:
ctrlrBox.result['hostname'] = name
self.controllers[name] = ctrlrBox.result
print 'New controller details for ' + name + ' = ' + str(self.controllers[name])
info( 'New controller details for ' + name + ' = ' + str(self.controllers[name]), '\n' )
# Find references to controller and change name
if oldName != name:
for widget in self.widgetToItem:
@@ -2642,7 +2662,7 @@ class MiniEdit( Frame ):
linkopts = {}
source.links[ dest ] = self.link
dest.links[ source ] = self.link
self.links[ self.link ] = {'type' :linktype,
self.links[ self.link ] = {'type':linktype,
'src':source,
'dest':dest,
'linkOpts':linkopts}
@@ -2698,15 +2718,15 @@ class MiniEdit( Frame ):
def buildNodes( self, net):
# Make nodes
print "Getting Hosts and Switches."
info( "Getting Hosts and Switches.\n" )
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
#print name+' has '+str(tags)
# debug( name+' has '+str(tags), '\n' )
if 'Switch' in tags:
opts = self.switchOpts[name]
#print str(opts)
# debug( str(opts), '\n' )
# Create the correct switch class
switchClass = customOvs
@@ -2772,7 +2792,7 @@ class MiniEdit( Frame ):
newSwitch = net.addHost( name , cls=LegacyRouter)
elif 'Host' in tags:
opts = self.hostOpts[name]
#print str(opts)
# debug( str(opts), '\n' )
ip = None
defaultRoute = None
if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
@@ -2797,7 +2817,7 @@ class MiniEdit( Frame ):
privateDirs=opts['privateDirectory'] )
else:
hostCls=Host
print hostCls
debug( hostCls, '\n' )
newHost = net.addHost( name,
cls=hostCls,
ip=ip,
@@ -2817,7 +2837,7 @@ class MiniEdit( Frame ):
Intf( extInterface, node=newHost )
if 'vlanInterfaces' in opts:
if len(opts['vlanInterfaces']) > 0:
print 'Checking that OS is VLAN prepared'
info( 'Checking that OS is VLAN prepared\n' )
self.pathCheck('vconfig', moduleName='vlan package')
moduleDeps( add='8021q' )
elif 'Controller' in tags:
@@ -2834,7 +2854,7 @@ class MiniEdit( Frame ):
controllerPort = opts['remotePort']
# Make controller
print 'Getting controller selection:'+controllerType
info( 'Getting controller selection:'+controllerType, '\n' )
if controllerType == 'remote':
net.addController(name=name,
controller=RemoteController,
@@ -2874,8 +2894,8 @@ class MiniEdit( Frame ):
def buildLinks( self, net):
# Make links
print "Getting Links."
for key,link in self.links.iteritems():
info( "Getting Links.\n" )
for key,link in self.links.items():
tags = self.canvas.gettags(key)
if 'data' in tags:
src=link['src']
@@ -2886,14 +2906,14 @@ class MiniEdit( Frame ):
if linkopts:
net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
else:
#print str(srcNode)
#print str(dstNode)
# debug( str(srcNode) )
# debug( str(dstNode), '\n' )
net.addLink(srcNode, dstNode)
self.canvas.itemconfig(key, dash=())
def build( self ):
print "Build network based on our topology."
"Build network based on our topology."
dpctl = None
if len(self.appPrefs['dpctl']) > 0:
@@ -2924,7 +2944,7 @@ class MiniEdit( Frame ):
# Attach vlan interfaces
if 'vlanInterfaces' in opts:
for vlanInterface in opts['vlanInterfaces']:
print 'adding vlan interface '+vlanInterface[1]
info( 'adding vlan interface '+vlanInterface[1], '\n' )
newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
# Run User Defined Start Command
if 'startCommand' in opts:
@@ -2950,7 +2970,7 @@ class MiniEdit( Frame ):
opts = self.switchOpts[name]
if 'netflow' in opts:
if opts['netflow'] == '1':
print name+' has Netflow enabled'
info( name+' has Netflow enabled\n' )
nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
nflowEnabled=True
if nflowEnabled:
@@ -2959,13 +2979,13 @@ class MiniEdit( Frame ):
nflowCmd = nflowCmd + ' add_id_to_interface=true'
else:
nflowCmd = nflowCmd + ' add_id_to_interface=false'
print 'cmd = '+nflowCmd+nflowSwitches
info( 'cmd = '+nflowCmd+nflowSwitches, '\n' )
call(nflowCmd+nflowSwitches, shell=True)
else:
print 'No switches with Netflow'
info( 'No switches with Netflow\n' )
else:
print 'No NetFlow targets specified.'
info( 'No NetFlow targets specified.\n' )
# Configure sFlow
sflowValues = self.appPrefs['sflow']
@@ -2980,23 +3000,23 @@ class MiniEdit( Frame ):
opts = self.switchOpts[name]
if 'sflow' in opts:
if opts['sflow'] == '1':
print name+' has sflow enabled'
info( name+' has sflow enabled\n' )
sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
sflowEnabled=True
if sflowEnabled:
sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
print 'cmd = '+sflowCmd+sflowSwitches
info( 'cmd = '+sflowCmd+sflowSwitches, '\n' )
call(sflowCmd+sflowSwitches, shell=True)
else:
print 'No switches with sflow'
info( 'No switches with sflow\n' )
else:
print 'No sFlow targets specified.'
info( 'No sFlow targets specified.\n' )
## NOTE: MAKE SURE THIS IS LAST THING CALLED
# Start the CLI if enabled
if self.appPrefs['startCLI'] == '1':
info( "\n\n NOTE: PLEASE REMEMBER TO EXIT THE CLI BEFORE YOU PRESS THE STOP BUTTON. Not exiting will prevent MiniEdit from quitting and will prevent you from starting the network again during this sessoin.\n\n")
info( "\n\n NOTE: PLEASE REMEMBER TO EXIT THE CLI BEFORE YOU PRESS THE STOP BUTTON. Not exiting will prevent MiniEdit from quitting and will prevent you from starting the network again during this session.\n\n")
CLI(self.net)
def start( self ):
@@ -3210,14 +3230,15 @@ class MiniEdit( Frame ):
"Parse custom file and add params before parsing cmd-line options."
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
with open( fileName, 'r' ) as f:
exec( f.read() ) # pylint: disable=exec-used
for name, val in customs.items():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
def importTopo( self ):
print 'topo='+self.options.topo
info( 'topo='+self.options.topo, '\n' )
if self.options.topo == 'none':
return
self.newTopology()
@@ -3231,7 +3252,7 @@ class MiniEdit( Frame ):
currentY = 100
# Add Controllers
print 'controllers:'+str(len(importNet.controllers))
info( 'controllers:'+str(len(importNet.controllers)), '\n' )
for controller in importNet.controllers:
name = controller.name
x = self.controllerCount*100+100
@@ -3251,7 +3272,7 @@ class MiniEdit( Frame ):
currentY = currentY + rowIncrement
# Add switches
print 'switches:'+str(len(importNet.switches))
info( 'switches:'+str(len(importNet.switches)), '\n' )
columnCount = 0
for switch in importNet.switches:
name = switch.name
@@ -3292,7 +3313,7 @@ class MiniEdit( Frame ):
currentY = currentY + rowIncrement
# Add hosts
print 'hosts:'+str(len(importNet.hosts))
info( 'hosts:'+str(len(importNet.hosts)), '\n' )
columnCount = 0
for host in importNet.hosts:
name = host.name
@@ -3312,10 +3333,10 @@ class MiniEdit( Frame ):
else:
columnCount =columnCount+1
print 'links:'+str(len(topo.links()))
info( 'links:'+str(len(topo.links())), '\n' )
#[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
for link in topo.links():
print str(link)
info( str(link), '\n' )
srcNode = link[0]
src = self.findWidgetByName(srcNode)
sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
@@ -3325,7 +3346,7 @@ class MiniEdit( Frame ):
dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
params = topo.linkInfo( srcNode, destNode )
print 'Link Parameters='+str(params)
info( 'Link Parameters='+str(params), '\n' )
self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
fill='blue', tag='link' )
@@ -3577,8 +3598,7 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
if __name__ == '__main__':
setLogLevel( 'info' )
app = MiniEdit()
### import topology if specified ###
app.parseArgs()
### import topology if specified ###
app.importTopo()
app.mainloop()
+18 -14
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of Mobility with Mininet
@@ -19,12 +19,12 @@ to-do:
- think about clearing last hop - why doesn't that work?
"""
from random import randint
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.log import output, warn
from random import randint
from mininet.log import info, output, warn, setLogLevel
class MobilitySwitch( OVSSwitch ):
@@ -37,6 +37,7 @@ class MobilitySwitch( OVSSwitch ):
del self.intfs[ port ]
del self.nameToIntf[ intf.name ]
# pylint: disable=arguments-differ
def addIntf( self, intf, rename=False, **kwargs ):
"Add (and reparent) an interface"
OVSSwitch.addIntf( self, intf, **kwargs )
@@ -105,30 +106,33 @@ def moveHost( host, oldSwitch, newSwitch, newPort=None ):
def mobilityTest():
"A simple test of mobility"
print '* Simple mobility test'
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
print '* Starting network:'
info( '* Simple mobility test\n' )
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch,
waitConnected=True )
info( '* Starting network:\n' )
net.start()
printConnections( net.switches )
print '* Testing network'
info( '* Testing network\n' )
net.pingAll()
print '* Identifying switch interface for h1'
info( '* Identifying switch interface for h1\n' )
h1, old = net.get( 'h1', 's1' )
for s in 2, 3, 1:
new = net[ 's%d' % s ]
port = randint( 10, 20 )
print '* Moving', h1, 'from', old, 'to', new, 'port', port
info( '* Moving', h1, 'from', old, 'to', new, 'port', port, '\n' )
hintf, sintf = moveHost( h1, old, new, newPort=port )
print '*', hintf, 'is now connected to', sintf
print '* Clearing out old flows'
info( '*', hintf, 'is now connected to', sintf, '\n' )
info( '* Clearing out old flows\n' )
for sw in net.switches:
sw.dpctl( 'del-flows' )
print '* New network:'
info( '* New network:\n' )
printConnections( net.switches )
print '* Testing connectivity:'
info( '* Testing connectivity:\n' )
net.pingAll()
old = new
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
mobilityTest()
+5 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This is a simple example that demonstrates multiple links
@@ -13,7 +13,7 @@ from mininet.topo import Topo
def runMultiLink():
"Create and run multiple link network"
topo = simpleMultiLinkTopo( n=2 )
net = Mininet( topo=topo )
net = Mininet( topo=topo, waitConnected=True )
net.start()
CLI( net )
net.stop()
@@ -21,9 +21,8 @@ def runMultiLink():
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
def __init__( self, n, **kwargs ):
Topo.__init__( self, **kwargs )
# pylint: disable=arguments-differ
def build( self, n, **_kwargs ):
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
s1 = self.addSwitch( 's1' )
@@ -31,6 +30,7 @@ class simpleMultiLinkTopo( Topo ):
self.addLink( s1, h1 )
self.addLink( s1, h2 )
if __name__ == '__main__':
setLogLevel( 'info' )
runMultiLink()
+10 -9
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
multiping.py: monitor multiple sets of hosts using ping
@@ -8,13 +8,14 @@ multiple hosts and monitor their output interactively for a period=
of time.
"""
from select import poll, POLLIN
from time import time
from mininet.net import Mininet
from mininet.node import Node
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel
from mininet.log import info, setLogLevel
from select import poll, POLLIN
from time import time
def chunks( l, n ):
"Divide list l into chunks of size n - thanks Stackoverflow"
@@ -34,8 +35,8 @@ def startpings( host, targetips ):
' done; '
'done &' )
print ( '*** Host %s (%s) will be pinging ips: %s' %
( host.name, host.IP(), targetips ) )
info( '*** Host %s (%s) will be pinging ips: %s\n' %
( host.name, host.IP(), targetips ) )
host.cmd( cmd )
@@ -44,7 +45,7 @@ def multiping( netsize, chunksize, seconds):
# Create network and identify subnets
topo = SingleSwitchTopo( netsize )
net = Mininet( topo=topo )
net = Mininet( topo=topo, waitConnected=True )
net.start()
hosts = net.hosts
subnets = chunks( hosts, chunksize )
@@ -58,7 +59,7 @@ def multiping( netsize, chunksize, seconds):
# Start pings
for subnet in subnets:
ips = [ host.IP() for host in subnet ]
#adding bogus to generate packet loss
# adding bogus to generate packet loss
ips.append( '10.0.0.200' )
for host in subnet:
startpings( host, ips )
@@ -69,7 +70,7 @@ def multiping( netsize, chunksize, seconds):
readable = poller.poll(1000)
for fd, _mask in readable:
node = Node.outToNode[ fd ]
print '%s:' % node.name, node.monitor().strip()
info( '%s:' % node.name, node.monitor().strip(), '\n' )
# Stop pings
for host in hosts:
+13 -10
View File
@@ -1,23 +1,26 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of sending output to multiple files and
monitoring them
"""
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import setLogLevel
from time import time
from select import poll, POLLIN
from subprocess import Popen, PIPE
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import info, setLogLevel
from mininet.util import decode
def monitorFiles( outfiles, seconds, timeoutms ):
"Monitor set of files and return [(host, line)...]"
devnull = open( '/dev/null', 'w' )
tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.iteritems():
for h, outfile in outfiles.items():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
@@ -38,7 +41,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
host = fdToHost[ fd ]
# Wait for a line of output
line = f.readline().strip()
yield host, line
yield host, decode( line )
else:
# If we timed out, return nothing
yield None, ''
@@ -50,10 +53,10 @@ def monitorFiles( outfiles, seconds, timeoutms ):
def monitorTest( N=3, seconds=3 ):
"Run pings and monitor multiple hosts"
topo = SingleSwitchTopo( N )
net = Mininet( topo )
net = Mininet( topo, waitConnected=True )
net.start()
hosts = net.hosts
print "Starting test..."
info( "Starting test...\n" )
server = hosts[ 0 ]
outfiles, errfiles = {}, {}
for h in hosts:
@@ -67,10 +70,10 @@ def monitorTest( N=3, seconds=3 ):
'>', outfiles[ h ],
'2>', errfiles[ h ],
'&' )
print "Monitoring output for", seconds, "seconds"
info( "Monitoring output for", seconds, "seconds\n" )
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
if h:
print '%s: %s' % ( h.name, line )
info( '%s: %s\n' % ( h.name, line ) )
for h in hosts:
h.cmd('kill %ping')
net.stop()
+4 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example shows how to create a network and run multiple tests.
@@ -17,12 +17,14 @@ def ifconfigTest( net ):
for host in hosts:
info( host.cmd( 'ifconfig' ) )
if __name__ == '__main__':
lg.setLogLevel( 'info' )
info( "*** Initializing Mininet and kernel modules\n" )
OVSKernelSwitch.setup()
info( "*** Creating network\n" )
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
network = Mininet( TreeTopo( depth=2, fanout=2), switch=OVSKernelSwitch,
waitConnected=True )
info( "*** Starting network\n" )
network.start()
info( "*** Running ping test\n" )
+7 -5
View File
@@ -1,21 +1,23 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Example to create a Mininet topology and connect it to the internet via NAT
"""
from mininet.cli import CLI
from mininet.log import lg
from mininet.log import lg, info
from mininet.topolib import TreeNet
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
net = TreeNet( depth=1, fanout=4, waitConnected=True )
# Add NAT connectivity
net.addNAT().configDefault()
net.start()
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
info( "*** Hosts are running and should have internet connectivity\n" )
info( "*** Type 'exit' or control-D to shut down network\n" )
CLI( net )
# Shut down NAT
net.stop()
+5 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
natnet.py: Example network with NATs
@@ -27,9 +27,8 @@ from mininet.util import irange
class InternetTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
# pylint: disable=arguments-differ
def build(self, n=2, **_kwargs ):
# set up inet switch
inetSwitch = self.addSwitch('s0')
# add inet host
@@ -59,11 +58,12 @@ class InternetTopo(Topo):
def run():
"Create network and run the CLI"
topo = InternetTopo()
net = Mininet(topo=topo)
net = Mininet(topo=topo, waitConnected=True )
net.start()
CLI(net)
net.stop()
if __name__ == '__main__':
setLogLevel('info')
run()
+7 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network with 5 hosts, numbered 1-4 and 9.
@@ -6,6 +6,7 @@ Validate that the port numbers match to the interface name,
and that the ovs ports match the mininet ports.
"""
from mininet.net import Mininet
from mininet.node import Controller
from mininet.log import setLogLevel, info, warn
@@ -27,7 +28,7 @@ def testPortNumbering():
mid-level API) and check that implicit and
explicit port numbering works as expected."""
net = Mininet( controller=Controller )
net = Mininet( controller=Controller, waitConnected=True )
info( '*** Adding controller\n' )
net.addController( 'c0' )
@@ -65,15 +66,16 @@ def testPortNumbering():
'is actually on port', s1.ports[intfs], '... ' )
if validatePort( s1, intfs ):
info( 'Validated.\n' )
print '\n'
info( '\n' )
# test the network with pingall
net.pingAll()
print '\n'
info( '\n' )
info( '*** Stopping network' )
info( '*** Stopping network\n' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
testPortNumbering()
+7 -9
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example monitors a number of hosts using host.popen() and
@@ -6,17 +6,14 @@ pmonitor()
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel
from mininet.util import custom, pmonitor
from mininet.log import setLogLevel, info
from mininet.util import pmonitor
def monitorhosts( hosts=5, sched='cfs' ):
def monitorhosts( hosts=5 ):
"Start a bunch of pings and monitor them using popen"
mytopo = SingleSwitchTopo( hosts )
cpu = .5 / hosts
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
net = Mininet( topo=mytopo, host=myhost )
net = Mininet( topo=mytopo, waitConnected=True )
net.start()
# Start a bunch of pings
popens = {}
@@ -27,10 +24,11 @@ def monitorhosts( hosts=5, sched='cfs' ):
# Monitor them and print output
for host, line in pmonitor( popens ):
if host:
print "<%s>: %s" % ( host.name, line.strip() )
info( "<%s>: %s" % ( host.name, line ) )
# Done
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
monitorhosts( hosts=5 )
+12 -7
View File
@@ -1,33 +1,38 @@
#!/usr/bin/python
#!/usr/bin/env python
"Monitor multiple hosts using popen()/pmonitor()"
from time import time
from signal import SIGINT
from mininet.net import Mininet
from mininet.topo import SingleSwitchTopo
from mininet.util import pmonitor
from time import time
from signal import SIGINT
from mininet.log import setLogLevel, info
def pmonitorTest( N=3, seconds=10 ):
"Run pings and monitor multiple hosts using pmonitor"
topo = SingleSwitchTopo( N )
net = Mininet( topo )
net = Mininet( topo, waitConnected=True )
net.start()
hosts = net.hosts
print "Starting test..."
info( "Starting test...\n" )
server = hosts[ 0 ]
popens = {}
for h in hosts:
popens[ h ] = h.popen('ping', server.IP() )
print "Monitoring output for", seconds, "seconds"
info( "Monitoring output for", seconds, "seconds\n" )
endTime = time() + seconds
for h, line in pmonitor( popens, timeoutms=500 ):
if h:
print '<%s>: %s' % ( h.name, line ),
info( '<%s>: %s' % ( h.name, line ) )
if time() >= endTime:
for p in popens.values():
p.send_signal( SIGINT )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
pmonitorTest()
+5 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Build a simple network from scratch, using mininet primitives.
@@ -8,13 +8,14 @@ but it exposes the configuration details and allows customization.
For most tasks, the higher-level API will be preferable.
"""
from time import sleep
from mininet.net import Mininet
from mininet.node import Node
from mininet.link import Link
from mininet.log import setLogLevel, info
from mininet.util import quietRun
from time import sleep
def scratchNet( cname='controller', cargs='-v ptcp:' ):
"Create network from scratch using Open vSwitch."
@@ -40,7 +41,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
switch.cmd( 'ovs-vsctl del-br dp0' )
switch.cmd( 'ovs-vsctl add-br dp0' )
for intf in switch.intfs.values():
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
switch.cmd( 'ovs-vsctl add-port dp0 %s\n' % intf )
# Note: controller and switch are in root namespace, and we
# can connect via loopback interface
@@ -61,6 +62,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
switch.deleteIntfs()
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (kernel datapath)\n' )
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Build a simple network from scratch, using mininet primitives.
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' )
intfs = [ str( i ) for i in sintf1, sintf2 ]
intfs = str( sintf1 ), str( sintf2 )
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
@@ -66,6 +66,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
switch.deleteIntfs()
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (user datapath)\n' )
+12 -8
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of setting network and CPU parameters
@@ -9,19 +9,22 @@ iperf will hang indefinitely if the TCP handshake fails
to complete.
"""
from sys import argv
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel
from mininet.log import setLogLevel, info
from sys import argv
class SingleSwitchTopo(Topo):
# It would be nice if we didn't have to do this:
# pylint: disable=arguments-differ
class SingleSwitchTopo( Topo ):
"Single switch connected to n hosts."
def __init__(self, n=2, lossy=True, **opts):
Topo.__init__(self, **opts)
def build( self, n=2, lossy=True ):
switch = self.addSwitch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
@@ -44,13 +47,14 @@ def perfTest( lossy=True ):
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
net.start()
print "Dumping host connections"
info( "Dumping host connections\n" )
dumpNodeConnections(net.hosts)
print "Testing bandwidth between h1 and h4"
info( "Testing bandwidth between h1 and h4\n" )
h1, h4 = net.getNodeByName('h1', 'h4')
net.iperf( ( h1, h4 ), l4Type='UDP' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
# Prevent test_simpleperf from failing due to packet loss
+10 -10
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network and start sshd(8) on each host.
@@ -20,15 +20,16 @@ import sys
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg
from mininet.log import lg, info
from mininet.node import Node
from mininet.topolib import TreeTopo
from mininet.util import waitListening
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout )
return Mininet( topo, **kwargs )
return Mininet( topo, waitConnected=True, **kwargs )
def connectToRootNS( network, switch, ip, routes ):
"""Connect hosts to root namespace via switch. Starts network.
@@ -46,6 +47,7 @@ def connectToRootNS( network, switch, ip, routes ):
for route in routes:
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
# pylint: disable=too-many-arguments
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.
@@ -59,22 +61,20 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
connectToRootNS( network, switch, ip, routes )
for host in network.hosts:
host.cmd( cmd + ' ' + opts + '&' )
print "*** Waiting for ssh daemons to start"
info( "*** Waiting for ssh daemons to start\n" )
for server in network.hosts:
waitListening( server=server, port=22, timeout=5 )
print
print "*** Hosts are running sshd at the following addresses:"
print
info( "\n*** Hosts are running sshd at the following addresses:\n" )
for host in network.hosts:
print host.name, host.IP()
print
print "*** Type 'exit' or control-D to shut down network"
info( host.name, host.IP(), '\n' )
info( "\n*** Type 'exit' or control-D to shut down network\n" )
CLI( network )
for host in network.hosts:
host.cmd( 'kill %' + cmd )
network.stop()
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
+2 -1
View File
@@ -32,7 +32,8 @@ def runTests( testDir, verbosity=1 ):
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
MininetTestRunner( verbosity=verbosity ).run( testSuite )
success = MininetTestRunner( verbosity=verbosity ).run( testSuite ).wasSuccessful()
sys.exit( 0 if success else 1 )
if __name__ == '__main__':
# get the directory containing example tests
+7 -3
View File
@@ -5,8 +5,9 @@ Tests for baresshd.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from mininet.clean import cleanup, sh
from sys import stdout
class testBareSSHD( unittest.TestCase ):
@@ -14,7 +15,9 @@ class testBareSSHD( unittest.TestCase ):
def connected( self ):
"Log into ssh server, check banner, then exit"
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
'-o StrictHostKeyChecking=no '
'-i /tmp/ssh/test_rsa exit' )
while True:
index = p.expect( self.opts )
if index == 0:
@@ -22,6 +25,7 @@ class testBareSSHD( unittest.TestCase ):
else:
return False
def setUp( self ):
# verify that sshd is not running
self.assertFalse( self.connected() )
@@ -55,7 +59,7 @@ class testBareSSHD( unittest.TestCase ):
def tearDown( self ):
# kill the ssh process
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
sh( "ps aux | grep ssh |grep Banner| awk '{ print $2 }' | xargs kill" )
cleanup()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for bind.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testBind( unittest.TestCase ):
+3 -1
View File
@@ -5,7 +5,7 @@ A simple sanity check test for cluster edition
'''
import unittest
import pexpect
from mininet.util import pexpect
class clusterSanityCheck( unittest.TestCase ):
@@ -14,6 +14,8 @@ class clusterSanityCheck( unittest.TestCase ):
def testClusterPingAll( self ):
p = pexpect.spawn( 'python -m mininet.examples.clusterSanity' )
p.expect( self.prompt )
p.sendline( 'py net.waitConnected()' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testControllers( unittest.TestCase ):
+6 -4
View File
@@ -5,7 +5,9 @@ Test for controlnet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from sys import stdout
class testControlNet( unittest.TestCase ):
@@ -13,7 +15,7 @@ class testControlNet( unittest.TestCase ):
def testPingall( self ):
"Simple pingall test that verifies 0% packet drop in data network"
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p = pexpect.spawn( 'python -m mininet.examples.controlnet', logfile=stdout)
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
@@ -26,9 +28,9 @@ class testControlNet( unittest.TestCase ):
def testFailover( self ):
"Kill controllers and verify that switch, s1, fails over properly"
count = 1
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p = pexpect.spawn( 'python -m mininet.examples.controlnet', logfile=stdout )
p.expect( self.prompt )
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' )
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log', logfile=stdout )
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
ip = int( lp.match.group( 1 ) )
self.assertEqual( count, ip )
+11 -12
View File
@@ -5,18 +5,17 @@ Test for cpu.py
results format:
sched cpu client MB/s
cfs 45.00% 13254.669841
cfs 40.00% 11822.441399
cfs 30.00% 5112.963009
cfs 20.00% 3449.090009
cfs 10.00% 2271.741564
sched cpu received bits/sec
cfs 50% 8.14e+09
cfs 40% 6.48e+09
cfs 30% 4.56e+09
cfs 20% 2.84e+09
cfs 10% 1.29e+09
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testCPU( unittest.TestCase ):
@@ -26,13 +25,13 @@ class testCPU( unittest.TestCase ):
@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' )
p = pexpect.spawn( 'python -m mininet.examples.cpu', timeout=300 )
# matches each line from results( shown above )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)',
pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts, timeout=600 )
index = p.expect( opts )
if index == 0:
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
@@ -41,7 +40,7 @@ class testCPU( unittest.TestCase ):
scheds.append( sched )
else:
self.assertTrue( bw < previous_bw,
"%f should be less than %f\n" %
"%e should be less than %e\n" %
( bw, previous_bw ) )
previous_bw = bw
else:
+1 -1
View File
@@ -5,7 +5,7 @@ Test for emptynet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testEmptyNet( unittest.TestCase ):
+3 -3
View File
@@ -7,7 +7,7 @@ Test for hwintf.py
import unittest
import re
import pexpect
from mininet.util import pexpect
from mininet.log import setLogLevel
from mininet.node import Node
@@ -57,8 +57,8 @@ class testHwintf( unittest.TestCase ):
p.wait()
def tearDown( self ):
self.h3.terminate()
self.n0.terminate()
self.h3.stop( deleteIntfs=True )
self.n0.stop( deleteIntfs=True )
if __name__ == '__main__':
setLogLevel( 'warning' )
+7 -5
View File
@@ -5,7 +5,7 @@ Test for intfOptions.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testIntfOptions( unittest.TestCase ):
@@ -13,7 +13,7 @@ class testIntfOptions( unittest.TestCase ):
def testIntfOptions( self ):
"verify that intf.config is correctly limiting traffic"
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
tolerance = .2 # plus or minus 20%
tolerance = .25 # plus or minus 25% for cloud CI tests
opts = [ "Results: \['([\d\.]+) .bits/sec",
"Results: \['10M', '([\d\.]+) .bits/sec",
"h(\d+)->h(\d+): (\d)/(\d),"
@@ -22,7 +22,7 @@ class testIntfOptions( unittest.TestCase ):
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
BW = 5
BW = 10
bw = float( p.match.group( 1 ) )
self.assertGreaterEqual( bw, BW * ( 1 - tolerance ) )
self.assertLessEqual( bw, BW * ( 1 + tolerance ) )
@@ -30,8 +30,10 @@ class testIntfOptions( unittest.TestCase ):
BW = 10
measuredBw = float( p.match.group( 1 ) )
loss = ( measuredBw / BW ) * 100
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ) )
self.assertLessEqual( loss, 50 * ( 1 + tolerance ) )
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ),
'loss of %d%% << 50%%' % loss )
self.assertLessEqual( loss, 50 * ( 1 + tolerance ),
'loss of %d%% >> 50%%' % loss )
elif index == 2:
delay = float( p.match.group( 6 ) )
self.assertGreaterEqual( delay, 15 * ( 1 - tolerance ) )
+1 -1
View File
@@ -5,7 +5,7 @@ Test for limit.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testLimit( unittest.TestCase ):
+3 -3
View File
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testLinearBandwidth( unittest.TestCase ):
@@ -34,8 +34,8 @@ class testLinearBandwidth( unittest.TestCase ):
bw *= 10 ** 9
# check that we have a previous result to compare to
if n != 1:
info = ( 'bw: %d bits/s across %d switches, '
'previous: %d bits/s across %d switches' %
info = ( 'bw: %.2e bits/s across %d switches, '
'previous: %.2e bits/s across %d switches' %
( bw, n, previous_bw, previous_n ) )
self.assertTrue( bw < previous_bw, info )
previous_bw, previous_n = bw, n
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linuxrouter.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from mininet.util import quietRun
class testLinuxRouter( unittest.TestCase ):
+1 -1
View File
@@ -6,7 +6,7 @@ validates mininet interfaces against systems interfaces
'''
import unittest
import pexpect
from mininet.util import pexpect
class testMultiLink( unittest.TestCase ):
+7 -5
View File
@@ -5,7 +5,7 @@ Test for multiping.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from collections import defaultdict
class testMultiPing( unittest.TestCase ):
@@ -31,18 +31,20 @@ class testMultiPing( unittest.TestCase ):
target = p.match.group(3)
received = int( p.match.group(4) )
if target == '10.0.0.200':
self.assertEqual( received, 0 )
self.assertEqual( received, 0, p.match.group(0) + '\n' +
target + ' received %d != 0 packets' % received )
else:
self.assertEqual( received, 1 )
self.assertEqual( received, 1, p.match.group(0) + '\n' +
target + ' received %d != 1 packets' % received )
try:
pings[ name ].remove( target )
except:
pass
else:
break
self.assertTrue( len( pings ) > 0 )
self.assertTrue( len( pings ) > 0, 'too few pings' )
for t in pings.values():
self.assertEqual( len( t ), 0 )
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
if __name__ == '__main__':
unittest.main()
+4 -3
View File
@@ -5,7 +5,7 @@ Test for multipoll.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testMultiPoll( unittest.TestCase ):
@@ -16,7 +16,7 @@ class testMultiPoll( unittest.TestCase ):
"(h\d+): \d+ bytes from",
"Monitoring output for (\d+) seconds",
pexpect.EOF ]
pings = {}
pings, seconds = {}, -1
while True:
index = p.expect( opts )
if index == 0:
@@ -32,7 +32,8 @@ class testMultiPoll( unittest.TestCase ):
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 )
self.assertTrue( count >= seconds,
'%d pings < %d seconds' % ( count, seconds ) )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multitest.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testMultiTest( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for nat.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from mininet.util import quietRun
destIP = '8.8.8.8' # Google DNS
Regular → Executable
+1 -1
View File
@@ -5,7 +5,7 @@ Test for natnet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from mininet.util import quietRun
class testNATNet( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for numberedports.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from collections import defaultdict
from mininet.node import OVSSwitch
+1 -1
View File
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testPopen( unittest.TestCase ):
+3 -2
View File
@@ -5,7 +5,7 @@ Test for scratchnet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testScratchNet( unittest.TestCase ):
@@ -14,8 +14,9 @@ class testScratchNet( unittest.TestCase ):
def pingTest( self, name ):
"Verify that no ping packets were dropped"
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts )
index = p.expect( self.opts, timeout=120 )
self.assertEqual( index, 0 )
p.wait()
def testPingKernel( self ):
self.pingTest( 'mininet.examples.scratchnet' )
+2 -2
View File
@@ -5,7 +5,7 @@ Test for simpleperf.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
from mininet.log import setLogLevel
@@ -18,7 +18,7 @@ class testSimplePerf( unittest.TestCase ):
"Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .2
TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
+4 -4
View File
@@ -5,7 +5,7 @@ Test for sshd.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from mininet.clean import sh
class testSSHD( unittest.TestCase ):
@@ -16,11 +16,12 @@ class testSSHD( unittest.TestCase ):
"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 )
ssh = 'ssh -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa ' + ip
p = pexpect.spawn( ssh, timeout=5 )
while True:
index = p.expect( self.opts )
if index == 0:
print p.match.group(0)
print( p.match.group(0) )
p.sendline( 'yes' )
elif index == 1:
return False
@@ -57,4 +58,3 @@ class testSSHD( unittest.TestCase ):
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for tree1024.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testTree1024( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for treeping64.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testTreePing64( unittest.TestCase ):
Regular → Executable
+1 -1
View File
@@ -5,7 +5,7 @@ Test for vlanhost.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
from mininet.util import quietRun
+4 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a 1024-host network, and run the CLI on it.
@@ -11,8 +11,10 @@ from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.node import OVSSwitch
from mininet.topolib import TreeNet
from mininet.examples.treeping64 import HostV4
if __name__ == '__main__':
setLogLevel( 'info' )
network = TreeNet( depth=2, fanout=32, switch=OVSSwitch )
network = TreeNet( depth=2, fanout=32, host=HostV4,
switch=OVSSwitch, waitConnected=True)
network.run( CLI, network )
+24 -12
View File
@@ -1,31 +1,43 @@
#!/usr/bin/python
#!/usr/bin/env python
"Create a 64-node tree network, and test connectivity using ping."
from mininet.log import setLogLevel
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
from mininet.log import setLogLevel, info
from mininet.node import UserSwitch, OVSKernelSwitch, Host
from mininet.topolib import TreeNet
class HostV4( Host ):
"Try to IPv6 and its awful neighbor discovery"
def __init__( self, *args, **kwargs ):
super( HostV4, self ).__init__( *args, **kwargs )
cfgs = [ 'all.disable_ipv6=1', 'default.disable_ipv6=1',
'default.autoconf=0', 'lo.autoconf=0' ]
for cfg in cfgs:
self.cmd( 'sysctl -w net.ipv6.conf.' + cfg )
def treePing64():
"Run ping test on 64-node tree networks."
results = {}
switches = { # 'reference kernel': KernelSwitch,
'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
switches = { 'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
for name in switches:
print "*** Testing", name, "datapath"
info( "*** Testing", name, "datapath\n" )
switch = switches[ name ]
network = TreeNet( depth=2, fanout=8, switch=switch )
network = TreeNet( depth=2, fanout=8, switch=switch,
waitConnected=True )
result = network.run( network.pingAll )
results[ name ] = result
print
print "*** Tree network ping results:"
info( "\n*** Tree network ping results:\n" )
for name in switches:
print "%s: %d%% packet loss" % ( name, results[ name ] )
print
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
+8 -2
View File
@@ -24,14 +24,18 @@ Usage (example uses VLAN ID=1000):
"""
from sys import exit # pylint: disable=redefined-builtin
from mininet.node import Host
from mininet.topo import Topo
from mininet.util import quietRun
from mininet.log import error
class VLANHost( Host ):
"Host connected to VLAN interface"
# pylint: disable=arguments-differ
def config( self, vlan=100, **params ):
"""Configure VLANHost according to (optional) parameters:
vlan: VLAN ID for default interface"""
@@ -54,6 +58,7 @@ class VLANHost( Host ):
return r
hosts = { 'vlan': VLANHost }
@@ -65,7 +70,7 @@ def exampleAllHosts( vlan ):
# Start a basic network using our VLANHost
topo = SingleSwitchTopo( k=2 )
net = Mininet( host=host, topo=topo )
net = Mininet( host=host, topo=topo, waitConnected=True )
net.start()
CLI( net )
net.stop()
@@ -96,11 +101,12 @@ class VLANStarTopo( Topo ):
def exampleCustomTags():
"""Simple example that exercises VLANStarTopo"""
net = Mininet( topo=VLANStarTopo() )
net = Mininet( topo=VLANStarTopo(), waitConnected=True )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
import sys
from functools import partial
+7 -6
View File
@@ -16,12 +16,13 @@ import time
from mininet.log import info
from mininet.term import cleanUpScreens
from mininet.util import decode
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 ]
result = Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
return decode( result )
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
@@ -50,8 +51,9 @@ class Cleanup( object ):
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
"pings/noxes\n" )
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
'lt-nox_core ovs-openflowd ovs-controller'
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
# 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.
@@ -75,7 +77,6 @@ class Cleanup( object ):
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths\n" )
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps:
@@ -92,7 +93,7 @@ class Cleanup( object ):
).splitlines()
# Delete blocks of links
n = 1000 # chunk size
for i in xrange( 0, len( links ), n ):
for i in range( 0, len( links ), n ):
cmd = ';'.join( 'ip link del %s' % link
for link in links[ i : i + n ] )
sh( '( %s ) 2> /dev/null' % cmd )
+33 -14
View File
@@ -29,6 +29,8 @@ from subprocess import call
from cmd import Cmd
from os import isatty
from select import poll, POLLIN
import select
import errno
import sys
import time
import os
@@ -44,7 +46,8 @@ class CLI( Cmd ):
prompt = 'mininet> '
def __init__( self, mininet, stdin=sys.stdin, script=None ):
def __init__( self, mininet, stdin=sys.stdin, script=None,
**kwargs ):
"""Start and run interactive or batch mode CLI
mininet: Mininet network object
stdin: standard input for CLI
@@ -53,11 +56,10 @@ class CLI( Cmd ):
# Local variable bindings for py command
self.locals = { 'net': mininet }
# Attempt to handle input
self.stdin = stdin
self.inPoller = poll()
self.inPoller.register( stdin )
self.inputFile = script
Cmd.__init__( self )
Cmd.__init__( self, stdin=stdin, **kwargs )
info( '*** Starting CLI:\n' )
if self.inputFile:
@@ -77,13 +79,16 @@ class CLI( Cmd ):
return
cls.readlineInited = True
try:
from readline import read_history_file, write_history_file
# pylint: disable=import-outside-toplevel
from readline import ( read_history_file, write_history_file,
set_history_length )
except ImportError:
pass
else:
history_path = os.path.expanduser( '~/.mininet_history' )
if os.path.isfile( history_path ):
read_history_file( history_path )
set_history_length( 1000 )
atexit.register( lambda: write_history_file( history_path ) )
def run( self ):
@@ -137,10 +142,10 @@ class CLI( Cmd ):
' mininet> xterm h2\n\n'
)
def do_help( self, line ):
def do_help( self, line ): # pylint: disable=arguments-differ
"Describe available CLI commands."
Cmd.do_help( self, line )
if line is '':
if line == '':
output( self.helpStr )
def do_nodes( self, _line ):
@@ -169,6 +174,7 @@ class CLI( Cmd ):
"""Evaluate a Python expression.
Node names may be used, e.g.: py h1.cmd('ls')"""
try:
# pylint: disable=eval-used
result = eval( line, globals(), self.getLocals() )
if not result:
return
@@ -176,7 +182,7 @@ class CLI( Cmd ):
output( result + '\n' )
else:
output( repr( result ) + '\n' )
except Exception, e:
except Exception as e:
output( str( e ) + '\n' )
# We are in fact using the exec() pseudo-function
@@ -187,7 +193,7 @@ class CLI( Cmd ):
Node names may be used, e.g.: px print h1.cmd('ls')"""
try:
exec( line, globals(), self.getLocals() )
except Exception, e:
except Exception as e:
output( str( e ) + '\n' )
# pylint: enable=broad-except,exec-used
@@ -371,7 +377,7 @@ class CLI( Cmd ):
def do_links( self, _line ):
"Report on links"
for link in self.mn.links:
print link, link.status()
output( link, link.status(), '\n' )
def do_switch( self, line ):
"Starts or stops a switch"
@@ -395,17 +401,22 @@ class CLI( Cmd ):
error( 'invalid command: '
'switch <switch name> {start, stop}\n' )
def do_wait( self, _line ):
"Wait until all switches have connected to a controller"
self.mn.waitConnected()
def default( self, line ):
"""Called on an input line when the command prefix is not recognized.
Overridden to run shell commands when a node is the first CLI argument.
Past the first CLI argument, node names are automatically replaced with
corresponding IP addrs."""
Overridden to run shell commands when a node is the first
CLI argument. Past the first CLI argument, node names are
automatically replaced with corresponding IP addrs."""
first, args, line = self.parseline( line )
if first in self.mn:
if not args:
print "*** Enter a command for node: %s <cmd>" % first
error( '*** Please enter a command for node: %s <cmd>\n'
% first )
return
node = self.mn[ first ]
rest = args.split( ' ' )
@@ -439,7 +450,7 @@ class CLI( Cmd ):
# XXX BL: this doesn't quite do what we want.
if False and self.inputFile:
key = self.inputFile.read( 1 )
if key is not '':
if key != '':
node.write( key )
else:
self.inputFile = None
@@ -456,6 +467,13 @@ class CLI( Cmd ):
# it's possible to interrupt ourselves after we've
# read data but before it has been printed.
node.sendInt()
except select.error as e:
# pylint: disable=unpacking-non-sequence
# pylint: disable=unbalanced-tuple-unpacking
errno_, errmsg = e.args
if errno_ != errno.EINTR:
error( "select.error: %s, %s" % (errno_, errmsg) )
node.sendInt()
def precmd( self, line ):
"allow for comments in the cli"
@@ -472,3 +490,4 @@ def isReadable( poller ):
mask = fdmask[ 1 ]
if mask & POLLIN:
return True
return False
+98 -50
View File
@@ -24,10 +24,14 @@ TCIntf: interface with bandwidth limiting and delay via tc
Link: basic link class for creating veth pairs
"""
import re
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
import mininet.node
import re
# Make pylint happy:
# pylint: disable=too-many-arguments
class Intf( object ):
@@ -51,11 +55,12 @@ class Intf( object ):
self.ip = '127.0.0.1'
self.prefixLen = 8
# Add to node (and move ourselves if necessary )
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
if node:
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
# Save params for future reference
self.params = params
self.config( **params )
@@ -146,6 +151,9 @@ class Intf( object ):
def rename( self, newname ):
"Rename interface"
if self.node and self.name in self.node.nameToIntf:
# rename intf in node's nameToIntf
self.node.nameToIntf[newname] = self.node.nameToIntf.pop(self.name)
self.ifconfig( 'down' )
result = self.cmd( 'ip link set', self.name, 'name', newname )
self.name = newname
@@ -164,10 +172,10 @@ class Intf( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
name, value = list( param.items() )[ 0 ]
f = getattr( self, method, None )
if not f or value is None:
return
return None
if isinstance( value, list ):
result = f( *value )
elif isinstance( value, dict ):
@@ -202,6 +210,8 @@ class Intf( object ):
# if self.node.inNamespace:
# Link may have been dumped into root NS
# quietRun( 'ip link del ' + self.name )
self.node.delIntf( self )
self.link = None
def status( self ):
"Return intf status as a string"
@@ -251,7 +261,7 @@ class TCIntf( Intf ):
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
if latency_ms is None:
latency_ms = 15 * 8 / bw
latency_ms = 15.0 * 8 / bw
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
@@ -283,18 +293,14 @@ class TCIntf( Intf ):
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 ):
if loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# 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 '',
'loss %.5f ' % loss if (loss is not None and loss > 0) else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
if netemargs:
@@ -310,27 +316,52 @@ class TCIntf( Intf ):
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
# pylint: disable=arguments-differ
def config( self, bw=None, delay=None, jitter=None, loss=None,
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
gro=False, txo=True, rxo=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."
"""Configure the port and set its properties.
bw: bandwidth in b/s (e.g. '10m')
delay: transmit delay (e.g. '1ms' )
jitter: jitter (e.g. '1ms')
loss: loss (e.g. '1%' )
gro: enable GRO (False)
txo: enable transmit checksum offload (True)
rxo: enable receive checksum offload (True)
speedup: experimental switch-side bw option
use_hfsc: use HFSC scheduling
use_tbf: use TBF scheduling
latency_ms: TBF latency parameter
enable_ecn: enable ECN (False)
enable_red: enable RED (False)
max_queue_size: queue limit parameter for netem"""
# Support old names for parameters
gro = not params.pop( 'disable_gro', not gro )
result = Intf.config( self, **params)
# Disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
def on( isOn ):
"Helper method: bool -> 'on'/'off'"
return 'on' if isOn else 'off'
# Set offload parameters with ethool
self.cmd( 'ethtool -K', self,
'gro', on( gro ),
'tx', on( txo ),
'rx', on( rxo ) )
# Optimization: return if nothing else to configure
# Question: what happens if we want to reset things?
if ( bw is None and not delay and not loss
and max_queue_size is None ):
return
return None
# Clear existing configuration
tcoutput = self.tc( '%s qdisc show dev %s' )
if "priomap" not in tcoutput:
if "priomap" not in tcoutput and "noqueue" not in tcoutput:
cmds = [ '%s qdisc del dev %s root' ]
else:
cmds = []
@@ -354,7 +385,7 @@ class TCIntf( Intf ):
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 [] ) +
( ['%.5f%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
@@ -382,7 +413,7 @@ class Link( object ):
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, addr1=None, addr2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None, fast=True ):
params2=None, fast=True, **params ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
@@ -392,18 +423,15 @@ class Link( object ):
cls1, cls2: optional interface-specific constructors
intfName1: node1 interface name (optional)
intfName2: node2 interface name (optional)
params1: parameters for interface 1
params2: parameters for interface 2"""
params1: parameters for interface 1 (optional)
params2: parameters for interface 2 (optional)
**params: additional parameters for both interfaces"""
# This is a bit awkward; it seems that having everything in
# params is more orthogonal, but being able to specify
# in-line arguments is more convenient! So we support both.
if params1 is None:
params1 = {}
if params2 is None:
params2 = {}
# Allow passing in params1=params2
if params2 is params1:
params2 = dict( params1 )
params1 = dict( params1 ) if params1 else {}
params2 = dict( params2 ) if params2 else {}
if port1 is not None:
params1[ 'port' ] = port1
if port2 is not None:
@@ -417,6 +445,10 @@ class Link( object ):
if not intfName2:
intfName2 = self.intfName( node2, params2[ 'port' ] )
# Update with remaining parameter list
params1.update( params )
params2.update( params )
self.fast = fast
if fast:
params1.setdefault( 'moveIntfFn', self._ignore )
@@ -438,6 +470,7 @@ class Link( object ):
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
# pylint: enable=too-many-branches
@staticmethod
@@ -471,9 +504,9 @@ class Link( object ):
def delete( self ):
"Delete this link"
self.intf1.delete()
# We only need to delete one side, though this doesn't seem to
# cost us much and might help subclasses.
# self.intf2.delete()
self.intf1 = None
self.intf2.delete()
self.intf2 = None
def stop( self ):
"Override to stop and clean up link as needed"
@@ -506,13 +539,19 @@ class OVSLink( Link ):
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
try:
OVSSwitch
except NameError:
# pylint: disable=import-outside-toplevel,cyclic-import
from mininet.node import OVSSwitch
self.isPatchLink = False
if ( isinstance( node1, mininet.node.OVSSwitch ) and
isinstance( node2, mininet.node.OVSSwitch ) ):
if ( isinstance( node1, OVSSwitch ) and
isinstance( node2, OVSSwitch ) ):
self.isPatchLink = True
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
# pylint: disable=arguments-differ, signature-differs
def makeIntfPair( self, *args, **kwargs ):
"Usually delegated to OVSSwitch"
if self.isPatchLink:
@@ -522,14 +561,23 @@ class OVSLink( Link ):
class TCLink( Link ):
"Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None,
addr1=None, addr2=None, **params ):
Link.__init__( self, node1, node2, port1=port1, port2=port2,
intfName1=intfName1, intfName2=intfName2,
cls1=TCIntf,
cls2=TCIntf,
addr1=addr1, addr2=addr2,
params1=params,
params2=params )
"Link with TC interfaces"
def __init__( self, *args, **kwargs):
kwargs.setdefault( 'cls1', TCIntf )
kwargs.setdefault( 'cls2', TCIntf )
Link.__init__( self, *args, **kwargs)
class TCULink( TCLink ):
"""TCLink with default settings optimized for UserSwitch
(txo=rxo=0/False). Unfortunately with recent Linux kernels,
enabling TX and RX checksum offload on veth pairs doesn't work
well with UserSwitch: either it gets terrible performance or
TCP packets with bad checksums are generated, forwarded, and
*dropped* due to having bad checksums! OVS and LinuxBridge seem
to cope with this somehow, but it is likely to be an issue with
many software Ethernet bridges."""
def __init__( self, *args, **kwargs ):
kwargs.update( txo=False, rxo=False )
TCLink.__init__( self, *args, **kwargs )
+24 -28
View File
@@ -4,6 +4,7 @@ import logging
from logging import Logger
import types
# Create a new loglevel, 'CLI info', which enables a Mininet user to see only
# the output of the commands they execute, plus any errors or warnings. This
# level is in between info and warning. CLI info-level commands should not be
@@ -14,13 +15,14 @@ LEVELS = { 'debug': logging.DEBUG,
'info': logging.INFO,
'output': OUTPUT,
'warning': logging.WARNING,
'warn': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL }
# change this to logging.INFO to get printouts when running unit tests
LOGLEVELDEFAULT = OUTPUT
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
LOGMSGFORMAT = '%(message)s'
@@ -51,7 +53,7 @@ class StreamHandlerNoNewline( logging.StreamHandler ):
self.flush()
except ( KeyboardInterrupt, SystemExit ):
raise
except:
except: # noqa pylint: disable=bare-except
self.handleError( record )
@@ -95,9 +97,9 @@ class MininetLogger( Logger, object ):
__metaclass__ = Singleton
def __init__( self ):
def __init__( self, name="mininet" ):
Logger.__init__( self, "mininet" )
Logger.__init__( self, name )
# create console handler
ch = StreamHandlerNoNewline()
@@ -105,30 +107,22 @@ class MininetLogger( Logger, object ):
formatter = logging.Formatter( LOGMSGFORMAT )
# add formatter to ch
ch.setFormatter( formatter )
# add ch to lg
# add ch to lg and initialize log level
self.addHandler( ch )
self.ch = ch
self.setLogLevel()
def setLogLevel( self, levelname=None ):
"""Setup loglevel.
Convenience function to support lowercase names.
levelName: level name from LEVELS"""
level = LOGLEVELDEFAULT
if levelname is not None:
if levelname not in LEVELS:
raise Exception( 'unknown levelname seen in setLogLevel' )
else:
level = LEVELS.get( levelname, level )
if levelname and levelname not in LEVELS:
print(LEVELS)
raise Exception( 'setLogLevel: unknown levelname %s' % levelname )
level = LEVELS.get( levelname, LOGLEVELDEFAULT )
self.setLevel( level )
self.handlers[ 0 ].setLevel( level )
self.ch.setLevel( level )
# pylint: disable=method-hidden
# "An attribute inherited from mininet.log hide this method" (sic)
# Not sure why this is occurring - this function definitely gets called.
# See /usr/lib/python2.5/logging/__init__.py; modified from warning()
def output( self, msg, *args, **kwargs ):
"""Log 'msg % args' with severity 'OUTPUT'.
@@ -137,14 +131,11 @@ class MininetLogger( Logger, object ):
logger.warning("Houston, we have a %s", "cli output", exc_info=1)
"""
if self.manager.disable >= OUTPUT:
if getattr( self.manager, 'disabled', 0 ) >= OUTPUT:
return
if self.isEnabledFor( OUTPUT ):
self._log( OUTPUT, msg, args, kwargs )
# pylint: enable=method-hidden
lg = MininetLogger()
# Make things a bit more convenient by adding aliases
# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
@@ -160,7 +151,7 @@ def makeListCompatible( fn ):
"Generated function. Closure-ish."
if len( args ) == 1:
return fn( *args )
args = ' '.join( [ str( arg ) for arg in args ] )
args = ' '.join( str( arg ) for arg in args )
return fn( args )
# Fix newfn's name and docstring
@@ -168,9 +159,14 @@ def makeListCompatible( fn ):
setattr( newfn, '__doc__', fn.__doc__ )
return newfn
info, output, warn, error, debug = (
lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
makeListCompatible( f ) for f in
lg.info, lg.output, lg.warn, lg.error, lg.debug ]
# Initialize logger and logging functions
logging.setLoggerClass( MininetLogger )
lg = logging.getLogger( "mininet" )
_loggers = lg.info, lg.output, lg.warning, lg.error, lg.debug
_loggers = tuple( makeListCompatible( logger ) for logger in _loggers )
lg.info, lg.output, lg.warning, lg.error, lg.debug = _loggers
info, output, warning, error, debug = _loggers
warn = warning # alternate/old name
setLogLevel = lg.setLogLevel
+8 -4
View File
@@ -1,8 +1,11 @@
"Module dependency utility functions for Mininet."
from mininet.util import quietRun
from mininet.log import info, error, debug
from os import environ
from sys import exit # pylint: disable=redefined-builtin
from mininet.util import quietRun, BaseString
from mininet.log import info, error, debug
def lsmod():
"Return output of lsmod."
@@ -18,6 +21,7 @@ def modprobe( mod ):
mod: module string"""
return quietRun( [ 'modprobe', mod ] )
OF_KMOD = 'ofdatapath'
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
TUN = 'tun'
@@ -28,9 +32,9 @@ def moduleDeps( subtract=None, add=None ):
add: string or list of module names to add, if not already loaded"""
subtract = subtract if subtract is not None else []
add = add if add is not None else []
if isinstance( subtract, basestring ):
if isinstance( subtract, BaseString ):
subtract = [ subtract ]
if isinstance( add, basestring ):
if isinstance( add, BaseString ):
add = [ add ]
for mod in subtract:
if mod in lsmod():
+77 -18
View File
@@ -92,6 +92,7 @@ import select
import signal
import random
from sys import exit # pylint: disable=redefined-builtin
from time import sleep
from itertools import chain, groupby
from math import ceil
@@ -104,15 +105,16 @@ from mininet.nodelib import NAT
from mininet.link import Link, Intf
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening )
waitListening, BaseString )
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.2.2b1"
VERSION = "2.3.0b2"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
# pylint: disable=too-many-arguments
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
controller=DefaultController, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
@@ -144,7 +146,9 @@ class Mininet( object ):
self.intf = intf
self.ipBase = ipBase
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
self.nextIP = 1 # start for address allocation
hostIP = ( 0xffffffff >> self.prefixLen ) & self.ipBaseNum
# Start for address allocation
self.nextIP = hostIP if hostIP > 0 else 1
self.inNamespace = inNamespace
self.xterms = xterms
self.cleanup = cleanup
@@ -171,7 +175,7 @@ class Mininet( object ):
if topo and build:
self.build()
def waitConnected( self, timeout=None, delay=.5 ):
def waitConnected( self, timeout=5, delay=.5 ):
"""wait for each switch to connect to a controller,
up to 5 seconds
timeout: time to wait, or None to wait indefinitely
@@ -188,7 +192,7 @@ class Mininet( object ):
if not remaining:
info( '\n' )
return True
if time > timeout and timeout is not None:
if timeout is not None and time > timeout:
break
sleep( delay )
time += delay
@@ -226,6 +230,24 @@ class Mininet( object ):
self.nameToNode[ name ] = h
return h
def delNode( self, node, nodes=None):
"""Delete node
node: node to delete
nodes: optional list to delete from (e.g. self.hosts)"""
if nodes is None:
nodes = ( self.hosts if node in self.hosts else
( self.switches if node in self.switches else
( self.controllers if node in self.controllers else
[] ) ) )
node.stop( deleteIntfs=True )
node.terminate()
nodes.remove( node )
del self.nameToNode[ node.name ]
def delHost( self, host ):
"Delete a host"
self.delNode( host, nodes=self.hosts )
def addSwitch( self, name, cls=None, **params ):
"""Add switch.
name: name of switch to add
@@ -244,6 +266,10 @@ class Mininet( object ):
self.nameToNode[ name ] = sw
return sw
def delSwitch( self, switch ):
"Delete a switch"
self.delNode( switch, nodes=self.switches )
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
@@ -265,6 +291,12 @@ class Mininet( object ):
self.nameToNode[ name ] = controller_new
return controller_new
def delController( self, controller ):
"""Delete a controller
Warning - does not reconfigure switches, so they
may still attempt to connect to it!"""
self.delNode( controller )
def addNAT( self, name='nat0', connect=True, inNamespace=False,
**params):
"""Add a NAT to the Mininet network
@@ -303,9 +335,13 @@ class Mininet( object ):
# Even more convenient syntax for node lookup and iteration
def __getitem__( self, key ):
"""net [ name ] operator: Return node(s) with given name(s)"""
"net[ name ] operator: Return node with given name"
return self.nameToNode[ key ]
def __delitem__( self, key ):
"del net[ name ] operator - delete node with given name"
self.delNode( self.nameToNode[ key ] )
def __iter__( self ):
"return iterator over node names"
for node in chain( self.hosts, self.switches, self.controllers ):
@@ -349,8 +385,8 @@ class Mininet( object ):
params: additional link params (optional)
returns: link object"""
# Accept node objects or names
node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ]
node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ]
node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
options = dict( params )
# Port is optional
if port1 is not None:
@@ -367,6 +403,30 @@ class Mininet( object ):
self.links.append( link )
return link
def delLink( self, link ):
"Remove a link from this network"
link.delete()
self.links.remove( link )
def linksBetween( self, node1, node2 ):
"Return Links between node1 and node2"
return [ link for link in self.links
if ( node1, node2 ) in (
( link.intf1.node, link.intf2.node ),
( link.intf2.node, link.intf1.node ) ) ]
def delLinkBetween( self, node1, node2, index=0, allLinks=False ):
"""Delete link(s) between node1 and node2
index: index of link to delete if multiple links (0)
allLinks: ignore index and delete all such links (False)
returns: deleted link(s)"""
links = self.linksBetween( node1, node2 )
if not allLinks:
links = [ links[ index ] ]
for link in links:
self.delLink( link )
return links
def configHosts( self ):
"Configure a set of hosts."
for host in self.hosts:
@@ -491,7 +551,8 @@ class Mininet( object ):
switch.start( self.controllers )
started = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches )
@@ -518,7 +579,8 @@ class Mininet( object ):
info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches )
@@ -576,7 +638,7 @@ class Mininet( object ):
# Check for downed link
if 'connect: Network is unreachable' in pingOutput:
return 1, 0
r = r'(\d+) packets transmitted, (\d+) received'
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -638,7 +700,7 @@ class Mininet( object ):
m = re.search( r, pingOutput )
if m is not None:
return errorTuple
r = r'(\d+) packets transmitted, (\d+) received'
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -769,7 +831,7 @@ class Mininet( object ):
debug( 'Client output: %s\n' % cliout )
servout = ''
# We want the last *b/sec from the iperf server output
# for TCP, there are two fo them because of waitListening
# for TCP, there are two of them because of waitListening
count = 2 if l4Type == 'TCP' else 1
while len( re.findall( '/sec', servout ) ) < count:
servout += server.monitor( timeoutms=5000 )
@@ -788,7 +850,6 @@ class Mininet( object ):
duration: test duration in seconds (integer)
returns a single list of measured CPU fractions as floats.
"""
cores = int( quietRun( 'nproc' ) )
pct = cpu * 100
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
hosts = self.hosts
@@ -840,10 +901,8 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
if isinstance( src, basestring ):
src = self.nameToNode[ src ]
if isinstance( dst, basestring ):
dst = self.nameToNode[ dst ]
src = self.nameToNode[ src ]
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
+112 -58
View File
@@ -57,16 +57,22 @@ import pty
import re
import signal
import select
from distutils.version import StrictVersion
from re import findall
from subprocess import Popen, PIPE
from sys import exit # pylint: disable=redefined-builtin
from time import sleep
from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
numCores, retry, mountCgroups )
numCores, retry, mountCgroups, BaseString, decode,
encode, getincrementaldecoder, Python3, which )
from mininet.moduledeps import moduleDeps, pathCheck, TUN
from mininet.link import Link, Intf, TCIntf, OVSIntf
from re import findall
from distutils.version import StrictVersion
# pylint: disable=too-many-arguments
class Node( object ):
"""A virtual network node is simply a shell in a network namespace.
@@ -87,12 +93,19 @@ class Node( object ):
self.privateDirs = params.get( 'privateDirs', [] )
self.inNamespace = params.get( 'inNamespace', inNamespace )
# Python 3 complains if we don't wait for shell exit
self.waitExited = params.get( 'waitExited', Python3 )
# Stash configuration parameters for future reference
self.params = params
self.intfs = {} # dict of port numbers to interfaces
self.ports = {} # dict of interfaces to port numbers
# replace with Port objects, eventually ?
# dict of port numbers to interfacse
self.intfs = {}
# dict of interfaces to port numbers
# todo: replace with Port objects, eventually ?
self.ports = {}
self.nameToIntf = {} # dict of interface names to Intfs
# Make pylint happy
@@ -102,7 +115,11 @@ class Node( object ):
self.waiting = False
self.readbuf = ''
# Incremental decoder for buffered reading
self.decoder = getincrementaldecoder()
# Start command interpreter shell
self.master, self.slave = None, None # pylint
self.startShell()
self.mountPrivateDirs()
@@ -135,14 +152,18 @@ class Node( object ):
# -s: pass $* to shell, and make process easy to find in ps
# prompt is set to sentinel chr( 127 )
cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
'bash', '--norc', '-is', 'mininet:' + self.name ]
'bash', '--norc', '--noediting',
'-is', 'mininet:' + self.name ]
# Spawn a shell subprocess in a pseudo-tty, to disable buffering
# in the subprocess and insulate it from signals (e.g. SIGINT)
# received by the parent
master, slave = pty.openpty()
self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
close_fds=False )
self.stdin = os.fdopen( master, 'rw' )
self.master, self.slave = pty.openpty()
self.shell = self._popen( cmd, stdin=self.slave, stdout=self.slave,
stderr=self.slave, close_fds=False )
# XXX BL: This doesn't seem right, and we should also probably
# close our files when we exit...
self.stdin = os.fdopen( self.master, 'r' )
self.stdout = self.stdin
self.pid = self.shell.pid
self.pollOut = select.poll()
@@ -169,7 +190,7 @@ class Node( object ):
def mountPrivateDirs( self ):
"mount private directories"
# Avoid expanding a string into a list of chars
assert not isinstance( self.privateDirs, basestring )
assert not isinstance( self.privateDirs, BaseString )
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
@@ -198,7 +219,9 @@ class Node( object ):
params: parameters to Popen()"""
# Leave this is as an instance method for now
assert self
return Popen( cmd, **params )
popen = Popen( cmd, **params )
debug( '_popen', cmd, popen.pid )
return popen
def cleanup( self ):
"Help python collect its garbage."
@@ -207,23 +230,30 @@ class Node( object ):
# for intfName in self.intfNames():
# if self.name in intfName:
# quietRun( 'ip link del ' + intfName )
if self.shell:
# Close ptys
self.stdin.close()
os.close(self.slave)
if self.waitExited:
debug( 'waiting for', self.pid, 'to terminate\n' )
self.shell.wait()
self.shell = None
# Subshell I/O, commands and control
def read( self, maxbytes=1024 ):
def read( self, size=1024 ):
"""Buffered read from node, potentially blocking.
maxbytes: maximum number of bytes to return"""
size: maximum number of characters to return"""
count = len( self.readbuf )
if count < maxbytes:
data = os.read( self.stdout.fileno(), maxbytes - count )
self.readbuf += data
if maxbytes >= len( self.readbuf ):
if count < size:
data = os.read( self.stdout.fileno(), size - count )
self.readbuf += self.decoder.decode( data )
if size >= len( self.readbuf ):
result = self.readbuf
self.readbuf = ''
else:
result = self.readbuf[ :maxbytes ]
self.readbuf = self.readbuf[ maxbytes: ]
result = self.readbuf[ :size ]
self.readbuf = self.readbuf[ size: ]
return result
def readline( self ):
@@ -240,7 +270,7 @@ class Node( object ):
def write( self, data ):
"""Write data to node.
data: string"""
os.write( self.stdin.fileno(), data )
os.write( self.stdin.fileno(), encode( data ) )
def terminate( self ):
"Send kill signal to Node and clean up after it."
@@ -263,6 +293,7 @@ class Node( object ):
returns: result of poll()"""
if len( self.readbuf ) == 0:
return self.pollOut.poll( timeoutms )
return None
def sendCmd( self, *args, **kwargs ):
"""Send a command, followed by a command to echo a sentinel,
@@ -356,6 +387,7 @@ class Node( object ):
return self.waitOutput( verbose )
else:
warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
return None
def cmdPrint( self, *args):
"""Call cmd and printing its output
@@ -370,23 +402,23 @@ class Node( object ):
'mncmd':
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
shell = defaults.pop( 'shell', False )
if len( args ) == 1:
if isinstance( args[ 0 ], list ):
# popen([cmd, arg1, arg2...])
cmd = args[ 0 ]
elif isinstance( args[ 0 ], basestring ):
elif isinstance( args[ 0 ], BaseString ):
# popen("cmd arg1 arg2...")
cmd = args[ 0 ].split()
cmd = [ args[ 0 ] ] if shell else args[ 0 ].split()
else:
raise Exception( 'popen() requires a string or list' )
elif len( args ) > 0:
# popen( cmd, arg1, arg2... )
cmd = list( args )
if shell:
cmd = [ os.environ[ 'SHELL' ], '-c' ] + [ ' '.join( cmd ) ]
# Attach to our namespace using mnexec -a
cmd = defaults.pop( 'mncmd' ) + cmd
# Shell requires a string, not a list!
if defaults.get( 'shell', False ):
cmd = ' '.join( cmd )
popen = self._popen( cmd, **defaults )
return popen
@@ -398,7 +430,7 @@ class Node( object ):
# Warning: this can fail with large numbers of fds!
out, err = popen.communicate()
exitcode = popen.wait()
return out, err, exitcode
return decode( out ), decode( err ), exitcode
# Interface management, configuration, and routing
@@ -431,6 +463,15 @@ class Node( object ):
debug( 'moving', intf, 'into namespace for', self.name, '\n' )
moveIntfFn( intf.name, self )
def delIntf( self, intf ):
"""Remove interface from Node's known interfaces
Note: to fully delete interface, call intf.delete() instead"""
port = self.ports.get( intf )
if port is not None:
del self.intfs[ port ]
del self.ports[ intf ]
del self.nameToIntf[ intf.name ]
def defaultIntf( self ):
"Return interface for lowest port"
ports = self.intfs.keys()
@@ -439,6 +480,7 @@ class Node( object ):
else:
warn( '*** defaultIntf: warning:', self.name,
'has no interfaces\n' )
return None
def intf( self, intf=None ):
"""Return our interface object with given string name,
@@ -451,7 +493,7 @@ class Node( object ):
"""
if not intf:
return self.defaultIntf()
elif isinstance( intf, basestring):
elif isinstance( intf, BaseString):
return self.nameToIntf[ intf ]
else:
return intf
@@ -478,7 +520,7 @@ class Node( object ):
# 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():
for intf in list( self.intfs.values() ):
# Protect against deleting hardware interfaces
if ( self.name in intf.name ) or ( not checkName ):
intf.delete()
@@ -503,7 +545,7 @@ class Node( object ):
"""Set the default route to go through intf.
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
# Note setParam won't call us if intf is none
if isinstance( intf, basestring ) and ' ' in intf:
if isinstance( intf, BaseString ) and ' ' in intf:
params = intf
else:
params = 'dev %s' % intf
@@ -550,12 +592,12 @@ class Node( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
name, value = list( param.items() )[ 0 ]
if value is None:
return
return None
f = getattr( self, method, None )
if not f:
return
return None
if isinstance( value, list ):
result = f( *value )
elif isinstance( value, dict ):
@@ -599,7 +641,7 @@ class Node( object ):
def intfList( self ):
"List of our interfaces sorted by port number"
return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
return [ self.intfs[ p ] for p in sorted( self.intfs.keys() ) ]
def intfNames( self ):
"The names of our interfaces sorted by port number"
@@ -623,11 +665,12 @@ class Node( object ):
@classmethod
def checkSetup( cls ):
"Make sure our class and superclasses are set up"
while cls and not getattr( cls, 'isSetup', True ):
cls.setup()
cls.isSetup = True
clas = cls
while clas and not getattr( clas, 'isSetup', True ):
clas.setup()
clas.isSetup = True
# Make pylint happy
cls = getattr( type( cls ), '__base__', None )
clas = getattr( type( clas ), '__base__', None )
@classmethod
def setup( cls ):
@@ -686,15 +729,15 @@ class CPULimitedHost( Host ):
_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
# Sometimes cgdelete returns a resource busy error but still
# deletes the group; next attempt will give "no such file"
return exitcode == 0 or ( 'no such file' in _err.lower() )
return exitcode == 0 or ( 'no such file' in _err.lower() )
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', '-g', self.name,
'-da', str( self.pid ) ]
mncmd = kwargs.pop( 'mncmd', [ 'mnexec', '-g', self.name,
'-da', str( self.pid ) ] )
# if our cgroup is not given any cpu time,
# we cannot assign the RR Scheduler.
if self.sched == 'rt':
@@ -808,6 +851,7 @@ class CPULimitedHost( Host ):
errFail( 'cgclassify -g cpuset:/%s %s' % (
self.name, self.pid ) )
# pylint: disable=arguments-differ
def config( self, cpu=-1, cores=None, **params ):
"""cpu: desired overall system CPU fraction
cores: (real) core(s) this host can run on
@@ -870,7 +914,7 @@ class Switch( Node ):
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
if dpid:
# Remove any colons and make sure it's a good hex number
dpid = dpid.translate( None, ':' )
dpid = dpid.replace( ':', '' )
assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
else:
# Use hex of the first number in the switch name
@@ -878,6 +922,7 @@ class Switch( Node ):
if nums:
dpid = hex( int( nums[ 0 ] ) )[ 2: ]
else:
self.terminate() # Python 3.6 crash workaround
raise Exception( 'Unable to derive default datapath ID - '
'please either specify a dpid or use a '
'canonical switch name such as s23.' )
@@ -899,6 +944,7 @@ class Switch( Node ):
else:
error( '*** Error: %s has execed and cannot accept commands' %
self.name )
return None
def connected( self ):
"Is the switch connected to a controller? (override this method)"
@@ -1084,6 +1130,7 @@ class OVSSwitch( Switch ):
if self.batch:
cmd = ' '.join( str( arg ).strip() for arg in args )
self.commands.append( cmd )
return None
else:
return self.cmd( 'ovs-vsctl', *args, **kwargs )
@@ -1151,6 +1198,7 @@ class OVSSwitch( Switch ):
opts += ' protocols=%s' % self.protocols
if self.stp and self.failMode == 'standalone':
opts += ' stp_enable=true'
opts += ' other-config:dp-desc=%s' % self.name
return opts
def start( self, controllers ):
@@ -1220,7 +1268,7 @@ class OVSSwitch( Switch ):
run( cmds, shell=True )
# Reapply link config if necessary...
for switch in switches:
for intf in switch.intfs.itervalues():
for intf in switch.intfs.values():
if isinstance( intf, TCIntf ):
intf.config( **intf.params )
return switches
@@ -1246,7 +1294,7 @@ class OVSSwitch( Switch ):
pids = ' '.join( str( switch.pid ) for switch in switches )
run( 'kill -HUP ' + pids )
for switch in switches:
switch.shell = None
switch.terminate()
return switches
@@ -1270,7 +1318,7 @@ class OVSBridge( OVSSwitch ):
"Are we forwarding yet?"
if self.stp:
status = self.dpctl( 'show' )
return 'STP_FORWARD' in status and not 'STP_LEARN' in status
return 'STP_FORWARD' in status and 'STP_LEARN' not in status
else:
return True
@@ -1350,10 +1398,12 @@ class Controller( Node ):
OpenFlow controller."""
def __init__( self, name, inNamespace=False, command='controller',
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
port=6653, protocol='tcp', **params ):
cargs='ptcp:%d', cdir=None, ip="127.0.0.1",
port=6653, protocol='tcp', verbose=False, **params ):
self.command = command
self.cargs = cargs
if verbose:
cargs = '-v ' + cargs
self.cdir = cdir
# Accept 'ip:port' syntax as shorthand
if ':' in ip:
@@ -1395,6 +1445,7 @@ class Controller( Node ):
' 1>' + cout + ' 2>' + cout + ' &' )
self.execed = False
# pylint: disable=arguments-differ,signature-differs
def stop( self, *args, **kwargs ):
"Stop controller."
self.cmd( 'kill %' + self.command )
@@ -1418,21 +1469,21 @@ class Controller( Node ):
@classmethod
def isAvailable( cls ):
"Is controller available?"
return quietRun( 'which controller' )
return which( 'controller' )
class OVSController( Controller ):
"Open vSwitch controller"
def __init__( self, name, command='ovs-controller', **kwargs ):
if quietRun( 'which test-controller' ):
command = 'test-controller'
Controller.__init__( self, name, command=command, **kwargs )
def __init__( self, name, **kwargs ):
kwargs.setdefault( 'command', self.isAvailable() or
'ovs-controller' )
Controller.__init__( self, name, **kwargs )
@classmethod
def isAvailable( cls ):
return ( quietRun( 'which ovs-controller' ) or
quietRun( 'which test-controller' ) or
quietRun( 'which ovs-testcontroller' ) )
return (which( 'ovs-controller' ) or
which( 'test-controller' ) or
which( 'ovs-testcontroller' ))
class NOX( Controller ):
"Controller to run a NOX application."
@@ -1445,7 +1496,7 @@ class NOX( Controller ):
warn( 'warning: no NOX modules specified; '
'running packetdump only\n' )
noxArgs = [ 'packetdump' ]
elif type( noxArgs ) not in ( list, tuple ):
elif not isinstance( noxArgs, ( list, tuple ) ):
noxArgs = [ noxArgs ]
if 'NOX_CORE_DIR' not in os.environ:
@@ -1471,7 +1522,7 @@ class Ryu( Controller ):
warn( 'warning: no Ryu modules specified; '
'running simple_switch only\n' )
ryuArgs = [ ryuCoreDir + 'simple_switch.py' ]
elif type( ryuArgs ) not in ( list, tuple ):
elif not isinstance( ryuArgs, ( list, tuple ) ):
ryuArgs = [ ryuArgs ]
Controller.__init__( self, name,
@@ -1498,6 +1549,7 @@ class RemoteController( Controller ):
"Overridden to do nothing."
return
# pylint: disable=arguments-differ
def stop( self ):
"Overridden to do nothing."
return
@@ -1529,6 +1581,7 @@ class RemoteController( Controller ):
else:
return True
DefaultControllers = ( Controller, OVSController )
def findController( controllers=DefaultControllers ):
@@ -1536,6 +1589,7 @@ def findController( controllers=DefaultControllers ):
for controller in controllers:
if controller.isAvailable():
return controller
return None
def DefaultController( name, controllers=DefaultControllers, **kwargs ):
"Find a controller that is available and instantiate it"
+28 -18
View File
@@ -84,13 +84,36 @@ class NAT( Node ):
self.flush = flush
self.forwardState = self.cmd( 'sysctl -n net.ipv4.ip_forward' ).strip()
def setManualConfig( self, intf ):
"""Prevent network-manager/networkd from messing with our interface
by specifying manual configuration in /etc/network/interfaces"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
try:
with open( cfile ) as f:
config = f.read()
except IOError:
config = ''
if ( line ) not in config:
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network manager to be safe -
# hopefully this won't disconnect you
self.cmd( 'service network-manager restart || netplan apply' )
# pylint: disable=arguments-differ
def config( self, **params ):
"""Configure the NAT and iptables"""
super( NAT, self).config( **params )
if not self.localIntf:
self.localIntf = self.defaultIntf()
self.setManualConfig( self.localIntf )
# Now we can configure manually without interference
super( NAT, self).config( **params )
if self.flush:
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
self.cmd( 'iptables -F' )
@@ -106,27 +129,14 @@ class NAT( Node ):
self.cmd( 'iptables -A FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -A FORWARD',
'-o', self.localIntf, '-d', self.subnet,'-j ACCEPT' )
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING',
'-s', self.subnet, "'!'", '-d', self.subnet, '-j MASQUERADE' )
'-s', self.subnet, "'!'", '-d', self.subnet,
'-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
# Prevent network-manager from messing with our interface
# by specifying manual configuration in /etc/network/interfaces
intf = self.localIntf
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if ( line ) not in config:
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
self.cmd( 'service network-manager restart' )
def terminate( self ):
"Stop NAT/forwarding between Mininet and external network"
# Remote NAT rules
@@ -135,7 +145,7 @@ class NAT( Node ):
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -D FORWARD',
'-o', self.localIntf, '-d', self.subnet,'-j ACCEPT' )
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -D POSTROUTING',
'-s', self.subnet, '\'!\'', '-d', self.subnet,
'-j MASQUERADE' )
+1 -1
View File
@@ -50,7 +50,7 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
return
return None
display, tunnel = tunnelX11( node, display )
if display is None:
return []
+4 -1
View File
@@ -21,7 +21,10 @@ def runTests( testDir, verbosity=1 ):
# discover all tests in testDir
testSuite = defaultTestLoader.discover( testDir )
# run tests
TextTestRunner( verbosity=verbosity ).run( testSuite )
success = ( TextTestRunner( verbosity=verbosity )
.run( testSuite ).wasSuccessful() )
sys.exit( 0 if success else 1 )
if __name__ == '__main__':
setLogLevel( 'warning' )
+4 -3
View File
@@ -45,7 +45,7 @@ class testOptionsTopoCommon( object ):
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
if sys.exc_info() != ( None, None, None ):
cleanup()
def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
@@ -95,7 +95,7 @@ class testOptionsTopoCommon( object ):
CPU_FRACTION = 0.1
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
hopts = { 'cpu': CPU_FRACTION }
#self.runOptionsTopoTest( N, hopts=hopts )
# self.runOptionsTopoTest( N, hopts=hopts )
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
host=CPULimitedHost, switch=self.switchClass,
@@ -118,7 +118,7 @@ class testOptionsTopoCommon( object ):
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
self.switchClass ) )
for pct in results:
#divide cpu by 100 to convert from percentage to fraction
# divide cpu by 100 to convert from percentage to fraction
self.assertWithinTolerance( pct/100, CPU_FRACTION,
CPU_TOLERANCE, msg )
@@ -263,6 +263,7 @@ class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
longMessage = True
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+1 -1
View File
@@ -26,7 +26,7 @@ class testSingleSwitchCommon( object ):
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
if sys.exc_info() != ( None, None, None ):
cleanup()
def testMinimal( self ):
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python
"""
Regression test for pty leak in Node()
"""
import unittest
from mininet.net import Mininet
from mininet.clean import cleanup
from mininet.topo import SingleSwitchTopo
class TestPtyLeak( unittest.TestCase ):
"Verify that there is no pty leakage"
@staticmethod
def testPtyLeak():
"Test for pty leakage"
net = Mininet( SingleSwitchTopo() )
net.start()
host = net[ 'h1' ]
for _ in range( 0, 10 ):
oldptys = host.slave, host.master
net.delHost( host )
host = net.addHost( 'h1' )
assert ( host.slave, host.master ) == oldptys
net.stop()
if __name__ == '__main__':
unittest.main()
cleanup()
+19 -14
View File
@@ -24,16 +24,16 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"Clean up if necessary"
# satisfy pylint
assert self
if sys.exc_info != ( None, None, None ):
if sys.exc_info() != ( None, None, None ):
cleanup()
def testDefaultDpid( self ):
"""Verify that the default dpid is assigned using a valid provided
canonical switchname if no dpid is passed in switch creation."""
switch = Mininet( Topo(),
self.switchClass,
Host, Controller ).addSwitch( 's1' )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
net.stop()
def dpidFrom( self, num ):
"Compute default dpid from number"
@@ -44,31 +44,34 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation."""
dpid = self.dpidFrom( 0xABCD )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch(
's1', dpid=dpid )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid )
net.stop()
def testDefaultDpidAssignmentFailure( self ):
"""Verify that Default dpid assignment raises an Exception if the
name of the switch does not contin a digit. Also verify the
exception message."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
with self.assertRaises( Exception ) as raises_cm:
Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 'A' )
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
net.addSwitch( 'A' )
self.assertTrue( 'Unable to derive '
'default datapath ID - please either specify a dpid '
'or use a canonical switch name such as s23.')
'or use a canonical switch name such as s23.'
in str( raises_cm.exception ) )
net.stop()
def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch
name) 0's followed by hex of first string of contiguous digits passed
in switch name."""
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 's123' )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
net.stop()
class OVSUser( OVSSwitch):
"OVS User Switch convenience class"
@@ -92,6 +95,8 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of Userspace switch."
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
cleanup()
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python
"""Package: mininet
Test functions defined in mininet.util."""
import unittest
from mininet.util import quietRun
class testQuietRun( unittest.TestCase ):
"""Test quietRun that runs a command and returns its merged output from
STDOUT and STDIN"""
@staticmethod
def getEchoCmd( n ):
"Return a command that will print n characters"
return "echo -n " + "x" * n
def testEmpty( self ):
"Run a command that prints nothing"
output = quietRun(testQuietRun.getEchoCmd( 0 ) )
self.assertEqual( 0, len( output ) )
def testOneRead( self ):
"""Run a command whose output is entirely read on the first call if
each call reads at most 1024 characters
"""
for n in [ 42, 1024 ]:
output = quietRun( testQuietRun.getEchoCmd( n ) )
self.assertEqual( n, len( output ) )
def testMultipleReads( self ):
"Run a command whose output is not entirely read on the first read"
for n in [ 1025, 4242 ]:
output = quietRun(testQuietRun.getEchoCmd( n ) )
self.assertEqual( n, len( output ) )
if __name__ == "__main__":
unittest.main()
+65 -30
View File
@@ -6,18 +6,22 @@ Tests for the Mininet Walkthrough
TODO: missing xterm test
"""
import unittest
import pexpect
import os
import re
from mininet.util import quietRun
import unittest
from distutils.version import StrictVersion
from sys import stdout
from mininet.util import quietRun, pexpect
from mininet.clean import cleanup
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
versionMatch = re.findall( r'TShark \d+.\d+.\d+', versionStr )[0]
return versionMatch.split()[ 1 ]
versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
return versionMatch[ 0 ]
# pylint doesn't understand pexpect.match, unfortunately!
# pylint:disable=maybe-no-member
@@ -27,6 +31,11 @@ class testWalkthrough( unittest.TestCase ):
prompt = 'mininet>'
@staticmethod
def setup():
"Be paranoid and run cleanup() before each test"
cleanup()
# PART 1
def testHelp( self ):
"Check the usage message"
@@ -42,15 +51,18 @@ class testWalkthrough( unittest.TestCase ):
tshark = pexpect.spawn( 'tshark -i lo -R of' )
else:
tshark = pexpect.spawn( 'tshark -i lo -Y openflow_v1' )
tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback'" ] )
tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback" ] )
mn = pexpect.spawn( 'mn --test pingall' )
mn.expect( '0% dropped' )
tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
tshark.sendintr()
mn.expect( pexpect.EOF )
tshark.expect( 'aptured' ) # 'xx packets captured'
tshark.expect( pexpect.EOF )
def testBasic( self ):
"Test basic CLI commands (help, nodes, net, dump)"
p = pexpect.spawn( 'mn' )
p = pexpect.spawn( 'mn -w' )
p.expect( self.prompt )
# help command
p.sendline( 'help' )
@@ -64,7 +76,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
# net command
p.sendline( 'net' )
expected = [ x for x in nodes ]
expected = list( nodes )
while len( expected ) > 0:
index = p.expect( expected )
node = p.match.group( 0 )
@@ -89,15 +101,18 @@ class testWalkthrough( unittest.TestCase ):
def testHostCommands( self ):
"Test ifconfig and ps on h1 and s1"
p = pexpect.spawn( 'mn' )
p = pexpect.spawn( 'mn -w' )
p.expect( self.prompt )
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index == 0 or index == 3:
if index in (0, 3):
ifcount += 1
elif index == 1:
self.fail( 's1 interface displayed in "h1 ifconfig"' )
@@ -113,11 +128,11 @@ class testWalkthrough( unittest.TestCase ):
index = p.expect( interfaces )
if index == 0:
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
elif index == 1 or index == 2 or index == 3:
elif index in (1, 2, 3):
ifcount += 1
else:
break
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
@@ -126,16 +141,19 @@ class testWalkthrough( unittest.TestCase ):
p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
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')
# strip command from ps output and compute diffs
h1Output = h1Output.split( '\n' )[ 1: ]
s1Output = s1Output.split( '\n' )[ 1: ]
diffs = set( h1Output ).difference( set( s1Output ) )
# allow up to two diffs to account for daemons, etc.
self.assertTrue( len( diffs ) <= 2,
'h1 and s1 "ps" output differ too much: %s' % diffs )
p.sendline( 'exit' )
p.wait()
def testConnectivity( self ):
"Test ping and pingall"
p = pexpect.spawn( 'mn' )
p = pexpect.spawn( 'mn -w' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect( '1 packets transmitted, 1 received' )
@@ -148,9 +166,20 @@ class testWalkthrough( unittest.TestCase ):
def testSimpleHTTP( self ):
"Start an HTTP server on h1 and wget from h2"
p = pexpect.spawn( 'mn' )
if 'Python 2' in quietRun( 'python --version' ):
httpserver = 'SimpleHTTPServer'
else:
httpserver = 'http.server'
p = pexpect.spawn( 'mn -w', logfile=stdout )
p.expect( self.prompt )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
p.sendline( 'h1 python -m %s 80 >& /dev/null &' % httpserver )
p.expect( self.prompt )
# The walkthrough doesn't specify a delay here, and
# we also don't read the output (also a possible problem),
# but for now let's wait a number of seconds to make
# it less likely to fail due to the race condition.
p.sendline( 'px from mininet.util import waitListening;'
'waitListening(h1, port=80, timeout=30)' )
p.expect( self.prompt )
p.sendline( ' h2 wget -O - h1' )
p.expect( '200 OK' )
@@ -195,22 +224,24 @@ class testWalkthrough( unittest.TestCase ):
def testLinkChange( self ):
"Test TCLink bw and delay"
p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
p = pexpect.spawn( 'mn -w --link tc,bw=10,delay=10ms' )
p.expect( self.prompt )
p.sendline( 'h1 route && ping -c1 h2' )
# test bw
p.expect( self.prompt )
p.sendline( 'iperf' )
p.expect( r"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')
self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
p.expect( self.prompt )
# test delay
p.sendline( 'h1 ping -c 4 h2' )
p.expect( r'rtt min/avg/max/mdev = '
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay > 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' )
self.assertTrue( delay >= 40, 'Delay < 40ms' )
self.assertTrue( delay <= 50, 'Delay > 50ms' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@@ -247,8 +278,10 @@ class testWalkthrough( unittest.TestCase ):
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( r'\s00:00:00:00:00:0%d\s' % i )
p.expect( self.prompt )
p.sendline( 'exit' )
p.expect( pexpect.EOF )
def testSwitches( self ):
"Run iperf test using user and ovsk switches"
@@ -271,12 +304,14 @@ class testWalkthrough( unittest.TestCase ):
"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 ]
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
self.prompt ]
p.sendline( 's1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index == 1 or index == 3:
if index in (1, 3):
ifcount += 1
elif index == 0:
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
@@ -297,7 +332,7 @@ class testWalkthrough( unittest.TestCase ):
# PART 3
def testPythonInterpreter( self ):
"Test py and px by checking IP for h1 and adding h3"
p = pexpect.spawn( 'mn' )
p = pexpect.spawn( 'mn -w' )
p.expect( self.prompt )
# test host IP
p.sendline( 'py h1.IP()' )
@@ -319,7 +354,7 @@ class testWalkthrough( unittest.TestCase ):
def testLink( self ):
"Test link CLI command using ping"
p = pexpect.spawn( 'mn' )
p = pexpect.spawn( 'mn -w' )
p.expect( self.prompt )
p.sendline( 'link s1 h1 down' )
p.expect( self.prompt )
+9 -7
View File
@@ -13,6 +13,9 @@ setup for testing, and can even be emulated with the Mininet package.
from mininet.util import irange, natural, naturalSeq
# pylint: disable=too-many-arguments
class MultiGraph( object ):
"Utility class to track nodes and edges - replaces networkx.MultiGraph"
@@ -34,7 +37,7 @@ class MultiGraph( object ):
key: optional key
attr_dict: optional attribute dict
attrs: more attributes
warning: udpates attr_dict with attrs"""
warning: updates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node.setdefault( src, {} )
@@ -56,13 +59,13 @@ class MultiGraph( object ):
return self.node.items() if data else self.node.keys()
def edges_iter( self, data=False, keys=False ):
"Iterator: return graph edges"
for src, entry in self.edge.iteritems():
for dst, keys in entry.iteritems():
"Iterator: return graph edges, optionally with data and keys"
for src, entry in self.edge.items():
for dst, entrykeys in entry.items():
if src > dst:
# Skip duplicate edges
continue
for k, attrs in keys.iteritems():
for k, attrs in entrykeys.items():
if data:
if keys:
yield( src, dst, k, attrs )
@@ -156,8 +159,7 @@ class Topo( object ):
port1, port2 = self.addPort( node1, node2, port1, port2 )
opts = dict( opts )
opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
self.g.add_edge(node1, node2, key, opts )
return key
return self.g.add_edge(node1, node2, key, opts )
def nodes( self, sort=True ):
"Return nodes in graph"
+127 -47
View File
@@ -1,16 +1,79 @@
"Utility functions for Mininet."
from mininet.log import output, info, error, warn, debug
import codecs
import os
import re
import sys
from time import sleep
from fcntl import fcntl, F_GETFL, F_SETFL
from functools import partial
from os import O_NONBLOCK
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
from os import O_NONBLOCK
import os
from functools import partial
from sys import exit # pylint: disable=redefined-builtin
from time import sleep
from mininet.log import output, info, error, warn, debug
# pylint: disable=too-many-arguments
# Python 2/3 compatibility
Python3 = sys.version_info[0] == 3
BaseString = str if Python3 else getattr( str, '__base__' )
Encoding = 'utf-8' if Python3 else None
class NullCodec( object ):
"Null codec for Python 2"
@staticmethod
def decode( buf ):
"Null decode"
return buf
@staticmethod
def encode( buf ):
"Null encode"
return buf
if Python3:
def decode( buf ):
"Decode buffer for Python 3"
return buf.decode( Encoding )
def encode( buf ):
"Encode buffer for Python 3"
return buf.encode( Encoding )
getincrementaldecoder = codecs.getincrementaldecoder( Encoding )
else:
decode, encode = NullCodec.decode, NullCodec.encode
def getincrementaldecoder():
"Return null codec for Python 2"
return NullCodec
try:
# pylint: disable=import-error
oldpexpect = None
import pexpect as oldpexpect
# pylint: enable=import-error
class Pexpect( object ):
"Custom pexpect that is compatible with str"
@staticmethod
def spawn( *args, **kwargs):
"pexpect.spawn that is compatible with str"
if Python3 and 'encoding' not in kwargs:
kwargs.update( encoding='utf-8' )
return oldpexpect.spawn( *args, **kwargs )
def __getattr__( self, name ):
return getattr( oldpexpect, name )
pexpect = Pexpect()
except ImportError:
pass
# Command execution support
@@ -32,7 +95,7 @@ def oldQuietRun( *cmd ):
cmd: list of command params"""
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, str ):
if isinstance( cmd, BaseString ):
cmd = cmd.split( ' ' )
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
# We can't use Popen.communicate() because it uses
@@ -56,7 +119,7 @@ def oldQuietRun( *cmd ):
# This is a bit complicated, but it enables us to
# monitor command output as it is happening
# pylint: disable=too-many-branches
# pylint: disable=too-many-branches,too-many-statements
def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code
cmd: string or list of command and args
@@ -73,7 +136,7 @@ def errRun( *cmd, **kwargs ):
if len( cmd ) == 1:
cmd = cmd[ 0 ]
# Allow passing in a list or a string
if isinstance( cmd, str ) and not shell:
if isinstance( cmd, BaseString ) and not shell:
cmd = cmd.split( ' ' )
cmd = [ str( arg ) for arg in cmd ]
elif isinstance( cmd, list ) and shell:
@@ -85,18 +148,21 @@ def errRun( *cmd, **kwargs ):
out, err = '', ''
poller = poll()
poller.register( popen.stdout, POLLIN )
fdtofile = { popen.stdout.fileno(): popen.stdout }
fdToFile = { popen.stdout.fileno(): popen.stdout }
fdToDecoder = { popen.stdout.fileno(): getincrementaldecoder() }
outDone, errDone = False, True
if popen.stderr:
fdtofile[ popen.stderr.fileno() ] = popen.stderr
fdToFile[ popen.stderr.fileno() ] = popen.stderr
fdToDecoder[ popen.stderr.fileno() ] = getincrementaldecoder()
poller.register( popen.stderr, POLLIN )
errDone = False
while not outDone or not errDone:
readable = poller.poll()
for fd, event in readable:
f = fdtofile[ fd ]
if event & POLLIN:
data = f.read( 1024 )
f = fdToFile[ fd ]
decoder = fdToDecoder[ fd ]
if event & ( POLLIN | POLLHUP ):
data = decoder.decode( f.read( 1024 ) )
if echo:
output( data )
if f == popen.stdout:
@@ -107,7 +173,7 @@ def errRun( *cmd, **kwargs ):
err += data
if data == '':
errDone = True
else: # POLLHUP or something unexpected
else: # something unexpected
if f == popen.stdout:
outDone = True
elif f == popen.stderr:
@@ -115,6 +181,10 @@ def errRun( *cmd, **kwargs ):
poller.unregister( fd )
returncode = popen.wait()
# Python 3 complains if we don't explicitly close these
popen.stdout.close()
if stderr == PIPE:
popen.stderr.close()
debug( out, err, returncode )
return out, err, returncode
# pylint: enable=too-many-branches
@@ -131,19 +201,26 @@ def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
def which(cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
out, _, ret = errRun( ["which", cmd], stderr=STDOUT, **kwargs )
return out.rstrip() if ret == 0 else None
# pylint: enable=maybe-no-member
def isShellBuiltin( cmd ):
"Return True if cmd is a bash builtin."
if isShellBuiltin.builtIns is None:
isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
isShellBuiltin.builtIns = set(quietRun( 'bash -c enable' ).split())
space = cmd.find( ' ' )
if space > 0:
cmd = cmd[ :space]
return cmd in isShellBuiltin.builtIns
isShellBuiltin.builtIns = None
# Interface management
#
# Interfaces are managed as strings which are simply the
@@ -320,7 +397,7 @@ def ipParse( ip ):
"Parse an IP address and return an unsigned int."
args = [ int( arg ) for arg in ip.split( '.' ) ]
while len(args) < 4:
args.append( 0 )
args.insert( len(args) - 1, 0 )
return ipNum( *args )
def netParse( ipstr ):
@@ -330,7 +407,7 @@ def netParse( ipstr ):
if '/' in ipstr:
ip, pf = ipstr.split( '/' )
prefixLen = int( pf )
#if no prefix is specified, set the prefix to 24
# if no prefix is specified, set the prefix to 24
else:
ip = ipstr
prefixLen = 24
@@ -373,30 +450,34 @@ def pmonitor(popens, timeoutms=500, readline=True,
terminates: when all EOFs received"""
poller = poll()
fdToHost = {}
for host, popen in popens.iteritems():
fdToDecoder = {}
for host, popen in popens.items():
fd = popen.stdout.fileno()
fdToHost[ fd ] = host
fdToDecoder[ fd ] = getincrementaldecoder()
poller.register( fd, POLLIN )
if not readline:
# Use non-blocking reads
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
# pylint: disable=too-many-nested-blocks
while popens:
fds = poller.poll( timeoutms )
if fds:
for fd, event in fds:
host = fdToHost[ fd ]
decoder = fdToDecoder[ fd ]
popen = popens[ host ]
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
elif event & POLLHUP:
if event & ( POLLIN | POLLHUP ):
while True:
try:
f = popen.stdout
line = decoder.decode( f.readline() if readline
else f.read( readmax ) )
except IOError:
line = ''
if line == '':
break
yield host, line
if event & POLLHUP:
poller.unregister( fd )
del popens[ host ]
else:
@@ -405,19 +486,19 @@ def pmonitor(popens, timeoutms=500, readline=True,
# Other stuff we use
def sysctlTestAndSet( name, limit ):
"Helper function to set sysctl limits"
#convert non-directory names into directory names
# convert non-directory names into directory names
if '/' not in name:
name = '/proc/sys/' + name.replace( '.', '/' )
#read limit
# read limit
with open( name, 'r' ) as readFile:
oldLimit = readFile.readline()
if isinstance( limit, int ):
#compare integer limits before overriding
# compare integer limits before overriding
if int( oldLimit ) < limit:
with open( name, 'w' ) as writeFile:
writeFile.write( "%d" % limit )
else:
#overwrite non-integer limits
# overwrite non-integer limits
with open( name, 'w' ) as writeFile:
writeFile.write( limit )
@@ -434,21 +515,21 @@ def fixLimits():
try:
rlimitTestAndSet( RLIMIT_NPROC, 8192 )
rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
#Increase open file limit
# Increase open file limit
sysctlTestAndSet( 'fs.file-max', 10000 )
#Increase network buffer space
# 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
# 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
# Increase routing table size
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
#Increase number of PTYs for nodes
# Increase number of PTYs for nodes
sysctlTestAndSet( 'kernel.pty.max', 20000 )
# pylint: disable=broad-except
except Exception:
@@ -459,7 +540,7 @@ def fixLimits():
def mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'cat /proc/mounts' )
mounts = quietRun( 'grep cgroup /proc/mounts' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if ('cgroup %s' % cgdir not in mounts and
@@ -587,9 +668,8 @@ def ensureRoot():
Probably we should only sudo when needed as per Big Switch's patch.
"""
if os.getuid() != 0:
print "*** Mininet must run as root."
error( '*** Mininet must run as root.\n' )
exit( 1 )
return
def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
"""Wait until server is listening on port.
@@ -599,7 +679,7 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
if not runCmd( 'which telnet' ):
raise Exception('Could not find telnet' )
# pylint: disable=maybe-no-member
serverIP = server if isinstance( server, basestring ) else server.IP()
serverIP = server if isinstance( server, BaseString ) else server.IP()
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
time = 0
result = runCmd( cmd )
+1 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2
"""
Convert simple documentation to epydoc/pydoctor-compatible markup
@@ -83,7 +83,3 @@ if __name__ == '__main__':
infile.close()
os.close( outfid )
call( [ 'doxypy', outname ] )
+155 -70
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
#!/bin/bash
# Mininet install script for Ubuntu (and Debian Wheezy+)
# Brandon Heller (brandonh@stanford.edu)
# Mininet install script for Ubuntu and Debian
# Original author: Brandon Heller
# Fail on error
set -e
@@ -40,6 +40,7 @@ if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
install='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q install'
remove='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q remove'
pkginst='sudo dpkg -i'
update='sudo apt-get'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install lsb-release
@@ -51,11 +52,22 @@ if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
install='sudo yum -y install'
remove='sudo yum -y erase'
pkginst='sudo rpm -ivh'
update='sudo yum'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install redhat-lsb-core
fi
fi
test -e /etc/SuSE-release && DIST="SUSE Linux"
if [ "$DIST" = "SUSE Linux" ]; then
install='sudo zypper --non-interactive install '
remove='sudo zypper --non-interactive remove '
pkginst='sudo rpm -ivh'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install openSUSE-release
fi
fi
if which lsb_release &> /dev/null; then
DIST=`lsb_release -is`
RELEASE=`lsb_release -rs`
@@ -68,8 +80,12 @@ echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
KERNEL_NAME=`uname -r`
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora|RedHatEnterpriseServer'; then
echo "Install.sh currently only supports Ubuntu, Debian, RedHat and Fedora."
# Treat Raspbian as Debian
[ "$DIST" = 'Raspbian' ] && DIST='Debian'
DISTS='Ubuntu|Debian|Fedora|RedHatEnterpriseServer|SUSE LINUX'
if ! echo $DIST | egrep "$DISTS" >/dev/null; then
echo "Install.sh currently only supports $DISTS."
exit 1
fi
@@ -86,6 +102,26 @@ function version_ge {
[ "$1" == "$latest" ]
}
# Attempt to detect Python version
PYTHON=${PYTHON:-python}
PRINTVERSION='import sys; print(sys.version_info)'
PYTHON_VERSION=unknown
for python in $PYTHON python2 python3; do
if $python -c "$PRINTVERSION" |& grep 'major=2'; then
PYTHON=$python; PYTHON_VERSION=2; PYPKG=python
break
elif $python -c "$PRINTVERSION" |& grep 'major=3'; then
PYTHON=$python; PYTHON_VERSION=3; PYPKG=python3
break
fi
done
if [ "$PYTHON_VERSION" == unknown ]; then
echo "Can't find a working python command ('$PYTHON' doesn't work.)"
echo "You may wish to export PYTHON or install a working 'python'."
exit 1
fi
echo "Detected Python (${PYTHON}) version ${PYTHON_VERSION}"
# Kernel Deb pkg to be removed:
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
@@ -103,7 +139,7 @@ OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
function kernel {
echo "Install Mininet-compatible kernel if necessary"
sudo apt-get update
$update update
if ! $install linux-image-$KERNEL_NAME; then
echo "Could not install linux-image-$KERNEL_NAME"
echo "Skipping - assuming installed kernel is OK."
@@ -129,21 +165,46 @@ function mn_deps {
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8 python-pexpect
else
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8 python-pexpect
elif [ "$DIST" = "SUSE LINUX" ]; then
$install gcc make socat psmisc xterm openssh iperf \
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
ethtool help2man python-pyflakes python3-pylint \
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
else # Debian/Ubuntu
pf=pyflakes
# Starting around 20.04, installing pyflakes instead of pyflakes3
# causes Python 2 to be installed, which is exactly NOT what we want.
if [ `expr $RELEASE '>=' 20.04` = "1" ]; then
pf=pyflakes3
fi
$install gcc make socat psmisc xterm ssh iperf telnet \
ethtool help2man $pf pylint pep8 \
net-tools \
${PYPKG}-pexpect ${PYPKG}-tk
# Install pip
$install ${PYPKG}-pip || $install ${PYPKG}-pip-whl
if ! ${PYTHON} -m pip -V; then
if [ $PYTHON_VERSION == 2 ]; then
wget https://bootstrap.pypa.io/2.6/get-pip.py
else
wget https://bootstrap.pypa.io/get-pip.py
fi
sudo ${PYTHON} get-pip.py
rm get-pip.py
fi
$install iproute2 || $install iproute
$install cgroup-tools || $install cgroup-bin
fi
echo "Installing Mininet core"
pushd $MININET_DIR/mininet
sudo make install
sudo PYTHON=${PYTHON} make install
popd
}
# Install Mininet developer dependencies
function mn_dev {
echo "Installing Mininet developer dependencies"
# Install Mininet documentation dependencies
function mn_doc {
echo "Installing Mininet documentation dependencies"
$install doxygen doxypy texlive-fonts-recommended
if ! $install doxygen-latex; then
echo "doxygen-latex not needed"
@@ -160,6 +221,8 @@ function of {
$install autoconf automake libtool make gcc
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install git pkgconfig glibc-devel
elif [ "$DIST" = "SUSE LINUX" ]; then
$install git pkgconfig glibc-devel
else
$install git-core autotools-dev pkg-config libc6-dev
fi
@@ -182,10 +245,14 @@ function of {
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 \
$install git-core autoconf automake autotools-dev pkg-config \
make gcc g++ libtool libc6-dev cmake libpcap-dev \
unzip libpcre3-dev flex bison libboost-dev
if [ "$DIST" = "Ubuntu" ] && version_le $RELEASE 16.04; then
$install libxerces-c2-dev
else
$install libxerces-c-dev
fi
if [ ! -d "ofsoftswitch13" ]; then
git clone https://github.com/CPqD/ofsoftswitch13.git
if [[ -n "$OF13_SWITCH_REV" ]]; then
@@ -196,24 +263,17 @@ function of13 {
fi
# Install netbee
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
NBEESRC="nbeesrc-feb-24-2015"
NBEEDIR="netbee"
else
NBEESRC="nbeesrc-jan-10-2013"
NBEEDIR="nbeesrc-jan-10-2013"
if [ ! -d "netbee" ]; then
git clone https://github.com/netgroup-polito/netbee.git
fi
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
wget -nc ${NBEEURL}${NBEESRC}.zip
unzip ${NBEESRC}.zip
cd ${NBEEDIR}/src
cd netbee/src
cmake .
make
cd $BUILD_DIR/
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
cd $BUILD_DIR
sudo cp netbee/bin/libn*.so /usr/local/lib
sudo ldconfig
sudo cp -R ${NBEEDIR}/include/ /usr/
sudo cp -R netbee/include/ /usr/
# Resume the install:
cd $BUILD_DIR/ofsoftswitch13
@@ -230,6 +290,8 @@ function install_wireshark {
echo "Installing Wireshark"
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install wireshark wireshark-gnome
elif [ "$DIST" = "SUSE LINUX" ]; then
$install wireshark
else
$install wireshark tshark
fi
@@ -301,30 +363,36 @@ function ubuntuOvs {
fi
# Remove any old packages
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
openvswitch-pki openvswitch-switch
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-pki openvswitch-switch \
openvswitch-controller || true
# Get build deps
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
pkg-config bzip2 openssl python-all procps python-qt4 \
python-zopeinterface python-twisted-conch dkms
pkg-config bzip2 openssl ${PYPKG}-all procps ${PYPKG}-qt4 \
${PYPKG}-zopeinterface ${PYPKG}-twisted-conch dkms dh-python dh-autoreconf \
uuid-runtime
# Build OVS
parallel=`grep processor /proc/cpuinfo | wc -l`
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
DEB_BUILD_OPTIONS='parallel=$parallel nocheck' fakeroot debian/rules binary
cd ..
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
for pkg in common datapath-dkms pki switch; do
pkg=openvswitch-${pkg}_$OVS_RELEASE*.deb
echo "Installing $pkg"
$pkginst $pkg
done
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb 2>/dev/null; then
echo "Ignoring error installing openvswitch-controller"
fi
modinfo openvswitch
/sbin/modinfo openvswitch
sudo ovs-vsctl show
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service openvswitch-controller stop; then
if sudo service openvswitch-controller stop 2>/dev/null; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/openvswitch-controller ]; then
@@ -372,13 +440,19 @@ function ovs {
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service $OVSC stop; then
if sudo service $OVSC stop 2>/dev/null; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/$OVSC ]; then
sudo update-rc.d $OVSC disable
fi
fi
# This service seems to hang on 20.04
if systemctl list-units | \
grep status netplan-ovs-cleanup.service>&/dev/null; then
echo 'TimeoutSec=10' | sudo EDITOR='tee -a' \
sudo systemctl edit --full netplan-ovs-cleanup.service
fi
}
function remove_ovs {
@@ -408,12 +482,19 @@ function ivs {
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 gcc make
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install git pkgconfig libnl3-devel libcap-devel openssl-devel
else
$install git-core pkg-config libnl-3-dev libnl-route-3-dev \
libnl-genl-3-dev
fi
# Install IVS from source
cd $BUILD_DIR
git clone git://github.com/floodlight/ivs $IVS_SRC --recursive
git clone git://github.com/floodlight/ivs $IVS_SRC
cd $IVS_SRC
git submodule update --init
make
sudo make install
}
@@ -425,24 +506,19 @@ function ryu {
# install Ryu dependencies"
$install autoconf automake g++ libtool python make
if [ "$DIST" = "Ubuntu" -o "$DIST" = "Debian" ]; then
$install libxml2 libxslt-dev python-pip python-dev
sudo pip install --upgrade gevent pbr webob routes paramiko \\
oslo.config
$install gcc ${PYPKG}-pip ${PYPKG}-dev libffi-dev libssl-dev \
libxml2-dev libxslt1-dev zlib1g-dev
fi
# if needed, update python-six
SIX_VER=`pip show six | grep Version | awk '{print $2}'`
if version_ge 1.7.0 $SIX_VER; then
echo "Installing python-six version 1.7.0..."
sudo pip install -I six==1.7.0
fi
# fetch RYU
cd $BUILD_DIR/
git clone git://github.com/osrg/ryu.git ryu
cd ryu
# install ryu
sudo python ./setup.py install
sudo pip install -r tools/pip-requires -r tools/optional-requires \
-r tools/test-requires
sudo python setup.py install
# Add symbolic link to /usr/bin
sudo ln -s ./bin/ryu-manager /usr/local/bin/ryu-manager
@@ -453,17 +529,17 @@ function nox {
echo "Installing NOX w/tutorial files..."
# Install NOX deps:
$install autoconf automake g++ libtool python python-twisted \
$install autoconf automake g++ libtool python ${PYPKG}-twisted \
swig libssl-dev make
if [ "$DIST" = "Debian" ]; then
$install libboost1.35-dev
elif [ "$DIST" = "Ubuntu" ]; then
$install python-dev libboost-dev
$install ${PYPKG}-dev libboost-dev
$install libboost-filesystem-dev
$install libboost-test-dev
fi
# Install NOX optional deps:
$install libsqlite3-dev python-simplejson
$install libsqlite3-dev ${PYPKG}-simplejson
# Fetch NOX destiny
cd $BUILD_DIR/
@@ -501,12 +577,12 @@ function nox13 {
echo "Installing NOX w/tutorial files..."
# Install NOX deps:
$install autoconf automake g++ libtool python python-twisted \
$install autoconf automake g++ libtool python ${PYPKG}-twisted \
swig libssl-dev make
if [ "$DIST" = "Debian" ]; then
$install libboost1.35-dev
elif [ "$DIST" = "Ubuntu" ]; then
$install python-dev libboost-dev
$install ${PYPKG}-dev libboost-dev
$install libboost-filesystem-dev
$install libboost-test-dev
fi
@@ -542,7 +618,8 @@ function oftest {
echo "Installing oftest..."
# Install deps:
$install tcpdump python-scapy
$install tcpdump
$install ${PYPKG}-scapy || sudo $PYTHON -m pip install scapy
# Install oftest:
cd $BUILD_DIR/
@@ -555,6 +632,8 @@ function cbench {
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
elif [ "$DIST" = "SUSE LINUX" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
else
$install libsnmp-dev libpcap-dev libconfig-dev
fi
@@ -566,6 +645,7 @@ function cbench {
sh boot.sh || true # possible error in autoreconf, so run twice
sh boot.sh
./configure --with-openflow-src-dir=$BUILD_DIR/openflow
make liboflops_test.la
make
sudo make install || true # make install fails; force past this
}
@@ -597,9 +677,10 @@ net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
fi
# Since the above doesn't disable neighbor discovery, also do this:
# disable via boot line, and also restore eth0 naming for VM use
if ! grep 'ipv6.disable' /etc/default/grub; then
sudo sed -i -e \
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 /' \
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 net.ifnames=0 /' \
/etc/default/grub
sudo update-grub
fi
@@ -677,8 +758,8 @@ function all {
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
# Skip mn_doc (doxypy/texlive/fonts/etc.) because it's huge
# mn_doc
of
install_wireshark
ovs
@@ -709,8 +790,12 @@ function vm_clean {
# Remove SSH keys and regenerate on boot
echo 'Removing SSH keys from /etc/ssh/'
sudo rm -f /etc/ssh/*key*
if [ ! -e /etc/rc.local ]; then
echo '#!/bin/bash' | sudo tee /etc/rc.local
sudo chmod +x /etc/rc.local
fi
if ! grep mininet /etc/rc.local >& /dev/null; then
sudo sed -i -e "s/exit 0//" /etc/rc.local
sudo sed -i -e "s/exit 0//" /etc/rc.local || true
echo '
# mininet: regenerate ssh keys if we deleted them
if ! stat -t /etc/ssh/*key* >/dev/null 2>&1; then
@@ -718,15 +803,15 @@ if ! stat -t /etc/ssh/*key* >/dev/null 2>&1; then
fi
exit 0
' | sudo tee -a /etc/rc.local > /dev/null
sudo chmod +x /etc/rc.local
fi
# Remove Mininet files
#sudo rm -f /lib/modules/python2.5/site-packages/mininet*
#sudo rm -f /usr/bin/mnexec
# Clear optional dev script for SSH keychain load on boot
rm -f ~/.bash_profile
# Remove leftover install script if any
rm -f install-mininet-vm.sh
# Clear git changes
git config --global user.name "None"
git config --global user.email "None"
@@ -753,7 +838,7 @@ function usage {
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 -- ' -e: install Mininet d(E)veloper dependencies\n' >&2
printf -- ' -e: install Mininet documentation/LaT(e)X 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
@@ -787,7 +872,7 @@ else
b) cbench;;
c) kernel_clean;;
d) vm_clean;;
e) mn_dev;;
e) mn_doc;;
f) case $OF_VERSION in
1.0) of;;
1.3) of13;;
+17 -16
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python
#!/usr/bin/python2
"""
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Python style
usage: unpep8 < old.py > new.py
@@ -40,6 +40,7 @@ Bob Lantz, rlantz@cs.stanford.edu
1/24/2010
"""
from __future__ import print_function
import re, sys
def fixUnderscoreTriplet( match ):
@@ -50,7 +51,7 @@ def fixUnderscoreTriplet( match ):
def reinstateCapWords( text ):
underscoreTriplet = re.compile( r'[A-Za-z0-9]_[A-Za-z0-9]' )
return underscoreTriplet.sub( fixUnderscoreTriplet, text )
def replaceTripleApostrophes( text ):
"Replace triple apostrophes with triple quotes."
return text.replace( "'''", '"""')
@@ -59,7 +60,7 @@ def simplifyTripleQuotes( text ):
"Fix single-line doc strings."
r = re.compile( r'"""([^\"\n]+)"""' )
return r.sub( r'"\1"', text )
def insertExtraSpaces( text ):
"Insert extra spaces inside of parentheses and brackets/curly braces."
lparen = re.compile( r'\((?![\s\)])' )
@@ -75,9 +76,9 @@ def insertExtraSpaces( text ):
lcurly = re.compile( r'\{(?![\s\}])' )
text = lcurly.sub( r'{ ', text )
rcurly = re.compile( r'([^\s\{])(?=\})' )
text = rcurly.sub( r'\1 ', text)
text = rcurly.sub( r'\1 ', text)
return text
def fixDoxygen( text ):
"""Translate @param foo to foo:, @return bar to returns: bar, and
@author me to author: me"""
@@ -95,11 +96,11 @@ def removeCommentFirstBlankLine( text ):
"Remove annoying blank lines after first line in comments."
line = re.compile( r'("""[^\n]*\n)\s*\n', re.MULTILINE )
return line.sub( r'\1', text )
def fixArgs( match, kwarg = re.compile( r'(\w+) = ' ) ):
"Replace foo = bar with foo=bar."
return kwarg.sub( r'\1=', match.group() )
def fixKeywords( text ):
"Change keyword argumentsfrom foo = bar to foo=bar."
args = re.compile( r'\(([^\)]+)\)', re.MULTILINE )
@@ -114,7 +115,7 @@ def fixKeywords( text ):
def lineIter( text ):
"Simple iterator over lines in text."
for line in text.splitlines(): yield line
def stringIter( strList ):
"Yield strings in strList."
for s in strList: yield s
@@ -133,7 +134,7 @@ def restoreRegex( regex, old, new ):
# This is a cheap hack, and it may not work 100%, since
# it doesn't handle multiline strings.
# However, it should be mostly harmless...
def restoreStrings( oldText, newText ):
"Restore strings from oldText into newText, returning result."
oldLines, newLines = lineIter( oldText ), lineIter( newText )
@@ -148,13 +149,13 @@ def restoreStrings( oldText, newText ):
newLine = restoreRegex( tickStrings, oldLine, newLine )
result += newLine + '\n'
return result
# This might be slightly controversial, since it uses
# three spaces to line up multiline comments. However,
# I much prefer it. Limitations: if you have deeper
# indents in comments, they will be eliminated. ;-(
def fixComment( match,
def fixComment( match,
indentExp=re.compile( r'\n([ ]*)(?=[^/s])', re.MULTILINE ),
trailingQuotes=re.compile( r'\s+"""' ) ):
"Re-indent comment, and join trailing quotes."
@@ -165,17 +166,17 @@ def fixComment( match,
if len( originalIndent ) is not 0: indent += ' '
comment = indentExp.sub( indent, comment )
return originalIndent + trailingQuotes.sub( '"""', comment )
def fixCommentIndents( text ):
"Fix multiline comment indentation."
comments = re.compile( r'^([ ]*)("""[^"]*""")$', re.MULTILINE )
return comments.sub( fixComment, text )
def removeBogusLinefeeds( text ):
"Remove extra linefeeds at the end of single-line comments."
bogusLfs = re.compile( r'"([^"\n]*)\n"', re.MULTILINE )
return bogusLfs.sub( '"\1"', text)
def convertFromPep8( program ):
oldProgram = program
# Program text transforms
@@ -195,4 +196,4 @@ def convertFromPep8( program ):
return program
if __name__ == '__main__':
print convertFromPep8( sys.stdin.read() )
print( convertFromPep8( sys.stdin.read() ) )
+11 -6
View File
@@ -1,14 +1,19 @@
#!/usr/bin/python
#!/usr/bin/env python
from subprocess import check_output as co
from sys import exit
from sys import exit, version_info
def run(*args, **kwargs):
"Run co and decode for python3"
result = co(*args, **kwargs)
return result.decode() if version_info[ 0 ] >= 3 else result
# Actually run bin/mn rather than importing via python path
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
version = 'Mininet ' + run( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = version.strip()
# Find all Mininet path references
lines = co( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
lines = run( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
error = False
@@ -16,8 +21,8 @@ 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 )
print( "%s: incorrect version '%s' (should be '%s')" % (
fname, fversion, version ) )
error = True
if error:
+126 -60
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2
"""
build.py: build a Mininet VM
@@ -31,6 +31,7 @@ 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 sys
import re
from glob import glob
from subprocess import check_output, call, Popen
@@ -39,11 +40,13 @@ from time import time, strftime, localtime
import argparse
from distutils.spawn import find_executable
import inspect
from traceback import print_exc
pexpect = None # For code check - imported dynamically
# boot can be slooooow!!!! need to debug/optimize somehow
TIMEOUT=600
TIMEOUT = 600
# Some configuration options
# Possibly change this to use the parsed arguments instead!
@@ -60,25 +63,40 @@ VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
Prompt = '\$ ' # Shell prompt that pexpect will wait for
# URLs for Ubunto .iso images
def serverURL( version, arch ):
"Return .iso URL for Ubuntu version and arch"
server = 'http://cdimage.ubuntu.com/ubuntu/releases/%s/release/'
iso = 'ubuntu-%s-server-%s.iso'
return (server + iso ) % ( version, version, arch )
def legacyURL( version, arch ):
"Return .iso URL for Ubuntu version"
server = ( 'http://cdimage.ubuntu.com/ubuntu-legacy-server/'
'releases/%s/release/' )
iso = 'ubuntu-%s-legacy-server-%s.iso'
return (server + iso ) % ( version, version, arch )
isoURLs = {
'precise32server':
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
'ubuntu-12.04.5-server-i386.iso',
'precise64server':
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
'ubuntu-12.04.5-server-amd64.iso',
'trusty32server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04.3-server-i386.iso',
'ubuntu-14.04.4-server-i386.iso',
'trusty64server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04.3-server-amd64.iso',
'wily32server':
'http://mirrors.kernel.org/ubuntu-releases/15.10/'
'ubuntu-15.10-server-i386.iso',
'wily64server':
'http://mirrors.kernel.org/ubuntu-releases/15.10/'
'ubuntu-15.10-server-amd64.iso',
'ubuntu-14.04.4-server-amd64.iso',
'xenial32server':
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
'ubuntu-16.04.6-server-i386.iso',
'xenial64server':
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
'ubuntu-16.04.7-server-amd64.iso',
'bionic64server': serverURL( '18.04.5', 'amd64' ),
'focal64server': legacyURL( '20.04.1', 'amd64' ),
}
@@ -112,11 +130,11 @@ def log( *args, **kwargs ):
msg = ' '.join( str( arg ) for arg in args )
output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg )
if cr:
print output
print( output )
else:
print output,
print( output, )
# Optionally mirror to LogFile
if type( LogFile ) is file:
if LogFile:
if cr:
output += '\n'
LogFile.write( output )
@@ -125,7 +143,7 @@ def log( *args, **kwargs ):
def run( cmd, **kwargs ):
"Convenient interface to check_output"
log( '-', cmd )
log( '+', cmd )
cmd = cmd.split()
arg0 = cmd[ 0 ]
if not find_executable( arg0 ):
@@ -147,8 +165,8 @@ def depend():
log( '* Installing package dependencies' )
run( 'sudo apt-get -qy update' )
run( 'sudo apt-get -qy install'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs dnsmasq curl'
' kvmtool cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs curl'
' python-setuptools mtools zip' )
run( 'sudo easy_install pexpect' )
@@ -173,9 +191,9 @@ def findiso( flavor ):
url = isoURLs[ flavor ]
name = path.basename( url )
iso = path.join( VMImageDir, name )
if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0777 != 0444 ):
if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0o777 != 0o444 ):
log( '* Retrieving', url )
run( 'curl -C - -o %s %s' % ( iso, url ) )
run( 'curl -L -C - -o %s %s' % ( iso, url ) )
# Make sure the file header/type is something reasonable like
# 'ISO' or 'x86 boot sector', and not random html or text
result = run( 'file ' + iso )
@@ -184,7 +202,7 @@ def findiso( flavor ):
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 )
os.chmod( iso, 0o444 )
log( '* Using iso', iso )
return iso
@@ -195,14 +213,15 @@ def attachNBD( cow, flags='' ):
# 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
for i in range ( 1, 63 ):
entry = 'nbd%d' % i
nbd = '/dev/' + entry
# Check whether someone's already messing with that device
if call( [ 'pgrep', '-f', nbd ] ) == 0:
if call( [ 'pgrep', '-f', entry ] ) == 0:
continue
srun( 'modprobe nbd max-part=64' )
srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
print
print()
return nbd
raise Exception( "Error: could not find unused /dev/nbdX device" )
@@ -216,14 +235,26 @@ 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
try:
print( srun( 'partx ' + nbd ) )
except:
log( 'Warning - partx failed with error' )
# Guess that kernel is in partition 1/boot/vmlinuz*generic
# ...but look for other Linux partitions just in case
part = nbd + 'p1'
partitions = srun( 'fdisk -l ' + nbd )
for line in partitions.split( '\n' ):
line = line.strip()
if line.endswith( 'Linux' ):
part = line.split()[ 0 ]
break
partnum = int( part.split( 'p' )[ -1 ] )
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0o777 ) == 0o444:
# If kernel is there, then initrd should also be there
detachNBD( nbd )
return kernel, initrd, partnum
mnt = mkdtemp()
srun( 'mount -o ro,noload %s %s' % ( part, mnt ) )
kernsrc = glob( '%s/boot/vmlinuz*generic' % mnt )[ 0 ]
@@ -235,7 +266,7 @@ def extractKernel( image, flavor, imageDir=VMImageDir ):
srun( 'umount ' + mnt )
run( 'rmdir ' + mnt )
detachNBD( nbd )
return kernel, initrd
return kernel, initrd, partnum
def findBaseImage( flavor, size='8G' ):
@@ -243,10 +274,11 @@ def findBaseImage( flavor, size='8G' ):
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?' )
perms = stat( image )[ ST_MODE ] & 0o777
if perms != 0o444:
raise Exception( 'Error - base image %s is writable.' % image +
' Are multiple builds running? if not,'
' remove %s and try again.' % image )
else:
# We create VMImageDir here since we are called first
run( 'mkdir -p %s' % VMImageDir )
@@ -256,10 +288,11 @@ def findBaseImage( flavor, size='8G' ):
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
os.chmod( image, 0o444 )
kernel, initrd, partnum = extractKernel( image, flavor )
log( '* Using base image', image, 'and kernel', kernel,
'and partition #', partnum )
return image, kernel, initrd, partnum
# Kickstart and Preseed files for Ubuntu/Debian installer
@@ -372,12 +405,28 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
# 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' )
for kdir in 'install', 'casper':
kernel = path.join( mnt, kdir, 'vmlinuz' )
if not path.exists( kernel ):
kernel = ''
for initrd in 'initrd.gz', 'initrd':
initrd = path.join( mnt, kdir, initrd )
if path.exists( initrd ):
break
else:
initrd = ''
if kernel and initrd:
break
if not kernel or not initrd:
raise Exception( 'unable to locate kernel and initrd in iso image' )
if NoKVM:
accel = 'tcg'
else:
accel = 'kvm'
try:
run( 'kvm-ok' )
except:
raise Exception( 'kvm-ok failed; try using --nokvm' )
cmd = [ 'sudo', kvm,
'-machine', 'accel=%s' % accel,
'-nographic',
@@ -393,6 +442,7 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
'-append',
' ks=floppy:/' + kickstart +
' preseed/file=floppy://' + preseed +
' net.ifnames=0' +
' console=ttyS0' ]
ubuntuStart = time()
log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image )
@@ -409,6 +459,8 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
logfile.close()
elapsed = time() - ubuntuStart
# Unmount iso and clean up
### DEBUGGING
srun( 'ls -l ' + mnt )
srun( 'umount ' + mnt )
run( 'rmdir ' + mnt )
if vm.returncode != 0:
@@ -418,7 +470,7 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1 ):
def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1, partnum=1 ):
"""Boot qemu/kvm with a COW disk and local/user data store
cow: COW disk path
kernel: kernel path
@@ -450,7 +502,9 @@ def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1 ):
'-kernel', kernel,
'-initrd', initrd,
'-drive file=%s,if=virtio' % cow,
'-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
'-append "root=/dev/vda%d init=/sbin/init'
' net.ifnames=0 console=ttyS0" ' % partnum ]
log( cmd )
if Forward:
cmd += sum( [ [ '-redir', f ] for f in Forward ], [] )
if cpuCores > 1:
@@ -576,13 +630,16 @@ def checkOutBranch( vm, branch, prompt=Prompt ):
# The branch will be rebased to its parent on origin.
# This probably doesn't matter since we're running on a COW disk
# anyway.
vm.sendline( 'cd ~/mininet; git fetch --all; git checkout '
+ branch + '; git pull --rebase origin ' + branch )
vm.sendline( 'cd ~/mininet; git fetch origin ' + branch +
'; git checkout ' + branch +
'; git pull --rebase origin ' + branch )
vm.expect( prompt )
vm.sendline( 'sudo -n make install' )
# Use install.sh since we may need to identify python version?
vm.sendline( 'util/install.sh -n' )
def interact( vm, tests, pre='', post='', prompt=Prompt ):
def interact( vm, tests, pre='', post='', prompt=Prompt,
clean=True):
"Interact with vm, which is a pexpect object"
login( vm )
log( '* Waiting for login...' )
@@ -605,8 +662,11 @@ def interact( vm, tests, pre='', post='', prompt=Prompt ):
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 )
# Long timeout since we may be on cloud CI
# 30min for kvm, 1.5hr for emulation
# TODO: detect installation errors
timeout = 5200 if NoKVM else 1800
vm.expect( 'Done preparing Mininet', timeout=timeout )
log( '* Completed successfully' )
vm.expect( prompt )
version = getMininetVersion( vm )
@@ -621,6 +681,10 @@ def interact( vm, tests, pre='', post='', prompt=Prompt ):
vm.sendline( "sudo sed -i -e 's/^GRUB_TERMINAL=serial/#GRUB_TERMINAL=serial/' "
"/etc/default/grub; sudo update-grub" )
vm.expect( prompt )
if clean:
log( '* Cleaning vm' )
vm.sendline( '~/mininet/util/install.sh -d' )
vm.expect( prompt )
log( '* Shutting down' )
vm.sendline( 'sync; sudo shutdown -h now' )
log( '* Waiting for EOF/shutdown' )
@@ -785,7 +849,7 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
ovfdate = strftime( '%y%m%d', lstart )
dir = 'mn-%s-%s' % ( flavor, date )
if Branch:
dirname = 'mn-%s-%s-%s' % ( Branch, flavor, date )
dir = 'mn-%s-%s-%s' % ( Branch, flavor, date )
try:
os.mkdir( dir)
except:
@@ -796,7 +860,7 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
LogFile = open( 'build.log', 'w' )
log( '* Logging to', abspath( LogFile.name ) )
log( '* Created working directory', dir )
image, kernel, initrd = findBaseImage( flavor )
image, kernel, initrd, partnum = findBaseImage( flavor )
basename = 'mininet-' + flavor
volume = basename + '.qcow2'
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, volume ) )
@@ -806,7 +870,7 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
else:
logfile = open( flavor + '.log', 'w+' )
log( '* Logging results to', abspath( logfile.name ) )
vm = boot( volume, kernel, initrd, logfile, memory=memory )
vm = boot( volume, kernel, initrd, logfile, memory=memory, partnum=partnum )
version = interact( vm, tests=tests, pre=pre, post=post )
size = qcow2size( volume )
arch = archFor( flavor )
@@ -865,7 +929,7 @@ def runTests( vm, tests=None, pre='', post='', prompt=Prompt, uninstallNtpd=Fals
def getMininetVersion( vm ):
"Run mn to find Mininet version in VM"
vm.sendline( '~/mininet/bin/mn --version' )
vm.sendline( '(cd ~/mininet; PYTHONPATH=. bin/mn --version)' )
# Eat command line echo, then read output line
vm.readline()
version = vm.readline().strip()
@@ -890,7 +954,8 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
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 )
kernel, initrd, partnum = extractKernel(
image, flavor=basename, imageDir=tmpdir )
if LogToConsole:
logfile = stdout
else:
@@ -898,7 +963,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
suffix='.testlog', delete=False )
log( '* Logging VM output to', logfile.name )
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
memory=memory, cpuCores=cpuCores )
memory=memory, cpuCores=cpuCores, partnum=partnum )
login( vm )
log( '* Waiting for prompt after login' )
vm.expect( prompt )
@@ -937,7 +1002,7 @@ def testDict():
def testString():
"Return string listing valid tests"
tests = [ '%s <%s>' % ( name, func.__doc__ )
for name, func in testDict().iteritems() ]
for name, func in testDict().items() ]
return 'valid tests: %s' % ', '.join( tests )
@@ -988,7 +1053,7 @@ def parseArgs():
if args.depend:
depend()
if args.list:
print buildFlavorString()
print( buildFlavorString() )
if args.clean:
cleanup()
if args.verbose:
@@ -1009,14 +1074,15 @@ def parseArgs():
Chown = args.chown
for flavor in args.flavor:
if flavor not in isoURLs:
print "Unknown build flavor:", flavor
print buildFlavorString()
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 )
print_exc( e )
exit( 1 )
for image in args.image:
bootAndRun( image, runFunction=runTests, tests=args.test, pre=args.run,
+13 -15
View File
@@ -15,13 +15,7 @@ sudo sed -i -e 's/splash//' /etc/default/grub
sudo sed -i -e 's/quiet/text/' /etc/default/grub
sudo update-grub
# Update from official archive
sudo apt-get update
# 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 -qq update
# Clean up vmware easy install junk if present
if [ -e /etc/issue.backup ]; then
sudo mv /etc/issue.backup /etc/issue
@@ -30,18 +24,22 @@ if [ -e /etc/rc.local.backup ]; then
sudo mv /etc/rc.local.backup /etc/rc.local
fi
# Fetch Mininet
sudo apt-get -y install git-core openssh-server
sudo apt-get -y -qq install git-core openssh-server
git clone git://github.com/mininet/mininet
# Optionally check out branch
if [ "$1" != "" ]; then
pushd mininet
#git checkout -b $1 $1
# TODO branch will in detached HEAD state if it is not master
git checkout $1
popd
pushd mininet
git fetch origin $1
git checkout $1
popd
fi
# Install Mininet
time mininet/util/install.sh
# Install Mininet for Python2 and Python3
APT="sudo apt-get -y -qq"
$APT install python3
$APT install python2 || $APT install python
python --version || $APT install python-is-python3
time PYTHON=python2 mininet/util/install.sh -n
time PYTHON=python3 mininet/util/install.sh
# Finalize VM
time mininet/util/install.sh -tcd
# Ignoring this since NOX classic is deprecated