Compare commits

...

52 Commits

Author SHA1 Message Date
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
63 changed files with 2520 additions and 1257 deletions
+3 -2
View File
@@ -1,4 +1,5 @@
**Mininet uses GitHub issues for bug reports and feature requests only.**
<!--
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**,
@@ -8,7 +9,7 @@ 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:
+1 -1
View File
@@ -17,6 +17,6 @@ jobs:
- name: Install Mininet code check dependencies
run: |
PYTHON=`which python` util/install.sh -n
python -m pip install pylint==2.4.4
python -m pip install pylint==2.15.7
- name: Run code check
run: make codecheck
+18 -18
View File
@@ -9,43 +9,43 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
python-version: [3.x, 2.x]
os: [ubuntu-22.04, ubuntu-20.04]
py: [python3, python2]
steps:
- name: Check out Mininet source
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
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=`which python` util/install.sh -nv
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"
# Verify major python-version number matches python and mininet
export PYVER=`echo ${{ matrix.python-version }} | cut -d . -f 1`
export CHKVER='px import platform; print(platform.python_version())'
python --version |& grep " $PYVER\."
echo $CHKVER | $sudo mn -v output | grep " $PYVER\."
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
pip install pexpect
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=`which python`
export PYTHON=${{ matrix.py }}
$sudo $PYTHON mininet/test/runner.py -v
- name: Run examples tests (quick)
- name: Run examples tests
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=`which python`
$sudo $PYTHON examples/test/runner.py -v -quick
export PYTHON=${{ matrix.py }}
$sudo $PYTHON examples/test/runner.py -v
+14 -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.
@@ -45,12 +45,13 @@ load-plugins=
# Note: we may want to re-enable some of these at some point, but many of them
# are just style issues rather than errors.
#
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
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, bad-whitespace,
locally-disabled, locally-enabled, bad-continuation,
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
no-else-raise, no-else-continue, super-with-arguments,
consider-using-f-string, unspecified-encoding
# bad-continuation, wrong-import-order
@@ -62,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
@@ -81,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=
@@ -103,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
@@ -144,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
@@ -161,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.
@@ -199,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
@@ -221,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
+13 -21
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0b2
Mininet 2.3.1b4
---
The supported installation methods for Mininet are 1) using a
@@ -51,7 +51,7 @@ like to contribute an installation script, we would welcome it!)
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
@@ -124,7 +124,7 @@ like to contribute an installation script, we would welcome it!)
Whichever version was installed last will be the default for `mn`.
As long as Mininet is installed for the appropriate version of
Python, you can run it using that versinon of Python:
Python, you can run it using that version of Python:
python3 `which mn`
python2 `which mn`
@@ -137,9 +137,7 @@ like to contribute an installation script, we would welcome it!)
This takes about 4 minutes on our test system.
3.2. Native installation from source on Fedora 18+.
*This may be out of date.*
3.2. (Experimental) Native installation from source on Fedora:
As root execute the following operations:
@@ -147,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
@@ -177,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:
@@ -211,7 +200,10 @@ 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.
Good luck, and have fun!
Mininet Developers
+23 -27
View File
@@ -1,33 +1,29 @@
Mininet 2.3.0b2 License
BSD 3-Clause License (original Mininet authors: Bob Lantz and Brandon Heller)
Copyright (c) 2013-2020 Open Networking Foundation
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.
+7 -3
View File
@@ -26,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)
@@ -47,7 +47,8 @@ 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: $(MNEXEC)
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
@@ -56,12 +57,15 @@ 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) -m pip uninstall -y mininet || true
$(PYTHON) -m pip install -e . --no-binary :all:
man: $(MANPAGES)
+17 -10
View File
@@ -2,7 +2,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.3.0b2
Mininet 2.3.1b4
[![Build Status][1]](https://github.com/mininet/mininet/actions)
@@ -70,7 +70,7 @@ Mininet includes:
### Python 3 Support
- Mininet 2.3.0b2 supports Python 3 and Python 2.
- Mininet 2.3.1b4 supports Python 3 and Python 2
- You can install both the Python 3 and Python 2 versions of
Mininet side by side, but the most recent installation will
@@ -82,12 +82,19 @@ determine which Python version is used by default by `mn`.
$ sudo python2 `which mn`
- More information regarding Python 3 and Python 2 support
may be found in the release notes on http://docs.mininet.org.
### Other Enhancements and Information
- Support for Ubuntu 20.04 LTS (and 18.04 and 16.04)
- 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.
@@ -114,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
Bob Lantz,
on behalf of the Mininet Contributors
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
+12 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Mininet runner
@@ -101,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()
@@ -131,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 '
@@ -191,8 +189,9 @@ class MininetRunner( object ):
customs = {}
if os.path.isfile( fileName ):
# pylint: disable=exec-used
exec( compile( open( fileName ).read(), fileName, 'exec' ),
customs, customs )
with open( fileName ) as f:
exec( compile( f.read(), fileName, 'exec' ),
customs, customs )
for name, val in customs.items():
self.setCustom( name, val )
else:
@@ -290,6 +289,9 @@ class MininetRunner( object ):
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!)' ) )
@@ -386,6 +388,11 @@ 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,
+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
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
bind.py: Bind mount example
+64 -36
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
cluster.py: prototyping/experimentation for distributed Mininet,
@@ -82,14 +82,13 @@ 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
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
@@ -247,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
@@ -314,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:
@@ -376,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
@@ -440,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 ):
@@ -778,7 +781,9 @@ class MininetCluster( Mininet ):
"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
@@ -848,27 +853,37 @@ 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 )
loopback = '127.0.0.1'
controllerIP = controller.IP()
if ( not isinstance( controller, Controller ) or
controller.IP() != loopback ):
return None
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 is not controller.IP() ]
if ip != controllerIP ]
if not serverIPs:
return None # no remote servers - loopback is fine
remoteIP = serverIPs[ 0 ]
# Route should contain 'dev <intfname>'
route = controller.cmd( 'ip route get', remoteIP,
r'| egrep -o "dev\s[^[:space:]]+"' )
if not route:
raise Exception('addController: no route from', controller,
'to', remoteIP )
intf = route.split()[ 1 ].strip()
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() )
@@ -883,7 +898,10 @@ 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, waitConnected=True )
h1 = net.addHost( 'h1')
@@ -898,14 +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" )
@@ -937,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"
@@ -954,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 ):
@@ -974,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 )
@@ -992,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 )
@@ -1002,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 )
@@ -1017,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 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"CLI for Mininet Cluster Edition prototype demo"
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
+9 -8
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
@@ -69,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."
@@ -111,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."
@@ -294,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' )
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network where different switches are connected to
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example creates a multi-controller network from semi-scratch by
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
controlnet.py: Mininet with a custom control network
+10 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
cpu.py: test iperf bandwidth for varying cpu limits
@@ -68,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 = decode( 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, [] )
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example shows how to create an empty Mininet object
+1 -1
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
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
'''
example of using various TCIntf options.
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
limit.py: example of using link and CPU limits
+2 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Test bandwidth (using iperf) on linear networks of varying size,
@@ -83,9 +83,8 @@ def linearBandwidthTest( lengths ):
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath in switches:
for datapath, Switch in switches.items():
info( "*** testing", datapath, "datapath\n" )
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='30ms', bw=100 )
net = Mininet( topo=topo, switch=Switch,
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
linuxrouter.py: Example network with Linux IP router
+58 -63
View File
@@ -18,16 +18,15 @@ import os
import re
import sys
from distutils.version import StrictVersion
from functools import partial
from optparse import OptionParser
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,
buildTopo, custom, customClass )
buildTopo, custom, customClass, StrictVersion )
from mininet.term import makeTerm, cleanUpScreens
from mininet.node import (Controller, RemoteController, NOX, OVSController,
CPULimitedHost, Host, Node,
@@ -1363,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 ] )
@@ -1421,10 +1420,7 @@ class MiniEdit( Frame ):
def convertJsonUnicode(self, text):
"Some part of Mininet don't like Unicode"
try:
unicode
except NameError:
return text
unicode = globals.get( 'unicode', str )
if isinstance(text, dict):
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
if isinstance(text, list):
@@ -1449,7 +1445,7 @@ class MiniEdit( Frame ):
# 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"]:
@@ -1607,7 +1603,7 @@ class MiniEdit( Frame ):
def newTopology( self ):
"New command."
for widget in self.widgetToItem:
for widget in tuple( self.widgetToItem ):
self.deleteItem( self.widgetToItem[ widget ] )
self.hostCount = 0
self.switchCount = 0
@@ -1635,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),
@@ -1683,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."
@@ -1702,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")
@@ -1718,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]
@@ -1746,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]
@@ -1780,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")
@@ -1820,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
@@ -1915,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'])
@@ -1926,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':
@@ -1956,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
@@ -1980,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]
@@ -2004,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]
@@ -2021,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
@@ -2445,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 )
@@ -2620,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']:
@@ -2703,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 ]
@@ -2719,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:
@@ -2935,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]
@@ -2962,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]
@@ -2992,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]
@@ -3037,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 = []
@@ -3059,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]
@@ -3204,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()
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of Mobility with Mininet
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This is a simple example that demonstrates multiple links
+3 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
multiping.py: monitor multiple sets of hosts using ping
@@ -17,9 +17,9 @@ from mininet.topo import SingleSwitchTopo
from mininet.log import info, setLogLevel
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"
+5 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of sending output to multiple files and
@@ -18,11 +18,12 @@ 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.items():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
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
+1 -1
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.
+1 -1
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
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
natnet.py: Example network with NATs
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network with 5 hosts, numbered 1-4 and 9.
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
This example monitors a number of hosts using host.popen() and
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"Monitor multiple hosts using popen()/pmonitor()"
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Build a simple network from scratch, using mininet primitives.
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Build a simple network from scratch, using mininet primitives.
+8 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Simple example of setting network and CPU parameters
@@ -49,13 +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 ) )
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a network and start sshd(8) on each host.
+6 -3
View File
@@ -19,9 +19,12 @@ class testSimplePerf( unittest.TestCase ):
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
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()
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
Create a 1024-host network, and run the CLI on it.
+2 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
"Create a 64-node tree network, and test connectivity using ping."
@@ -25,9 +25,8 @@ def treePing64():
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,
waitConnected=True )
result = network.run( network.pingAll )
+1
View File
@@ -0,0 +1 @@
../bin/mn
+6 -4
View File
@@ -21,7 +21,9 @@ from mininet.util import decode
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
result = 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 ):
@@ -51,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.
+20 -10
View File
@@ -89,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"
@@ -142,7 +150,7 @@ class CLI( Cmd ):
' mininet> xterm h2\n\n'
)
def do_help( self, line ): # pylint: disable=arguments-differ
def do_help( self, line ): # pylint: disable=arguments-renamed
"Describe available CLI commands."
Cmd.do_help( self, line )
if line == '':
@@ -176,7 +184,7 @@ class CLI( Cmd ):
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' )
@@ -344,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()
@@ -448,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 != '':
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 )
+5 -6
View File
@@ -316,8 +316,9 @@ class TCIntf( Intf ):
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
# pylint: disable=arguments-differ
def config( self, bw=None, delay=None, jitter=None, loss=None,
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,
@@ -539,9 +540,7 @@ class OVSLink( Link ):
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
try:
OVSSwitch
except NameError:
if 'OVSSwitch' not in globals():
# pylint: disable=import-outside-toplevel,cyclic-import
from mininet.node import OVSSwitch
self.isPatchLink = False
@@ -551,7 +550,7 @@ class OVSLink( Link ):
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
# pylint: disable=arguments-differ, signature-differs
# pylint: disable=arguments-renamed, arguments-differ, signature-differs
def makeIntfPair( self, *args, **kwargs ):
"Usually delegated to OVSSwitch"
if self.isPatchLink:
+59 -42
View File
@@ -98,18 +98,18 @@ 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
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, BaseString )
waitListening, BaseString, fmtBps )
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.0b2"
VERSION = "2.3.1b4"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -137,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
@@ -175,15 +177,17 @@ class Mininet( object ):
if topo and build:
self.build()
def waitConnected( self, timeout=5, delay=.5 ):
"""wait for each switch to connect to a controller,
up to 5 seconds
timeout: time to wait, or None to wait indefinitely
def waitConnected( self, timeout=None, delay=.5 ):
"""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():
@@ -192,12 +196,12 @@ class Mininet( object ):
if not remaining:
info( '\n' )
return True
if timeout is not None and time > timeout:
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 )
@@ -559,7 +563,7 @@ class Mininet( object ):
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"
@@ -568,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()
@@ -667,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:
@@ -778,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
@@ -799,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
@@ -812,33 +828,34 @@ 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 )
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
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 )
+59 -45
View File
@@ -57,7 +57,6 @@ import pty
import re
import signal
import select
from distutils.version import StrictVersion
from re import findall
from subprocess import Popen, PIPE
from sys import exit # pylint: disable=redefined-builtin
@@ -66,7 +65,8 @@ from time import sleep
from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
numCores, retry, mountCgroups, BaseString, decode,
encode, getincrementaldecoder, Python3, which )
encode, getincrementaldecoder, Python3, which,
StrictVersion )
from mininet.moduledeps import moduleDeps, pathCheck, TUN
from mininet.link import Link, Intf, TCIntf, OVSIntf
@@ -219,7 +219,7 @@ class Node( object ):
params: parameters to Popen()"""
# Leave this is as an instance method for now
assert self
popen = Popen( cmd, **params )
popen = Popen( cmd, **params ) # pylint: disable=consider-using-with
debug( '_popen', cmd, popen.pid )
return popen
@@ -685,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 mininimum 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()
@@ -696,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"
@@ -788,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
@@ -797,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:
@@ -827,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"
@@ -857,6 +870,7 @@ class CPULimitedHost( Host ):
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 )
@@ -864,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:
#
@@ -1505,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 not isinstance( ryuArgs, ( 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 ):
+2 -3
View File
@@ -10,10 +10,9 @@ import os
import re
import unittest
from distutils.version import StrictVersion
from sys import stdout
from mininet.util import quietRun, pexpect
from mininet.util import quietRun, pexpect, StrictVersion
from mininet.clean import cleanup
@@ -132,7 +131,7 @@ class testWalkthrough( unittest.TestCase ):
ifcount += 1
else:
break
self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
+4 -2
View File
@@ -336,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
+63 -21
View File
@@ -5,6 +5,7 @@ import os
import re
import sys
from collections import namedtuple
from fcntl import fcntl, F_GETFL, F_SETFL
from functools import partial
from os import O_NONBLOCK
@@ -46,6 +47,7 @@ if Python3:
"Encode buffer for Python 3"
return buf.encode( Encoding )
getincrementaldecoder = codecs.getincrementaldecoder( Encoding )
else:
decode, encode = NullCodec.decode, NullCodec.encode
@@ -54,11 +56,16 @@ else:
return NullCodec
try:
# pylint: disable=import-error
oldpexpect = None
import pexpect as oldpexpect
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
# pylint: enable=import-error
class Pexpect( object ):
"Custom pexpect that is compatible with str"
@staticmethod
@@ -97,11 +104,12 @@ def oldQuietRun( *cmd ):
cmd = cmd[ 0 ]
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:
@@ -119,6 +127,8 @@ def oldQuietRun( *cmd ):
# This is a bit complicated, but it enables us to
# monitor command output as it is happening
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
@@ -142,6 +152,7 @@ def errRun( *cmd, **kwargs ):
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
@@ -186,7 +197,8 @@ def errRun( *cmd, **kwargs ):
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 ):
@@ -195,11 +207,11 @@ 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"
@@ -537,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( 'grep cgroup /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 )"
@@ -697,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 -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 ] )
+31 -14
View File
@@ -164,7 +164,9 @@ function mn_deps {
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8 python-pexpect
ethtool help2man net-tools
$install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming
${PYPKG}-pexpect
elif [ "$DIST" = "SUSE LINUX" ]; then
$install gcc make socat psmisc xterm openssh iperf \
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
@@ -172,28 +174,39 @@ function mn_deps {
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
else # Debian/Ubuntu
pf=pyflakes
pep8=pep8
# Starting around 20.04, installing pyflakes instead of pyflakes3
# causes Python 2 to be installed, which is exactly NOT what we want.
if [ `expr $RELEASE '>=' 20.04` = "1" ]; then
if [ "$DIST" = "Ubuntu" -a `expr $RELEASE '>=' 20.04` = "1" ]; then
pf=pyflakes3
fi
# Debian 11 "bullseye" renamed
# * pep8 to python3-pep8
# * pyflakes to pyflakes3
if [ "$DIST" = "Debian" -a `expr $RELEASE '>=' 11` = "1" ]; then
pf=pyflakes3
pep8=python3-pep8
fi
$install gcc make socat psmisc xterm ssh iperf telnet \
ethtool help2man $pf pylint pep8 \
net-tools \
${PYPKG}-pexpect ${PYPKG}-tk
ethtool help2man $pf pylint $pep8 \
net-tools ${PYPKG}-tk
# Install pip
$install ${PYPKG}-pip || $install ${PYPKG}-pip-whl
if ! ${PYTHON} -m pip -V; then
if [ $PYTHON_VERSION == 2 ]; then
wget https://bootstrap.pypa.io/2.6/get-pip.py
wget https://bootstrap.pypa.io/pip/2.7/get-pip.py
else
wget https://bootstrap.pypa.io/get-pip.py
fi
sudo ${PYTHON} get-pip.py
rm get-pip.py
fi
${python} -m pip install pexpect
$install iproute2 || $install iproute
$install cgroup-tools || $install cgroup-bin
$install cgroupfs-mount
fi
echo "Installing Mininet core"
@@ -205,10 +218,11 @@ function mn_deps {
# Install Mininet documentation dependencies
function mn_doc {
echo "Installing Mininet documentation dependencies"
$install doxygen doxypy texlive-fonts-recommended
$install doxygen texlive-fonts-recommended
if ! $install doxygen-latex; then
echo "doxygen-latex not needed"
fi
sudo pip2 install doxypy
}
# The following will cause a full OF install, covering:
@@ -228,7 +242,7 @@ function of {
fi
# was: git clone git://openflowswitch.org/openflow.git
# Use our own fork on github for now:
git clone git://github.com/mininet/openflow
git clone https://github.com/mininet/openflow
cd $BUILD_DIR/openflow
# Patch controller to handle more than 16 switches
@@ -407,7 +421,10 @@ function ovs {
echo "Installing Open vSwitch..."
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install openvswitch openvswitch-controller
$install openvswitch
if ! $install openvswitch-controller; then
echo "openvswitch-controller not installed"
fi
return
fi
@@ -492,7 +509,7 @@ function ivs {
# Install IVS from source
cd $BUILD_DIR
git clone git://github.com/floodlight/ivs $IVS_SRC
git clone https://github.com/floodlight/ivs $IVS_SRC
cd $IVS_SRC
git submodule update --init
make
@@ -512,7 +529,7 @@ function ryu {
# fetch RYU
cd $BUILD_DIR/
git clone git://github.com/osrg/ryu.git ryu
git clone https://github.com/osrg/ryu.git ryu
cd ryu
# install ryu
@@ -623,7 +640,7 @@ function oftest {
# Install oftest:
cd $BUILD_DIR/
git clone git://github.com/floodlight/oftest
git clone https://github.com/floodlight/oftest
}
# Install cbench
@@ -640,7 +657,7 @@ function cbench {
cd $BUILD_DIR/
# was: git clone git://gitosis.stanford.edu/oflops.git
# Use our own fork on github for now:
git clone git://github.com/mininet/oflops
git clone https://github.com/mininet/oflops
cd oflops
sh boot.sh || true # possible error in autoreconf, so run twice
sh boot.sh
@@ -791,7 +808,7 @@ function vm_clean {
echo 'Removing SSH keys from /etc/ssh/'
sudo rm -f /etc/ssh/*key*
if [ ! -e /etc/rc.local ]; then
echo '#!/usr/bin/bash' | sudo tee /etc/rc.local
echo '#!/bin/bash' | sudo tee /etc/rc.local
sudo chmod +x /etc/rc.local
fi
if ! grep mininet /etc/rc.local >& /dev/null; then
+15 -15
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python
#!/usr/bin/python2
"""
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
Python style
usage: unpep8 < old.py > new.py
@@ -51,7 +51,7 @@ def fixUnderscoreTriplet( match ):
def reinstateCapWords( text ):
underscoreTriplet = re.compile( r'[A-Za-z0-9]_[A-Za-z0-9]' )
return underscoreTriplet.sub( fixUnderscoreTriplet, text )
def replaceTripleApostrophes( text ):
"Replace triple apostrophes with triple quotes."
return text.replace( "'''", '"""')
@@ -60,7 +60,7 @@ def simplifyTripleQuotes( text ):
"Fix single-line doc strings."
r = re.compile( r'"""([^\"\n]+)"""' )
return r.sub( r'"\1"', text )
def insertExtraSpaces( text ):
"Insert extra spaces inside of parentheses and brackets/curly braces."
lparen = re.compile( r'\((?![\s\)])' )
@@ -76,9 +76,9 @@ def insertExtraSpaces( text ):
lcurly = re.compile( r'\{(?![\s\}])' )
text = lcurly.sub( r'{ ', text )
rcurly = re.compile( r'([^\s\{])(?=\})' )
text = rcurly.sub( r'\1 ', text)
text = rcurly.sub( r'\1 ', text)
return text
def fixDoxygen( text ):
"""Translate @param foo to foo:, @return bar to returns: bar, and
@author me to author: me"""
@@ -96,11 +96,11 @@ def removeCommentFirstBlankLine( text ):
"Remove annoying blank lines after first line in comments."
line = re.compile( r'("""[^\n]*\n)\s*\n', re.MULTILINE )
return line.sub( r'\1', text )
def fixArgs( match, kwarg = re.compile( r'(\w+) = ' ) ):
"Replace foo = bar with foo=bar."
return kwarg.sub( r'\1=', match.group() )
def fixKeywords( text ):
"Change keyword argumentsfrom foo = bar to foo=bar."
args = re.compile( r'\(([^\)]+)\)', re.MULTILINE )
@@ -115,7 +115,7 @@ def fixKeywords( text ):
def lineIter( text ):
"Simple iterator over lines in text."
for line in text.splitlines(): yield line
def stringIter( strList ):
"Yield strings in strList."
for s in strList: yield s
@@ -134,7 +134,7 @@ def restoreRegex( regex, old, new ):
# This is a cheap hack, and it may not work 100%, since
# it doesn't handle multiline strings.
# However, it should be mostly harmless...
def restoreStrings( oldText, newText ):
"Restore strings from oldText into newText, returning result."
oldLines, newLines = lineIter( oldText ), lineIter( newText )
@@ -149,13 +149,13 @@ def restoreStrings( oldText, newText ):
newLine = restoreRegex( tickStrings, oldLine, newLine )
result += newLine + '\n'
return result
# This might be slightly controversial, since it uses
# three spaces to line up multiline comments. However,
# I much prefer it. Limitations: if you have deeper
# indents in comments, they will be eliminated. ;-(
def fixComment( match,
def fixComment( match,
indentExp=re.compile( r'\n([ ]*)(?=[^/s])', re.MULTILINE ),
trailingQuotes=re.compile( r'\s+"""' ) ):
"Re-indent comment, and join trailing quotes."
@@ -166,17 +166,17 @@ def fixComment( match,
if len( originalIndent ) is not 0: indent += ' '
comment = indentExp.sub( indent, comment )
return originalIndent + trailingQuotes.sub( '"""', comment )
def fixCommentIndents( text ):
"Fix multiline comment indentation."
comments = re.compile( r'^([ ]*)("""[^"]*""")$', re.MULTILINE )
return comments.sub( fixComment, text )
def removeBogusLinefeeds( text ):
"Remove extra linefeeds at the end of single-line comments."
bogusLfs = re.compile( r'"([^"\n]*)\n"', re.MULTILINE )
return bogusLfs.sub( '"\1"', text)
def convertFromPep8( program ):
oldProgram = program
# Program text transforms
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
from subprocess import check_output as co
from sys import exit, version_info
+4 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python2.7
#!/usr/bin/python2
"""
build.py: build a Mininet VM
@@ -954,7 +954,8 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
log( '* Creating COW disk', cow )
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) )
log( '* Extracting kernel and initrd' )
kernel, initrd, part = extractKernel( image, flavor=basename, imageDir=tmpdir )
kernel, initrd, partnum = extractKernel(
image, flavor=basename, imageDir=tmpdir )
if LogToConsole:
logfile = stdout
else:
@@ -962,7 +963,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
suffix='.testlog', delete=False )
log( '* Logging VM output to', logfile.name )
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
memory=memory, cpuCores=cpuCores, part=part )
memory=memory, cpuCores=cpuCores, partnum=partnum )
login( vm )
log( '* Waiting for prompt after login' )
vm.expect( prompt )
+1 -1
View File
@@ -25,7 +25,7 @@ if [ -e /etc/rc.local.backup ]; then
fi
# Fetch Mininet
sudo apt-get -y -qq install git-core openssh-server
git clone git://github.com/mininet/mininet
git clone https://github.com/mininet/mininet
# Optionally check out branch
if [ "$1" != "" ]; then
pushd mininet