Compare commits

...

202 Commits

Author SHA1 Message Date
Renato Monteiro 6eb8973c0b Fix typo in test_switchdpidassignment.py (#1195)
* Fix typo in test_switchdpidassignment.py

* Fix another typo in test_switchdpidassignment.py
2023-09-16 18:50:27 -07:00
Renato Monteiro 0848c5f3c1 Fix typo in test_hifi.py (#1194) 2023-09-16 18:48:51 -07:00
Renato Monteiro 41ac7c4a6e Remove unused import setup.py (#1203) 2023-09-16 18:48:21 -07:00
Renato Monteiro 5ae59fdbc9 Add missing option to usage string install.sh (#1207)
Also reordered the x and y options to be in alphabetical order.
2023-09-16 16:12:33 -07:00
Renato Monteiro ac7c68089b Fix typo in node.py (#1199)
* Fix typo in node.py

* Fix another typo in node.py
2023-09-16 16:11:46 -07:00
Renato Monteiro ba569f4d82 Fix typo in net.py (#1198) 2023-09-16 16:11:21 -07:00
Renato Monteiro 615f37dbbe Fix typo in build.py (#1204)
* Fix typo in build.py

* Fix another typo in build.py
2023-09-16 16:10:37 -07:00
Renato Monteiro aaa8886328 Fix typo in clustersetup.sh (#1205) 2023-09-16 16:10:11 -07:00
Renato Monteiro 05ee42ee3c Fix typo in install.sh (#1206)
* Fix typo in install.sh

* Fix another typo in install.sh
2023-09-16 16:09:38 -07:00
Renato Monteiro 7685e41a3e Fix typo in INSTALL (#1202) 2023-09-16 16:08:58 -07:00
lantz 39103f4c9d Fix typo in install.sh/mn_deps for Fedora/RHEL
thanks to Andy Fingerhut

closes #1188
2023-07-06 06:49:40 -07:00
lantz 5b1b376336 fix typo in miniedit.py (#1187)
globals.get(...) -> globals().get(...)

fixes #1186
2023-05-28 15:27:56 -07:00
lantz 8725056d7b Debugging for failing test_simpleperf (#1185) 2023-05-02 19:12:33 -07:00
lantz 88f14e946a 2.3.1b4 (#1184)
Including changes to mnexec -c to reduce the number of
close() system calls, which may help with performance in
containers on Ubuntu 22.04.

Note that systemd-udevd also seems to be a cause of performance issues
and should probably be disabled while running Mininet.

Adding dbeug logging to test_simpleperf because it is failing
intermittently. (Note with packet loss it can fail, but this
is not how we should be testing it.)
2023-05-02 18:29:26 -07:00
lantz 0cefa0b8de Add fallback to old mnexec -c method (#1183)
If opendir("/proc/self/fd") fails, fall back to the old
method of closing all file decriptors from 3-1024 (getdtablesize()).
2023-05-02 16:17:49 -07:00
lantz 1b4c7d706d Use /proc/self/fd for open fd list (#1182)
mnexec -c attempts to close all open files except for
standard in/out/error.

Previously we just closed every fd>2, but this seems to take
a long time on docker. So now we read /proc/self/fd to see which
file descriptors are actually in use.
2023-05-02 15:27:27 -07:00
lantz 5e909c6e4f 2.3.1b3 (#1181) 2023-05-02 13:05:56 -07:00
lantz 740acfb350 Unlimit cfs hosts for startup/shutdown config (#1178)
We defer cfs cgroup bandwidth limiting until
CPULimitedHost.configDefault(), and we change
Mininet.stop() to call a new host.unlimit()
method if available.
2023-04-28 18:48:35 -07:00
lantz a978ce9484 Disable udevd to speed up tests; reinstate slow tests (#1177)
systemd-udevd appears to drastically increase
Mininet startup time.

With systemd-udevd disabled,

mn --topo single,100 --test none

takes 8s rather than 40s (31s with NM_UNMANAGED) on
my test VM.

We need a way of disabling it globally and always for
Mininet interfaces, but this is a start for github
actions at least.

For now, we execute this command before running the CI tests:

systemctl stop systemd-udevd systemd-udevd-kernel.socket \
             systemd-udevd-control.socket

We also restore the "slow" examples tests, including
test_tree1024.
2023-04-28 14:44:52 -07:00
lantz 1e546cb73b Fix list of processes to kill in cleanup() (#1173)
The list is specified as a string, but when it was
split across lines spaces were not properly added.

Probably it should actually be a list to make it more
robust to adding additional process names.

For now we are adding spaces to all of the lines, so
hopefully anyone who updates it will follow the pattern.

This should fix the problem of ovs-testcontroller processes
not being killed.

Thanks to Rwitick Ghosh.

Closes #1164
2023-04-25 17:48:01 -07:00
lantz a2cb3e4051 2.3.1b2 (#1172)
Support for Ubuntu 22.04 and 20.04.

- Update Mininet version
- Install pexpect with pip to support python 3 and 2
- Update code-check workflow
  - refactor to enable testing python 2 on ubuntu 22.04
  - update to actions/checkout@v3 (@v2 is deprecated)

errata:

mountCgroups has not actually been renamed, but has been
changed to use cgroupfs-mount and to return the cgroups version.
2023-04-24 19:20:25 -07:00
lantz 4cfe97ee49 Add ubuntu 22.04 to CI workflow (#1169)
- run-tests: test ubuntu 22.04 with python 3.x
- install.sh: add cgroupfs-mount to dependencies
2023-04-22 20:08:37 -07:00
lantz b762d0bf96 Add cgroup2 support (#1166)
util.py: replace mountCgroups with checkCgroups, which
         returns the cgroup version (cgroup, cgroup2)

node.py: handle cpu.max='quota period' for cgroup2
         vs. cpu.{cfs_quota_us,cfs_period_us} for cgroup

net.py: make _iperfVals more robust in the case of a missing
        UDP ACK

examples/cpu.py: user _iperfVals to handle new iperf behavior
                 of no output from telnet
2023-04-22 15:31:40 -07:00
lantz 4840bc20c5 remove unused _iperfVals parameter (#1170) 2023-04-22 09:22:08 -07:00
lantz 30a5fd0df4 Use iperf CSV output in Mininet.iperf() (#1165)
+ identify client and server (different on Ubuntu 20 vs. 22)
+ add util.{unitScale,fmtBps} to help with output format

- internal function _parseIperfOutput has been removed
2023-04-21 20:07:50 -07:00
lantz 4707e90028 Fix comparison error in testWalkthrough.testHostCommands (#1168) 2023-04-21 18:07:53 -07:00
lantz cf5675876c update CI workflows: update/fix codecheck; remove Ubuntu 18.04 (#1167)
1. update/fix codecheck

Run workflow with pylint 2.15.7

Add a  bunch of fiddly and mostly cosmetic changes to
make pylint (and make codecheck) happy.

Also try to run pyflakes3 vs. pyflakes

2. remove Ubuntu 18.04

Ubuntu 18.04 has been removed from github actions, so
we remove it from our workflow as well
2023-04-21 17:37:09 -07:00
lantz 1169982c0a Update node.py 2022-11-22 16:43:54 -08:00
lantz 32a7126d2b Update README.md 2022-11-22 16:43:05 -08:00
lantz 678add6202 Update README.md
closes #1143
2022-11-22 16:41:27 -08:00
lantz 936d303f5d Update INSTALL
closes #1144
2022-11-22 16:36:30 -08:00
lantz c5413a74f5 Update miniedit.py
closes #1147
2022-11-22 16:33:52 -08:00
lantz 3f5503d773 Update LICENSE to standard BSD 3-clause license text (#1141)
Approved by the copyright holders.

The historical "OpenFlow" license was adopted for harmony
with the OpenFlow project and associated software. Mininet
has always been BSD-licensed, but we're adopting the standard
license text that is recommended/approved by OSI and
automatically recognized by github.
2022-07-10 19:45:38 -07:00
lantz aa0176fce6 2.3.1b1 (#1124) 2022-03-31 17:58:42 -07:00
Max Hausch bdd2f8d87f Transforms the ISSUE_TEMPLATE disclaimer into a comment (#1108) 2022-03-31 17:36:50 -07:00
AndreasAc 1a47699f21 Add support for Debian Bullseye to the setup script (#1118) 2022-03-31 15:13:57 -07:00
Max Hausch 270a6ba333 When eval-ing python expressions, return falsy but not-None values (#1111) 2022-02-24 11:27:34 -08:00
AndreasAc c3ba039a97 Remove unencrypted git protocol use (#1100)
Because Github removes support for unencrypted git protocol in March 2022, URLs in scripts are changed to use https.

See https://github.blog/2021-09-01-improving-git-protocol-security-github/ for details
2022-02-23 17:17:02 -08:00
Ramon Fontes 8a50d3867c get-pip (#1066)
The URL of get-pip.py has changed to https://bootstrap.pypa.io/pip/2.6/get-pip.py

Co-authored-by: lantz <rlantz@cs.stanford.edu>
2021-11-08 16:30:30 -08:00
Santiago d1b0b32c8c Call ping with LANG=C to avoid erros parsing localised ping output (#1090)
Fixes: #1063

Co-authored-by: Santiago Ruano Rincón <santiago.ruano-rincon@imt-atlantique.fr>
2021-10-14 17:58:36 -07:00
lantz 71d3e58d8c Remove ubuntu-16.04 from run-tests.yaml (#1091)
GitHub seems to have pulled the plug on 16.04 VMs for
Actions, and this is breaking our integration.

Ideally we should come up with another test method
for 16.04 (notably the 16.04 kernel) as well as
other distros that Actions doesn't support
directly such as Debian.
2021-10-14 17:09:34 -07:00
lantz 9edbb3b4a3 Iterate over tuple() when deleting items (#1062)
Fixes #1061
2021-10-07 15:49:03 -07:00
lantz 57294d013e Add symlink from bin/mn to mininet/__main__.py (#1056)
Should allow `python -m mininet` to work.

Closes #1047
2021-03-28 15:54:33 -07:00
toblu302 bfb6b9f4c4 Fix python3 compability issues in miniedit (#1059) 2021-03-28 14:33:39 -07:00
lantz 1b55f09c4e Fix mnexec -v (#1053)
We were discarding the version number sent to stderr

fixes #1052
2021-03-13 11:08:02 -08:00
lantz d7f399d7a1 2.3.0 (#1043)
- 2.3.0rc2 -> 2.3.0
- update copyright date in LICENSE
2021-02-10 12:30:36 -08:00
lantz 1c42632978 2.3.0rc2 (#1042) 2021-02-09 11:44:42 -08:00
lantz 5ef6e1dedc fix --twait (#1040)
- pass timeout correctly to waitConnected()
- handle surprising behavior that 1 == True in Python
- mn --test none does not call waitConnected()
2021-02-09 11:26:39 -08:00
lantz f8e54cad47 add pip uninstall -y (#1041)
This avoids hanging in scripts if pip uninstall decides
to ask for confirmation
2021-02-09 10:55:45 -08:00
lantz fc3152d724 Update INSTALL 2021-02-09 00:47:32 -08:00
lantz 9a3a3edf75 2.3.0rc1 (#1037) 2021-02-09 00:44:22 -08:00
lantz 377a4b5af5 Add net-tools dependency for fedora (#1039) 2021-02-09 00:17:34 -08:00
lantz 4a1dbac09b Basic fixes for install.sh -fnv for Fedora 33 (#1038)
Closes #1003
2021-02-08 23:33:40 -08:00
lantz 92fe3cc120 Make mn -w wait indefinitely and add --twait option (#1036)
Background: In Mininet 2.2, waitConnected() waits forever
by default. We are going to preserve this behavior for 2.3.

Therefore, the --wait/-w option will wait forever. This is used
in the tests to make sure that all switches have connected to
their controllers.

A new --twait/-t <int> option has been added for timed waits.

The API for Mininet(....waitConnected=False/True) is preserved,
but you can now pass in an integer wait time. False means
do not wait at all. True means wait forever. I have elected
for now to preserve None also meaning wait forever as it was
in 2.2, but note that you should probably use the boolean
True/False instead.
2021-02-08 23:22:36 -08:00
lantz 39ed456de2 Fixes for cluster.py (#1035)
- use decode() for python 3 compatibility
- try to identify non-loopback interface for controllers
-- avoid hardwired 'eth0'
- use remoteServer consistently in tests
- pass tests on ubuntu 20/python 3
- pass hybrid test with hybrid python 2 and python 3 mininet
2021-02-08 22:28:12 -08:00
lantz 5d4ec1ab9e catch IOError when writing .mininet_history (#1031)
write_history seems to raise a spurious IOError,
so we catch it.
2021-02-07 19:12:38 -08:00
lantz 6b90434b9c Allow reinstall with make install (#1033) 2021-02-07 18:42:29 -08:00
lantz c2fb4d2e8c Fix ryu (#1032)
* Force reinstall on 'make install'

This allows you to install a modified version with 'make install'.

* Simplify RyuController and update for current Ryu

Notes:

The Ryu() constructor has changed slightly. We still add
`--ofp-tcp-listen-port %s` to the end of `cargs` to make
Controller() happy.

`command` is now `ryu run` so it includes the `run` command
but can be specified explicitly as needed.

You should be able to run Ryu's simple_switch by using:

    mn --controller ryu

and pass alternate modules such as simple_switch_13:

    mn --controller ryu,ryu.app.simple_switch_13

Unfortunately simple_switch_stp seems like it may be broken
for Python 3.8, which is too bad because we'd like to test
the Torus topo with it.
2021-02-07 16:08:45 -08:00
lantz 7b240ce5b8 Minor fixes for miniedit (#1028)
- avoid deleting from collection we're iterating over
- fix variable name misspelling
2021-02-06 23:23:09 -08:00
lantz c7de350a4e update make doc for current doxygen/doxypy (#1027) 2021-02-06 19:42:15 -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
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
105 changed files with 3768 additions and 1887 deletions
+3 -2
View File
@@ -1,14 +1,15 @@
<!--
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,
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:
+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.15.7
- 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-22.04, ubuntu-20.04]
py: [python3, python2]
steps:
- name: Check out Mininet source
uses: actions/checkout@v3
- name: Install Python ${{ matrix.py }}
run: sudo apt install ${{ matrix.py }}
- name: Install Mininet and base dependencies
run: |
sudo apt-get update -qq
# This seems too slow unfortunately:
# sudo apt-get upgrade -y -qq
PYTHON=${{ matrix.py }} util/install.sh -nv
- name: Disable slow udevd
run: sudo systemctl stop systemd-udevd
systemd-udevd-kernel.socket
systemd-udevd-control.socket
|| echo "couldn't disable udevd"
- name: Sanity test
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=${{ matrix.py }}
# 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
export PYTHON=${{ matrix.py }}
sudo $PYTHON -m pip install pexpect
util/install.sh -fw
- name: Run core tests
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=${{ matrix.py }}
$sudo $PYTHON mininet/test/runner.py -v
- name: Run examples tests
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=${{ matrix.py }}
$sudo $PYTHON examples/test/runner.py -v
+20 -13
View File
@@ -16,7 +16,7 @@
#init-hook=
# Profiled execution.
profile=no
#profile=no
# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
@@ -41,10 +41,17 @@ 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).
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-locals, too-many-public-methods, duplicate-code, bad-whitespace,
locally-disabled, locally-enabled
#
# 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=invalid-name, super-init-not-called, fixme,
too-many-instance-attributes, too-few-public-methods,
too-many-locals, too-many-public-methods, duplicate-code,
locally-disabled,
useless-object-inheritance, unnecessary-pass, no-else-return,
no-else-raise, no-else-continue, super-with-arguments,
consider-using-f-string, unspecified-encoding
# bad-continuation, wrong-import-order
@@ -56,12 +63,12 @@ output-format=colorized
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
# Include message's id in output
include-ids=yes
# include-ids=yes
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# files-output=no
# Tells whether to display a full report or only the messages
reports=no
@@ -75,7 +82,7 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (R0004).
comment=no
#comment=no
# Enable the report(s) with the given id(s).
#enable-report=
@@ -97,7 +104,7 @@ comment=no
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=
#required-attributes=
# Regular expression which should only match functions or classes name which do
# not require a docstring
@@ -138,7 +145,7 @@ good-names=i,j,k,ex,Run,_
bad-names=foo,bar,baz,toto,tutu,tata
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,inpu
#bad-functions=map,filter,apply,inpu
# try to find bugs in the code using type inference
@@ -155,7 +162,7 @@ ignored-classes=SQLObjec
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
#zope=no
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed.
@@ -193,7 +200,7 @@ additional-builtins=
# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
#ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp,build
@@ -215,7 +222,7 @@ max-locals=15
max-returns=6
# Maximum number of branch for function / method body
max-branchs=12
#max-branchs=12
# Maximum number of statements in function / method body
max-statements=50
-31
View File
@@ -1,31 +0,0 @@
language: python
sudo: required
matrix:
include:
- dist: trusty
env: dist="14.04 LTS trusty"
# - dist: xenial
# env: dist="16.04 LTS xenial"
# 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 Ubuntu version (14.04)
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq vlan
- sudo util/install.sh -n
install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- sudo util/install.sh -fnvw
script:
- sudo mn --test pingall
- sudo python mininet/test/runner.py -v -quick
- sudo python examples/test/runner.py -v -quick
notifications:
email:
on_success: never
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
+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
+72 -41
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0d1
Mininet 2.3.1b4
---
The supported installation methods for Mininet are 1) using a
@@ -32,28 +32,31 @@ 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:
git clone git://github.com/mininet/mininet.git
git clone https://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 Python, 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 version 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,12 +137,7 @@ 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+.
3.2. (Experimental) Native installation from source on Fedora:
As root execute the following operations:
@@ -109,21 +145,9 @@ like to contribute an installation script, we would welcome it!)
yum install git
* create an user account (e.g. mininet) and add it to the wheel group
useradd [...] mininet
usermod -a -G wheel mininet
* change the SElinux setting to permissive. It can be done
temporarily with:
setenforce 0
then login with the new account (e.g. mininet) and do the following:
* clone the Mininet repository
git clone git://github.com/mininet/mininet.git
git clone https://github.com/mininet/mininet.git
* install Mininet, the OpenFlow reference implementation, and
Open vSwitch
@@ -139,7 +163,10 @@ like to contribute an installation script, we would welcome it!)
sudo mn --test pingall
4. Creating your own Mininet/OpenFlow tutorial VM
Note that `install.sh -fnv `may not install all dependencies on Fedora,
and many tests may still fail.
4. Creating your own Mininet/OpenFlow tutorial VM on Ubuntu/Debian
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
@@ -155,7 +182,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 +200,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
---
+23 -27
View File
@@ -1,33 +1,29 @@
Mininet 2.3.0d1 License
BSD 3-Clause License (original Mininet authors: Bob Lantz and Brandon Heller)
Copyright (c) 2013-2016 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
Copyright (c) 2013-2022 Open Networking Foundation
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of The Leland Stanford Junior University
Original authors: Bob Lantz and Brandon Heller
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
We are making Mininet available for public use and benefit with the
expectation that others will use, modify and enhance the Software and
contribute those enhancements back to the community. However, since we
would like to make the Software available for broadest use, with as few
restrictions as possible permission is hereby granted, free of charge, to
any person obtaining a copy of this Software to deal in the Software
under the copyrights without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
The name and trademarks of copyright holder(s) may NOT be used in
advertising or publicity pertaining to the Software or any derivatives
without specific, written prior permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+23 -12
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
@@ -23,14 +26,14 @@ clean:
codecheck: $(PYSRC)
-echo "Running code check"
util/versioncheck.py
pyflakes $(PYSRC)
pyflakes3 $(PYSRC) || pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
# Exclude miniedit from pep8 checking for now
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
errcheck: $(PYSRC)
-echo "Running check for errors only"
pyflakes $(PYSRC)
pyflakes3 $(PYSRC) || pyflakes $(PYSRC)
pylint -E --rcfile=.pylint $(PYSRC)
test: $(MININET) $(TEST)
@@ -44,18 +47,26 @@ 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 2>&1`\" $< -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
# This seems to work on all pip versions
$(PYTHON) -m pip uninstall -y mininet || true
$(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 uninstall -y mininet || true
$(PYTHON) -m pip install -e . --no-binary :all:
man: $(MANPAGES)
+37 -27
View File
@@ -2,9 +2,10 @@ Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.3.0d1
Mininet 2.3.1b4
[![Build Status][1]](https://github.com/mininet/mininet/actions)
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
### What is Mininet?
@@ -67,27 +68,35 @@ 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.1b4 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.
- Additional information for this release and previous releases
may be found in the release notes on docs.mininet.org
### Other Enhancements and Information
- Support for Ubuntu 22.04 LTS (and 20.04)
- More reliable testing and CI via github actions
- Preliminary support for cgroups v2 (and v1)
- Minor bug fixes (2.3.1)
- Additional information about this release and previous releases
may be found in the release notes on http://docs.mininet.org.
### Installation
@@ -100,7 +109,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
@@ -111,22 +121,22 @@ Mininet mailing list, `mininet-discuss` at:
### Join Us
Thanks again to all of the Mininet contributors!
Thanks again to all of the Mininet contributors and users!
Mininet is an open source project and is currently hosted
at <https://github.com/mininet>. You are encouraged to download
the code, examine it, modify it, and submit bug reports, bug fixes,
feature requests, new features and other issues and pull requests.
at <https://github.com/mininet>. You are encouraged to download,
examine, and modify the code, and to submit bug reports, bug fixes,
feature requests, new features, and other issues and pull requests.
Thanks to everyone who has contributed code to the Mininet project
(see CONTRIBUTORS for more info!) It is because of everyone's
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
Bob Lantz,
on behalf of the Mininet Contributors
[1]: https://travis-ci.org/mininet/mininet.svg?branch=master
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
+32 -12
View File
@@ -11,15 +11,20 @@ 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
import mininet.cli
from mininet.log import lg, LEVELS, info, debug, warn, error, output
@@ -34,10 +39,7 @@ 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
@@ -98,7 +101,6 @@ CLI = None # Set below if needed
# Locally defined tests
def allTest( net ):
"Run ping and iperf tests"
net.waitConnected()
net.start()
net.ping()
net.iperf()
@@ -107,6 +109,7 @@ def nullTest( _net ):
"Null 'test' (does nothing)"
pass
TESTS.update( all=allTest, none=nullTest, build=nullTest )
# Map to alternate spellings of Mininet() methods
@@ -127,7 +130,6 @@ def runTests( mn, options ):
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 '
@@ -186,8 +188,11 @@ 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
with open( fileName ) as f:
exec( compile( f.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 )
@@ -256,7 +261,7 @@ 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?' )
@@ -282,11 +287,16 @@ 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( '--twait', '-t', action='store', type='int',
dest='wait',
help='timed wait (s) 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!) ' ) )
@@ -378,14 +388,24 @@ class MininetRunner( object ):
placement=PLACEMENT[ opts.placement ] )
mininet.cli.CLI = ClusterCLI
# Wait for controllers to connect unless we're running null test
if ( opts.test and opts.test != [ 'none' ] and
isinstance( opts.wait, bool ) ):
opts.wait = True
mn = Net( topo=topo,
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 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()
# --custom files can set CLI or change mininet.cli.CLI
@@ -416,7 +436,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' )
+1927 -821
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -158,7 +158,7 @@ A simple example of configuring network and CPU bandwidth limits.
This example shows how to run an `sshd` process in each host, allowing
you to log in via `ssh`. This requires connecting the Mininet data network
to an interface in the root namespace (generaly the control network
to an interface in the root namespace (generally the control network
already lives in the root namespace, so it does not need to be explicitly
connected.)
+3 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
@@ -27,9 +27,8 @@ h1.setIP( '10.0.0.1', 8 )
root.setIP( '10.0.0.2', 8 )
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()
with open( '/tmp/%s.banner' % h1.name, 'w' ) as f:
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
info( "*** Running sshd\n" )
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
+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()
+112 -58
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
cluster.py: prototyping/experimentation for distributed Mininet,
@@ -74,17 +74,6 @@ Things to do:
- hifi support (e.g. delay compensation)
"""
from mininet.node import Node, Host, OVSSwitch, Controller
from mininet.link import Link, Intf
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, errRun
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
@@ -93,7 +82,18 @@ 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
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, errRun, decode, StrictVersion
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from mininet.clean import addCleanupCallback
# pylint: disable=too-many-arguments
def findUser():
@@ -126,7 +126,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
@@ -246,7 +246,7 @@ class RemoteMixin( object ):
result = ''
while True:
poll = popen.poll()
result += popen.stdout.read()
result += decode( popen.stdout.read() )
if poll is not None:
break
return result
@@ -261,7 +261,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:
@@ -289,6 +289,7 @@ class RemoteMixin( object ):
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
# kwargs.update( moveIntfFn=RemoteLink.moveIntf )
# pylint: disable=useless-super-delegation
return super( RemoteMixin, self).addIntf( *args, **kwargs )
@@ -312,7 +313,7 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
kwargs.update( batch=True )
super( RemoteOVSSwitch, self ).__init__( *args, **kwargs )
def isOldOVS( self ):
def isOldOVS( self ): # pylint: disable=arguments-differ
"Is remote switch using an old OVS version?"
cls = type( self )
if self.server not in cls.OVSVersions:
@@ -325,6 +326,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' )
@@ -336,6 +338,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' )
@@ -372,8 +375,9 @@ class RemoteLink( Link ):
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
def makeIntfPair( self, # pylint: disable=arguments-renamed
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
@@ -413,8 +417,9 @@ class RemoteLink( Link ):
# 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
@@ -435,13 +440,16 @@ class RemoteLink( Link ):
# When we receive the character '@', it means that our
# tunnel should be set up
debug( 'Waiting for tunnel to come up...\n' )
ch = tunnel.stdout.read( 1 )
ch = decode( tunnel.stdout.read( 1 ) )
if ch != '@':
raise Exception( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
ch += decode( tunnel.stdout.read() )
cmd = ' '.join( cmd )
raise Exception( 'makeTunnel:\n'
'Tunnel setup failed for '
'%s:%s' % ( node1, node1.dest ) + ' to '
'%s:%s\n' % ( node2, node2.dest ) +
'command was: %s' % cmd + '\n' +
'result was: ' + ch )
# 3. Move interfaces if necessary
for node in node1, node2:
if not self.moveIntf( 'tap9', node ):
@@ -526,8 +534,9 @@ class RemoteGRELink( RemoteLink ):
# We should never try to create a tunnel to ourselves!
assert node1.server != node2.server
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 )
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
@@ -555,6 +564,7 @@ class RemoteGRELink( RemoteLink ):
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
@@ -589,10 +599,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 ) ) ]
@@ -606,10 +616,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 )
@@ -647,7 +657,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"
@@ -704,21 +714,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
@@ -764,13 +774,16 @@ 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 ):
"Popen() for server connections"
assert self # please pylint
old = signal( SIGINT, SIG_IGN )
# pylint: disable=consider-using-with
conn = Popen( cmd, stdin=PIPE, stdout=PIPE, close_fds=True )
# pylint: enable=consider-using-with
signal( SIGINT, old )
return conn
@@ -840,16 +853,43 @@ class MininetCluster( Mininet ):
if cfile:
config.setdefault( 'controlPath', cfile )
@staticmethod
def isLoopback( ipaddr ):
"Is ipaddr an IPv4 loopback address?"
return ipaddr.startswith( '127.' )
# 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()
controllerIP = controller.IP()
if ( not isinstance( controller, Controller ) or
not self.isLoopback( controller.IP() ) ):
return controller
# Find route to a different server IP address
serverIPs = [ ip for ip in self.serverIP.values()
if ip != controllerIP ]
if not serverIPs:
return None # no remote servers - loopback is fine
for remoteIP in serverIPs:
# 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()
if intf != 'lo':
break
if intf == 'lo':
raise Exception( 'addController: could not find external '
'interface/IP for %s' % controller )
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' )
@@ -858,9 +898,12 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# Default remote server for tests
remoteServer = 'ubuntu2'
def testNsTunnels( remote=remoteServer, link=RemoteGRELink ):
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=link )
net = Mininet( host=RemoteHost, link=link, waitConnected=True )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server=remote )
net.addLink( h1, h2 )
@@ -873,13 +916,14 @@ def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteNet( remote=remoteServer, link=RemoteGRELink ):
"Test remote Node classes"
info( '*** Remote Node Test\n' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=link, controller=ClusterController,
waitConnected=True )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
info( "*** Creating local h1\n" )
h1 = net.addHost( 'h1' )
info( "*** Creating remote h2\n" )
@@ -911,7 +955,7 @@ def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
remoteHosts = [ 'h2' ]
remoteSwitches = [ 's2' ]
remoteServer = 'ubuntu2'
def HostPlacer( name, *args, **params ):
"Custom Host() constructor which places hosts on servers"
@@ -928,10 +972,21 @@ def SwitchPlacer( name, *args, **params ):
return RemoteOVSSwitch( name, *args, **params )
def ClusterController( *args, **kwargs):
"Custom Controller() constructor which updates its eth0 IP address"
"Custom Controller() constructor which updates its intf IP address"
intf = kwargs.pop( 'intf', '' )
controller = Controller( *args, **kwargs )
# Find out its IP address so that cluster switches can connect
Intf( 'eth0', node=controller ).updateIP()
if not intf:
output = controller.cmd(
r"ip a | egrep -o '\w+:\s\w+'" ).split( '\n' )
for line in output:
intf = line.split()[ -1 ]
if intf != 'lo':
break
if intf == 'lo':
raise Exception( 'Could not find non-loopback interface'
'for %s' % controller )
Intf( intf, node=controller ).updateIP()
return controller
def testRemoteTopo( link=RemoteGRELink ):
@@ -948,7 +1003,7 @@ def testRemoteTopo( link=RemoteGRELink ):
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteSwitches( remote=remoteServer, link=RemoteGRELink ):
"Test with local hosts and remote switches"
servers = [ 'localhost', remote]
topo = TreeTopo( depth=4, fanout=2 )
@@ -966,7 +1021,7 @@ def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
def testMininetCluster( remote=remoteServer, link=RemoteGRELink ):
"Test MininetCluster()"
servers = [ 'localhost', remote ]
topo = TreeTopo( depth=3, fanout=3 )
@@ -976,7 +1031,7 @@ def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
net.pingAll()
net.stop()
def signalTest( remote='ubuntu2'):
def signalTest( remote=remoteServer):
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server=remote )
h.shell.send_signal( SIGINT )
@@ -991,7 +1046,6 @@ def signalTest( remote='ubuntu2'):
if __name__ == '__main__':
setLogLevel( 'info' )
remoteServer = 'ubuntu2'
remoteLink = RemoteSSHLink
testRemoteTopo(link=remoteLink)
testNsTunnels( remote=remoteServer, link=remoteLink )
+1
View File
@@ -17,6 +17,7 @@ def clusterSanity():
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
clusterSanity()
+4 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"CLI for Mininet Cluster Edition prototype demo"
@@ -30,7 +30,8 @@ class ClusterCLI( CLI ):
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
@@ -42,7 +43,7 @@ class ClusterCLI( CLI ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error
# pylint: enable=import-error,no-member
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
@@ -13,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, Link=RemoteLink,
net = MininetCluster( topo=topo, servers=servers, link=RemoteLink,
placement=SwitchBinPlacer )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
demo()
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
@@ -8,7 +8,7 @@ from mininet.log import setLogLevel
def perf(Link):
"Test connectivity nand performance over Link"
net = Mininet( host=RemoteHost, link=Link )
net = Mininet( host=RemoteHost, link=Link, waitConnected=True )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
@@ -17,6 +17,7 @@ def perf(Link):
net.iperf()
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perf( RemoteSSHLink )
+14 -9
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."
@@ -65,7 +69,10 @@ class Console( Frame ):
self.bindEvents()
self.sendCmd( 'export TERM=dumb' )
self.outputHook = None
def outputHook( _obj, _text):
return True
self.outputHook = outputHook
def makeWidgets( self ):
"Make a label, a text area, and a scroll bar."
@@ -107,10 +114,8 @@ class Console( Frame ):
self.text.insert( 'end', text )
self.text.mark_set( 'insert', 'end' )
self.text.see( 'insert' )
outputHook = lambda x, y: True # make pylint happier
if self.outputHook:
outputHook = self.outputHook
outputHook( self, text )
if callable( self.outputHook ):
self.outputHook( self, text )
def handleKey( self, event ):
"If it's an interactive command, send it to the node."
@@ -290,10 +295,10 @@ class ConsoleApp( Frame ):
'switches': 'Switch',
'controllers': 'Controller'
}
for name in titles:
for name, title in titles.items():
nodes = getattr( net, name )
frame, consoles = self.createConsoles(
cframe, nodes, width, titles[ name ] )
cframe, nodes, width, title )
self.consoles[ name ] = Object( frame=frame, consoles=consoles )
self.selected = None
self.select( 'hosts' )
@@ -319,7 +324,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()
+4 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example creates a multi-controller network from semi-scratch by
@@ -20,7 +20,8 @@ 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 )
info( "*** Creating (reference) controllers\n" )
c1 = net.addController( 'c1', port=6633 )
@@ -57,6 +58,7 @@ def multiControllerNet():
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):
+16 -10
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
cpu.py: test iperf bandwidth for varying cpu limits
@@ -31,9 +31,9 @@ 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=10 ):
"""Example/test of link and CPU bandwidth limits
@@ -52,10 +52,11 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
period_us=period_us,
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 scheduler %s\n' % sched )
except: # noqa
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
cleanup()
break
net.start()
net.pingAll()
@@ -67,11 +68,16 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
# the client's buffer fill rate
popen = server.popen( 'iperf -yc -s -p 5001' )
waitListening( client, server, 5001 )
# ignore empty result from waitListening/telnet
popen.stdout.readline()
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
result = popen.stdout.readline().split( ',' )
bps = float( result[ -1 ] )
# ignore empty result from waitListening/telnet for old iperf
svals = {}
while not svals or int( svals[ 'rate' ] ) == 0:
line = decode( popen.stdout.readline() )
# Probably shouldn't depend on an internal method, but
# this is the easiest way
svals = Mininet._iperfVals( # pylint: disable=protected-access
line, server.IP() )
bps = float( svals[ 'rate' ] )
popen.terminate()
net.stop()
updated = results.get( sched, [] )
+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()
+16 -16
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Test bandwidth (using iperf) on linear networks of varying size,
@@ -24,25 +24,25 @@ 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, 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 ) ]
@@ -83,14 +83,13 @@ def linearBandwidthTest( lengths ):
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath in switches.keys():
for datapath, Switch in switches.items():
info( "*** testing", datapath, "datapath\n" )
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='2ms', bw=10 )
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()
info( "*** testing basic connectivity\n" )
for n in lengths:
@@ -103,13 +102,13 @@ def linearBandwidthTest( lengths ):
src.cmd( 'telnet', dst.IP(), '5001' )
info( "testing", src.name, "<->", dst.name, '\n' )
# serverbw = received; _clientbw = buffered
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=10 )
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=5 )
info( serverbw, '\n' )
flush()
results[ datapath ] += [ ( n, serverbw ) ]
net.stop()
for datapath in switches.keys():
for datapath in switches:
info( "\n*** Linear network results for", datapath, "datapath:\n" )
result = results[ datapath ]
info( "SwitchCount\tiperf Results\n" )
@@ -119,8 +118,9 @@ def linearBandwidthTest( lengths ):
info( '\n')
info( '\n' )
if __name__ == '__main__':
lg.setLogLevel( 'info' )
sizes = [ 1, 10, 20, 40, 60, 80 ]
sizes = [ 1, 2, 3, 4 ]
info( "*** Running linearBandwidthTest", sizes, '\n' )
linearBandwidthTest( sizes )
+6 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
linuxrouter.py: Example network with Linux IP router
@@ -38,6 +38,7 @@ 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
@@ -51,6 +52,7 @@ 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
@@ -79,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' )
info( net[ 'r0' ].cmd( 'route' ) )
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
run()
+143 -128
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
MiniEdit: a simple network editor for Mininet
@@ -13,57 +13,71 @@ 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 functools import partial
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
# someday: from ttk import *
from optparse import OptionParser # pylint: disable=deprecated-module
from subprocess import call
from sys import exit # pylint: disable=redefined-builtin
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, StrictVersion )
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
# 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 +137,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' )
@@ -803,8 +818,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 +855,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):
@@ -873,7 +886,7 @@ class TableFrame(Frame):
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)
@@ -1349,7 +1362,7 @@ class MiniEdit( Frame ):
# Tools
for tool in self.tools:
cmd = ( lambda t=tool: self.activate( t ) )
cmd = partial( self.activate, tool )
b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
if tool in self.images:
b.config( height=35, image=self.images[ tool ] )
@@ -1386,11 +1399,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 +1420,14 @@ class MiniEdit( Frame ):
def convertJsonUnicode(self, text):
"Some part of Mininet don't like Unicode"
unicode = globals().get( 'unicode', str )
if isinstance(text, dict):
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.iteritems()}
elif isinstance(text, list):
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]
elif isinstance(text, unicode):
if isinstance(text, unicode): # pylint: disable=undefined-variable
return text.encode('utf-8')
else:
return text
return text
def loadTopology( self ):
"Load command."
@@ -1425,14 +1438,14 @@ 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))
# Load application preferences
if 'application' in loadedTopology:
self.appPrefs = dict(self.appPrefs.items() + loadedTopology['application'].items())
self.appPrefs.update(loadedTopology['application'])
if "ovsOf10" not in self.appPrefs["openFlowVersions"]:
self.appPrefs["openFlowVersions"]["ovsOf10"] = '0'
if "ovsOf11" not in self.appPrefs["openFlowVersions"]:
@@ -1586,10 +1599,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 tuple( self.widgetToItem ):
self.deleteItem( self.widgetToItem[ widget ] )
self.hostCount = 0
self.switchCount = 0
@@ -1617,10 +1631,10 @@ class MiniEdit( Frame ):
hostsToSave = []
switchesToSave = []
controllersToSave = []
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
x1, y1 = self.canvas.coords( item )
if 'Switch' in tags or 'LegacySwitch' in tags or 'LegacyRouter' in tags:
nodeNum = self.switchOpts[name]['nodeNum']
nodeToSave = {'number':str(nodeNum),
@@ -1665,14 +1679,13 @@ class MiniEdit( Frame ):
savingDictionary['application'] = self.appPrefs
try:
f = open(fileName, 'wb')
f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
# pylint: disable=broad-except
except Exception as er:
with open(fileName, 'w') as f:
f.write(
json.dumps(savingDictionary,
sort_keys=True,
indent=4, separators=(',', ': ')))
except Exception as er: # pylint: disable=broad-except
warn( er, '\n' )
# pylint: enable=broad-except
finally:
f.close()
def exportScript( self ):
"Export command."
@@ -1684,9 +1697,9 @@ class MiniEdit( Frame ):
fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
if len(fileName ) > 0:
# debug( "Now saving under %s\n" % fileName )
f = open(fileName, 'wb')
f = open(fileName, 'w') # pylint: disable=consider-using-with
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")
@@ -1700,9 +1713,9 @@ class MiniEdit( Frame ):
f.write("from subprocess import call\n")
inBandCtrl = False
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Controller' in tags:
opts = self.controllers[name]
@@ -1710,7 +1723,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")
@@ -1728,9 +1741,9 @@ class MiniEdit( Frame ):
f.write(" ipBase='"+self.appPrefs['ipBase']+"')\n")
f.write("\n")
f.write(" info( '*** Adding controller\\n' )\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Controller' in tags:
opts = self.controllers[name]
@@ -1762,9 +1775,9 @@ class MiniEdit( Frame ):
# Save Switches and Hosts
f.write(" info( '*** Add switches\\n')\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'LegacyRouter' in tags:
f.write(" "+name+" = net.addHost('"+name+"', cls=Node, ip='0.0.0.0')\n")
f.write(" "+name+".cmd('sysctl -w net.ipv4.ip_forward=1')\n")
@@ -1802,9 +1815,9 @@ class MiniEdit( Frame ):
f.write("\n")
f.write(" info( '*** Add hosts\\n')\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Host' in tags:
opts = self.hostOpts[name]
ip = None
@@ -1835,7 +1848,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
@@ -1897,9 +1910,9 @@ class MiniEdit( Frame ):
f.write("\n")
f.write(" info( '*** Starting switches\\n')\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags or 'LegacySwitch' in tags:
opts = self.switchOpts[name]
ctrlList = ",".join(opts['controllers'])
@@ -1908,9 +1921,9 @@ class MiniEdit( Frame ):
f.write("\n")
f.write(" info( '*** Post configure switches and hosts\\n')\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
opts = self.switchOpts[name]
if opts['switchType'] == 'default':
@@ -1938,9 +1951,9 @@ class MiniEdit( Frame ):
if 'switchIP' in opts:
if len(opts['switchIP']) > 0:
f.write(" "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Host' in tags:
opts = self.hostOpts[name]
# Attach vlan interfaces
@@ -1962,9 +1975,9 @@ class MiniEdit( Frame ):
if len(nflowValues['nflowTarget']) > 0:
nflowEnabled = False
nflowSwitches = ''
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
opts = self.switchOpts[name]
@@ -1986,9 +1999,9 @@ class MiniEdit( Frame ):
if len(sflowValues['sflowTarget']) > 0:
sflowEnabled = False
sflowSwitches = ''
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
opts = self.switchOpts[name]
@@ -2003,9 +2016,9 @@ class MiniEdit( Frame ):
f.write("\n")
f.write(" CLI(net)\n")
for widget in self.widgetToItem:
for widget, item in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Host' in tags:
opts = self.hostOpts[name]
# Run User Defined Stop Command
@@ -2107,7 +2120,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 +2128,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 +2143,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 +2167,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 +2245,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 +2253,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 +2372,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
@@ -2425,7 +2440,8 @@ class MiniEdit( Frame ):
line3.pack(pady=10 )
line4.pack(pady=10 )
line5.pack(pady=10 )
hide = ( lambda about=about: about.withdraw() )
def hide():
about.withdraw()
self.aboutBox = about
# Hide on close rather than destroying window
Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
@@ -2600,9 +2616,9 @@ class MiniEdit( Frame ):
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:
for widget, item in self.widgetToItem.items():
switchName = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
switch = self.switchOpts[switchName]
if oldName in switch['controllers']:
@@ -2642,7 +2658,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}
@@ -2683,14 +2699,13 @@ class MiniEdit( Frame ):
tags = self.canvas.gettags(item)
if 'Controller' in tags:
# remove from switch controller lists
for serachwidget in self.widgetToItem:
name = serachwidget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ serachwidget ] )
for searchwidget, searchitem in self.widgetToItem.items():
name = searchwidget[ 'text' ]
tags = self.canvas.gettags( searchitem )
if 'Switch' in tags:
if widget['text'] in self.switchOpts[name]['controllers']:
self.switchOpts[name]['controllers'].remove(widget['text'])
for link in widget.links.values():
for link in tuple( widget.links.values() ):
# Delete from view and model
self.deleteItem( link )
del self.itemToWidget[ item ]
@@ -2699,9 +2714,9 @@ class MiniEdit( Frame ):
def buildNodes( self, net):
# Make nodes
info( "Getting Hosts and Switches.\n" )
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
# debug( name+' has '+str(tags), '\n' )
if 'Switch' in tags:
@@ -2875,7 +2890,7 @@ class MiniEdit( Frame ):
def buildLinks( self, net):
# Make links
info( "Getting Links.\n" )
for key,link in self.links.iteritems():
for key,link in self.links.items():
tags = self.canvas.gettags(key)
if 'data' in tags:
src=link['src']
@@ -2915,9 +2930,9 @@ class MiniEdit( Frame ):
def postStartSetup( self ):
# Setup host details
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Host' in tags:
newHost = self.net.get(name)
opts = self.hostOpts[name]
@@ -2942,9 +2957,9 @@ class MiniEdit( Frame ):
if len(nflowValues['nflowTarget']) > 0:
nflowEnabled = False
nflowSwitches = ''
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
opts = self.switchOpts[name]
@@ -2972,9 +2987,9 @@ class MiniEdit( Frame ):
if len(sflowValues['sflowTarget']) > 0:
sflowEnabled = False
sflowSwitches = ''
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
opts = self.switchOpts[name]
@@ -2996,7 +3011,7 @@ class MiniEdit( Frame ):
## 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 ):
@@ -3017,9 +3032,9 @@ class MiniEdit( Frame ):
#for switch in self.net.switches:
# info( switch.name + ' ')
# switch.start( self.net.controllers )
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Switch' in tags:
opts = self.switchOpts[name]
switchControllers = []
@@ -3039,9 +3054,9 @@ class MiniEdit( Frame ):
"Stop network."
if self.net is not None:
# Stop host details
for widget in self.widgetToItem:
for widget, item in self.widgetToItem.items():
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
tags = self.canvas.gettags( item )
if 'Host' in tags:
newHost = self.net.get(name)
opts = self.hostOpts[name]
@@ -3184,7 +3199,7 @@ class MiniEdit( Frame ):
addDictOption( opts, LINKS, LINKDEF, 'link' )
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py' +
help='read custom topo and node params from .py ' +
'file' )
self.options, self.args = opts.parse_args()
@@ -3210,8 +3225,9 @@ 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 )
@@ -3577,8 +3593,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()
+6 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of Mobility with Mininet
@@ -19,14 +19,13 @@ 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 info, output, warn, setLogLevel
from random import randint
class MobilitySwitch( OVSSwitch ):
"Switch that can reattach and rename interfaces"
@@ -38,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 )
@@ -107,7 +107,8 @@ def moveHost( host, oldSwitch, newSwitch, newPort=None ):
def mobilityTest():
"A simple test of mobility"
info( '* Simple mobility test\n' )
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch,
waitConnected=True )
info( '* Starting network:\n' )
net.start()
printConnections( net.switches )
@@ -131,6 +132,7 @@ def mobilityTest():
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()
+7 -7
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
multiping.py: monitor multiple sets of hosts using ping
@@ -8,18 +8,18 @@ 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 info, setLogLevel
from select import poll, POLLIN
from time import time
def chunks( l, n ):
def chunks( items, n ):
"Divide list l into chunks of size n - thanks Stackoverflow"
return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
return [ items[ i: i + n ] for i in range( 0, len( items ), n ) ]
def startpings( host, targetips ):
"Tell host to repeatedly ping targets"
@@ -45,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 )
@@ -59,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 )
+13 -11
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of sending output to multiple files and
@@ -6,22 +6,24 @@ monitoring them
"""
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import info, 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' )
devnull = open( '/dev/null', 'w' ) # pylint: disable=consider-using-with
tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.iteritems():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
for h, outfile in outfiles.items():
tail = Popen( # pylint: disable=consider-using-with
[ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
tails[ h ] = tail
fdToFile[ fd ] = tail.stdout
@@ -40,7 +42,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, ''
@@ -52,7 +54,7 @@ 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
info( "Starting test...\n" )
+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" )
+2 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Example to create a Mininet topology and connect it to the internet via NAT
@@ -12,7 +12,7 @@ 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()
+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()
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network with 5 hosts, numbered 1-4 and 9.
@@ -28,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' )
@@ -75,6 +75,7 @@ def testPortNumbering():
info( '*** Stopping network\n' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
testPortNumbering()
+5 -8
View File
@@ -1,23 +1,19 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example monitors a number of hosts using host.popen() and
pmonitor()
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from mininet.util import custom, pmonitor
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 = {}
@@ -32,6 +28,7 @@ def monitorhosts( hosts=5, sched='cfs' ):
# Done
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
monitorhosts( hosts=5 )
+6 -4
View File
@@ -1,19 +1,20 @@
#!/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 mininet.log import setLogLevel, info
from time import time
from signal import SIGINT
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
info( "Starting test...\n" )
@@ -31,6 +32,7 @@ def pmonitorTest( N=3, seconds=10 ):
p.send_signal( SIGINT )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
pmonitorTest()
+3 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Build a simple network from scratch, using mininet primitives.
@@ -8,6 +8,7 @@ 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
@@ -15,7 +16,6 @@ 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."
@@ -62,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' )
+2 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Build a simple network from scratch, using mininet primitives.
@@ -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' )
+10 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of setting network and CPU parameters
@@ -9,6 +9,7 @@ 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
@@ -17,7 +18,6 @@ from mininet.link import TCLink
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel, info
from sys import argv
# It would be nice if we didn't have to do this:
# pylint: disable=arguments-differ
@@ -49,12 +49,19 @@ def perfTest( lossy=True ):
net.start()
info( "Dumping host connections\n" )
dumpNodeConnections(net.hosts)
info( "Testing bandwidth between h1 and h4\n" )
info( "Testing bandwidth between h1 and h4 (lossy=%s)\n" % lossy )
h1, h4 = net.getNodeByName('h1', 'h4')
net.iperf( ( h1, h4 ), l4Type='UDP' )
# Debugging
h1.cmd('jobs')
h4.cmd('jobs')
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
# Debug for now
if 'testmode' in argv:
setLogLevel( 'debug' )
# Prevent test_simpleperf from failing due to packet loss
perfTest( lossy=( 'testmode' not in argv ) )
+4 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network and start sshd(8) on each host.
@@ -29,7 +29,7 @@ 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.
@@ -47,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.
@@ -73,6 +74,7 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
host.cmd( 'kill %' + cmd )
network.stop()
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
+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 )
+1 -1
View File
@@ -15,7 +15,7 @@ cfs 10% 1.29e+09
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testCPU( unittest.TestCase ):
+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 ):
+1 -1
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
+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 ):
+1 -1
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 ):
+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()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multipoll.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testMultiPoll( unittest.TestCase ):
+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 ):
+2 -1
View File
@@ -5,7 +5,7 @@ Test for scratchnet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testScratchNet( unittest.TestCase ):
@@ -16,6 +16,7 @@ class testScratchNet( unittest.TestCase ):
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts, timeout=120 )
self.assertEqual( index, 0 )
p.wait()
def testPingKernel( self ):
self.pingTest( 'mininet.examples.scratchnet' )
+8 -5
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,10 +18,13 @@ class testSimplePerf( unittest.TestCase ):
"Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
TOLERANCE = .2
p = pexpect.spawn(
'python -m mininet.examples.simpleperf testmode' )
# Log since this seems to be failing intermittently
p.logfile = sys.stdout
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=90 )
measuredBw = float( p.match.group( 1 ) )
lowerBound = BW * ( 1 - TOLERANCE )
upperBound = BW + ( 1 + TOLERANCE )
@@ -30,5 +33,5 @@ class testSimplePerf( unittest.TestCase ):
p.wait()
if __name__ == '__main__':
setLogLevel( 'warning' )
setLogLevel( 'debug' )
unittest.main()
+3 -3
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,7 +16,8 @@ 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:
@@ -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 )
+19 -8
View File
@@ -1,24 +1,34 @@
#!/usr/bin/python
#!/usr/bin/env python
"Create a 64-node tree network, and test connectivity using ping."
from mininet.log import setLogLevel, info
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
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:
for name, switch in switches.items():
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
@@ -27,6 +37,7 @@ def treePing64():
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
treePing64()
+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
+1
View File
@@ -0,0 +1 @@
../bin/mn
+8 -6
View File
@@ -16,12 +16,15 @@ 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 ]
p = Popen( # pylint: disable=consider-using-with
[ '/bin/sh', '-c', cmd ], stdout=PIPE )
result = p.communicate()[ 0 ]
return decode( result )
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
@@ -50,9 +53,9 @@ class Cleanup( object ):
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
"pings/noxes\n" )
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
'lt-nox_core ovs-openflowd ovs-controller'
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
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.
@@ -76,7 +79,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:
+41 -15
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,6 +79,7 @@ class CLI( Cmd ):
return
cls.readlineInited = True
try:
# pylint: disable=import-outside-toplevel
from readline import ( read_history_file, write_history_file,
set_history_length )
except ImportError:
@@ -86,7 +89,15 @@ class CLI( Cmd ):
if os.path.isfile( history_path ):
read_history_file( history_path )
set_history_length( 1000 )
atexit.register( lambda: write_history_file( history_path ) )
def writeHistory():
"Write out history file"
try:
write_history_file( history_path )
except IOError:
# Ignore probably spurious IOError
pass
atexit.register( writeHistory )
def run( self ):
"Run our cmdloop(), catching KeyboardInterrupt"
@@ -139,10 +150,10 @@ class CLI( Cmd ):
' mininet> xterm h2\n\n'
)
def do_help( self, line ):
def do_help( self, line ): # pylint: disable=arguments-renamed
"Describe available CLI commands."
Cmd.do_help( self, line )
if line is '':
if line == '':
output( self.helpStr )
def do_nodes( self, _line ):
@@ -171,8 +182,9 @@ 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:
if result is None:
return
elif isinstance( result, str ):
output( result + '\n' )
@@ -340,13 +352,13 @@ class CLI( Cmd ):
error( 'usage: source <file>\n' )
return
try:
self.inputFile = open( args[ 0 ] )
while True:
line = self.inputFile.readline()
if len( line ) > 0:
self.onecmd( line )
else:
break
with open( args[ 0 ] ) as self.inputFile:
while True:
line = self.inputFile.readline()
if len( line ) > 0:
self.onecmd( line )
else:
break
except IOError:
error( 'error reading file %s\n' % args[ 0 ] )
self.inputFile.close()
@@ -397,6 +409,10 @@ 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
@@ -440,12 +456,14 @@ class CLI( Cmd ):
try:
bothPoller.poll()
# XXX BL: this doesn't quite do what we want.
# pylint: disable=condition-evals-to-constant
if False and self.inputFile:
key = self.inputFile.read( 1 )
if key is not '':
if key != '':
node.write( key )
else:
self.inputFile = None
# pylint: enable=condition-evals-to-constant
if isReadable( self.inPoller ):
key = self.stdin.read( 1 )
node.write( key )
@@ -459,6 +477,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"
@@ -475,3 +500,4 @@ def isReadable( poller ):
mask = fdmask[ 1 ]
if mask & POLLIN:
return True
return False
+38 -33
View File
@@ -24,9 +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 re
# Make pylint happy:
# pylint: disable=too-many-arguments
class Intf( object ):
@@ -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 ):
@@ -285,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 %.5f ' % 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:
@@ -312,7 +316,9 @@ class TCIntf( Intf ):
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
def config( self, bw=None, delay=None, jitter=None, loss=None,
def config( # pylint: disable=arguments-renamed,arguments-differ
self,
bw=None, delay=None, jitter=None, loss=None,
gro=False, txo=True, rxo=True,
speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False,
@@ -352,7 +358,7 @@ class TCIntf( Intf ):
# 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' )
@@ -408,7 +414,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
@@ -418,18 +424,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:
@@ -443,6 +446,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 )
@@ -464,6 +471,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
@@ -532,7 +540,9 @@ class OVSLink( Link ):
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
from mininet.node import OVSSwitch
if 'OVSSwitch' not in globals():
# pylint: disable=import-outside-toplevel,cyclic-import
from mininet.node import OVSSwitch
self.isPatchLink = False
if ( isinstance( node1, OVSSwitch ) and
isinstance( node2, OVSSwitch ) ):
@@ -540,6 +550,7 @@ class OVSLink( Link ):
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
# pylint: disable=arguments-renamed, arguments-differ, signature-differs
def makeIntfPair( self, *args, **kwargs ):
"Usually delegated to OVSSwitch"
if self.isPatchLink:
@@ -549,17 +560,11 @@ 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 ):
+23 -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' )
@@ -168,10 +159,14 @@ def makeListCompatible( fn ):
setattr( newfn, '__doc__', fn.__doc__ )
return newfn
_loggers = lg.info, lg.output, lg.warn, lg.error, lg.debug
_loggers = tuple( makeListCompatible( logger )
for logger in _loggers )
lg.info, lg.output, lg.warn, lg.error, lg.debug = _loggers
info, output, warn, error, debug = _loggers
# 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():
+69 -46
View File
@@ -92,27 +92,29 @@ 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
from mininet.cli import CLI
from mininet.log import info, error, debug, output, warn
from mininet.log import info, error, output, warn, debug
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
Controller )
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, fmtBps )
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.0d1"
VERSION = "2.3.1b4"
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',
@@ -135,7 +137,9 @@ class Mininet( object ):
autoStaticArp: set all-pairs static MAC addrs?
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
listenPort: base listening port to open; will be incremented for
each additional switch in the net if inNamespace=False"""
each additional switch in the net if inNamespace=False
waitConnected: wait for switches to Connect?
(False; True/None=wait indefinitely; time(s)=timed wait)"""
self.topo = topo
self.switch = switch
self.host = host
@@ -174,14 +178,16 @@ class Mininet( object ):
self.build()
def waitConnected( self, timeout=None, delay=.5 ):
"""wait for each switch to connect to a controller,
up to 5 seconds
timeout: time to wait, or None to wait indefinitely
"""wait for each switch to connect to a controller
timeout: time to wait, or None or True to wait indefinitely
delay: seconds to sleep per iteration
returns: True if all switches are connected"""
info( '*** Waiting for switches to connect\n' )
time = 0
time = 0.0
remaining = list( self.switches )
# False: 0s timeout; None: wait forever (preserve 2.2 behavior)
if isinstance( timeout, bool ):
timeout = None if timeout else 0
while True:
for switch in tuple( remaining ):
if switch.connected():
@@ -190,12 +196,12 @@ 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
warn( 'Timed out after %d seconds\n' % time )
for switch in remaining:
for switch in remaining.copy():
if not switch.connected():
warn( 'Warning: %s is not connected to a controller\n'
% switch.name )
@@ -383,8 +389,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:
@@ -437,7 +443,7 @@ class Mininet( object ):
host.configDefault( ip=None, mac=None )
# You're low priority, dude!
# BL: do we want to do this here or not?
# May not make sense if we have CPU lmiting...
# May not make sense if we have CPU limiting...
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
# This may not be the right place to do this, but
# it needs to be done somewhere.
@@ -549,14 +555,15 @@ 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 )
started.update( { s: s for s in success } )
info( '\n' )
if self.waitConn:
self.waitConnected()
self.waitConnected( self.waitConn )
def stop( self ):
"Stop the controller(s), switches and hosts"
@@ -565,6 +572,10 @@ class Mininet( object ):
info( controller.name + ' ' )
controller.stop()
info( '\n' )
# Unlimit cfs hosts to speed up shutdown
for h in self.hosts:
if hasattr( h, 'unlimit' ):
h.unlimit()
if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
@@ -576,7 +587,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 )
@@ -663,7 +675,7 @@ class Mininet( object ):
if timeout:
opts = '-W %s' % timeout
if dest.intfs:
result = node.cmd( 'ping -c1 %s %s' %
result = node.cmd( 'LANG=C ping -c1 %s %s' %
(opts, dest.IP()) )
sent, received = self._parsePing( result )
else:
@@ -774,18 +786,26 @@ class Mininet( object ):
return self.pingFull( hosts=hosts )
@staticmethod
def _parseIperf( iperfOutput ):
"""Parse iperf output and return bandwidth.
iperfOutput: string
returns: result string"""
r = r'([\d\.]+ \w+/sec)'
m = re.findall( r, iperfOutput )
if m:
return m[-1]
else:
# was: raise Exception(...)
error( 'could not parse iperf output: ' + iperfOutput )
return ''
def _iperfVals( iperfcsv, serverip ):
"""Return iperf CSV as dict
iperfcsv: iperf -y C output
serverip: iperf server IP address
"""
fields = 'date cip cport sip sport ipver interval sent rate'
lines = iperfcsv.strip().split('\n')
svals = {}
for line in lines:
if ',' not in line:
continue
line = line.split( ',' )
svals = dict( zip( fields.split(), line ) )
# Return client in cip:cport, server in sip:sport
if svals[ 'cip' ] == serverip:
svals[ 'cip' ], svals[ 'sip' ] = (
svals[ 'sip' ], svals[ 'cip' ] )
svals[ 'cport' ], svals[ 'sport' ] = (
svals[ 'sport' ], svals[ 'cport' ] )
return svals
# XXX This should be cleaned up
@@ -795,7 +815,7 @@ class Mininet( object ):
hosts: list of hosts; if None, uses first and last hosts
l4Type: string, one of [ TCP, UDP ]
udpBw: bandwidth target for UDP test
fmt: iperf format argument if any
fmt: scale/format argument (e.g. m/M for Mbps)
seconds: iperf time to transmit
port: iperf port
returns: two-element array of [ server, client ] speeds
@@ -808,33 +828,36 @@ class Mininet( object ):
output( '*** Iperf: testing', l4Type, 'bandwidth between',
client, 'and', server, '\n' )
server.cmd( 'killall -9 iperf' )
iperfArgs = 'iperf -p %d ' % port
# Note: CSV mode
iperfArgs = 'iperf -y C -p %d ' % port
bwArgs = ''
if l4Type == 'UDP':
iperfArgs += '-u '
bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if fmt:
iperfArgs += '-f %s ' % fmt
server.sendCmd( iperfArgs + '-s' )
serverip = server.IP()
if l4Type == 'TCP':
if not waitListening( client, server.IP(), port ):
if not waitListening( client, serverip, port ):
raise Exception( 'Could not connect to iperf on port %d'
% port )
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
server.IP() + ' ' + bwArgs )
debug( 'Client output: %s\n' % cliout )
servout = ''
# We want the last *b/sec from the iperf server output
# 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 )
cvals = self._iperfVals( cliout, serverip )
debug( 'iperf client output:', cliout, cvals )
serverout = ''
# Wait for output from the client session
while True:
serverout += server.monitor( timeoutms=5000 )
svals = self._iperfVals( serverout, serverip )
# Check for the client's source/output port
if ( svals and cvals[ 'sport' ] == svals[ 'sport' ]
and int( svals[ 'rate' ] ) > 0 ):
break
debug( 'iperf server output:', serverout, svals )
server.sendInt()
servout += server.waitOutput()
debug( 'Server output: %s\n' % servout )
result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
serverout += server.waitOutput()
result = [ fmtBps( svals[ 'rate'], fmt ),
fmtBps( cvals[ 'rate' ], fmt ) ]
if l4Type == 'UDP':
result.insert( 0, udpBw )
output( '*** Results: %s\n' % result )
+150 -92
View File
@@ -57,16 +57,22 @@ import pty
import re
import signal
import select
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,
StrictVersion )
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 interfaces
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 ) # pylint: disable=consider-using-with
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
@@ -448,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,
@@ -460,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
@@ -487,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()
@@ -512,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
@@ -559,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 ):
@@ -608,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"
@@ -632,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 ):
@@ -651,8 +685,21 @@ class CPULimitedHost( Host ):
"CPU limited host"
def __init__( self, name, sched='cfs', **kwargs ):
Host.__init__( self, name, **kwargs )
def __init__( self, name, sched='cfs', **params ):
Host.__init__( self, name, **params )
# BL: Setting the correct period/quota is tricky, particularly
# for RT. RT allows very small quotas, but the overhead
# seems to be high. CFS has a minimum quota of 1 ms, but
# still does better with larger period values.
self.period_us = params.get( 'period_us', 100000 )
self.sched = sched
self.cgroupsInited = False
self.cgroup, self.rtprio = None, None
def initCgroups( self ):
"Deferred cgroup initialization"
if self.cgroupsInited:
return
# Initialize class if necessary
if not CPULimitedHost.inited:
CPULimitedHost.init()
@@ -662,32 +709,26 @@ class CPULimitedHost( Host ):
# We don't add ourselves to a cpuset because you must
# specify the cpu and memory placement first
errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
# BL: Setting the correct period/quota is tricky, particularly
# for RT. RT allows very small quotas, but the overhead
# seems to be high. CFS has a mininimum quota of 1 ms, but
# still does better with larger period values.
self.period_us = kwargs.get( 'period_us', 100000 )
self.sched = sched
if sched == 'rt':
if self.sched == 'rt':
self.checkRtGroupSched()
self.rtprio = 20
def cgroupSet( self, param, value, resource='cpu' ):
"Set a cgroup parameter and return its value"
cmd = 'cgset -r %s.%s=%s /%s' % (
resource, param, value, self.name )
quietRun( cmd )
nvalue = int( self.cgroupGet( param, resource ) )
if nvalue != value:
cmd = [ 'cgset', '-r', "%s.%s=%s" % (
resource, param, value), '/' + self.name ]
errFail( cmd )
nvalue = self.cgroupGet( param, resource )
if nvalue != str( value ):
error( '*** error: cgroupSet: %s set to %s instead of %s\n'
% ( param, nvalue, value ) )
return nvalue
def cgroupGet( self, param, resource='cpu' ):
"Return value of cgroup parameter"
cmd = 'cgget -r %s.%s /%s' % (
resource, param, self.name )
return int( quietRun( cmd ).split()[ -1 ] )
pname = '%s.%s' % ( resource, param )
cmd = 'cgget -n -r %s /%s' % ( pname, self.name )
return quietRun( cmd )[len(pname)+1:].strip()
def cgroupDel( self ):
"Clean up our cgroup"
@@ -754,6 +795,8 @@ class CPULimitedHost( Host ):
def cfsInfo( self, f ):
"Internal method: return parameters for CFS bandwidth"
pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
if self.cgversion == 'cgroup2':
pstr, qstr = 'max', ''
# CFS uses wall clock time for period and CPU time for quota.
quota = int( self.period_us * f * numCores() )
period = self.period_us
@@ -763,7 +806,7 @@ class CPULimitedHost( Host ):
period = int( quota / f / numCores() )
# Reset to unlimited on negative quota
if quota < 0:
quota = -1
quota = 'max' if self.cgversion == 'cgroup2' else -1
return pstr, qstr, period, quota
# BL comment:
@@ -793,12 +836,16 @@ class CPULimitedHost( Host ):
else:
return
# Set cgroup's period and quota
setPeriod = self.cgroupSet( pstr, period )
setQuota = self.cgroupSet( qstr, quota )
if self.cgversion == 'cgroup':
setPeriod = self.cgroupSet( pstr, period )
setQuota = self.cgroupSet( qstr, quota )
else:
setQuota, setPeriod = self.cgroupSet(
pstr, '%s %s' % (quota, period) ).split()
if sched == 'rt':
# Set RT priority if necessary
sched = self.chrt()
info( '(%s %d/%dus) ' % ( sched, setQuota, setPeriod ) )
info( '(%s %s/%dus) ' % ( sched, setQuota, int( setPeriod ) ) )
def setCPUs( self, cores, mems=0 ):
"Specify (real) cores that our cgroup can run on"
@@ -817,11 +864,13 @@ 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
params: parameters for Node.config()"""
r = Node.config( self, **params )
self.initCgroups()
# Was considering cpu={'cpu': cpu , 'sched': sched}, but
# that seems redundant
self.setParam( r, 'setCPUFrac', cpu=cpu )
@@ -829,13 +878,19 @@ class CPULimitedHost( Host ):
return r
inited = False
cgversion = 'cgroup2'
@classmethod
def init( cls ):
"Initialization for CPULimitedHost class"
mountCgroups()
cls.cgversion = mountCgroups()
cls.inited = True
def unlimit( self ):
"Unlimit cpu for cfs"
if self.sched == 'cfs' and self.params.get( 'cpu', -1 ) != -1:
self.setCPUFrac( -1, sched=self.sched )
# Some important things to note:
#
@@ -879,7 +934,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
@@ -887,6 +942,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.' )
@@ -908,6 +964,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)"
@@ -1093,6 +1150,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 )
@@ -1230,7 +1288,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
@@ -1256,7 +1314,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
@@ -1280,7 +1338,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
@@ -1360,10 +1418,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:
@@ -1405,6 +1465,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 )
@@ -1428,7 +1489,7 @@ class Controller( Node ):
@classmethod
def isAvailable( cls ):
"Is controller available?"
return quietRun( 'which controller' )
return which( 'controller' )
class OVSController( Controller ):
@@ -1440,9 +1501,9 @@ class OVSController( Controller ):
@classmethod
def isAvailable( cls ):
return ( quietRun( 'which ovs-controller' ) or
quietRun( 'which test-controller' ) or
quietRun( 'which ovs-testcontroller' ) ).strip()
return (which( 'ovs-controller' ) or
which( 'test-controller' ) or
which( 'ovs-testcontroller' ))
class NOX( Controller ):
"Controller to run a NOX application."
@@ -1455,7 +1516,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:
@@ -1464,32 +1525,26 @@ class NOX( Controller ):
Controller.__init__( self, name,
command=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
cargs='--libdir=/usr/local/lib -v '
'-i ptcp:%s ' +
' '.join( noxArgs ),
cdir=noxCoreDir,
**kwargs )
class Ryu( Controller ):
"Controller to run Ryu application"
def __init__( self, name, *ryuArgs, **kwargs ):
"Ryu OpenFlow Controller"
def __init__( self, name, ryuArgs='ryu.app.simple_switch',
command='ryu run', **kwargs ):
"""Init.
name: name to give controller.
ryuArgs: arguments and modules to pass to Ryu"""
homeDir = quietRun( 'printenv HOME' ).strip( '\r\n' )
ryuCoreDir = '%s/ryu/ryu/app/' % homeDir
if not ryuArgs:
warn( 'warning: no Ryu modules specified; '
'running simple_switch only\n' )
ryuArgs = [ ryuCoreDir + 'simple_switch.py' ]
elif type( ryuArgs ) not in ( list, tuple ):
ryuArgs = [ ryuArgs ]
Controller.__init__( self, name,
command='ryu-manager',
cargs='--ofp-tcp-listen-port %s ' +
' '.join( ryuArgs ),
cdir=ryuCoreDir,
**kwargs )
name: name to give controller.
ryuArgs: modules to pass to Ryu (ryu.app.simple_switch)
command: command to run Ryu ('ryu run')"""
if isinstance( ryuArgs, ( list, tuple ) ):
ryuArgs = ' '.join( ryuArgs )
cargs = kwargs.pop(
'cargs', ryuArgs + ' --ofp-tcp-listen-port %s' )
Controller.__init__( self, name, command=command,
cargs=cargs, **kwargs )
class RemoteController( Controller ):
@@ -1508,6 +1563,7 @@ class RemoteController( Controller ):
"Overridden to do nothing."
return
# pylint: disable=arguments-differ
def stop( self ):
"Overridden to do nothing."
return
@@ -1539,6 +1595,7 @@ class RemoteController( Controller ):
else:
return True
DefaultControllers = ( Controller, OVSController )
def findController( controllers=DefaultControllers ):
@@ -1546,6 +1603,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"
+24 -15
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' )
@@ -114,20 +137,6 @@ class NAT( Node ):
# 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
+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 []
+1
View File
@@ -25,6 +25,7 @@ def runTests( testDir, verbosity=1 ):
.run( testSuite ).wasSuccessful() )
sys.exit( 0 if success else 1 )
if __name__ == '__main__':
setLogLevel( 'warning' )
# get the directory containing example tests
+5 -4
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 )
@@ -131,7 +131,7 @@ class testOptionsTopoCommon( object ):
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first;
lopts = { 'bw': BW, 'use_htb': True }
# Also verify correctness of limit limitng within a bound.
# Also verify correctness of limit limiting within a bound.
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass,
waitConnected=True )
@@ -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()
+21 -16
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
name of the switch does not contain 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"
@@ -77,7 +80,7 @@ class OVSUser( OVSSwitch):
OVSSwitch.__init__( self, *args, **kwargs )
class testSwitchOVSUser( TestSwitchDpidAssignmentOVS ):
"Test dpid assignnment of OVS User Switch."
"Test dpid assignment of OVS User Switch."
switchClass = OVSUser
@unittest.skipUnless( quietRun( 'which ivs-ctl' ),
@@ -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()
+44 -25
View File
@@ -6,13 +6,15 @@ Tests for the Mininet Walkthrough
TODO: missing xterm test
"""
import unittest
import pexpect
import os
import re
from mininet.util import quietRun
from distutils.version import StrictVersion
from time import sleep
import unittest
from sys import stdout
from mininet.util import quietRun, pexpect, StrictVersion
from mininet.clean import cleanup
def tsharkVersion():
"Return tshark version"
@@ -28,6 +30,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"
@@ -43,17 +50,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' )
@@ -67,7 +75,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 )
@@ -92,17 +100,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 )
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ 'h1-eth0', 's1-eth1', r'[^-](eth|en)\w*\d', 'lo',
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"' )
@@ -118,7 +127,7 @@ 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
@@ -143,7 +152,7 @@ class testWalkthrough( unittest.TestCase ):
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' )
@@ -156,14 +165,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 %s 80 >& /dev/null &' % httpserver )
p.expect( self.prompt )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
# 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 couple of seconds to make
# but for now let's wait a number of seconds to make
# it less likely to fail due to the race condition.
sleep( 2 )
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' )
@@ -208,7 +223,9 @@ 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' )
@@ -222,8 +239,8 @@ class testWalkthrough( unittest.TestCase ):
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()
@@ -260,7 +277,7 @@ 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 )
@@ -286,12 +303,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"' )
@@ -312,7 +331,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()' )
@@ -334,7 +353,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 )
+13 -9
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"
@@ -334,9 +336,11 @@ class LinearTopo( Topo ):
self.n = n
if n == 1:
genHostName = lambda i, j: 'h%s' % i
def genHostName( i, _j ):
return 'h%s' % i
else:
genHostName = lambda i, j: 'h%ss%d' % ( j, i )
def genHostName( i, j ):
return 'h%ss%d' % ( j, i )
lastSwitch = None
for i in irange( 1, k ):
+4 -2
View File
@@ -53,9 +53,11 @@ class TorusTopo( Topo ):
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
if n == 1:
genHostName = lambda loc, k: 'h%s' % ( loc )
def genHostName( loc, _k ):
return 'h%s' % ( loc )
else:
genHostName = lambda loc, k: 'h%sx%d' % ( loc, k )
def genHostName( loc, k ):
return 'h%sx%d' % ( loc, k )
hosts, switches, dpid = {}, {}, 0
# Create and wire interior
+183 -62
View File
@@ -1,17 +1,86 @@
"Utility functions for Mininet."
import codecs
import os
import re
import sys
from mininet.log import output, info, error, warn, debug
from time import sleep
from collections import namedtuple
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:
import packaging.version # replacement for distutils.version
StrictVersion = packaging.version.parse
except ImportError: # python2.7 lacks ModuleNotFoundError
import distutils.version # pylint: disable=deprecated-module
StrictVersion = distutils.version.StrictVersion
try:
oldpexpect = None
import pexpect as oldpexpect # pylint: disable=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
@@ -33,13 +102,14 @@ 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 )
out = ''
popen = Popen( # pylint: disable=consider-using-with
cmd, stdout=PIPE, stderr=STDOUT )
# We can't use Popen.communicate() because it uses
# select(), which can't handle
# high file descriptor numbers! poll() can, however.
out = ''
readable = poll()
readable.register( popen.stdout )
while True:
@@ -57,7 +127,9 @@ 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
CmdResult = namedtuple( 'CmdResult', 'out err ret' )
# 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
@@ -74,30 +146,34 @@ 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:
cmd = " ".join( arg for arg in cmd )
debug( '*** errRun:', cmd, '\n' )
# pylint: disable=consider-using-with
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
# We use poll() because select() doesn't work with large fd numbers,
# and thus communicate() doesn't work either
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:
@@ -108,7 +184,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:
@@ -116,8 +192,13 @@ 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
return CmdResult( out, err, returncode )
# pylint: enable=too-many-branches
def errFail( *cmd, **kwargs ):
@@ -126,25 +207,32 @@ def errFail( *cmd, **kwargs ):
if ret:
raise Exception( "errFail: %s failed with return code %s: %s"
% ( cmd, ret, err ) )
return out, err, ret
return CmdResult( out, err, ret )
def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
return errRun( cmd, stderr=STDOUT, **kwargs ).out
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
@@ -331,7 +419,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
@@ -374,30 +462,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:
@@ -406,19 +498,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 )
@@ -435,21 +527,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:
@@ -457,18 +549,25 @@ def fixLimits():
"Mininet's performance may be affected.\n" )
# pylint: enable=broad-except
def mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'cat /proc/mounts' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if ('cgroup %s' % cgdir not in mounts and
'cgroups %s' % cgdir not in mounts):
raise Exception( "cgroups not mounted on " + cgdir )
if 'cpuset %s' % csdir not in mounts:
errRun( 'mkdir -p ' + csdir )
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
def mountCgroups( cgcontrol='cpu cpuacct cpuset' ):
"""Mount cgroupfs if needed and return cgroup version
cgcontrol: cgroup controllers to check ('cpu cpuacct cpuset')
Returns: 'cgroup' | 'cgroup2' """
# Try to read the cgroup controllers in cgcontrol
cglist = cgcontrol.split()
paths = ' '.join( '-g ' + c for c in cglist )
cmd = 'cgget -n %s /' % paths
result = errRun( cmd )
# If it failed, mount cgroupfs and retry
if result.ret or result.err or any(
c not in result.out for c in cglist ):
errFail( 'cgroupfs-mount' )
result = errRun( cmd )
errFail( cmd )
# cpu.cfs_period_us is used for cgroup but not cgroup2
if 'cpu.cfs_period_us' in result.out:
return 'cgroup'
return 'cgroup2'
def natural( text ):
"To sort sanely/alphabetically: sorted( l, key=natural )"
@@ -590,7 +689,6 @@ def ensureRoot():
if os.getuid() != 0:
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.
@@ -600,7 +698,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 )
@@ -618,3 +716,26 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
time += .5
result = runCmd( cmd )
return True
def unitScale( num, prefix='' ):
"Return unit scale prefix and factor"
scale = 'kMGTP'
if prefix:
pos = scale.lower().index( prefix.lower() )
return prefix, float( 10**(3*(pos+1)) )
num, prefix, factor = float( num ), '', 1
for i, c in enumerate(scale, start=1):
f = 10**(3*i)
if num < f:
break
prefix, factor = c, f
return prefix, float( factor )
def fmtBps( bps, prefix='', fmt='%.1f %sbits/sec' ):
"""Return bps as iperf-style formatted rate string
prefix: lock to specific prefix (k, M, G, ...)
fmt: default format string for bps, prefix"""
bps = float( bps )
prefix, factor = unitScale( bps, prefix )
bps /= factor
return fmt % ( bps, prefix)
+14 -3
View File
@@ -24,6 +24,9 @@
#include <sched.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#if !defined(VERSION)
#define VERSION "(devel)"
@@ -96,18 +99,26 @@ int main(int argc, char *argv[])
{
int c;
int fd;
DIR *dir;
struct dirent *de;
char path[PATH_MAX];
int nsid;
int pid;
char *cwd = get_current_dir_name();
static struct sched_param sp;
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
switch(c) {
case 'c':
/* close file descriptors except stdin/out/error */
for (fd = getdtablesize(); fd > 2; fd--)
close(fd);
if ((dir = opendir("/proc/self/fd"))) {
while ((de = readdir(dir)))
if ((fd = atoi(de->d_name)) > 2)
close(fd);
}
/* fall back to old method if needed */
else for (fd = getdtablesize(); fd > 2; fd--)
close(fd);
break;
case 'd':
/* detach from tty */
+1 -1
View File
@@ -2,7 +2,7 @@
"Setuptools params"
from setuptools import setup, find_packages
from setuptools import setup
from os.path import join
# Get version number from source tree
+1 -1
View File
@@ -169,7 +169,7 @@ echo
if $persistent; then
echo '***Setting up persistent SSH configuration between all nodes'
persistentSetup
echo $'\n*** Sucessfully set up ssh throughout the cluster!'
echo $'\n*** Successfully set up ssh throughout the cluster!'
else
echo '*** Setting up temporary SSH configuration between all nodes'
+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 ] )

Some files were not shown because too many files have changed in this diff Show More