Compare commits

..

1 Commits

Author SHA1 Message Date
Bob Lantz 176870d586 2.1.0p1 2013-12-11 19:24:47 -08:00
107 changed files with 3247 additions and 12203 deletions
-20
View File
@@ -1,20 +0,0 @@
<!--
Mininet uses GitHub issues for bug reports and feature requests only.
These issues can be viewed at bugs.mininet.org
If you have a question that is not a **bug report** or a **feature request**,
please use the documentation at docs.mininet.org, the FAQ
at faq.mininet.org, and the mininet-discuss mailing list.
For bug reports, please fill in the following information in detail,
and also feel free to include additional information such as debug
output from mn -v debug, etc.
-->
### Expected/Desired Behavior:
### Actual Behavior:
### Detailed Steps to Reproduce the Behavior:
### Additional Information:
-22
View File
@@ -1,22 +0,0 @@
name: code-check
on: [push, pull_request]
jobs:
code-check:
name: Mininet Code Check
runs-on: ubuntu-latest
steps:
- name: Set up Python 3.x
uses: actions/setup-python@v2
with:
python-version: 3.x
- name: Check out Mininet source
uses: actions/checkout@v2
- name: Install Mininet code check dependencies
run: |
PYTHON=`which python` util/install.sh -n
python -m pip install pylint==2.15.7
- name: Run code check
run: make codecheck
-51
View File
@@ -1,51 +0,0 @@
name: mininet-tests
on: [push, pull_request]
jobs:
test:
name: Mininet Tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-20.04]
py: [python3, python2]
steps:
- name: Check out Mininet source
uses: actions/checkout@v3
- name: Install Python ${{ matrix.py }}
run: sudo apt install ${{ matrix.py }}
- name: Install Mininet and base dependencies
run: |
sudo apt-get update -qq
# This seems too slow unfortunately:
# sudo apt-get upgrade -y -qq
PYTHON=${{ matrix.py }} util/install.sh -nv
- name: Disable slow udevd
run: sudo systemctl stop systemd-udevd
systemd-udevd-kernel.socket
systemd-udevd-control.socket
|| echo "couldn't disable udevd"
- name: Sanity test
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=${{ matrix.py }}
# Newer OvS tries OpenFlow15 which crashes ovsc on ubuntu-20.04
$sudo mn --switch ovs,protocols=OpenFlow13 --test pingall
- name: Install test dependencies
run: |
sudo apt-get install -qq vlan
export PYTHON=${{ matrix.py }}
sudo $PYTHON -m pip install pexpect
util/install.sh -fw
- name: Run core tests
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=${{ matrix.py }}
$sudo $PYTHON mininet/test/runner.py -v
- name: Run examples tests
run: |
export sudo="sudo env PATH=$PATH"
export PYTHON=${{ matrix.py }}
$sudo $PYTHON examples/test/runner.py -v
+17 -30
View File
@@ -16,7 +16,7 @@
#init-hook=
# Profiled execution.
#profile=no
profile=no
# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
@@ -41,36 +41,24 @@ load-plugins=
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
#
# Note: we may want to re-enable some of these at some point, but many of them
# are just style issues rather than errors.
#
disable=invalid-name, super-init-not-called, fixme,
too-many-instance-attributes, too-few-public-methods,
too-many-locals, too-many-public-methods, duplicate-code,
locally-disabled,
useless-object-inheritance, unnecessary-pass, no-else-return,
no-else-raise, no-else-continue, super-with-arguments,
consider-using-f-string, unspecified-encoding
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
# bad-continuation, wrong-import-order
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=colorized
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
# Include message's id in output
# include-ids=yes
# Include message's id in outpu
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
# Tells wether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highes
@@ -82,7 +70,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=
@@ -104,7 +92,7 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme
[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
@@ -120,10 +108,10 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9]{2,30}$
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9]{2,30}$
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
@@ -132,7 +120,7 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9]{2,30}$
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
@@ -145,7 +133,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
@@ -162,7 +150,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.
@@ -200,10 +188,10 @@ 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
defining-attr-methods=__init__,__new__,setUp
# checks for sign of poor/misdesign:
@@ -222,7 +210,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
@@ -276,8 +264,7 @@ int-import-graph=
max-line-length=80
# Maximum number of lines in a module
# XXX 1500 -> 4000 for miniedit.py
max-module-lines=4000
max-module-lines=1500
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
+2 -44
View File
@@ -7,74 +7,32 @@ or send a pull request.
Contributors include:
Mininet Core Team (and alumni)
Mininet Core Team
Bob Lantz
Brandon Heller
Nikhil Handigol
Vimal Jeyakumar
Brian O'Connor
Cody Burkard
Additional Mininet Contributors
Joseph Beshay
M S Vishwanath Bhat
Muhammad Umair Bhatti
Arie Bregman
Tomasz Buchert
Fernando Cappi
HW Chiu
Gustavo Pantuza Coelho Pinto
Ryan Cox
Shaun Crampton
Jason Croft
Hantao Cui
Nirmoy Das
Lenoardo D'avila
Giuseppe Di Lena
David Erickson
Juan Gascon
Glen Gibb
Andrew Ferguson
Eder Leao Fernandes
Julian Filter
Ben Frankel
Tim Gates
Gregory Gee
Jon Hall
Roan Huang
Vitaly Ivanov
Theo Jepsen
Mathieu Jadin
Babis Kaidos
Rich Lane
Rémy Léone
Xiaozhou Li
Zi Shen Lim
David Mahler
Felix Maurer
Murphy McCauley
Alex Moijes
Felician Nemeth
José Pedro Oliveira
James Page
Gustavo Pantuza Coelho Pinto
Ramon Pujianto
Stempha Reiter
Damien Saucez
Shan Sikdar
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Olivier Tl]ilmans
Niels van Adrichem
Brad Walker
Andreas Wundsam
Vikas Yadav
Isaku Yamahata
Baohua Yang
Zhuo
Thanks also to everyone who has submitted issues and pull
requests on github, and to our friendly mininet-discuss
+45 -89
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.1b4
Mininet 2.1.0p1
---
The supported installation methods for Mininet are 1) using a
@@ -32,112 +32,61 @@ like to contribute an installation script, we would welcome it!)
2. Next-easiest option: use our Ubuntu package!
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
16.04+:
12.10+:
sudo apt-get install mininet
Note: this may install an older version of Mininet which may not
support Python 3. If you would like the latest version of Mininet,
consider installing from source as described in the next section.
Note: if you are upgrading from an older version of Mininet, make
sure you remove the old OVS from `/usr/local`:
sudo rm /usr/local/bin/ovs*
sudo rm /usr/local/sbin/ovs*
3. Native installation from source
If you are running Ubuntu, Debian, or Fedora, you may be able to use
our handy `install.sh` script, which is in `util/`. Please read the
following sections first.
3.1. Obtaining the Mininet source code
3.1. Native installation from source on Ubuntu 12.04+
If you're reading this, you've probably already done so, but the
command to download the Mininet source code is:
git clone https://github.com/mininet/mininet.git
git clone git://github.com/mininet/mininet.git
Note that the above git command will check out the latest and greatest
Mininet (which we recommend!) If you want to run the last
tagged/released version of Mininet, you can look at the release tags
using
If you are running Ubuntu, you may be able to use our handy
`install.sh` script, which is in `mininet/util`.
cd mininet
git tag
*WARNING: USE AT YOUR OWN RISK!*
and then
git checkout <release tag>
where <release tag> is the release you want to check out.
3.1.1 *CAUTION: USE AT YOUR OWN RISK!*
`install.sh` can be a bit intrusive and may possibly damage your OS
`install.sh` is a bit intrusive and may possibly damage your OS
and/or home directory, by creating/modifying several directories
such as `mininet`, `openflow`, `oftest`, `pox`, etc.. We recommend
trying it in a VM before trying it on a system you use from day to
day.
such as `mininet`, `openflow`, `oftest`, `pox`, etc..
Although we hope it won't do anything completely terrible, you may
want to look at the script before you run it, and you should make
sure your system and home directory are backed up just in case!
You can change the directory where the dependencies are installed
using the -s <directory> flag.
util/install.sh -s <directory> ...
3.1.2 Running `install.sh`
Installing a "minimal" version of Mininet with Open vSwitch should
be reasonably non-perturbing since it should not create directories
for other tools:
util/install.sh -nv
Note this will not install a controller, so you will have to either
install your own controller, or use a switch such OVSBridge that does
not require a controller:
sudo mn --switch ovsbr --test pingall
To install Mininet itself, the OpenFlow reference controller, and
To install Mininet itself, the OpenFlow reference implementation, and
Open vSwitch, you may use:
util/install.sh -fnv
mininet/util/install.sh -fnv
This should be reasonably quick, and the following command should
work after the installation:
sudo mn --test pingall
3.1.3 Python 3 and Python 2 support
Mininet supports Python 3 and Python 2. By default, `install.sh`
will use whatever `python` is on your system. To specify a
specific version of Python, you can set the PYTHON environment
variable:
PYTHON=python3 util/install.sh -fnv
You can install Mininet for both Python 3 and Python 2:
PYTHON=python2 util/install.sh -fnv
PYTHON=python3 util/install.sh -n
Whichever version was installed last will be the default for `mn`.
As long as Mininet is installed for the appropriate version of
Python, you can run it using that version of Python:
python3 `which mn`
python2 `which mn`
To install ALL of the software which we use for OpenFlow tutorials,
including POX, the OpenFlow WireShark dissector, the `oftest`
framework, and other potentially useful software, you may use:
util/install.sh -a
mininet/util/install.sh -a
This takes about 4 minutes on our test system.
3.2. (Experimental) Native installation from source on Fedora:
You can change the directory where the dependencies are installed using
the -s <directory> flag.
mininet/util/install.sh -s <directory> -a
3.2. Native installation from source on Fedora 18+.
As root execute the following operations:
@@ -145,14 +94,26 @@ 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 https://github.com/mininet/mininet.git
git clone git://github.com/mininet/mininet.git
* install Mininet, the OpenFlow reference implementation, and
Open vSwitch
util/install.sh -fnv
mininet/util/install.sh -fnv
* enable and start openvswitch
@@ -163,10 +124,7 @@ like to contribute an installation script, we would welcome it!)
sudo mn --test pingall
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
4. Creating your own Mininet/OpenFlow tutorial VM
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:
@@ -182,15 +140,16 @@ like to contribute an installation script, we would welcome it!)
Although we don't support other Linux distributions directly, it
should be possible to install and run Mininet with some degree of
manual effort. People have even gotten `mn --switch user` to run
in a ChromeOS container.
manual effort.
In general, you must have:
* A Linux kernel compiled with network namespace support enabled
* An compatible software switch such as Open vSwitch or
the Linux bridge.
* An OpenFlow implementation (either the reference user or kernel
space implementations, or Open vSwitch.) Appropriate kernel
modules (e.g. tun and ofdatapath for the reference kernel
implementation) must be loaded.
* Python, `bash`, `ping`, `iperf`, etc.
@@ -200,11 +159,8 @@ like to contribute an installation script, we would welcome it!)
support other Linux distributions.
As always, please feel free to submit issues or pull requests for
installation-related features.
Good luck!
Good luck, and have fun!
Mininet Developers
Mininet Team
---
+27 -23
View File
@@ -1,29 +1,33 @@
BSD 3-Clause License (original Mininet authors: Bob Lantz and Brandon Heller)
Mininet 2.1.0p1 License
Copyright (c) 2013-2022 Open Networking Foundation
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of The Leland Stanford Junior University
Copyright (c) 2013 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Original authors: Bob Lantz and Brandon Heller
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
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:
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 above copyright notice and this permission notice shall be included
in all copies or substantial portions of 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 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.
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.
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.
+17 -35
View File
@@ -2,19 +2,15 @@ MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
PYTHON ?= python
PYMN = $(PYTHON) -B bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226,E402,W504,W503,E731
PREFIX ?= /usr
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man/man1
P8IGN = E251,E201,E302,E202
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
DOCDIRS = doc/html doc/latex
PDF = doc/latex/refman.pdf
CC ?= cc
CFLAGS += -Wall -Wextra
@@ -26,14 +22,13 @@ clean:
codecheck: $(PYSRC)
-echo "Running code check"
util/versioncheck.py
pyflakes3 $(PYSRC) || pyflakes $(PYSRC)
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
# Exclude miniedit from pep8 checking for now
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
errcheck: $(PYSRC)
-echo "Running check for errors only"
pyflakes3 $(PYSRC) || pyflakes $(PYSRC)
pyflakes $(PYSRC)
pylint -E --rcfile=.pylint $(PYSRC)
test: $(MININET) $(TEST)
@@ -41,42 +36,29 @@ test: $(MININET) $(TEST)
mininet/test/test_nets.py
mininet/test/test_hifi.py
slowtest: $(MININET)
-echo "Running slower tests (walkthrough, examples)"
mininet/test/test_walkthrough.py -v
mininet/examples/test/runner.py -v
mnexec: mnexec.c $(MN) mininet/net.py
$(CC) $(CFLAGS) $(LDFLAGS) \
-DVERSION=\"`PYTHONPATH=. $(PYMN) --version 2>&1`\" $< -o $@
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
install-mnexec: $(MNEXEC)
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
install-manpages: $(MANPAGES)
install -D -t $(MANDIR) $(MANPAGES)
install: install-mnexec install-manpages
# This seems to work on all pip versions
$(PYTHON) -m pip uninstall -y mininet || true
$(PYTHON) -m pip install .
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
$(PYTHON) -m pip uninstall -y mininet || true
$(PYTHON) -m pip install -e . --no-binary :all:
python setup.py install
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py develop
man: $(MANPAGES)
mn.1: $(MN)
PYTHONPATH=. help2man -N -n "create a Mininet network." \
--no-discard-stderr "$(PYMN)" -o $@
--no-discard-stderr $< -o $@
mnexec.1: mnexec
help2man -N -n "execution utility for Mininet." \
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
.PHONY: doc
+36 -45
View File
@@ -1,11 +1,9 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.3.1b4
[![Build Status][1]](https://github.com/mininet/mininet/actions)
Version 2.1.0p1
### What is Mininet?
@@ -68,35 +66,32 @@ Mininet includes:
`mn -c`
### Python 3 Support
### New features in 2.1.0p1
- Mininet 2.3.1b4 supports Python 3 and Python 2
Mininet 2.1.0p1 provides a number of bug fixes as well as
several new features, including:
- You can install both the Python 3 and Python 2 versions of
Mininet side by side, but the most recent installation will
determine which Python version is used by default by `mn`.
* Convenient access to `Mininet()` as a dict of nodes
* X11 tunneling (wireshark in Mininet hosts, finally!)
* Accurate reflection of the `Mininet()` object in the CLI
* Automatically detecting and adjusting resource limits
* Automatic cleanup on failure of the `mn` command
* Preliminary support for running OVS in user space mode
* Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch
* support for installing the OpenFlow 1.3 versions of the reference
user switch and NOX from CPqD
* The ability to import modules from `mininet.examples`
- You can run `mn` directly with Python 2 or Python 3,
as long as the appropriate version of Mininet is installed,
e.g.
We have provided several new examples (which can easily be
imported to provide useful functionality) including:
$ sudo python2 `which mn`
* Modeling separate control and data networks: `mininet.examples.controlnet`
* Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat`
* Creating per-host custom directories using bind mounts: `mininet.examples.bind`
- 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 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.
Note that examples contain experimental features which might
"graduate" into mainline Mininet in the future, but they should
not be considered a stable part of the Mininet API!
### Installation
@@ -109,8 +104,7 @@ information, including a Mininet walkthrough and an introduction
to the Python API, is available on the
[Mininet Web Site](http://mininet.org).
There is also a wiki which you are encouraged to read and to
contribute to, particularly the Frequently Asked Questions
(FAQ) at http://faq.mininet.org.
contribute to, particularly the Frequently Asked Questions (FAQ.)
### Support
@@ -119,24 +113,21 @@ Mininet mailing list, `mininet-discuss` at:
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
### Join Us
Thanks again to all of the Mininet contributors and users!
### Contributing
Mininet is an open source project and is currently hosted
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.
at <https://github.com/mininet>. You are encouraged to download
the code, examine it, modify it, and submit bug reports, bug fixes,
feature requests, new features and other issues and pull requests.
Thanks to everyone who has contributed to the project
(see CONTRIBUTORS for more info!)
### 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.
### Credits
Bob Lantz,
on behalf of the Mininet Contributors
The Mininet 2.1.0p1 Team:
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
* Bob Lantz
* Brian O'Connor
+111 -263
View File
@@ -11,153 +11,94 @@ Example to pull custom params (topo, switch, etc.) from a file:
sudo mn --custom ~/mininet/custom/custom_example.py
"""
from optparse import OptionParser
import os
import sys
import time
from functools import partial
from optparse import OptionParser # pylint: disable=deprecated-module
from sys import exit # pylint: disable=redefined-builtin
# Fix setuptools' evil madness, and open up (more?) security holes
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
# pylint: disable=wrong-import-position
from mininet.clean import cleanup
import mininet.cli
from mininet.log import lg, LEVELS, info, debug, warn, error, output
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, error
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
Ryu, NOX, RemoteController, findController,
DefaultController, NullController,
UserSwitch, OVSSwitch, OVSBridge,
IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink, TCULink, OVSLink
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
SingleSwitchReversedTopo, MinimalTopo )
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import customClass, specialClass, splitArgs, buildTopo
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
OVSLegacyKernelSwitch, IVSSwitch )
from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
from mininet.util import custom, customConstructor
from mininet.util import buildTopo
# Experimental! cluster edition prototype
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
RemoteOVSSwitch, RemoteLink,
SwitchBinPlacer, RandomPlacer,
ClusterCleanup )
from mininet.examples.clustercli import ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
# built in topologies, created only when run
TOPODEF = 'minimal'
TOPOS = { 'minimal': MinimalTopo,
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo,
'torus': TorusTopo }
'tree': TreeTopo }
SWITCHDEF = 'default'
SWITCHDEF = 'ovsk'
SWITCHES = { 'user': UserSwitch,
'ovs': OVSSwitch,
'ovsbr' : OVSBridge,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ivs': IVSSwitch,
'lxbr': LinuxBridge,
'default': OVSSwitch }
'ovsk': OVSKernelSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'default'
CONTROLLERDEF = 'ovsc'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'ryu': Ryu,
'default': DefaultController, # Note: overridden below
'none': NullController }
'none': lambda name: None }
LINKDEF = 'default'
LINKS = { 'default': Link, # Note: overridden below
'tc': TCLink,
'tcu': TCULink,
'ovs': OVSLink }
# TESTS dict can contain functions and/or Mininet() method names
# XXX: it would be nice if we could specify a default test, but
# this may be tricky
TESTS = { name: True
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
CLI = None # Set below if needed
# Locally defined tests
def allTest( net ):
"Run ping and iperf tests"
net.start()
net.ping()
net.iperf()
def nullTest( _net ):
"Null 'test' (does nothing)"
pass
LINKS = { 'default': Link,
'tc': TCLink }
TESTS.update( all=allTest, none=nullTest, build=nullTest )
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
'none' ]
# Map to alternate spellings of Mininet() methods
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp' }
def runTests( mn, options ):
"""Run tests
mn: Mininet object
option: list of test optinos """
# Split option into test name and parameters
for option in options:
# Multiple tests may be separated by '+' for now
for test in option.split( '+' ):
test, args, kwargs = splitArgs( test )
test = ALTSPELLING.get( test.lower(), test )
testfn = TESTS.get( test, test )
if callable( testfn ):
testfn( mn, *args, **kwargs )
elif hasattr( mn, test ):
getattr( mn, test )( *args, **kwargs )
else:
raise Exception( 'Test %s is unknown - please specify one of '
'%s ' % ( test, TESTS.keys() ) )
ALTSPELLING = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
def addDictOption( opts, choicesDict, default, name, **kwargs ):
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
"""Convenience function to add choices dicts to OptionParser.
opts: OptionParser instance
choicesDict: dictionary of valid choices, must include default
default: default choice key
name: long option name
kwargs: additional arguments to add_option"""
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
helpList = [ '%s=%s' % ( k, v.__name__ )
for k, v in choicesDict.items() ]
helpStr += ' ' + ( ' '.join( helpList ) )
params = dict( type='string', default=default, help=helpStr )
params.update( **kwargs )
opts.add_option( '--' + name, **params )
help: string"""
if default not in choicesDict:
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
if not helpStr:
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
help = helpStr )
def version( *_args ):
"Print Mininet version and exit"
output( "%s\n" % VERSION )
print "%s" % VERSION
exit()
class MininetRunner( object ):
"Build, setup, and run Mininet."
@@ -171,36 +112,9 @@ class MininetRunner( object ):
self.setup()
self.begin()
def custom( self, _option, _opt_str, value, _parser ):
"""Parse custom file and add params.
option: option e.g. --custom
opt_str: option string e.g. --custom
value: the value the follows the option
parser: option parser instance"""
files = []
if os.path.isfile( value ):
# Accept any single file (including those with commas)
files.append( value )
else:
# Accept a comma-separated list of filenames
files += value.split(',')
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
# pylint: disable=exec-used
with open( fileName ) as f:
exec( compile( f.read(), fileName, 'exec' ),
customs, customs )
for name, val in customs.items():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
def setCustom( self, name, value ):
"Set custom parameters for MininetRunner."
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links'
'testnames', 'tests' ):
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
# Update dictionaries
param = name.upper()
globals()[ param ].update( value )
@@ -211,23 +125,26 @@ class MininetRunner( object ):
# Add or modify global variable or class
globals()[ name ] = value
def setNat( self, _option, opt_str, value, parser ):
"Set NAT option(s)"
assert self # satisfy pylint
parser.values.nat = True
# first arg, first char != '-'
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-':
value = parser.rargs.pop( 0 )
_, args, kwargs = splitArgs( opt_str + ',' + value )
parser.values.nat_args = args
parser.values.nat_kwargs = kwargs
def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options."
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
parser.values.nat_args = []
parser.values.nat_kwargs = {}
raise Exception( 'could not find custom file: %s' % fileName )
def parseArgs( self ):
"""Parse command-line args and return options object.
returns: opts parse options dict"""
if '--custom' in sys.argv:
index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1:
filename = sys.argv[ index + 1 ]
self.parseCustomFile( filename )
else:
raise Exception( 'Custom file name not found' )
desc = ( "The %prog utility creates Mininet network from the\n"
"command line. It can create parametrized topologies,\n"
@@ -239,19 +156,18 @@ class MininetRunner( object ):
opts = OptionParser( description=desc, usage=usage )
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
addDictOption( opts, CONTROLLERS, [], 'controller', action='append' )
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
addDictOption( opts, LINKS, LINKDEF, 'link' )
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
opts.add_option( '--clean', '-c', action='store_true',
default=False, help='clean and exit' )
opts.add_option( '--custom', action='callback',
callback=self.custom,
type='string',
help='read custom classes or params from .py file(s)'
)
opts.add_option( '--test', default=[], action='append',
dest='test', help='|'.join( TESTS.keys() ) )
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py' +
'file' )
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
@@ -261,11 +177,11 @@ class MininetRunner( object ):
opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice',
choices=list( LEVELS.keys() ), default = 'info',
choices=LEVELS.keys(), default = 'info',
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6654,
opts.add_option( '--listenport', type='int', default=6634,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening " +
@@ -277,29 +193,7 @@ class MininetRunner( object ):
opts.add_option( '--pin', action='store_true',
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
opts.add_option( '--nat', action='callback', callback=self.setNat,
help="[option=val...] adds a NAT to the topology that"
" connects Mininet hosts to the physical network."
" Warning: This may route any traffic on the machine"
" that uses Mininet's"
" IP subnet into the Mininet network."
" If you need to change"
" Mininet's IP subnet, see the --ipbase option." )
opts.add_option( '--version', action='callback', callback=version,
help='prints the version and exits' )
opts.add_option( '--wait', '-w', action='store_true',
default=False, help='wait for switches to connect' )
opts.add_option( '--twait', '-t', action='store', type='int',
dest='wait',
help='timed wait (s) for switches to connect' )
opts.add_option( '--cluster', type='string', default=None,
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
opts.add_option( '--placement', type='choice',
choices=list( PLACEMENT.keys() ), default='block',
metavar='block|random',
help=( 'node placement for --cluster '
'(experimental!) ' ) )
opts.add_option( '--version', action='callback', callback=version )
self.options, self.args = opts.parse_args()
@@ -313,116 +207,70 @@ class MininetRunner( object ):
# set logging verbosity
if LEVELS[self.options.verbosity] > LEVELS['output']:
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
'output!\n'
'Please restart Mininet with -v [debug, info, output].\n'
'Please restart Mininet with -v [debug, info, output].'
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# Maybe we'll reorganize this someday...
# pylint: disable=too-many-branches,too-many-statements,global-statement
def begin( self ):
"Create and run mininet."
global CLI
opts = self.options
if opts.cluster:
servers = opts.cluster.split( ',' )
for server in servers:
ClusterCleanup.add( server )
if opts.clean:
if self.options.clean:
cleanup()
exit()
start = time.time()
if not opts.controller:
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
opts.controller = [ 'default' ]
if not CONTROLLERS[ 'default' ]:
opts.controller = [ 'none' ]
if opts.switch == 'default':
info( '*** No default OpenFlow controller found '
'for default switch!\n' )
info( '*** Falling back to OVS Bridge\n' )
opts.switch = 'ovsbr'
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
raise Exception( "Could not find a default controller "
"for switch %s" %
opts.switch )
topo = buildTopo( TOPOS, opts.topo )
switch = customClass( SWITCHES, opts.switch )
host = customClass( HOSTS, opts.host )
controller = [ customClass( CONTROLLERS, c )
for c in opts.controller ]
if opts.switch == 'user' and opts.link == 'default':
debug( '*** Using TCULink with UserSwitch\n' )
# Use link configured correctly for UserSwitch
opts.link = 'tcu'
link = customClass( LINKS, opts.link )
topo = buildTopo( TOPOS, self.options.topo )
switch = customConstructor( SWITCHES, self.options.switch )
host = customConstructor( HOSTS, self.options.host )
controller = customConstructor( CONTROLLERS, self.options.controller )
link = customConstructor( LINKS, self.options.link )
if self.validate:
self.validate( opts )
if opts.nolistenport:
opts.listenport = None
# Handle innamespace, cluster options
if opts.innamespace and opts.cluster:
error( "Please specify --innamespace OR --cluster\n" )
exit()
Net = MininetWithControlNet if opts.innamespace else Mininet
if opts.cluster:
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
Net = partial( MininetCluster, servers=servers,
placement=PLACEMENT[ 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
self.validate( self.options )
inNamespace = self.options.innamespace
Net = MininetWithControlNet if inNamespace else Mininet
ipBase = self.options.ipbase
xterms = self.options.xterms
mac = self.options.mac
arp = self.options.arp
pin = self.options.pin
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
mn = Net( topo=topo,
switch=switch, host=host, controller=controller, link=link,
ipBase=opts.ipbase, inNamespace=opts.innamespace,
xterms=opts.xterms, autoSetMacs=opts.mac,
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
waitConnected=opts.wait,
listenPort=opts.listenport )
switch=switch, host=host, controller=controller,
link=link,
ipBase=ipBase,
inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
if opts.ensure_value( 'nat', False ):
with open( '/etc/resolv.conf' ) as f:
if 'nameserver 127.' in f.read():
warn( '*** Warning: loopback address in /etc/resolv.conf '
'may break host DNS over NAT\n')
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
if self.options.pre:
CLI( mn, script=self.options.pre )
# --custom files can set CLI or change mininet.cli.CLI
CLI = mininet.cli.CLI if CLI is None else CLI
if opts.pre:
CLI( mn, script=opts.pre )
test = self.options.test
test = ALTSPELLING.get( test, test )
mn.start()
if opts.test:
runTests( mn, opts.test )
else:
if test == 'none':
pass
elif test == 'all':
mn.start()
mn.ping()
mn.iperf()
elif test == 'cli':
CLI( mn )
elif test != 'build':
getattr( mn, test )()
if opts.post:
CLI( mn, script=opts.post )
if self.options.post:
CLI( mn, script=self.options.post )
mn.stop()
@@ -436,7 +284,7 @@ if __name__ == "__main__":
except KeyboardInterrupt:
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
cleanup()
except Exception: # pylint: disable=broad-except
except Exception:
# Print exception
type_, val_, trace_ = sys.exc_info()
errorMsg = ( "-"*80 + "\n" +
+4 -1
View File
@@ -13,9 +13,12 @@ from mininet.topo import Topo
class MyTopo( Topo ):
"Simple topology example."
def build( self ):
def __init__( self ):
"Create custom topo."
# Initialize topology
Topo.__init__( self )
# Add hosts and switches
leftHost = self.addHost( 'h1' )
rightHost = self.addHost( 'h2' )
+830 -1936
View File
File diff suppressed because it is too large Load Diff
+3 -63
View File
@@ -11,34 +11,9 @@ Mininet's Python API.
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
#### bind.py:
This example shows how you can create private directories for each
node in a Mininet topology.
#### cluster.py:
This example contains all of the code for experimental cluster
edition. Remote classes and MininetCluster can be imported from
here to create a topology with nodes on remote machines.
#### clusterSanity.py:
This example runs cluster edition locally as a sanity check to test
basic functionality.
#### clustercli.py:
This example contains a CLI for experimental cluster edition.
#### clusterdemo.py:
This example is a basic demo of cluster edition on 3 servers with
a tree topology of depth 3 and fanout 3.
#### consoles.py:
This example creates a grid of console windows, one for each node,
This example creates a grid of console windows, one for each node,
and allows interaction with and monitoring of each console, including
graphical monitoring.
@@ -72,11 +47,6 @@ topology object) and adding nodes to it.
This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
#### intfoptions.py:
This example reconfigures a TCIntf during runtime with different
traffic control commands to test bandwidth, loss, and delay.
#### limit.py:
This example shows how to use link and CPU limits.
@@ -86,28 +56,13 @@ This example shows how to use link and CPU limits.
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
#### linuxrouter.py:
This example shows how to create and configure a router in Mininet
that uses Linux IP forwarding.
#### miniedit.py:
This example demonstrates creating a network via a graphical editor.
#### mobility.py:
This example demonstrates detaching an interface from one switch and
attaching it another as a basic way to move a host around a network.
#### multiLink.py:
This example demonstrates the creation of multiple links between
nodes using a custom Topology class.
#### multiping.py:
This example demonstrates one method for
This example demonstrates one method for
monitoring output from multiple hosts, using `node.monitor()`.
#### multipoll.py:
@@ -124,16 +79,6 @@ This example shows how to connect a Mininet network to the Internet
using NAT. It also answers the eternal question "why can't I ping
`google.com`?"
#### natnet.py:
This example demonstrates how to create a network using a NAT node
to connect hosts to the internet.
#### numberedports.py:
This example verifies the mininet ofport numbers match up to the ovs port numbers.
It also verifies that the port numbers match up to the interface numbers
#### popen.py:
This example monitors a number of hosts using `host.popen()` and
@@ -158,7 +103,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 (generally the control network
to an interface in the root namespace (generaly the control network
already lives in the root namespace, so it does not need to be explicitly
connected.)
@@ -172,8 +117,3 @@ memory and `sysctl` configuration (see `INSTALL`.)
This example creates a 64-host tree network, and attempts to check full
connectivity using `ping`, for different switch/datapath types.
#### vlanhost.py:
An example of how to subclass Host to use a VLAN on its primary interface.
+12 -20
View File
@@ -1,45 +1,37 @@
#!/usr/bin/env python
#!/usr/bin/python
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
import sys
from mininet.node import Host
from mininet.util import ensureRoot, waitListening
from mininet.log import info, warn, output
from mininet.util import ensureRoot
ensureRoot()
timeout = 5
info( "*** Creating nodes\n" )
print "*** Creating nodes"
h1 = Host( 'h1' )
root = Host( 'root', inNamespace=False )
info( "*** Creating link\n" )
print "*** Creating links"
h1.linkTo( root )
info( h1 )
print h1
info( "*** Configuring nodes\n" )
print "*** Configuring nodes"
h1.setIP( '10.0.0.1', 8 )
root.setIP( '10.0.0.2', 8 )
info( "*** Creating banner file\n" )
with open( '/tmp/%s.banner' % h1.name, 'w' ) as f:
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
print "*** Creating banner file"
f = open( '/tmp/%s.banner' % h1.name, 'w' )
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
f.close()
info( "*** Running sshd\n" )
print "*** Running sshd"
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
# add arguments from the command line
if len( sys.argv ) > 1:
cmd += ' ' + ' '.join( sys.argv[ 1: ] )
h1.cmd( cmd )
listening = waitListening( server=h1, port=22, timeout=timeout )
if listening:
output( "*** You may now ssh into", h1.name, "at", h1.IP(), '\n' )
else:
warn( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ), '\n' )
print "*** You may now ssh into", h1.name, "at", h1.IP()
+172 -43
View File
@@ -1,46 +1,170 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
bind.py: Bind mount example
bind.py: Bind mount prototype
This creates hosts with private directories that the user specifies.
These hosts may have persistent directories that will be available
across multiple mininet session, or temporary directories that will
only last for one mininet session. To specify a persistent
directory, add a tuple to a list of private directories:
[ ( 'directory to be mounted on', 'directory to be mounted' ) ]
String expansion may be used to create a directory template for
each host. To do this, add a %(name)s in place of the host name
when creating your list of directories:
[ ( '/var/run', '/tmp/%(name)s/var/run' ) ]
If no persistent directory is specified, the directories will default
to temporary private directories. To do this, simply create a list of
directories to be made private. A tmpfs will then be mounted on them.
You may use both temporary and persistent directories at the same
time. In the following privateDirs string, each host will have a
persistent directory in the root filesystem at
"/tmp/(hostname)/var/run" mounted on "/var/run". Each host will also
have a temporary private directory mounted on "/var/log".
[ ( '/var/run', '/tmp/%(name)s/var/run' ), '/var/log' ]
This example has both persistent directories mounted on '/var/log'
and '/var/run'. It also has a temporary private directory mounted
on '/var/mn'
This creates hosts with private directories as desired.
"""
from functools import partial
from mininet.net import Mininet
from mininet.node import Host
from mininet.cli import CLI
from mininet.util import errFail, quietRun, errRun
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel, info, debug
from os.path import realpath
from functools import partial
# Utility functions for unmounting a tree
MNRUNDIR = realpath( '/var/run/mn' )
def mountPoints():
"Return list of mounted file systems"
mtab, _err, _ret = errFail( 'cat /proc/mounts' )
lines = mtab.split( '\n' )
mounts = []
for line in lines:
if not line:
continue
fields = line.split( ' ')
mount = fields[ 1 ]
mounts.append( mount )
return mounts
def unmountAll( rootdir=MNRUNDIR ):
"Unmount all mounts under a directory tree"
rootdir = realpath( rootdir )
# Find all mounts below rootdir
# This is subtle because /foo is not
# a parent of /foot
dirslash = rootdir + '/'
mounts = [ m for m in mountPoints()
if m == dir or m.find( dirslash ) == 0 ]
# Unmount them from bottom to top
mounts.sort( reverse=True )
for mount in mounts:
debug( 'Unmounting', mount, '\n' )
_out, err, code = errRun( 'umount', mount )
if code != 0:
info( '*** Warning: failed to umount', mount, '\n' )
info( err )
class HostWithPrivateDirs( Host ):
"Host with private directories"
mnRunDir = MNRUNDIR
def __init__(self, name, *args, **kwargs ):
"""privateDirs: list of private directories
remounts: dirs to remount
unmount: unmount dirs in cleanup? (True)
Note: if unmount is False, you must call unmountAll()
manually."""
self.privateDirs = kwargs.pop( 'privateDirs', [] )
self.remounts = kwargs.pop( 'remounts', [] )
self.unmount = kwargs.pop( 'unmount', True )
Host.__init__( self, name, *args, **kwargs )
self.rundir = '%s/%s' % ( self.mnRunDir, name )
self.root, self.private = None, None # set in createBindMounts
if self.privateDirs:
self.privateDirs = [ realpath( d ) for d in self.privateDirs ]
self.createBindMounts()
# These should run in the namespace before we chroot,
# in order to put the right entries in /etc/mtab
# Eventually this will allow a local pid space
# Now we chroot and cd to wherever we were before.
pwd = self.cmd( 'pwd' ).strip()
self.sendCmd( 'exec chroot', self.root, 'bash -ms mininet:'
+ self.name )
self.waiting = False
self.cmd( 'cd', pwd )
# In order for many utilities to work,
# we need to remount /proc and /sys
self.cmd( 'mount /proc' )
self.cmd( 'mount /sys' )
def mountPrivateDirs( self ):
"Create and bind mount private dirs"
for dir_ in self.privateDirs:
privateDir = self.private + dir_
errFail( 'mkdir -p ' + privateDir )
mountPoint = self.root + dir_
errFail( 'mount -B %s %s' %
( privateDir, mountPoint) )
def mountDirs( self, dirs ):
"Mount a list of directories"
for dir_ in dirs:
mountpoint = self.root + dir_
errFail( 'mount -B %s %s' %
( dir_, mountpoint ) )
@classmethod
def findRemounts( cls, fstypes=None ):
"""Identify mount points in /proc/mounts to remount
fstypes: file system types to match"""
if fstypes is None:
fstypes = [ 'nfs' ]
dirs = quietRun( 'cat /proc/mounts' ).strip().split( '\n' )
remounts = []
for dir_ in dirs:
line = dir_.split()
mountpoint, fstype = line[ 1 ], line[ 2 ]
# Don't re-remount directories!!!
if mountpoint.find( cls.mnRunDir ) == 0:
continue
if fstype in fstypes:
remounts.append( mountpoint )
return remounts
def createBindMounts( self ):
"""Create a chroot directory structure,
with self.privateDirs as private dirs"""
errFail( 'mkdir -p '+ self.rundir )
unmountAll( self.rundir )
# Create /root and /private directories
self.root = self.rundir + '/root'
self.private = self.rundir + '/private'
errFail( 'mkdir -p ' + self.root )
errFail( 'mkdir -p ' + self.private )
# Recursively mount / in private doort
# note we'll remount /sys and /proc later
errFail( 'mount -B / ' + self.root )
self.mountDirs( self.remounts )
self.mountPrivateDirs()
def unmountBindMounts( self ):
"Unmount all of our bind mounts"
unmountAll( self.rundir )
def popen( self, *args, **kwargs ):
"Popen with chroot support"
chroot = kwargs.pop( 'chroot', True )
mncmd = kwargs.get( 'mncmd',
[ 'mnexec', '-a', str( self.pid ) ] )
if chroot:
mncmd = [ 'chroot', self.root ] + mncmd
kwargs[ 'mncmd' ] = mncmd
return Host.popen( self, *args, **kwargs )
def cleanup( self ):
"""Clean up, then unmount bind mounts
unmount: actually unmount bind mounts?"""
# Wait for process to actually terminate
self.shell.wait()
Host.cleanup( self )
if self.unmount:
self.unmountBindMounts()
errFail( 'rmdir ' + self.root )
# Convenience aliases
findRemounts = HostWithPrivateDirs.findRemounts
# Sample usage
@@ -48,21 +172,26 @@ from mininet.log import setLogLevel, info
def testHostWithPrivateDirs():
"Test bind mounts"
topo = SingleSwitchTopo( 10 )
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/var/run', '/tmp/%(name)s/var/run' ),
'/var/mn' ]
host = partial( Host,
privateDirs=privateDirs )
net = Mininet( topo=topo, host=host, waitConnected=True )
remounts = findRemounts( fstypes=[ 'nfs' ] )
privateDirs = [ '/var/log', '/var/run' ]
host = partial( HostWithPrivateDirs, remounts=remounts,
privateDirs=privateDirs, unmount=False )
net = Mininet( topo=topo, host=host )
net.start()
directories = [ directory[ 0 ] if isinstance( directory, tuple )
else directory for directory in privateDirs ]
info( 'Private Directories:', directories, '\n' )
info( 'Private Directories:', privateDirs, '\n' )
CLI( net )
net.stop()
# We do this all at once to save a bit of time
info( 'Unmounting host bind mounts...\n' )
unmountAll()
if __name__ == '__main__':
unmountAll()
setLogLevel( 'info' )
testHostWithPrivateDirs()
info( 'Done.\n')
-1055
View File
File diff suppressed because it is too large Load Diff
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env python
'''
A sanity check for cluster edition
'''
from mininet.examples.cluster import MininetCluster
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
from mininet.topo import SingleSwitchTopo
def clusterSanity():
"Sanity check for cluster mode"
topo = SingleSwitchTopo()
net = MininetCluster( topo=topo )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
clusterSanity()
-107
View File
@@ -1,107 +0,0 @@
#!/usr/bin/env python
"CLI for Mininet Cluster Edition prototype demo"
from mininet.cli import CLI
from mininet.log import output, error
# pylint: disable=global-statement
nx, graphviz_layout, plt = None, None, None # Will be imported on demand
class ClusterCLI( CLI ):
"CLI with additional commands for Cluster Edition demo"
@staticmethod
def colorsFor( seq ):
"Return a list of background colors for a sequence"
colors = [ 'red', 'lightgreen', 'cyan', 'yellow', 'orange',
'magenta', 'pink', 'grey', 'brown',
'white' ]
slen, clen = len( seq ), len( colors )
reps = max( 1, slen / clen )
colors = colors * reps
colors = colors[ 0 : slen ]
return colors
def do_plot( self, _line ):
"Plot topology colored by node placement"
# Import networkx if needed
global nx, plt, graphviz_layout
if not nx:
try:
# pylint: disable=import-error,no-member
# pylint: disable=import-outside-toplevel
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
plt = pyplot # satisfy pylint
import pygraphviz
assert pygraphviz # silence pyflakes
# Networkx moved this around
if hasattr( nx, 'graphviz_layout' ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error,no-member
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
return
# Make a networkx Graph
g = nx.Graph()
mn = self.mn
servers = getattr( mn, 'servers', [ 'localhost' ] )
hosts, switches = mn.hosts, mn.switches
nodes = hosts + switches
g.add_nodes_from( nodes )
links = [ ( link.intf1.node, link.intf2.node )
for link in self.mn.links ]
g.add_edges_from( links )
# Pick some shapes and colors
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
color = dict( zip( servers, self.colorsFor( servers ) ) )
# Plot it!
pos = graphviz_layout( g )
opts = { 'ax': None, 'font_weight': 'bold',
'width': 2, 'edge_color': 'darkblue' }
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
for h in hosts ]
scolors = [ color[ getattr( s, 'server', 'localhost' ) ]
for s in switches ]
nx.draw_networkx( g, pos=pos, nodelist=hosts, node_size=800,
label='host', node_color=hcolors, node_shape='s',
**opts )
nx.draw_networkx( g, pos=pos, nodelist=switches, node_size=1000,
node_color=scolors, node_shape='o', **opts )
# Get rid of axes, add title, and show
fig = plt.gcf()
ax = plt.gca()
ax.get_xaxis().set_visible( False )
ax.get_yaxis().set_visible( False )
fig.canvas.set_window_title( 'Mininet')
plt.title( 'Node Placement', fontweight='bold' )
plt.show()
def do_status( self, _line ):
"Report on node shell status"
nodes = self.mn.hosts + self.mn.switches
for node in nodes:
node.shell.poll()
exited = [ node for node in nodes
if node.shell.returncode is not None ]
if exited:
for node in exited:
output( '%s has exited with code %d\n'
% ( node, node.shell.returncode ) )
else:
output( 'All nodes are still running.\n' )
def do_placement( self, _line ):
"Describe node placement"
mn = self.mn
nodes = mn.hosts + mn.switches + mn.controllers
for server in mn.servers:
names = [ n.name for n in nodes if hasattr( n, 'server' )
and n.server == server ]
output( '%s: %s\n' % ( server, ' '.join( names ) ) )
-25
View File
@@ -1,25 +0,0 @@
#!/usr/bin/env python
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
from mininet.examples.cluster import ( MininetCluster, SwitchBinPlacer,
RemoteLink )
# ^ Could also use: RemoteSSHLink, RemoteGRELink
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
def demo():
"Simple Demo of Cluster Mode"
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers, link=RemoteLink,
placement=SwitchBinPlacer )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
demo()
-24
View File
@@ -1,24 +0,0 @@
#!/usr/bin/env python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
from mininet.net import Mininet
from mininet.log import setLogLevel
def perf(Link):
"Test connectivity nand performance over Link"
net = Mininet( host=RemoteHost, link=Link, waitConnected=True )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
net.iperf()
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perf( RemoteSSHLink )
perf( RemoteGRELink )
+10 -15
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
consoles.py: bring up a bunch of miniature consoles on a virtual network
@@ -27,7 +27,6 @@ Bob Lantz, April 2010
import re
# pylint: disable=import-error
from Tkinter import Frame, Button, Label, Text, Scrollbar, Canvas, Wm, READABLE
from mininet.log import setLogLevel
@@ -35,9 +34,6 @@ from mininet.topolib import TreeNet
from mininet.term import makeTerms, cleanUpScreens
from mininet.util import quietRun
# pylint: disable=too-many-arguments
class Console( Frame ):
"A simple console on a host."
@@ -69,10 +65,7 @@ class Console( Frame ):
self.bindEvents()
self.sendCmd( 'export TERM=dumb' )
def outputHook( _obj, _text):
return True
self.outputHook = outputHook
self.outputHook = None
def makeWidgets( self ):
"Make a label, a text area, and a scroll bar."
@@ -114,8 +107,10 @@ class Console( Frame ):
self.text.insert( 'end', text )
self.text.mark_set( 'insert', 'end' )
self.text.see( 'insert' )
if callable( self.outputHook ):
self.outputHook( self, text )
outputHook = lambda x, y: True # make pylint happier
if self.outputHook:
outputHook = self.outputHook
outputHook( self, text )
def handleKey( self, event ):
"If it's an interactive command, send it to the node."
@@ -295,10 +290,10 @@ class ConsoleApp( Frame ):
'switches': 'Switch',
'controllers': 'Controller'
}
for name, title in titles.items():
for name in titles:
nodes = getattr( net, name )
frame, consoles = self.createConsoles(
cframe, nodes, width, title )
cframe, nodes, width, titles[ name ] )
self.consoles[ name ] = Object( frame=frame, consoles=consoles )
self.selected = None
self.select( 'hosts' )
@@ -324,7 +319,7 @@ class ConsoleApp( Frame ):
if not m:
return
val, units = float( m.group( 1 ) ), m.group( 2 )
# convert to Gbps
#convert to Gbps
if units[0] == 'M':
val *= 10 ** -3
elif units[0] == 'K':
@@ -332,7 +327,7 @@ class ConsoleApp( Frame ):
elif units[0] == 'b':
val *= 10 ** -9
self.updates += 1
self.bw += val
self.bw += val
if self.updates >= self.hostCount:
self.graph.addBar( self.bw )
self.bw = 0
+3 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Create a network where different switches are connected to
@@ -17,7 +17,7 @@ setLogLevel( 'info' )
# Ignore the warning message that the remote isn't (yet) running
c0 = Controller( 'c0', port=6633 )
c1 = Controller( 'c1', port=6634 )
c2 = RemoteController( 'c2', ip='127.0.0.1', port=6633 )
c2 = RemoteController( 'c2', ip='127.0.0.1' )
cmap = { 's1': c0, 's2': c1, 's3': c2 }
@@ -26,9 +26,8 @@ class MultiSwitch( OVSSwitch ):
def start( self, controllers ):
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
topo = TreeTopo( depth=2, fanout=2 )
net = Mininet( topo=topo, switch=MultiSwitch, build=False, waitConnected=True )
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
for c in [ c0, c1 ]:
net.addController(c)
net.build()
+13 -16
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
This example creates a multi-controller network from semi-scratch by
@@ -11,54 +11,51 @@ Note that one could also create a custom switch class and pass it into
the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel
def multiControllerNet():
"Create a network from semi-scratch with multiple controllers."
net = Mininet( controller=Controller, switch=OVSSwitch,
waitConnected=True )
net = Mininet( controller=Controller, switch=OVSSwitch, build=False )
info( "*** Creating (reference) controllers\n" )
print "*** Creating (reference) controllers"
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
info( "*** Creating switches\n" )
print "*** Creating switches"
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
info( "*** Creating hosts\n" )
hosts1 = [ net.addHost( 'h%d' % n ) for n in ( 3, 4 ) ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in ( 5, 6 ) ]
print "*** Creating hosts"
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
info( "*** Creating links\n" )
print "*** Creating links"
for h in hosts1:
net.addLink( s1, h )
for h in hosts2:
net.addLink( s2, h )
net.addLink( s1, s2 )
info( "*** Starting network\n" )
print "*** Starting network"
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
info( "*** Testing network\n" )
print "*** Testing network"
net.pingAll()
info( "*** Running CLI\n" )
print "*** Running CLI"
CLI( net )
info( "*** Stopping network\n" )
print "*** Stopping network"
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' ) # for CLI output
multiControllerNet()
+11 -21
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
controlnet.py: Mininet with a custom control network
@@ -27,29 +27,22 @@ from mininet.log import setLogLevel, info
class DataController( Controller ):
"""Data Network Controller.
patched to avoid checkListening error and to delete intfs"""
patched to avoid checkListening error"""
def checkListening( self ):
"Ignore spurious error"
pass
def stop( self, *args, **kwargs ):
"Make sure intfs are deleted"
kwargs.update( deleteIntfs=True )
super( DataController, self ).stop( *args, **kwargs )
class MininetFacade( object ):
"""Mininet object facade that allows a single CLI to
talk to one or more networks"""
def __init__( self, net, *args, **kwargs ):
"""Create MininetFacade object.
net: Primary Mininet object
args: unnamed networks passed as arguments
kwargs: named networks passed as arguments"""
self.net = net
self.nets = [ net ] + list( args ) + list( kwargs.values() )
self.nets = [ net ] + list( args ) + kwargs.values()
self.nameToNet = kwargs
self.nameToNet['net'] = net
@@ -59,14 +52,13 @@ class MininetFacade( object ):
def __getitem__( self, key ):
"returns primary/named networks or node from any net"
# search kwargs for net named key
#search kwargs for net named key
if key in self.nameToNet:
return self.nameToNet[ key ]
# search each net for node named key
#search each net for node named key
for net in self.nets:
if key in net:
return net[ key ]
return None
def __iter__( self ):
"Iterate through all nodes in all Mininet objects"
@@ -101,10 +93,10 @@ class MininetFacade( object ):
class ControlNetwork( Topo ):
"Control Network Topology"
# pylint: disable=arguments-differ
def build( self, n, dataController=DataController, **_kwargs ):
def __init__( self, n, dataController=DataController, **kwargs ):
"""n: number of data network controller nodes
dataController: class for data network controllers"""
Topo.__init__( self, **kwargs )
# Connect everything to a single switch
cs0 = self.addSwitch( 'cs0' )
# Add hosts which will serve as data network controllers
@@ -122,11 +114,10 @@ class ControlNetwork( Topo ):
def run():
"Create control and data networks, and invoke the CLI"
info( '* Creating Control Network\n' )
ctopo = ControlNetwork( n=4, dataController=DataController )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24',
controller=None, waitConnected=True )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
info( '* Adding Control Network Controller\n')
cnet.addController( 'cc0', controller=Controller )
info( '* Starting Control Network\n')
@@ -136,8 +127,7 @@ def run():
topo = TreeTopo( depth=2, fanout=2 )
# UserSwitch so we can easily test failover
sw = partial( UserSwitch, opts='--inactivity-probe=1 --max-backoff=1' )
net = Mininet( topo=topo, switch=sw, controller=None,
waitConnected=True )
net = Mininet( topo=topo, switch=sw, controller=None )
info( '* Adding Controllers to Data Network\n' )
for host in cnet.hosts:
if isinstance(host, Controller):
+34 -65
View File
@@ -1,41 +1,30 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
cpu.py: test iperf bandwidth for varying cpu limits
Since we are limiting the hosts (only), we should expect the iperf
processes to be affected, as well as any system processing which is
billed to the hosts.
We reserve >50% of cycles for system processing; we assume that
this is enough for it not to affect results. Hosts are limited to
40% of total cycles, which we assume is enough to make them CPU
bound.
As CPU performance increases over time, we may have to reduce the
overall CPU allocation so that the host processing is still CPU bound.
This is perhaps an argument for specifying performance in a more
system-independent manner.
It would also be nice to have a better handle on limiting packet
processing cycles. It's not entirely clear to me how those are
billed to user or system processes if we are using OVS with a kernel
datapath. With a user datapath, they are easier to account for, but
overall performance is usually lower.
Although the iperf client uses more CPU and should be CPU bound (?),
we measure the received data at the server since the client transmit
rate includes buffering.
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, waitListening, decode
from mininet.log import setLogLevel, info
from mininet.clean import cleanup
from mininet.util import custom
from mininet.log import setLogLevel, output
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
from time import sleep
def waitListening(client, server, port):
"Wait until server is listening on port"
if not client.cmd('which telnet'):
raise Exception('Could not find telnet')
cmd = ('sh -c "echo A | telnet -e A %s %s"' %
(server.IP(), port))
while 'Connected' not in client.cmd(cmd):
output('waiting for', server,
'to listen on port', port, '\n')
sleep(.5)
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
"""Example/test of link and CPU bandwidth limits
cpu: cpu limit as fraction of overall CPU time"""
@@ -44,41 +33,22 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
results = {}
for sched in 'rt', 'cfs':
info( '*** Testing with', sched, 'bandwidth limiting\n' )
print '*** Testing with', sched, 'bandwidth limiting'
for cpu in cpuLimits:
# cpu is the cpu fraction for all hosts, so we divide
# it across two hosts
host = custom( CPULimitedHost, sched=sched,
period_us=period_us,
cpu=.5*cpu )
try:
net = Mininet( topo=topo, host=host, waitConnected=True )
# pylint: disable=bare-except
except: # noqa
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
cleanup()
break
cpu=cpu )
net = Mininet( topo=topo, host=host )
net.start()
net.pingAll()
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
client, server = hosts[ 0 ], hosts[ -1 ]
info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' %
( 100.0 * cpu ) )
# We measure at the server because it doesn't include
# the client's buffer fill rate
popen = server.popen( 'iperf -yc -s -p 5001' )
server.cmd( 'iperf -s -p 5001 &' )
waitListening( client, server, 5001 )
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
# 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()
result = client.cmd( 'iperf -yc -t %s -c %s' % (
seconds, server.IP() ) ).split( ',' )
bps = float( result[ -1 ] )
server.cmdPrint( 'kill %iperf' )
net.stop()
updated = results.get( sched, [] )
updated += [ ( cpu, bps ) ]
@@ -90,23 +60,22 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
def dump( results ):
"Dump results"
fmt = '%s\t%s\t%s\n'
fmt = '%s\t%s\t%s'
info( '\n' )
info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) )
print
print fmt % ( 'sched', 'cpu', 'client MB/s' )
print
for sched in sorted( results.keys() ):
entries = results[ sched ]
for cpu, bps in entries:
pct = '%d%%' % ( cpu * 100 )
mbps = '%.2e' % bps
info( fmt % ( sched, pct, mbps ) )
pct = '%.2f%%' % ( cpu * 100 )
mbps = bps / 1e6
print fmt % ( sched, pct, mbps )
if __name__ == '__main__':
setLogLevel( 'info' )
# These are the limits for the hosts/iperfs - the
# rest is for system processes
limits = [ .5, .4, .3, .2, .1 ]
limits = [ .45, .4, .3, .2, .1 ]
out = bwtest( limits )
dump( out )
+2 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
This example shows how to create an empty Mininet object
@@ -14,7 +14,7 @@ def emptyNet():
"Create an empty network and add nodes to it."
net = Mininet( controller=Controller, waitConnected=True )
net = Mininet( controller=Controller )
info( '*** Adding controller\n' )
net.addController( 'c0' )
@@ -39,7 +39,6 @@ def emptyNet():
info( '*** Stopping network' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
emptyNet()
+5 -11
View File
@@ -1,14 +1,11 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
"""
import re
import sys
from sys import exit # pylint: disable=redefined-builtin
import re, sys
from mininet.cli import CLI
from mininet.log import setLogLevel, info, error
@@ -17,20 +14,17 @@ from mininet.link import Intf
from mininet.topolib import TreeTopo
from mininet.util import quietRun
def checkIntf( intf ):
"Make sure intf exists and is not configured."
config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True )
if not config:
if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
error( 'Error:', intf, 'does not exist!\n' )
exit( 1 )
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', config )
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
if ips:
error( 'Error:', intf, 'has an IP address,'
'and is probably in use!\n' )
exit( 1 )
if __name__ == '__main__':
setLogLevel( 'info' )
@@ -42,7 +36,7 @@ if __name__ == '__main__':
checkIntf( intfName )
info( '*** Creating network\n' )
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ), waitConnected=True )
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
switch = net.switches[ 0 ]
info( '*** Adding hardware interface', intfName, 'to switch',
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python
'''
example of using various TCIntf options.
reconfigures a single interface using intf.config()
to use different traffic control commands to test
bandwidth, loss, and delay
'''
from mininet.net import Mininet
from mininet.log import setLogLevel, info
from mininet.link import TCLink
def intfOptions():
"run various traffic control commands on a single interface"
net = Mininet( autoStaticArp=True, waitConnected=True )
net.addController( 'c0' )
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2' )
s1 = net.addSwitch( 's1' )
link1 = net.addLink( h1, s1, cls=TCLink )
net.addLink( h2, s1 )
net.start()
# flush out latency from reactive forwarding delay
net.pingAll()
info( '\n*** Configuring one intf with bandwidth of 10 Mb\n' )
link1.intf1.config( bw=10 )
info( '\n*** Running iperf to test\n' )
net.iperf( seconds=10 )
info( '\n*** Configuring one intf with loss of 50%\n' )
link1.intf1.config( loss=50 )
info( '\n' )
net.iperf( ( h1, h2 ), l4Type='UDP' )
info( '\n*** Configuring one intf with delay of 15ms\n' )
link1.intf1.config( delay='15ms' )
info( '\n*** Run a ping to confirm delay\n' )
net.pingPairFull()
info( '\n*** Done testing\n' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
intfOptions()
+9 -17
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
limit.py: example of using link and CPU limits
@@ -8,14 +8,15 @@ from mininet.net import Mininet
from mininet.link import TCIntf
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, quietRun
from mininet.log import setLogLevel, info
from mininet.util import custom
from mininet.log import setLogLevel
def testLinkLimit( net, bw ):
"Run bandwidth limit test"
info( '*** Testing network %.2f Mbps bandwidth limit\n' % bw )
net.iperf()
print '*** Testing network %.2f Mbps bandwidth limit' % bw
net.iperf( )
def limit( bw=10, cpu=.1 ):
"""Example/test of link and CPU bandwidth limits
@@ -24,17 +25,9 @@ def limit( bw=10, cpu=.1 ):
intf = custom( TCIntf, bw=bw )
myTopo = TreeTopo( depth=1, fanout=2 )
for sched in 'rt', 'cfs':
info( '*** Testing with', sched, 'bandwidth limiting\n' )
if sched == 'rt':
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s'
% release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
info( '*** RT Scheduler is not enabled in your kernel. '
'Skipping this test\n' )
continue
print '*** Testing with', sched, 'bandwidth limiting'
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
net = Mininet( topo=myTopo, intf=intf, host=host, waitConnected=True )
net = Mininet( topo=myTopo, intf=intf, host=host )
net.start()
testLinkLimit( net, bw=bw )
net.runCpuLimitTest( cpu=cpu )
@@ -43,7 +36,7 @@ def limit( bw=10, cpu=.1 ):
def verySimpleLimit( bw=150 ):
"Absurdly simple limiting test"
intf = custom( TCIntf, bw=bw )
net = Mininet( intf=intf, waitConnected=True )
net = Mininet( intf=intf )
h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' )
net.addLink( h1, h2 )
net.start()
@@ -55,7 +48,6 @@ def verySimpleLimit( bw=150 ):
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
limit()
+33 -49
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Test bandwidth (using iperf) on linear networks of varying size,
@@ -23,26 +23,23 @@ of switches, this example demonstrates:
"""
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch
from mininet.topo import Topo
from mininet.log import lg
from mininet.util import irange
import sys
from functools import partial
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
from mininet.log import lg, info
from mininet.util import irange, quietRun
from mininet.link import TCLink
flush = sys.stdout.flush
class LinearTestTopo( Topo ):
"Topology for a string of N hosts and N-1 switches."
# pylint: disable=arguments-differ
def build( self, N, **params ):
def __init__( self, N, **params ):
# Initialize topology
Topo.__init__( self, **params )
# Create switches and hosts
hosts = [ self.addHost( 'h%s' % h )
for h in irange( 1, N ) ]
@@ -73,54 +70,41 @@ def linearBandwidthTest( lengths ):
switches = { 'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
# UserSwitch is horribly slow with recent kernels.
# We can reinstate it once its performance is fixed
del switches[ 'reference user' ]
topo = LinearTestTopo( hostCount )
# Select TCP Reno
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath, Switch in switches.items():
info( "*** testing", datapath, "datapath\n" )
for datapath in switches.keys():
print "*** testing", datapath, "datapath"
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='30ms', bw=100 )
net = Mininet( topo=topo, switch=Switch,
controller=Controller, link=link,
waitConnected=True )
net = Mininet( topo=topo, switch=Switch )
net.start()
info( "*** testing basic connectivity\n" )
print "*** testing basic connectivity"
for n in lengths:
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
info( "*** testing bandwidth\n" )
print "*** testing bandwidth"
for n in lengths:
src, dst = net.hosts[ 0 ], net.hosts[ n ]
# Try to prime the pump to reduce PACKET_INs during test
# since the reference controller is reactive
src.cmd( 'telnet', dst.IP(), '5001' )
info( "testing", src.name, "<->", dst.name, '\n' )
# serverbw = received; _clientbw = buffered
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=5 )
info( serverbw, '\n' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ] )
print bandwidth
flush()
results[ datapath ] += [ ( n, serverbw ) ]
results[ datapath ] += [ ( n, bandwidth ) ]
net.stop()
for datapath in switches:
info( "\n*** Linear network results for", datapath, "datapath:\n" )
for datapath in switches.keys():
print
print "*** Linear network results for", datapath, "datapath:"
print
result = results[ datapath ]
info( "SwitchCount\tiperf Results\n" )
for switchCount, serverbw in result:
info( switchCount, '\t\t' )
info( serverbw, '\n' )
info( '\n')
info( '\n' )
print "SwitchCount\tiperf Results"
for switchCount, bandwidth in result:
print switchCount, '\t\t',
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
print
print
if __name__ == '__main__':
lg.setLogLevel( 'info' )
sizes = [ 1, 2, 3, 4 ]
info( "*** Running linearBandwidthTest", sizes, '\n' )
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
print "*** Running linearBandwidthTest", sizes
linearBandwidthTest( sizes )
-95
View File
@@ -1,95 +0,0 @@
#!/usr/bin/env python
"""
linuxrouter.py: Example network with Linux IP router
This example converts a Node into a router using IP forwarding
already built into Linux.
The example topology creates a router and three IP subnets:
- 192.168.1.0/24 (r0-eth1, IP: 192.168.1.1)
- 172.16.0.0/12 (r0-eth2, IP: 172.16.0.1)
- 10.0.0.0/8 (r0-eth3, IP: 10.0.0.1)
Each subnet consists of a single host connected to
a single switch:
r0-eth1 - s1-eth1 - h1-eth0 (IP: 192.168.1.100)
r0-eth2 - s2-eth1 - h2-eth0 (IP: 172.16.0.100)
r0-eth3 - s3-eth1 - h3-eth0 (IP: 10.0.0.100)
The example relies on default routing entries that are
automatically created for each router interface, as well
as 'defaultRoute' parameters for the host interfaces.
Additional routes may be added to the router or hosts by
executing 'ip route' or 'route' commands on the router or hosts.
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
class LinuxRouter( Node ):
"A Node with IP forwarding enabled."
# pylint: disable=arguments-differ
def config( self, **params ):
super( LinuxRouter, self).config( **params )
# Enable forwarding on the router
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
def terminate( self ):
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
super( LinuxRouter, self ).terminate()
class NetworkTopo( Topo ):
"A LinuxRouter connecting three IP subnets"
# pylint: disable=arguments-differ
def build( self, **_opts ):
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
s1, s2, s3 = [ self.addSwitch( s ) for s in ( 's1', 's2', 's3' ) ]
self.addLink( s1, router, intfName2='r0-eth1',
params2={ 'ip' : defaultIP } ) # for clarity
self.addLink( s2, router, intfName2='r0-eth2',
params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( s3, router, intfName2='r0-eth3',
params2={ 'ip' : '10.0.0.1/8' } )
h1 = self.addHost( 'h1', ip='192.168.1.100/24',
defaultRoute='via 192.168.1.1' )
h2 = self.addHost( 'h2', ip='172.16.0.100/12',
defaultRoute='via 172.16.0.1' )
h3 = self.addHost( 'h3', ip='10.0.0.100/8',
defaultRoute='via 10.0.0.1' )
for h, s in [ (h1, s1), (h2, s2), (h3, s3) ]:
self.addLink( h, s )
def run():
"Test linux router"
topo = NetworkTopo()
net = Mininet( topo=topo,
waitConnected=True ) # controller is used by s1-s3
net.start()
info( '*** Routing Table on Router:\n' )
info( net[ 'r0' ].cmd( 'route' ) )
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
run()
+102 -2997
View File
File diff suppressed because it is too large Load Diff
-138
View File
@@ -1,138 +0,0 @@
#!/usr/bin/env python
"""
Simple example of Mobility with Mininet
(aka enough rope to hang yourself.)
We move a host from s1 to s2, s2 to s3, and then back to s1.
Gotchas:
The reference controller doesn't support mobility, so we need to
manually flush the switch flow tables!
Good luck!
to-do:
- think about wifi/hub behavior
- think about clearing last hop - why doesn't that work?
"""
from random import randint
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.log import info, output, warn, setLogLevel
class MobilitySwitch( OVSSwitch ):
"Switch that can reattach and rename interfaces"
def delIntf( self, intf ):
"Remove (and detach) an interface"
port = self.ports[ intf ]
del self.ports[ intf ]
del self.intfs[ port ]
del self.nameToIntf[ intf.name ]
# pylint: disable=arguments-differ
def addIntf( self, intf, rename=False, **kwargs ):
"Add (and reparent) an interface"
OVSSwitch.addIntf( self, intf, **kwargs )
intf.node = self
if rename:
self.renameIntf( intf )
def attach( self, intf ):
"Attach an interface and set its port"
port = self.ports[ intf ]
if port:
if self.isOldOVS():
self.cmd( 'ovs-vsctl add-port', self, intf )
else:
self.cmd( 'ovs-vsctl add-port', self, intf,
'-- set Interface', intf,
'ofport_request=%s' % port )
self.validatePort( intf )
def validatePort( self, intf ):
"Validate intf's OF port number"
ofport = int( self.cmd( 'ovs-vsctl get Interface', intf,
'ofport' ) )
if ofport != self.ports[ intf ]:
warn( 'WARNING: ofport for', intf, 'is actually', ofport,
'\n' )
def renameIntf( self, intf, newname='' ):
"Rename an interface (to its canonical name)"
intf.ifconfig( 'down' )
if not newname:
newname = '%s-eth%d' % ( self.name, self.ports[ intf ] )
intf.cmd( 'ip link set', intf, 'name', newname )
del self.nameToIntf[ intf.name ]
intf.name = newname
self.nameToIntf[ intf.name ] = intf
intf.ifconfig( 'up' )
def moveIntf( self, intf, switch, port=None, rename=True ):
"Move one of our interfaces to another switch"
self.detach( intf )
self.delIntf( intf )
switch.addIntf( intf, port=port, rename=rename )
switch.attach( intf )
def printConnections( switches ):
"Compactly print connected nodes to each switch"
for sw in switches:
output( '%s: ' % sw )
for intf in sw.intfList():
link = intf.link
if link:
intf1, intf2 = link.intf1, link.intf2
remote = intf1 if intf1.node != sw else intf2
output( '%s(%s) ' % ( remote.node, sw.ports[ intf ] ) )
output( '\n' )
def moveHost( host, oldSwitch, newSwitch, newPort=None ):
"Move a host from old switch to new switch"
hintf, sintf = host.connectionsTo( oldSwitch )[ 0 ]
oldSwitch.moveIntf( sintf, newSwitch, port=newPort )
return hintf, sintf
def mobilityTest():
"A simple test of mobility"
info( '* Simple mobility test\n' )
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch,
waitConnected=True )
info( '* Starting network:\n' )
net.start()
printConnections( net.switches )
info( '* Testing network\n' )
net.pingAll()
info( '* Identifying switch interface for h1\n' )
h1, old = net.get( 'h1', 's1' )
for s in 2, 3, 1:
new = net[ 's%d' % s ]
port = randint( 10, 20 )
info( '* Moving', h1, 'from', old, 'to', new, 'port', port, '\n' )
hintf, sintf = moveHost( h1, old, new, newPort=port )
info( '*', hintf, 'is now connected to', sintf, '\n' )
info( '* Clearing out old flows\n' )
for sw in net.switches:
sw.dpctl( 'del-flows' )
info( '* New network:\n' )
printConnections( net.switches )
info( '* Testing connectivity:\n' )
net.pingAll()
old = new
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
mobilityTest()
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/env python
"""
This is a simple example that demonstrates multiple links
between nodes.
"""
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.topo import Topo
def runMultiLink():
"Create and run multiple link network"
topo = simpleMultiLinkTopo( n=2 )
net = Mininet( topo=topo, waitConnected=True )
net.start()
CLI( net )
net.stop()
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
# pylint: disable=arguments-differ
def build( self, n, **_kwargs ):
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
s1 = self.addSwitch( 's1' )
for _ in range( n ):
self.addLink( s1, h1 )
self.addLink( s1, h2 )
if __name__ == '__main__':
setLogLevel( 'info' )
runMultiLink()
+11 -12
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
multiping.py: monitor multiple sets of hosts using ping
@@ -8,18 +8,17 @@ multiple hosts and monitor their output interactively for a period=
of time.
"""
from select import poll, POLLIN
from time import time
from mininet.net import Mininet
from mininet.node import Node
from mininet.topo import SingleSwitchTopo
from mininet.log import info, setLogLevel
from mininet.log import setLogLevel
from select import poll, POLLIN
from time import time
def chunks( items, n ):
def chunks( l, n ):
"Divide list l into chunks of size n - thanks Stackoverflow"
return [ items[ i: i + n ] for i in range( 0, len( items ), n ) ]
return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
def startpings( host, targetips ):
"Tell host to repeatedly ping targets"
@@ -35,8 +34,8 @@ def startpings( host, targetips ):
' done; '
'done &' )
info( '*** Host %s (%s) will be pinging ips: %s\n' %
( host.name, host.IP(), targetips ) )
print ( '*** Host %s (%s) will be pinging ips: %s' %
( host.name, host.IP(), targetips ) )
host.cmd( cmd )
@@ -45,7 +44,7 @@ def multiping( netsize, chunksize, seconds):
# Create network and identify subnets
topo = SingleSwitchTopo( netsize )
net = Mininet( topo=topo, waitConnected=True )
net = Mininet( topo=topo )
net.start()
hosts = net.hosts
subnets = chunks( hosts, chunksize )
@@ -59,7 +58,7 @@ def multiping( netsize, chunksize, seconds):
# Start pings
for subnet in subnets:
ips = [ host.IP() for host in subnet ]
# adding bogus to generate packet loss
#adding bogus to generate packet loss
ips.append( '10.0.0.200' )
for host in subnet:
startpings( host, ips )
@@ -70,7 +69,7 @@ def multiping( netsize, chunksize, seconds):
readable = poller.poll(1000)
for fd, _mask in readable:
node = Node.outToNode[ fd ]
info( '%s:' % node.name, node.monitor().strip(), '\n' )
print '%s:' % node.name, node.monitor().strip()
# Stop pings
for host in hosts:
+13 -17
View File
@@ -1,29 +1,25 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Simple example of sending output to multiple files and
monitoring them
"""
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import setLogLevel
from time import time
from select import poll, POLLIN
from subprocess import Popen, PIPE
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import info, setLogLevel
from mininet.util import decode
def monitorFiles( outfiles, seconds, timeoutms ):
"Monitor set of files and return [(host, line)...]"
devnull = open( '/dev/null', 'w' ) # pylint: disable=consider-using-with
devnull = open( '/dev/null', 'w' )
tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.items():
tail = Popen( # pylint: disable=consider-using-with
[ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
for h, outfile in outfiles.iteritems():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
tails[ h ] = tail
fdToFile[ fd ] = tail.stdout
@@ -42,7 +38,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
host = fdToHost[ fd ]
# Wait for a line of output
line = f.readline().strip()
yield host, decode( line )
yield host, line
else:
# If we timed out, return nothing
yield None, ''
@@ -54,10 +50,10 @@ def monitorFiles( outfiles, seconds, timeoutms ):
def monitorTest( N=3, seconds=3 ):
"Run pings and monitor multiple hosts"
topo = SingleSwitchTopo( N )
net = Mininet( topo, waitConnected=True )
net = Mininet( topo )
net.start()
hosts = net.hosts
info( "Starting test...\n" )
print "Starting test..."
server = hosts[ 0 ]
outfiles, errfiles = {}, {}
for h in hosts:
@@ -71,10 +67,10 @@ def monitorTest( N=3, seconds=3 ):
'>', outfiles[ h ],
'2>', errfiles[ h ],
'&' )
info( "Monitoring output for", seconds, "seconds\n" )
print "Monitoring output for", seconds, "seconds"
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
if h:
info( '%s: %s\n' % ( h.name, line ) )
print '%s: %s' % ( h.name, line )
for h in hosts:
h.cmd('kill %ping')
net.stop()
+2 -4
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
This example shows how to create a network and run multiple tests.
@@ -17,14 +17,12 @@ def ifconfigTest( net ):
for host in hosts:
info( host.cmd( 'ifconfig' ) )
if __name__ == '__main__':
lg.setLogLevel( 'info' )
info( "*** Initializing Mininet and kernel modules\n" )
OVSKernelSwitch.setup()
info( "*** Creating network\n" )
network = Mininet( TreeTopo( depth=2, fanout=2), switch=OVSKernelSwitch,
waitConnected=True )
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
info( "*** Starting network\n" )
network.start()
info( "*** Running ping test\n" )
+98 -9
View File
@@ -1,23 +1,112 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Example to create a Mininet topology and connect it to the internet via NAT
through eth0 on the host.
Glen Gibb, February 2011
(slight modifications by BL, 5/13)
"""
from mininet.cli import CLI
from mininet.log import lg, info
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeNet
#################################
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
"""Start NAT/forwarding between Mininet and external network
root: node to access iptables from
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
# Identify the interface connecting to the mininet network
localIntf = root.defaultIntf()
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
root.cmd( 'iptables -P INPUT ACCEPT' )
root.cmd( 'iptables -P OUTPUT ACCEPT' )
root.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
def stopNAT( root ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
def fixNetworkManager( root, intf ):
"""Prevent network-manager from messing with our interface,
by specifying manual configuration in /etc/network/interfaces
root: a node in the root namespace (for running commands)
intf: interface name"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if ( line ) not in config:
print '*** Adding', line.strip(), 'to', cfile
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
root.cmd( 'service network-manager restart' )
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
"""Connect the network to the internet
switch: switch to connect to root namespace
rootip: address for interface in root namespace
subnet: Mininet subnet"""
switch = network.get( switch )
prefixLen = subnet.split( '/' )[ 1 ]
# Create a node in root namespace
root = Node( 'root', inNamespace=False )
# Prevent network-manager from interfering with our interface
fixNetworkManager( root, 'root-eth0' )
# Create link between root NS and switch
link = network.addLink( root, switch )
link.intf1.setIP( rootip, prefixLen )
# Start network that now includes link to root namespace
network.start()
# Start NAT and establish forwarding
startNAT( root )
# Establish routes from end hosts
for host in network.hosts:
host.cmd( 'ip route flush root 0/0' )
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
host.cmd( 'route add default gw', rootip )
return root
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4, waitConnected=True )
# Add NAT connectivity
net.addNAT().configDefault()
net.start()
info( "*** Hosts are running and should have internet connectivity\n" )
info( "*** Type 'exit' or control-D to shut down network\n" )
net = TreeNet( depth=1, fanout=4 )
# Configure and start NATted connectivity
rootnode = connectToInternet( net )
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
CLI( net )
# Shut down NAT
stopNAT( rootnode )
net.stop()
-69
View File
@@ -1,69 +0,0 @@
#!/usr/bin/env python
"""
natnet.py: Example network with NATs
h0
|
s0
|
----------------
| |
nat1 nat2
| |
s1 s2
| |
h1 h2
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.nodelib import NAT
from mininet.log import setLogLevel
from mininet.cli import CLI
from mininet.util import irange
class InternetTopo(Topo):
"Single switch connected to n hosts."
# pylint: disable=arguments-differ
def build(self, n=2, **_kwargs ):
# set up inet switch
inetSwitch = self.addSwitch('s0')
# add inet host
inetHost = self.addHost('h0')
self.addLink(inetSwitch, inetHost)
# add local nets
for i in irange(1, n):
inetIntf = 'nat%d-eth0' % i
localIntf = 'nat%d-eth1' % i
localIP = '192.168.%d.1' % i
localSubnet = '192.168.%d.0/24' % i
natParams = { 'ip' : '%s/24' % localIP }
# add NAT to topology
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
inetIntf=inetIntf, localIntf=localIntf)
switch = self.addSwitch('s%d' % i)
# connect NAT to inet and local switches
self.addLink(nat, inetSwitch, intfName1=inetIntf)
self.addLink(nat, switch, intfName1=localIntf, params1=natParams)
# add host and connect to local switch
host = self.addHost('h%d' % i,
ip='192.168.%d.100/24' % i,
defaultRoute='via %s' % localIP)
self.addLink(host, switch)
def run():
"Create network and run the CLI"
topo = InternetTopo()
net = Mininet(topo=topo, waitConnected=True )
net.start()
CLI(net)
net.stop()
if __name__ == '__main__':
setLogLevel('info')
run()
-81
View File
@@ -1,81 +0,0 @@
#!/usr/bin/env python
"""
Create a network with 5 hosts, numbered 1-4 and 9.
Validate that the port numbers match to the interface name,
and that the ovs ports match the mininet ports.
"""
from mininet.net import Mininet
from mininet.node import Controller
from mininet.log import setLogLevel, info, warn
def validatePort( switch, intf ):
"Validate intf's OF port number"
ofport = int( switch.cmd( 'ovs-vsctl get Interface', intf,
'ofport' ) )
if ofport != switch.ports[ intf ]:
warn( 'WARNING: ofport for', intf, 'is actually', ofport, '\n' )
return 0
else:
return 1
def testPortNumbering():
"""Test port numbering:
Create a network with 5 hosts (using Mininet's
mid-level API) and check that implicit and
explicit port numbering works as expected."""
net = Mininet( controller=Controller, waitConnected=True )
info( '*** Adding controller\n' )
net.addController( 'c0' )
info( '*** Adding hosts\n' )
h1 = net.addHost( 'h1', ip='10.0.0.1' )
h2 = net.addHost( 'h2', ip='10.0.0.2' )
h3 = net.addHost( 'h3', ip='10.0.0.3' )
h4 = net.addHost( 'h4', ip='10.0.0.4' )
h5 = net.addHost( 'h5', ip='10.0.0.5' )
info( '*** Adding switch\n' )
s1 = net.addSwitch( 's1' )
info( '*** Creating links\n' )
# host 1-4 connect to ports 1-4 on the switch
net.addLink( h1, s1 )
net.addLink( h2, s1 )
net.addLink( h3, s1 )
net.addLink( h4, s1 )
# specify a different port to connect host 5 to on the switch.
net.addLink( h5, s1, port1=1, port2= 9)
info( '*** Starting network\n' )
net.start()
# print the interfaces and their port numbers
info( '\n*** printing and validating the ports '
'running on each interface\n' )
for intfs in s1.intfList():
if not intfs.name == "lo":
info( intfs, ': ', s1.ports[intfs],
'\n' )
info( 'Validating that', intfs,
'is actually on port', s1.ports[intfs], '... ' )
if validatePort( s1, intfs ):
info( 'Validated.\n' )
info( '\n' )
# test the network with pingall
net.pingAll()
info( '\n' )
info( '*** Stopping network\n' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
testPortNumbering()
+9 -7
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
This example monitors a number of hosts using host.popen() and
@@ -6,14 +6,17 @@ pmonitor()
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from mininet.util import pmonitor
from mininet.log import setLogLevel
from mininet.util import custom, pmonitor
def monitorhosts( hosts=5 ):
def monitorhosts( hosts=5, sched='cfs' ):
"Start a bunch of pings and monitor them using popen"
mytopo = SingleSwitchTopo( hosts )
net = Mininet( topo=mytopo, waitConnected=True )
cpu = .5 / hosts
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
net = Mininet( topo=mytopo, host=myhost )
net.start()
# Start a bunch of pings
popens = {}
@@ -24,11 +27,10 @@ def monitorhosts( hosts=5 ):
# Monitor them and print output
for host, line in pmonitor( popens ):
if host:
info( "<%s>: %s" % ( host.name, line ) )
print "<%s>: %s" % ( host.name, line.strip() )
# Done
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
monitorhosts( hosts=5 )
+7 -12
View File
@@ -1,38 +1,33 @@
#!/usr/bin/env python
#!/usr/bin/python
"Monitor multiple hosts using popen()/pmonitor()"
from time import time
from signal import SIGINT
from mininet.net import Mininet
from mininet.topo import SingleSwitchTopo
from mininet.util import pmonitor
from mininet.log import setLogLevel, info
from time import time
from signal import SIGINT
def pmonitorTest( N=3, seconds=10 ):
"Run pings and monitor multiple hosts using pmonitor"
topo = SingleSwitchTopo( N )
net = Mininet( topo, waitConnected=True )
net = Mininet( topo )
net.start()
hosts = net.hosts
info( "Starting test...\n" )
print "Starting test..."
server = hosts[ 0 ]
popens = {}
for h in hosts:
popens[ h ] = h.popen('ping', server.IP() )
info( "Monitoring output for", seconds, "seconds\n" )
print "Monitoring output for", seconds, "seconds"
endTime = time() + seconds
for h, line in pmonitor( popens, timeoutms=500 ):
if h:
info( '<%s>: %s' % ( h.name, line ) )
print '<%s>: %s' % ( h.name, line ),
if time() >= endTime:
for p in popens.values():
p.send_signal( SIGINT )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
pmonitorTest()
+3 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Build a simple network from scratch, using mininet primitives.
@@ -8,14 +8,13 @@ but it exposes the configuration details and allows customization.
For most tasks, the higher-level API will be preferable.
"""
from time import sleep
from mininet.net import Mininet
from mininet.node import Node
from mininet.link import Link
from mininet.log import setLogLevel, info
from mininet.util import quietRun
from time import sleep
def scratchNet( cname='controller', cargs='-v ptcp:' ):
"Create network from scratch using Open vSwitch."
@@ -41,7 +40,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
switch.cmd( 'ovs-vsctl del-br dp0' )
switch.cmd( 'ovs-vsctl add-br dp0' )
for intf in switch.intfs.values():
switch.cmd( 'ovs-vsctl add-port dp0 %s\n' % intf )
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
# Note: controller and switch are in root namespace, and we
# can connect via loopback interface
@@ -62,7 +61,6 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
switch.deleteIntfs()
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (kernel datapath)\n' )
+2 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Build a simple network from scratch, using mininet primitives.
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' )
intfs = str( sintf1 ), str( sintf2 )
intfs = [ str( i ) for i in sintf1, sintf2 ]
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
@@ -66,7 +66,6 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
switch.deleteIntfs()
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (user datapath)\n' )
+19 -37
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Simple example of setting network and CPU parameters
@@ -9,59 +9,41 @@ iperf will hang indefinitely if the TCP handshake fails
to complete.
"""
from sys import argv
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel
# It would be nice if we didn't have to do this:
# pylint: disable=arguments-differ
class SingleSwitchTopo( Topo ):
class SingleSwitchTopo(Topo):
"Single switch connected to n hosts."
def build( self, n=2, lossy=True ):
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
switch = self.addSwitch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
host = self.addHost('h%s' % (h + 1),
cpu=.5 / n)
if lossy:
# 10 Mbps, 5ms delay, 10% packet loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
else:
# 10 Mbps, 5ms delay, no packet loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=0, use_htb=True)
# 10 Mbps, 5ms delay, 10% loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
def perfTest( lossy=True ):
def perfTest():
"Create network and run simple performance test"
topo = SingleSwitchTopo( n=4, lossy=lossy )
net = Mininet( topo=topo,
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
topo = SingleSwitchTopo(n=4)
net = Mininet(topo=topo,
host=CPULimitedHost, link=TCLink)
net.start()
info( "Dumping host connections\n" )
print "Dumping host connections"
dumpNodeConnections(net.hosts)
info( "Testing bandwidth between h1 and h4 (lossy=%s)\n" % lossy )
print "Testing network connectivity"
net.pingAll()
print "Testing bandwidth between h1 and h4"
h1, h4 = net.getNodeByName('h1', 'h4')
net.iperf( ( h1, h4 ), l4Type='UDP' )
# Debugging
h1.cmd('jobs')
h4.cmd('jobs')
net.iperf((h1, h4))
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 ) )
setLogLevel('info')
perfTest()
+13 -17
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Create a network and start sshd(8) on each host.
@@ -20,16 +20,15 @@ import sys
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg, info
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeTopo
from mininet.util import waitListening
from mininet.link import Link
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout )
return Mininet( topo, waitConnected=True, **kwargs )
return Mininet( topo, **kwargs )
def connectToRootNS( network, switch, ip, routes ):
"""Connect hosts to root namespace via switch. Starts network.
@@ -39,7 +38,7 @@ def connectToRootNS( network, switch, ip, routes ):
routes: host networks to route to"""
# Create a node in root namespace and link to switch 0
root = Node( 'root', inNamespace=False )
intf = network.addLink( root, switch ).intf1
intf = Link( root, switch ).intf1
root.setIP( ip, intf=intf )
# Start network that now includes link to root namespace
network.start()
@@ -47,7 +46,6 @@ def connectToRootNS( network, switch, ip, routes ):
for route in routes:
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
# pylint: disable=too-many-arguments
def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
ip='10.123.123.1/32', routes=None, switch=None ):
"""Start a network, connect it to root ns, and run sshd on all hosts.
@@ -61,25 +59,23 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
connectToRootNS( network, switch, ip, routes )
for host in network.hosts:
host.cmd( cmd + ' ' + opts + '&' )
info( "*** Waiting for ssh daemons to start\n" )
for server in network.hosts:
waitListening( server=server, port=22, timeout=5 )
info( "\n*** Hosts are running sshd at the following addresses:\n" )
print
print "*** Hosts are running sshd at the following addresses:"
print
for host in network.hosts:
info( host.name, host.IP(), '\n' )
info( "\n*** Type 'exit' or control-D to shut down network\n" )
print host.name, host.IP()
print
print "*** Type 'exit' or control-D to shut down network"
CLI( network )
for host in network.hosts:
host.cmd( 'kill %' + cmd )
network.stop()
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
# get sshd args from the command line or use default args
# useDNS=no -u0 to avoid reverse DNS lookup timeout
argvopts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
'-D -o UseDNS=no -u0' )
sshd( net, opts=argvopts )
sshd( net, opts=opts )
+1 -14
View File
@@ -12,18 +12,6 @@ import sys
from mininet.util import ensureRoot
from mininet.clean import cleanup
class MininetTestResult( unittest.TextTestResult ):
def addFailure( self, test, err ):
super( MininetTestResult, self ).addFailure( test, err )
cleanup()
def addError( self,test, err ):
super( MininetTestResult, self ).addError( test, err )
cleanup()
class MininetTestRunner( unittest.TextTestRunner ):
def _makeResult( self ):
return MininetTestResult( self.stream, self.descriptions, self.verbosity )
def runTests( testDir, verbosity=1 ):
"discover and run all tests in testDir"
# ensure root and cleanup before starting tests
@@ -32,8 +20,7 @@ def runTests( testDir, verbosity=1 ):
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
success = MininetTestRunner( verbosity=verbosity ).run( testSuite ).wasSuccessful()
sys.exit( 0 if success else 1 )
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
# get the directory containing example tests
+14 -20
View File
@@ -5,27 +5,26 @@ Tests for baresshd.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from time import sleep
from mininet.clean import cleanup, sh
from sys import stdout
class testBareSSHD( unittest.TestCase ):
opts = [ 'Welcome to h1', pexpect.EOF, pexpect.TIMEOUT ]
opts = [ '\(yes/no\)\?', 'Welcome to h1', 'refused', pexpect.EOF, pexpect.TIMEOUT ]
def connected( self ):
"Log into ssh server, check banner, then exit"
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
'-o StrictHostKeyChecking=no '
'-i /tmp/ssh/test_rsa exit' )
p = pexpect.spawn( 'ssh 10.0.0.1 -i /tmp/ssh/test_rsa exit' )
while True:
index = p.expect( self.opts )
if index == 0:
p.sendline( 'yes' )
elif index == 1:
return True
else:
return False
def setUp( self ):
# verify that sshd is not running
self.assertFalse( self.connected() )
@@ -38,28 +37,23 @@ class testBareSSHD( unittest.TestCase ):
cmd = ( 'python -m mininet.examples.baresshd '
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
'-o StrictModes=no' )
p = pexpect.spawn( cmd )
runOpts = [ 'You may now ssh into h1 at 10.0.0.1',
'after 5 seconds, h1 is not listening on port 22',
pexpect.EOF, pexpect.TIMEOUT ]
while True:
index = p.expect( runOpts )
if index == 0:
break
else:
self.tearDown()
self.fail( 'sshd failed to start in host h1' )
sh( cmd )
def testSSH( self ):
"Simple test to verify that we can ssh into h1"
result = False
# try to connect up to 3 times; sshd can take a while to start
result = self.connected()
for _ in range( 3 ):
result = self.connected()
if result:
break
else:
sleep( 1 )
self.assertTrue( result )
def tearDown( self ):
# kill the ssh process
sh( "ps aux | grep ssh |grep Banner| awk '{ print $2 }' | xargs kill" )
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
cleanup()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for bind.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testBind( unittest.TestCase ):
-29
View File
@@ -1,29 +0,0 @@
#!/usr/bin/env python
'''
A simple sanity check test for cluster edition
'''
import unittest
from mininet.util import pexpect
class clusterSanityCheck( unittest.TestCase ):
prompt = 'mininet>'
def testClusterPingAll( self ):
p = pexpect.spawn( 'python -m mininet.examples.clusterSanity' )
p.expect( self.prompt )
p.sendline( 'py net.waitConnected()' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testControllers( unittest.TestCase ):
+4 -6
View File
@@ -5,9 +5,7 @@ Test for controlnet.py
"""
import unittest
from mininet.util import pexpect
from sys import stdout
import pexpect
class testControlNet( unittest.TestCase ):
@@ -15,7 +13,7 @@ class testControlNet( unittest.TestCase ):
def testPingall( self ):
"Simple pingall test that verifies 0% packet drop in data network"
p = pexpect.spawn( 'python -m mininet.examples.controlnet', logfile=stdout)
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
@@ -28,9 +26,9 @@ class testControlNet( unittest.TestCase ):
def testFailover( self ):
"Kill controllers and verify that switch, s1, fails over properly"
count = 1
p = pexpect.spawn( 'python -m mininet.examples.controlnet', logfile=stdout )
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p.expect( self.prompt )
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log', logfile=stdout )
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' )
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
ip = int( lp.match.group( 1 ) )
self.assertEqual( count, ip )
+7 -21
View File
@@ -2,20 +2,10 @@
"""
Test for cpu.py
results format:
sched cpu received bits/sec
cfs 50% 8.14e+09
cfs 40% 6.48e+09
cfs 30% 4.56e+09
cfs 20% 2.84e+09
cfs 10% 1.29e+09
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testCPU( unittest.TestCase ):
@@ -25,23 +15,19 @@ class testCPU( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testCPU( self ):
"Verify that CPU utilization is monotonically decreasing for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.cpu', timeout=300 )
# matches each line from results( shown above )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)',
pexpect.EOF ]
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)', pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts )
index = p.expect( opts, timeout=600 )
if index == 0:
sched = p.match.group( 1 )
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
bw = float( p.match.group( 3 ) )
if sched not in scheds:
scheds.append( sched )
else:
self.assertTrue( bw < previous_bw,
"%e should be less than %e\n" %
( bw, previous_bw ) )
previous_bw = 10 ** 4 # 10 GB/s
self.assertTrue( bw < previous_bw )
previous_bw = bw
else:
break
+2 -2
View File
@@ -5,7 +5,7 @@ Test for emptynet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testEmptyNet( unittest.TestCase ):
@@ -19,7 +19,7 @@ class testEmptyNet( unittest.TestCase ):
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# iperf test
p.sendline( 'iperf' )
+5 -7
View File
@@ -5,14 +5,12 @@ Test for hwintf.py
"""
import unittest
import pexpect
import re
from mininet.util import pexpect
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.node import Node
from mininet.link import Link
from mininet.link import Link, Intf
class testHwintf( unittest.TestCase ):
@@ -57,8 +55,8 @@ class testHwintf( unittest.TestCase ):
p.wait()
def tearDown( self ):
self.h3.stop( deleteIntfs=True )
self.n0.stop( deleteIntfs=True )
self.h3.terminate()
self.n0.terminate()
if __name__ == '__main__':
setLogLevel( 'warning' )
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env python
"""
Test for intfOptions.py
"""
import unittest
from mininet.util import pexpect
import sys
class testIntfOptions( unittest.TestCase ):
def testIntfOptions( self ):
"verify that intf.config is correctly limiting traffic"
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
tolerance = .25 # plus or minus 25% for cloud CI tests
opts = [ "Results: \['([\d\.]+) .bits/sec",
"Results: \['10M', '([\d\.]+) .bits/sec",
"h(\d+)->h(\d+): (\d)/(\d),"
"rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
BW = 10
bw = float( p.match.group( 1 ) )
self.assertGreaterEqual( bw, BW * ( 1 - tolerance ) )
self.assertLessEqual( bw, BW * ( 1 + tolerance ) )
elif index == 1:
BW = 10
measuredBw = float( p.match.group( 1 ) )
loss = ( measuredBw / BW ) * 100
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ),
'loss of %d%% << 50%%' % loss )
self.assertLessEqual( loss, 50 * ( 1 + tolerance ),
'loss of %d%% >> 50%%' % loss )
elif index == 2:
delay = float( p.match.group( 6 ) )
self.assertGreaterEqual( delay, 15 * ( 1 - tolerance ) )
self.assertLessEqual( delay, 15 * ( 1 + tolerance ) )
else:
break
if __name__ == '__main__':
unittest.main()
+3 -3
View File
@@ -5,7 +5,7 @@ Test for limit.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testLimit( unittest.TestCase ):
@@ -14,8 +14,8 @@ class testLimit( unittest.TestCase ):
def testLimit( self ):
"Verify that CPU limits are within a 2% tolerance of limit for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.limit' )
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
pexpect.EOF ]
count = 0
bw = 0
+6 -11
View File
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testLinearBandwidth( unittest.TestCase ):
@@ -15,12 +15,13 @@ class testLinearBandwidth( unittest.TestCase ):
"Verify that bandwidth is monotonically decreasing as # of hops increases"
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
count = 0
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
previous_bw = 10 ** 10 # 10 Gbits
count += 1
elif index == 1:
n = int( p.match.group( 1 ) )
@@ -32,17 +33,11 @@ class testLinearBandwidth( unittest.TestCase ):
bw *= 10 ** 6
elif unit[ 0 ] == 'G':
bw *= 10 ** 9
# check that we have a previous result to compare to
if n != 1:
info = ( 'bw: %.2e bits/s across %d switches, '
'previous: %.2e bits/s across %d switches' %
( bw, n, previous_bw, previous_n ) )
self.assertTrue( bw < previous_bw, info )
previous_bw, previous_n = bw, n
self.assertTrue( bw < previous_bw )
previous_bw = bw
else:
break
# verify that we received results from at least one switch
self.assertTrue( count > 0 )
if __name__ == '__main__':
-52
View File
@@ -1,52 +0,0 @@
#!/usr/bin/env python
"""
Test for linuxrouter.py
"""
import unittest
from mininet.util import pexpect
from mininet.util import quietRun
class testLinuxRouter( unittest.TestCase ):
prompt = 'mininet>'
def testPingall( self ):
"Test connectivity between hosts"
p = pexpect.spawn( 'python -m mininet.examples.linuxrouter' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
def testRouterPing( self ):
"Test connectivity from h1 to router"
p = pexpect.spawn( 'python -m mininet.examples.linuxrouter' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 r0' )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
def testTTL( self ):
"Verify that the TTL is decremented"
p = pexpect.spawn( 'python -m mininet.examples.linuxrouter' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect ( 'ttl=(\d+)' )
ttl = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( ttl, 63 ) # 64 - 1
if __name__ == '__main__':
unittest.main()
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env python
"""
Test for mobility.py
"""
import unittest
from subprocess import check_output
class testMobility( unittest.TestCase ):
def testMobility( self ):
"Run the example and verify its 4 ping results"
cmd = 'python -m mininet.examples.mobility 2>&1'
grep = ' | grep -c " 0% dropped" '
result = check_output( cmd + grep, shell=True )
assert int( result ) == 4
if __name__ == '__main__':
unittest.main()
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env python
'''
Test for multiple links between nodes
validates mininet interfaces against systems interfaces
'''
import unittest
from mininet.util import pexpect
class testMultiLink( unittest.TestCase ):
prompt = 'mininet>'
def testMultiLink(self):
p = pexpect.spawn( 'python -m mininet.examples.multilink' )
p.expect( self.prompt )
p.sendline( 'intfs' )
p.expect( 's(\d): lo' )
intfsOutput = p.before
# parse interfaces from mininet intfs, and store them in a list
hostToIntfs = intfsOutput.split( '\r\n' )[ 1:3 ]
intfList = []
for hostToIntf in hostToIntfs:
intfList += [ intf for intf in
hostToIntf.split()[1].split(',') ]
# get interfaces from system by running ifconfig on every host
sysIntfList = []
opts = [ 'h(\d)-eth(\d)', self.prompt ]
p.expect( self.prompt )
p.sendline( 'h1 ifconfig' )
while True:
p.expect( opts )
if p.after == self.prompt:
break
sysIntfList.append( p.after )
p.sendline( 'h2 ifconfig' )
while True:
p.expect( opts )
if p.after == self.prompt:
break
sysIntfList.append( p.after )
failMsg = ( 'The systems interfaces and mininet interfaces\n'
'are not the same' )
self.assertEqual( sysIntfList, intfList, msg=failMsg )
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
+6 -8
View File
@@ -5,13 +5,13 @@ Test for multiping.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from collections import defaultdict
class testMultiPing( unittest.TestCase ):
def testMultiPing( self ):
"""Verify that each target is pinged at least once, and
"""Verify that each target is pinged at least once, and
that pings to 'real' targets are successful and unknown targets fail"""
p = pexpect.spawn( 'python -m mininet.examples.multiping' )
opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)",
@@ -31,20 +31,18 @@ class testMultiPing( unittest.TestCase ):
target = p.match.group(3)
received = int( p.match.group(4) )
if target == '10.0.0.200':
self.assertEqual( received, 0, p.match.group(0) + '\n' +
target + ' received %d != 0 packets' % received )
self.assertEqual( received, 0 )
else:
self.assertEqual( received, 1, p.match.group(0) + '\n' +
target + ' received %d != 1 packets' % received )
self.assertEqual( received, 1 )
try:
pings[ name ].remove( target )
except:
pass
else:
break
self.assertTrue( len( pings ) > 0, 'too few pings' )
self.assertTrue( len( pings ) > 0 )
for t in pings.values():
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
self.assertEqual( len( t ), 0 )
if __name__ == '__main__':
unittest.main()
+3 -4
View File
@@ -5,7 +5,7 @@ Test for multipoll.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testMultiPoll( unittest.TestCase ):
@@ -16,7 +16,7 @@ class testMultiPoll( unittest.TestCase ):
"(h\d+): \d+ bytes from",
"Monitoring output for (\d+) seconds",
pexpect.EOF ]
pings, seconds = {}, -1
pings = {}
while True:
index = p.expect( opts )
if index == 0:
@@ -32,8 +32,7 @@ class testMultiPoll( unittest.TestCase ):
self.assertTrue( len( pings ) > 0 )
# make sure we have received at least one ping per second
for count in pings.values():
self.assertTrue( count >= seconds,
'%d pings < %d seconds' % ( count, seconds ) )
self.assertTrue( count >= seconds )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multitest.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testMultiTest( unittest.TestCase ):
+2 -2
View File
@@ -5,7 +5,7 @@ Test for nat.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from mininet.util import quietRun
destIP = '8.8.8.8' # Google DNS
@@ -14,7 +14,7 @@ class testNAT( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
'Destination IP is not reachable' )
def testNAT( self ):
"Attempt to ping an IP on the Internet and verify 0% packet loss"
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/env python
"""
Test for natnet.py
"""
import unittest
from mininet.util import pexpect
from mininet.util import quietRun
class testNATNet( unittest.TestCase ):
prompt = 'mininet>'
def setUp( self ):
self.net = pexpect.spawn( 'python -m mininet.examples.natnet' )
self.net.expect( self.prompt )
def testPublicPing( self ):
"Attempt to ping the public server (h0) from h1 and h2"
self.net.sendline( 'h1 ping -c 1 h0' )
self.net.expect ( '(\d+)% packet loss' )
percent = int( self.net.match.group( 1 ) ) if self.net.match else -1
self.assertEqual( percent, 0 )
self.net.expect( self.prompt )
self.net.sendline( 'h2 ping -c 1 h0' )
self.net.expect ( '(\d+)% packet loss' )
percent = int( self.net.match.group( 1 ) ) if self.net.match else -1
self.assertEqual( percent, 0 )
self.net.expect( self.prompt )
def testPrivatePing( self ):
"Attempt to ping h1 and h2 from public server"
self.net.sendline( 'h0 ping -c 1 -t 1 h1' )
result = self.net.expect ( [ 'unreachable', 'loss' ] )
self.assertEqual( result, 0 )
self.net.expect( self.prompt )
self.net.sendline( 'h0 ping -c 1 -t 1 h2' )
result = self.net.expect ( [ 'unreachable', 'loss' ] )
self.assertEqual( result, 0 )
self.net.expect( self.prompt )
def testPrivateToPrivatePing( self ):
"Attempt to ping from NAT'ed host h1 to NAT'ed host h2"
self.net.sendline( 'h1 ping -c 1 -t 1 h2' )
result = self.net.expect ( [ '[Uu]nreachable', 'loss' ] )
self.assertEqual( result, 0 )
self.net.expect( self.prompt )
def tearDown( self ):
self.net.sendline( 'exit' )
self.net.wait()
if __name__ == '__main__':
unittest.main()
-52
View File
@@ -1,52 +0,0 @@
#!/usr/bin/env python
"""
Test for numberedports.py
"""
import unittest
from mininet.util import pexpect
from collections import defaultdict
from mininet.node import OVSSwitch
class testNumberedports( unittest.TestCase ):
@unittest.skipIf( OVSSwitch.setup() or OVSSwitch.isOldOVS(), "old version of OVS" )
def testConsistency( self ):
"""verify consistency between mininet and ovs ports"""
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
'Validating that s1-eth\d is actually on port \d ... WARNING',
pexpect.EOF ]
correct_ports = True
count = 0
while True:
index = p.expect( opts )
if index == 0:
count += 1
elif index == 1:
correct_ports = False
elif index == 2:
self.assertNotEqual( 0, count )
break
self.assertTrue( correct_ports )
def testNumbering( self ):
"""verify that all of the port numbers are printed correctly and consistent with their interface"""
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
opts = [ 's1-eth(\d+) : (\d+)',
pexpect.EOF ]
count_intfs = 0
while True:
index = p.expect( opts )
if index == 0:
count_intfs += 1
intfport = p.match.group( 1 )
ofport = p.match.group( 2 )
self.assertEqual( intfport, ofport )
elif index == 1:
break
self.assertNotEqual( 0, count_intfs )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testPopen( unittest.TestCase ):
+2 -3
View File
@@ -5,7 +5,7 @@ Test for scratchnet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testScratchNet( unittest.TestCase ):
@@ -14,9 +14,8 @@ class testScratchNet( unittest.TestCase ):
def pingTest( self, name ):
"Verify that no ping packets were dropped"
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts, timeout=120 )
index = p.expect( self.opts )
self.assertEqual( index, 0 )
p.wait()
def testPingKernel( self ):
self.pingTest( 'mininet.examples.scratchnet' )
+32 -16
View File
@@ -5,9 +5,13 @@ Test for simpleperf.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import re
import sys
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.examples.simpleperf import SingleSwitchTopo
@@ -15,23 +19,35 @@ class testSimplePerf( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testE2E( self ):
"Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .2
p = pexpect.spawn(
'python -m mininet.examples.simpleperf testmode' )
# Log since this seems to be failing intermittently
p.logfile = sys.stdout
"Run the example and verify ping and iperf results"
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
# check ping results
p.expect( "Results: (\d+)% dropped", timeout=120 )
loss = int( p.match.group( 1 ) )
self.assertTrue( loss > 0 and loss < 100 )
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=90 )
measuredBw = float( p.match.group( 1 ) )
lowerBound = BW * ( 1 - TOLERANCE )
upperBound = BW + ( 1 + TOLERANCE )
self.assertGreaterEqual( measuredBw, lowerBound )
self.assertLessEqual( measuredBw, upperBound )
p.expect( "Results: \['([\d\.]+) .bits/sec", timeout=480 )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.wait()
def testTopo( self ):
"""Import SingleSwitchTopo from example and test connectivity between two hosts
Note: this test may fail very rarely because it is non-deterministic
i.e. links are configured with 10% packet loss, but if we get unlucky and
none or all of the packets are dropped, the test will fail"""
topo = SingleSwitchTopo( n=4 )
net = Mininet( topo=topo, host=CPULimitedHost, link=TCLink )
net.start()
h1, h4 = net.get( 'h1', 'h4' )
# have h1 ping h4 ten times
expectStr = '(\d+) packets transmitted, (\d+) received, (\d+)% packet loss'
output = h1.cmd( 'ping -c 10 %s' % h4.IP() )
m = re.search( expectStr, output )
loss = int( m.group( 3 ) )
net.stop()
self.assertTrue( loss > 0 and loss < 100 )
if __name__ == '__main__':
setLogLevel( 'debug' )
setLogLevel( 'warning' )
unittest.main()
+7 -6
View File
@@ -5,7 +5,8 @@ Test for sshd.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from time import sleep
from mininet.clean import sh
class testSSHD( unittest.TestCase ):
@@ -14,20 +15,19 @@ class testSSHD( unittest.TestCase ):
def connected( self, ip ):
"Log into ssh server, check banner, then exit"
# Note: this test will fail if "Welcome" is not in the sshd banner
# Note: this test will fail if "Welcome" is not in the sshd banner
# and '#'' or '$'' are not in the prompt
ssh = 'ssh -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa ' + ip
p = pexpect.spawn( ssh, timeout=5 )
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
while True:
index = p.expect( self.opts )
if index == 0:
print( p.match.group(0) )
print p.match.group(0)
p.sendline( 'yes' )
elif index == 1:
return False
elif index == 2:
p.sendline( 'exit' )
p.wait()
p.wait()
return True
else:
return False
@@ -58,3 +58,4 @@ class testSSHD( unittest.TestCase ):
if __name__ == '__main__':
unittest.main()
+4 -6
View File
@@ -5,7 +5,7 @@ Test for tree1024.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testTree1024( unittest.TestCase ):
@@ -17,15 +17,13 @@ class testTree1024( unittest.TestCase ):
"Run the example and do a simple ping test from h1 to h1024"
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
p.sendline( 'h1 ping -c 20 h1024' )
p.sendline( 'h1 ping -c 1 h1024' )
p.expect ( '(\d+)% packet loss' )
packetLossPercent = int( p.match.group( 1 ) ) if p.match else -1
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
# Tolerate slow startup on some systems - we should revisit this
# and determine the root cause.
self.assertLess( packetLossPercent, 60 )
self.assertEqual( percent, 0 )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for treeping64.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testTreePing64( unittest.TestCase ):
-50
View File
@@ -1,50 +0,0 @@
#!/usr/bin/env python
"""
Test for vlanhost.py
"""
import unittest
from mininet.util import pexpect
import sys
from mininet.util import quietRun
class testVLANHost( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testVLANTopo( self ):
"Test connectivity (or lack thereof) between hosts in VLANTopo"
p = pexpect.spawn( 'python -m mininet.examples.vlanhost' )
p.expect( self.prompt )
p.sendline( 'pingall 1' ) #ping timeout=1
p.expect( '(\d+)% dropped', timeout=30 ) # there should be 24 failed pings
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 80 )
def testSpecificVLAN( self ):
"Test connectivity between hosts on a specific VLAN"
vlan = 1001
p = pexpect.spawn( 'python -m mininet.examples.vlanhost %d' % vlan )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'h1 ifconfig' )
i = p.expect( ['h1-eth0.%d' % vlan, pexpect.TIMEOUT ], timeout=2 )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 ) # no packet loss on ping
self.assertEqual( i, 0 ) # check vlan intf is present
if __name__ == '__main__':
unittest.main()
+3 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
"""
Create a 1024-host network, and run the CLI on it.
@@ -9,12 +9,10 @@ and running sysctl -p. Check util/sysctl_addon.
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.node import OVSSwitch
from mininet.node import OVSKernelSwitch
from mininet.topolib import TreeNet
from mininet.examples.treeping64 import HostV4
if __name__ == '__main__':
setLogLevel( 'info' )
network = TreeNet( depth=2, fanout=32, host=HostV4,
switch=OVSSwitch, waitConnected=True)
network = TreeNet( depth=2, fanout=32, switch=OVSKernelSwitch )
network.run( CLI, network )
+14 -25
View File
@@ -1,42 +1,31 @@
#!/usr/bin/env python
#!/usr/bin/python
"Create a 64-node tree network, and test connectivity using ping."
from mininet.log import setLogLevel, info
from mininet.node import UserSwitch, OVSKernelSwitch, Host
from mininet.log import setLogLevel
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
from mininet.topolib import TreeNet
class HostV4( Host ):
"Try to IPv6 and its awful neighbor discovery"
def __init__( self, *args, **kwargs ):
super( HostV4, self ).__init__( *args, **kwargs )
cfgs = [ 'all.disable_ipv6=1', 'default.disable_ipv6=1',
'default.autoconf=0', 'lo.autoconf=0' ]
for cfg in cfgs:
self.cmd( 'sysctl -w net.ipv6.conf.' + cfg )
def treePing64():
"Run ping test on 64-node tree networks."
results = {}
switches = { 'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
switches = { # 'reference kernel': KernelSwitch,
'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
for name, switch in switches.items():
info( "*** Testing", name, "datapath\n" )
network = TreeNet( depth=2, fanout=8, switch=switch,
waitConnected=True )
for name in switches:
print "*** Testing", name, "datapath"
switch = switches[ name ]
network = TreeNet( depth=2, fanout=8, switch=switch )
result = network.run( network.pingAll )
results[ name ] = result
info( "\n*** Tree network ping results:\n" )
print
print "*** Tree network ping results:"
for name in switches:
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
info( '\n' )
print "%s: %d%% packet loss" % ( name, results[ name ] )
print
if __name__ == '__main__':
setLogLevel( 'info' )
-130
View File
@@ -1,130 +0,0 @@
#!/usr/bin/env python
"""
vlanhost.py: Host subclass that uses a VLAN tag for the default interface.
Dependencies:
This class depends on the "vlan" package
$ sudo apt-get install vlan
Usage (example uses VLAN ID=1000):
From the command line:
sudo mn --custom vlanhost.py --host vlan,vlan=1000
From a script (see exampleUsage function below):
from functools import partial
from vlanhost import VLANHost
....
host = partial( VLANHost, vlan=1000 )
net = Mininet( host=host, ... )
Directly running this script:
sudo python vlanhost.py 1000
"""
from sys import exit # pylint: disable=redefined-builtin
from mininet.node import Host
from mininet.topo import Topo
from mininet.util import quietRun
from mininet.log import error
class VLANHost( Host ):
"Host connected to VLAN interface"
# pylint: disable=arguments-differ
def config( self, vlan=100, **params ):
"""Configure VLANHost according to (optional) parameters:
vlan: VLAN ID for default interface"""
r = super( VLANHost, self ).config( **params )
intf = self.defaultIntf()
# remove IP from default, "physical" interface
self.cmd( 'ifconfig %s inet 0' % intf )
# create VLAN interface
self.cmd( 'vconfig add %s %d' % ( intf, vlan ) )
# assign the host's IP to the VLAN interface
self.cmd( 'ifconfig %s.%d inet %s' % ( intf, vlan, params['ip'] ) )
# update the intf name and host's intf map
newName = '%s.%d' % ( intf, vlan )
# update the (Mininet) interface to refer to VLAN interface name
intf.name = newName
# add VLAN interface to host's name to intf map
self.nameToIntf[ newName ] = intf
return r
hosts = { 'vlan': VLANHost }
def exampleAllHosts( vlan ):
"""Simple example of how VLANHost can be used in a script"""
# This is where the magic happens...
host = partial( VLANHost, vlan=vlan )
# vlan (type: int): VLAN ID to be used by all hosts
# Start a basic network using our VLANHost
topo = SingleSwitchTopo( k=2 )
net = Mininet( host=host, topo=topo, waitConnected=True )
net.start()
CLI( net )
net.stop()
# pylint: disable=arguments-differ
class VLANStarTopo( Topo ):
"""Example topology that uses host in multiple VLANs
The topology has a single switch. There are k VLANs with
n hosts in each, all connected to the single switch. There
are also n hosts that are not in any VLAN, also connected to
the switch."""
def build( self, k=2, n=2, vlanBase=100 ):
s1 = self.addSwitch( 's1' )
for i in range( k ):
vlan = vlanBase + i
for j in range(n):
name = 'h%d-%d' % ( j+1, vlan )
h = self.addHost( name, cls=VLANHost, vlan=vlan )
self.addLink( h, s1 )
for j in range( n ):
h = self.addHost( 'h%d' % (j+1) )
self.addLink( h, s1 )
def exampleCustomTags():
"""Simple example that exercises VLANStarTopo"""
net = Mininet( topo=VLANStarTopo(), waitConnected=True )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
import sys
from functools import partial
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel
setLogLevel( 'info' )
if not quietRun( 'which vconfig' ):
error( "Cannot find command 'vconfig'\nThe package",
"'vlan' is required in Ubuntu or Debian,",
"or 'vconfig' in Fedora\n" )
exit()
if len( sys.argv ) >= 2:
exampleAllHosts( vlan=int( sys.argv[ 1 ] ) )
else:
exampleCustomTags()
-1
View File
@@ -1 +0,0 @@
../bin/mn
+37 -99
View File
@@ -10,120 +10,58 @@ It may also get rid of 'false positives', but hopefully
nothing irreplaceable!
"""
from subprocess import ( Popen, PIPE, check_output as co,
CalledProcessError )
from subprocess import Popen, PIPE
import time
from mininet.log import info
from mininet.term import cleanUpScreens
from mininet.util import decode
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
p = Popen( # pylint: disable=consider-using-with
[ '/bin/sh', '-c', cmd ], stdout=PIPE )
result = p.communicate()[ 0 ]
return decode( result )
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
sh( 'pkill -9 -f %s' % pattern )
# Make sure they are gone
while True:
try:
pids = co( [ 'pgrep', '-f', pattern ] )
except CalledProcessError:
pids = ''
if pids:
sh( 'pkill -9 -f %s' % pattern )
time.sleep( .5 )
else:
break
def cleanup():
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
class Cleanup( object ):
"Wrapper for cleanup()"
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
"\n")
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep(1)
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
callbacks = []
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
@classmethod
def cleanup( cls):
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
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 ' )
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' )
for dp in dps:
if dp != '':
sh( 'dpctl deldp ' + dp )
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'"
).splitlines()
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths\n" )
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps:
sh( "ovs-vsctl " + " -- ".join( "--if-exists del-br " + dp
for dp in dps if dp ) )
# And in case the above didn't work...
dps = sh( "ovs-vsctl --timeout=1 list-br" ).strip().splitlines()
for dp in dps:
info( "*** Removing OVS datapaths" )
dps = sh("ovs-vsctl --timeout=1 list-br").split( '\n' )
for dp in dps:
if dp:
sh( 'ovs-vsctl del-br ' + dp )
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | "
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'"
).splitlines()
# Delete blocks of links
n = 1000 # chunk size
for i in range( 0, len( links ), n ):
cmd = ';'.join( 'ip link del %s' % link
for link in links[ i : i + n ] )
sh( '( %s ) 2> /dev/null' % cmd )
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( r"ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
for link in links:
if link != '':
sh( "ip link del " + link )
if 'tap9' in sh( 'ip link show' ):
info( "*** Removing tap9 - assuming it's from cluster edition\n" )
sh( 'ip link del tap9' )
info( "*** Killing stale mininet node processes\n" )
killprocs( 'mininet:' )
info( "*** Shutting down stale tunnels\n" )
killprocs( 'Tunnel=Ethernet' )
killprocs( '.ssh/mn')
sh( 'rm -f ~/.ssh/mn/*' )
# Call any additional cleanup code if necessary
for callback in cls.callbacks:
callback()
info( "*** Cleanup complete.\n" )
@classmethod
def addCleanupCallback( cls, callback ):
"Add cleanup callback"
if callback not in cls.callbacks:
cls.callbacks.append( callback )
cleanup = Cleanup.cleanup
addCleanupCallback = Cleanup.addCleanupCallback
info( "*** Cleanup complete.\n" )
+51 -166
View File
@@ -29,98 +29,45 @@ from subprocess import call
from cmd import Cmd
from os import isatty
from select import poll, POLLIN
import select
import errno
import sys
import time
import os
import atexit
from mininet.log import info, output, error
from mininet.term import makeTerms, runX11
from mininet.util import ( quietRun, dumpNodeConnections,
dumpPorts )
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
prompt = 'mininet> '
def __init__( self, mininet, stdin=sys.stdin, script=None,
**kwargs ):
"""Start and run interactive or batch mode CLI
mininet: Mininet network object
stdin: standard input for CLI
script: script to run in batch mode"""
def __init__( self, mininet, stdin=sys.stdin, script=None ):
self.mn = mininet
# Local variable bindings for py command
self.locals = { 'net': mininet }
# Attempt to handle input
self.stdin = stdin
self.inPoller = poll()
self.inPoller.register( stdin )
self.inputFile = script
Cmd.__init__( self, stdin=stdin, **kwargs )
Cmd.__init__( self )
info( '*** Starting CLI:\n' )
if self.inputFile:
self.do_source( self.inputFile )
return
self.initReadline()
self.run()
readlineInited = False
@classmethod
def initReadline( cls ):
"Set up history if readline is available"
# Only set up readline once to prevent multiplying the history file
if cls.readlineInited:
return
cls.readlineInited = True
try:
# pylint: disable=import-outside-toplevel
from readline import ( read_history_file, write_history_file,
set_history_length )
except ImportError:
pass
else:
history_path = os.path.expanduser( '~/.mininet_history' )
if os.path.isfile( history_path ):
read_history_file( history_path )
set_history_length( 1000 )
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"
while True:
try:
# Make sure no nodes are still waiting
for node in self.mn.values():
while node.waiting:
info( 'stopping', node, '\n' )
node.sendInt()
node.waitOutput()
node.monitor()
if self.isatty():
quietRun( 'stty echo sane intr ^C' )
quietRun( 'stty sane' )
self.cmdloop()
break
except KeyboardInterrupt:
# Output a message - unless it's also interrupted
# pylint: disable=broad-except
try:
output( '\nInterrupt\n' )
except Exception:
pass
# pylint: enable=broad-except
output( '\nInterrupt\n' )
def emptyline( self ):
"Don't repeat last command when you hit return."
@@ -131,6 +78,11 @@ class CLI( Cmd ):
self.locals.update( self.mn )
return self.locals
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
' <node> command {args}\n'
@@ -150,10 +102,10 @@ class CLI( Cmd ):
' mininet> xterm h2\n\n'
)
def do_help( self, line ): # pylint: disable=arguments-renamed
def do_help( self, line ):
"Describe available CLI commands."
Cmd.do_help( self, line )
if line == '':
if line is '':
output( self.helpStr )
def do_nodes( self, _line ):
@@ -161,61 +113,54 @@ class CLI( Cmd ):
nodes = ' '.join( sorted( self.mn ) )
output( 'available nodes are: \n%s\n' % nodes )
def do_ports( self, _line ):
"display ports and interfaces for each switch"
dumpPorts( self.mn.switches )
def do_net( self, _line ):
"List network connections."
dumpNodeConnections( self.mn.values() )
def do_sh( self, line ):
"""Run an external shell command
Usage: sh [cmd args]"""
assert self # satisfy pylint and allow override
"Run an external shell command"
call( line, shell=True )
# do_py() and do_px() need to catch any exception during eval()/exec()
# pylint: disable=broad-except
# pylint: disable-msg=W0703
def do_py( self, line ):
"""Evaluate a Python expression.
Node names may be used, e.g.: py h1.cmd('ls')"""
try:
# pylint: disable=eval-used
result = eval( line, globals(), self.getLocals() )
if result is None:
if not result:
return
elif isinstance( result, str ):
output( result + '\n' )
else:
output( repr( result ) + '\n' )
except Exception as e:
except Exception, e:
output( str( e ) + '\n' )
# We are in fact using the exec() pseudo-function
# pylint: disable=exec-used
# pylint: disable-msg=W0122
def do_px( self, line ):
"""Execute a Python statement.
Node names may be used, e.g.: px print h1.cmd('ls')"""
try:
exec( line, globals(), self.getLocals() )
except Exception as e:
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable=broad-except,exec-used
# pylint: enable-msg=W0703,W0122
def do_pingall( self, line ):
def do_pingall( self, _line ):
"Ping between all hosts."
self.mn.pingAll( line )
self.mn.pingAll()
def do_pingpair( self, _line ):
"Ping between first two hosts, useful for testing."
self.mn.pingPair()
def do_pingallfull( self, _line ):
"Ping between all hosts, returns all ping results."
"Ping between first two hosts, returns all ping results."
self.mn.pingAllFull()
def do_pingpairfull( self, _line ):
@@ -223,8 +168,7 @@ class CLI( Cmd ):
self.mn.pingPairFull()
def do_iperf( self, line ):
"""Simple iperf TCP test between two (optionally specified) hosts.
Usage: iperf node1 node2"""
"Simple iperf TCP test between two (optionally specified) hosts."
args = line.split()
if not args:
self.mn.iperf()
@@ -243,8 +187,7 @@ class CLI( Cmd ):
error( 'invalid number of args: iperf src dst\n' )
def do_iperfudp( self, line ):
"""Simple iperf UDP test between two (optionally specified) hosts.
Usage: iperfudp bw node1 node2"""
"Simple iperf TCP test between two (optionally specified) hosts."
args = line.split()
if not args:
self.mn.iperf( l4Type='UDP' )
@@ -276,8 +219,7 @@ class CLI( Cmd ):
output( '%s\n' % repr( node ) )
def do_link( self, line ):
"""Bring link(s) between two nodes up or down.
Usage: link node1 node2 [up/down]"""
"Bring link(s) between two nodes up or down."
args = line.split()
if len(args) != 3:
error( 'invalid number of args: link end1 end2 [up down]\n' )
@@ -287,8 +229,7 @@ class CLI( Cmd ):
self.mn.configLinkStatus( *args )
def do_xterm( self, line, term='xterm' ):
"""Spawn xterm(s) for the given node(s).
Usage: xterm node1 node2 ..."""
"Spawn xterm(s) for the given node(s)."
args = line.split()
if not args:
error( 'usage: %s node1 node2 ...\n' % term )
@@ -302,8 +243,7 @@ class CLI( Cmd ):
def do_x( self, line ):
"""Create an X11 tunnel to the given node,
optionally starting a client.
Usage: x node [cmd args]"""
optionally starting a client."""
args = line.split()
if not args:
error( 'usage: x node [cmd args]...\n' )
@@ -313,13 +253,11 @@ class CLI( Cmd ):
self.mn.terms += runX11( node, cmd )
def do_gterm( self, line ):
"""Spawn gnome-terminal(s) for the given node(s).
Usage: gterm node1 node2 ..."""
"Spawn gnome-terminal(s) for the given node(s)."
self.do_xterm( line, term='gterm' )
def do_exit( self, _line ):
"Exit"
assert self # satisfy pylint and allow override
return 'exited by user command'
def do_quit( self, line ):
@@ -336,8 +274,7 @@ class CLI( Cmd ):
return isatty( self.stdin.fileno() )
def do_noecho( self, line ):
"""Run an interactive command with echoing turned off.
Usage: noecho [cmd args]"""
"Run an interactive command with echoing turned off."
if self.isatty():
quietRun( 'stty -echo' )
self.default( line )
@@ -345,28 +282,25 @@ class CLI( Cmd ):
quietRun( 'stty echo' )
def do_source( self, line ):
"""Read commands from an input file.
Usage: source <file>"""
"Read commands from an input file."
args = line.split()
if len(args) != 1:
error( 'usage: source <file>\n' )
return
try:
with open( args[ 0 ] ) as self.inputFile:
while True:
line = self.inputFile.readline()
if len( line ) > 0:
self.onecmd( line )
else:
break
self.inputFile = open( args[ 0 ] )
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()
self.inputFile = None
def do_dpctl( self, line ):
"""Run dpctl (or ovs-ofctl) command on all switches.
Usage: dpctl command [arg1] [arg2] ..."""
"Run dpctl (or ovs-ofctl) command on all switches."
args = line.split()
if len(args) < 1:
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
@@ -382,49 +316,17 @@ class CLI( Cmd ):
elapsed = time.time() - start
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
def do_links( self, _line ):
"Report on links"
for link in self.mn.links:
output( link, link.status(), '\n' )
def do_switch( self, line ):
"Starts or stops a switch"
args = line.split()
if len(args) != 2:
error( 'invalid number of args: switch <switch name>'
'{start, stop}\n' )
return
sw = args[ 0 ]
command = args[ 1 ]
if sw not in self.mn or self.mn.get( sw ) not in self.mn.switches:
error( 'invalid switch: %s\n' % args[ 1 ] )
else:
sw = args[ 0 ]
command = args[ 1 ]
if command == 'start':
self.mn.get( sw ).start( self.mn.controllers )
elif command == 'stop':
self.mn.get( sw ).stop( deleteIntfs=False )
else:
error( 'invalid command: '
'switch <switch name> {start, stop}\n' )
def do_wait( self, _line ):
"Wait until all switches have connected to a controller"
self.mn.waitConnected()
def default( self, line ):
"""Called on an input line when the command prefix is not recognized.
Overridden to run shell commands when a node is the first
CLI argument. Past the first CLI argument, node names are
automatically replaced with corresponding IP addrs."""
Overridden to run shell commands when a node is the first CLI argument.
Past the first CLI argument, node names are automatically replaced with
corresponding IP addrs."""
first, args, line = self.parseline( line )
if first in self.mn:
if not args:
error( '*** Please enter a command for node: %s <cmd>\n'
% first )
print "*** Enter a command for node: %s <cmd>" % first
return
node = self.mn[ first ]
rest = args.split( ' ' )
@@ -435,13 +337,16 @@ class CLI( Cmd ):
for arg in rest ]
rest = ' '.join( rest )
# Run cmd on node:
node.sendCmd( rest )
builtin = isShellBuiltin( first )
node.sendCmd( rest, printPid=( not builtin ) )
self.waitForNode( node )
else:
error( '*** Unknown command: %s\n' % line )
# pylint: enable-msg=R0201
def waitForNode( self, node ):
"Wait for a node to finish, and print its output."
"Wait for a node to finish, and print its output."
# Pollers
nodePoller = poll()
nodePoller.register( node.stdout )
@@ -456,14 +361,12 @@ 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 )
if key is not '':
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 )
@@ -473,24 +376,7 @@ class CLI( Cmd ):
if not node.waiting:
break
except KeyboardInterrupt:
# There is an at least one race condition here, since
# it's possible to interrupt ourselves after we've
# read data but before it has been printed.
node.sendInt()
except select.error as e:
# pylint: disable=unpacking-non-sequence
# pylint: disable=unbalanced-tuple-unpacking
errno_, errmsg = e.args
if errno_ != errno.EINTR:
error( "select.error: %s, %s" % (errno_, errmsg) )
node.sendInt()
def precmd( self, line ):
"allow for comments in the cli"
if '#' in line:
line = line.split( '#' )[ 0 ]
return line
# Helper functions
@@ -500,4 +386,3 @@ def isReadable( poller ):
mask = fdmask[ 1 ]
if mask & POLLIN:
return True
return False
+75 -257
View File
@@ -24,21 +24,15 @@ TCIntf: interface with bandwidth limiting and delay via tc
Link: basic link class for creating veth pairs
"""
import re
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
# Make pylint happy:
# pylint: disable=too-many-arguments
from mininet.util import makeIntfPair, quietRun
import re
class Intf( object ):
"Basic interface object that can configure itself."
def __init__( self, name, node=None, port=None, link=None,
mac=None, **params ):
def __init__( self, name, node=None, port=None, link=None, **params ):
"""name: interface name (e.g. h1-eth0)
node: owning node (where this intf most likely lives)
link: parent link if we're part of a link
@@ -46,21 +40,9 @@ class Intf( object ):
self.node = node
self.name = name
self.link = link
self.mac = mac
self.ip, self.prefixLen = None, None
# if interface is lo, we know the ip is 127.0.0.1.
# This saves an ifconfig command per node
if self.name == 'lo':
self.ip = '127.0.0.1'
self.prefixLen = 8
self.mac, self.ip, self.prefixLen = None, None, None
# Add to node (and move ourselves if necessary )
if node:
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
node.addIntf( self, port=port )
# Save params for future reference
self.params = params
self.config( **params )
@@ -81,9 +63,6 @@ class Intf( object ):
self.ip, self.prefixLen = ipstr.split( '/' )
return self.ifconfig( ipstr, 'up' )
else:
if prefixLen is None:
raise Exception( 'No prefix length set for IP address %s'
% ( ipstr, ) )
self.ip, self.prefixLen = ipstr, prefixLen
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
@@ -100,10 +79,7 @@ class Intf( object ):
def updateIP( self ):
"Return updated IP address based on ifconfig"
# use pexec instead of node.cmd so that we dont read
# backgrounded output from the cli.
ifconfig, _err, _exitCode = self.node.pexec(
'ifconfig %s' % self.name )
ifconfig = self.ifconfig()
ips = self._ipMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
return self.ip
@@ -115,19 +91,6 @@ class Intf( object ):
self.mac = macs[ 0 ] if macs else None
return self.mac
# Instead of updating ip and mac separately,
# use one ifconfig call to do it simultaneously.
# This saves an ifconfig command, which improves performance.
def updateAddr( self ):
"Return IP address and MAC address based on ifconfig."
ifconfig = self.ifconfig()
ips = self._ipMatchRegex.findall( ifconfig )
macs = self._macMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
self.mac = macs[ 0 ] if macs else None
return self.ip, self.mac
def IP( self ):
"Return IP address"
return self.ip
@@ -139,21 +102,11 @@ class Intf( object ):
def isUp( self, setUp=False ):
"Return whether interface is up"
if setUp:
cmdOutput = self.ifconfig( 'up' )
# no output indicates success
if cmdOutput:
error( "Error setting %s up: %s " % ( self.name, cmdOutput ) )
return False
else:
return True
else:
return "UP" in self.ifconfig()
self.ifconfig( 'up' )
return "UP" in self.ifconfig()
def rename( self, newname ):
"Rename interface"
if self.node and self.name in self.node.nameToIntf:
# rename intf in node's nameToIntf
self.node.nameToIntf[newname] = self.node.nameToIntf.pop(self.name)
self.ifconfig( 'down' )
result = self.cmd( 'ip link set', self.name, 'name', newname )
self.name = newname
@@ -172,13 +125,13 @@ class Intf( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = list( param.items() )[ 0 ]
name, value = param.items()[ 0 ]
f = getattr( self, method, None )
if not f or value is None:
return None
if isinstance( value, list ):
return
if type( value ) is list:
result = f( *value )
elif isinstance( value, dict ):
elif type( value ) is dict:
result = f( **value )
else:
result = f( value )
@@ -201,25 +154,16 @@ class Intf( object ):
self.setParam( r, 'setIP', ip=ip )
self.setParam( r, 'isUp', up=up )
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
self.updateIP()
self.updateMAC()
return r
def delete( self ):
"Delete interface"
self.cmd( 'ip link del ' + self.name )
# We used to do this, but it slows us down:
# if self.node.inNamespace:
# Link may have been dumped into root NS
# quietRun( 'ip link del ' + self.name )
self.node.delIntf( self )
self.link = None
def status( self ):
"Return intf status as a string"
links, _err, _result = self.node.pexec( 'ip link show' )
if self.name in links:
return "OK"
else:
return "MISSING"
if self.node.inNamespace:
# Link may have been dumped into root NS
quietRun( 'ip link del ' + self.name )
def __repr__( self ):
return '<%s %s>' % ( self.__class__.__name__, self.name )
@@ -233,19 +177,15 @@ class TCIntf( Intf ):
Allows specification of bandwidth limits (various methods)
as well as delay, loss and max queue length"""
# The parameters we use seem to work reasonably up to 1 Gb/sec
# For higher data rates, we will probably need to change them.
bwParamMax = 1000
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > self.bwParamMax ):
error( 'Bandwidth limit', bw, 'is outside supported range 0..%d'
% self.bwParamMax, '- ignoring\n' )
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
elif bw is not None:
# BL: this seems a bit brittle...
if ( speedup > 0 and
@@ -261,7 +201,7 @@ class TCIntf( Intf ):
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
if latency_ms is None:
latency_ms = 15.0 * 8 / bw
latency_ms = 15 * 8 / bw
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
@@ -293,14 +233,18 @@ class TCIntf( Intf ):
loss=None, max_queue_size=None ):
"Internal method: return tc commands for delay and loss"
cmds = []
if loss and ( loss < 0 or loss > 100 ):
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
elif jitter and jitter < 0:
error( 'Negative jitter', jitter, '\n' )
elif loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# Delay/jitter/loss/max queue size
netemargs = '%s%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'%s ' % jitter if jitter is not None else '',
'loss %.5f ' % loss if (loss is not None and loss > 0) else '',
'loss %d ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
if netemargs:
@@ -316,56 +260,26 @@ class TCIntf( Intf ):
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
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,
def config( self, bw=None, delay=None, jitter=None, loss=None,
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False,
max_queue_size=None, **params ):
"""Configure the port and set its properties.
bw: bandwidth in b/s (e.g. '10m')
delay: transmit delay (e.g. '1ms' )
jitter: jitter (e.g. '1ms')
loss: loss (e.g. '1%' )
gro: enable GRO (False)
txo: enable transmit checksum offload (True)
rxo: enable receive checksum offload (True)
speedup: experimental switch-side bw option
use_hfsc: use HFSC scheduling
use_tbf: use TBF scheduling
latency_ms: TBF latency parameter
enable_ecn: enable ECN (False)
enable_red: enable RED (False)
max_queue_size: queue limit parameter for netem"""
# Support old names for parameters
gro = not params.pop( 'disable_gro', not gro )
"Configure the port and set its properties."
result = Intf.config( self, **params)
def on( isOn ):
"Helper method: bool -> 'on'/'off'"
return 'on' if isOn else 'off'
# Set offload parameters with ethool
self.cmd( 'ethtool -K', self,
'gro', on( gro ),
'tx', on( txo ),
'rx', on( rxo ) )
# Disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
# Optimization: return if nothing else to configure
# Question: what happens if we want to reset things?
if ( bw is None and not delay and not loss
and max_queue_size is None ):
return None
return
# Clear existing configuration
tcoutput = self.tc( '%s qdisc show dev %s' )
if "priomap" not in tcoutput and "noqueue" not in tcoutput:
cmds = [ '%s qdisc del dev %s root' ]
else:
cmds = []
cmds = [ '%s qdisc del dev %s root' ]
# Bandwidth limits via various methods
bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
@@ -377,16 +291,15 @@ class TCIntf( Intf ):
# Delay/jitter/loss/max_queue_size using netem
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter,
loss=loss,
max_queue_size=max_queue_size,
parent=parent )
loss=loss, max_queue_size=max_queue_size,
parent=parent )
cmds += delaycmds
# Ugly but functional: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
( ['%.5f%% loss' % loss ] if loss is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
@@ -394,9 +307,6 @@ class TCIntf( Intf ):
# Execute all the commands in our node
debug("at map stage w/cmds: %s\n" % cmds)
tcoutputs = [ self.tc(cmd) for cmd in cmds ]
for output in tcoutputs:
if output != '':
error( "*** Error: %s" % output )
debug( "cmds:", cmds, '\n' )
debug( "outputs:", tcoutputs, '\n' )
result[ 'tcoutputs'] = tcoutputs
@@ -410,11 +320,10 @@ class Link( object ):
"""A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc.."""
# pylint: disable=too-many-branches
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, addr1=None, addr2=None,
intfName1=None, intfName2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None, fast=True, **params ):
params2=None ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
@@ -424,159 +333,68 @@ class Link( object ):
cls1, cls2: optional interface-specific constructors
intfName1: node1 interface name (optional)
intfName2: node2 interface name (optional)
params1: parameters for interface 1 (optional)
params2: parameters for interface 2 (optional)
**params: additional parameters for both interfaces"""
params1: parameters for interface 1
params2: parameters for interface 2"""
# This is a bit awkward; it seems that having everything in
# params is more orthogonal, but being able to specify
# in-line arguments is more convenient! So we support both.
params1 = dict( params1 ) if params1 else {}
params2 = dict( params2 ) if params2 else {}
if port1 is not None:
params1[ 'port' ] = port1
if port2 is not None:
params2[ 'port' ] = port2
if 'port' not in params1:
params1[ 'port' ] = node1.newPort()
if 'port' not in params2:
params2[ 'port' ] = node2.newPort()
# params would be more orthogonal, but being able to specify
# in-line arguments is more convenient!
if port1 is None:
port1 = node1.newPort()
if port2 is None:
port2 = node2.newPort()
if not intfName1:
intfName1 = self.intfName( node1, params1[ 'port' ] )
intfName1 = self.intfName( node1, port1 )
if not intfName2:
intfName2 = self.intfName( node2, params2[ 'port' ] )
intfName2 = self.intfName( node2, port2 )
# Update with remaining parameter list
params1.update( params )
params2.update( params )
self.fast = fast
if fast:
params1.setdefault( 'moveIntfFn', self._ignore )
params2.setdefault( 'moveIntfFn', self._ignore )
self.makeIntfPair( intfName1, intfName2, addr1, addr2,
node1, node2, deleteIntfs=False )
else:
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
self.makeIntfPair( intfName1, intfName2 )
if not cls1:
cls1 = intf
if not cls2:
cls2 = intf
if not params1:
params1 = {}
if not params2:
params2 = {}
intf1 = cls1( name=intfName1, node=node1,
link=self, mac=addr1, **params1 )
intf2 = cls2( name=intfName2, node=node2,
link=self, mac=addr2, **params2 )
intf1 = cls1( name=intfName1, node=node1, port=port1,
link=self, **params1 )
intf2 = cls2( name=intfName2, node=node2, port=port2,
link=self, **params2 )
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
# pylint: enable=too-many-branches
@staticmethod
def _ignore( *args, **kwargs ):
"Ignore any arguments"
pass
def intfName( self, node, n ):
@classmethod
def intfName( cls, node, n ):
"Construct a canonical interface name node-ethN for interface n."
# Leave this as an instance method for now
assert self
return node.name + '-eth' + repr( n )
@classmethod
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
def makeIntfPair( cls, intf1, intf2 ):
"""Create pair of interfaces
intfname1: name for interface 1
intfname2: name for interface 2
addr1: MAC address for interface 1 (optional)
addr2: MAC address for interface 2 (optional)
node1: home node for interface 1 (optional)
node2: home node for interface 2 (optional)
(override this method [and possibly delete()]
intf1: name of interface 1
intf2: name of interface 2
(override this class method [and possibly delete()]
to change link type)"""
# Leave this as a class method for now
assert cls
return makeIntfPair( intfname1, intfname2, addr1, addr2, node1, node2,
deleteIntfs=deleteIntfs )
makeIntfPair( intf1, intf2 )
def delete( self ):
"Delete this link"
self.intf1.delete()
self.intf1 = None
self.intf2.delete()
self.intf2 = None
def stop( self ):
"Override to stop and clean up link as needed"
self.delete()
def status( self ):
"Return link status as a string"
return "(%s %s)" % ( self.intf1.status(), self.intf2.status() )
def __str__( self ):
return '%s<->%s' % ( self.intf1, self.intf2 )
class OVSIntf( Intf ):
"Patch interface on an OVSSwitch"
def ifconfig( self, *args ):
cmd = ' '.join( args )
if cmd == 'up':
# OVSIntf is always up
return
else:
raise Exception( 'OVSIntf cannot do ifconfig ' + cmd )
class OVSLink( Link ):
"""Link that makes patch links between OVSSwitches
Warning: in testing we have found that no more
than ~64 OVS patch links should be used in row."""
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
if 'OVSSwitch' not in globals():
# pylint: disable=import-outside-toplevel,cyclic-import
from mininet.node import OVSSwitch
self.isPatchLink = False
if ( isinstance( node1, OVSSwitch ) and
isinstance( node2, OVSSwitch ) ):
self.isPatchLink = True
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
# pylint: disable=arguments-renamed, arguments-differ, signature-differs
def makeIntfPair( self, *args, **kwargs ):
"Usually delegated to OVSSwitch"
if self.isPatchLink:
return None, None
else:
return Link.makeIntfPair( *args, **kwargs )
class TCLink( Link ):
"Link with TC interfaces"
def __init__( self, *args, **kwargs):
kwargs.setdefault( 'cls1', TCIntf )
kwargs.setdefault( 'cls2', TCIntf )
Link.__init__( self, *args, **kwargs)
class TCULink( TCLink ):
"""TCLink with default settings optimized for UserSwitch
(txo=rxo=0/False). Unfortunately with recent Linux kernels,
enabling TX and RX checksum offload on veth pairs doesn't work
well with UserSwitch: either it gets terrible performance or
TCP packets with bad checksums are generated, forwarded, and
*dropped* due to having bad checksums! OVS and LinuxBridge seem
to cope with this somehow, but it is likely to be an issue with
many software Ethernet bridges."""
def __init__( self, *args, **kwargs ):
kwargs.update( txo=False, rxo=False )
TCLink.__init__( self, *args, **kwargs )
"Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, **params ):
Link.__init__( self, node1, node2, port1=port1, port2=port2,
intfName1=intfName1, intfName2=intfName2,
cls1=TCIntf,
cls2=TCIntf,
params1=params,
params2=params)
+30 -26
View File
@@ -4,7 +4,6 @@ import logging
from logging import Logger
import types
# Create a new loglevel, 'CLI info', which enables a Mininet user to see only
# the output of the commands they execute, plus any errors or warnings. This
# level is in between info and warning. CLI info-level commands should not be
@@ -15,14 +14,13 @@ LEVELS = { 'debug': logging.DEBUG,
'info': logging.INFO,
'output': OUTPUT,
'warning': logging.WARNING,
'warn': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL }
# change this to logging.INFO to get printouts when running unit tests
LOGLEVELDEFAULT = OUTPUT
# default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
LOGMSGFORMAT = '%(message)s'
@@ -53,7 +51,7 @@ class StreamHandlerNoNewline( logging.StreamHandler ):
self.flush()
except ( KeyboardInterrupt, SystemExit ):
raise
except: # noqa pylint: disable=bare-except
except:
self.handleError( record )
@@ -71,7 +69,7 @@ class Singleton( type ):
def __call__( cls, *args, **kw ):
if cls.instance is None:
cls.instance = super( Singleton, cls ).__call__( *args, **kw )
return cls.instance
return cls.instance
class MininetLogger( Logger, object ):
@@ -97,9 +95,9 @@ class MininetLogger( Logger, object ):
__metaclass__ = Singleton
def __init__( self, name="mininet" ):
def __init__( self ):
Logger.__init__( self, name )
Logger.__init__( self, "mininet" )
# create console handler
ch = StreamHandlerNoNewline()
@@ -107,22 +105,30 @@ class MininetLogger( Logger, object ):
formatter = logging.Formatter( LOGMSGFORMAT )
# add formatter to ch
ch.setFormatter( formatter )
# add ch to lg and initialize log level
# add ch to lg
self.addHandler( ch )
self.ch = ch
self.setLogLevel()
def setLogLevel( self, levelname=None ):
"""Setup loglevel.
Convenience function to support lowercase names.
levelName: level name from LEVELS"""
if levelname and levelname not in LEVELS:
print(LEVELS)
raise Exception( 'setLogLevel: unknown levelname %s' % levelname )
level = LEVELS.get( levelname, LOGLEVELDEFAULT )
self.setLevel( level )
self.ch.setLevel( level )
level = LOGLEVELDEFAULT
if levelname is not None:
if levelname not in LEVELS:
raise Exception( 'unknown levelname seen in setLogLevel' )
else:
level = LEVELS.get( levelname, level )
self.setLevel( level )
self.handlers[ 0 ].setLevel( level )
# pylint: disable-msg=E0202
# "An attribute inherited from mininet.log hide this method"
# Not sure why this is occurring - this function definitely gets called.
# See /usr/lib/python2.5/logging/__init__.py; modified from warning()
def output( self, msg, *args, **kwargs ):
"""Log 'msg % args' with severity 'OUTPUT'.
@@ -131,11 +137,14 @@ class MininetLogger( Logger, object ):
logger.warning("Houston, we have a %s", "cli output", exc_info=1)
"""
if getattr( self.manager, 'disabled', 0 ) >= OUTPUT:
if self.manager.disable >= OUTPUT:
return
if self.isEnabledFor( OUTPUT ):
self._log( OUTPUT, msg, args, kwargs )
# pylint: enable-msg=E0202
lg = MininetLogger()
# Make things a bit more convenient by adding aliases
# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
@@ -151,7 +160,7 @@ def makeListCompatible( fn ):
"Generated function. Closure-ish."
if len( args ) == 1:
return fn( *args )
args = ' '.join( str( arg ) for arg in args )
args = ' '.join( [ str( arg ) for arg in args ] )
return fn( args )
# Fix newfn's name and docstring
@@ -159,14 +168,9 @@ def makeListCompatible( fn ):
setattr( newfn, '__doc__', fn.__doc__ )
return newfn
info, output, warn, error, debug = (
lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
makeListCompatible( f ) for f in
lg.info, lg.output, lg.warn, lg.error, lg.debug ]
# Initialize logger and logging functions
logging.setLoggerClass( MininetLogger )
lg = logging.getLogger( "mininet" )
_loggers = lg.info, lg.output, lg.warning, lg.error, lg.debug
_loggers = tuple( makeListCompatible( logger ) for logger in _loggers )
lg.info, lg.output, lg.warning, lg.error, lg.debug = _loggers
info, output, warning, error, debug = _loggers
warn = warning # alternate/old name
setLogLevel = lg.setLogLevel
+4 -8
View File
@@ -1,11 +1,8 @@
"Module dependency utility functions for Mininet."
from os import environ
from sys import exit # pylint: disable=redefined-builtin
from mininet.util import quietRun, BaseString
from mininet.util import quietRun
from mininet.log import info, error, debug
from os import environ
def lsmod():
"Return output of lsmod."
@@ -21,7 +18,6 @@ def modprobe( mod ):
mod: module string"""
return quietRun( [ 'modprobe', mod ] )
OF_KMOD = 'ofdatapath'
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
TUN = 'tun'
@@ -32,9 +28,9 @@ def moduleDeps( subtract=None, add=None ):
add: string or list of module names to add, if not already loaded"""
subtract = subtract if subtract is not None else []
add = add if add is not None else []
if isinstance( subtract, BaseString ):
if type( subtract ) is str:
subtract = [ subtract ]
if isinstance( add, BaseString ):
if type( add ) is str:
add = [ add ]
for mod in subtract:
if mod in lsmod():
+122 -335
View File
@@ -90,37 +90,29 @@ import os
import re
import select
import signal
import random
from sys import exit # pylint: disable=redefined-builtin
from time import sleep
from itertools import chain, groupby
from math import ceil
from itertools import chain
from mininet.cli import CLI
from mininet.log import info, error, output, warn, debug
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
Controller )
from mininet.nodelib import NAT
from mininet.log import info, error, debug, output
from mininet.node import Host, OVSKernelSwitch, Controller
from mininet.link import Link, Intf
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening, BaseString, fmtBps )
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.1b4"
VERSION = "2.1.0p1"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
# pylint: disable=too-many-arguments
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
controller=DefaultController, link=Link, intf=Intf,
controller=Controller, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None, waitConnected=False ):
listenPort=None ):
"""Create Mininet object.
topo: Topo (topology) object or None
switch: default Switch class
@@ -137,9 +129,7 @@ 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
waitConnected: wait for switches to Connect?
(False; True/None=wait indefinitely; time(s)=timed wait)"""
each additional switch in the net if inNamespace=False"""
self.topo = topo
self.switch = switch
self.host = host
@@ -148,9 +138,7 @@ class Mininet( object ):
self.intf = intf
self.ipBase = ipBase
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
hostIP = ( 0xffffffff >> self.prefixLen ) & self.ipBaseNum
# Start for address allocation
self.nextIP = hostIP if hostIP > 0 else 1
self.nextIP = 1 # start for address allocation
self.inNamespace = inNamespace
self.xterms = xterms
self.cleanup = cleanup
@@ -160,12 +148,10 @@ class Mininet( object ):
self.numCores = numCores()
self.nextCore = 0 # next core for pinning hosts to CPUs
self.listenPort = listenPort
self.waitConn = waitConnected
self.hosts = []
self.switches = []
self.controllers = []
self.links = []
self.nameToNode = {} # name to Node (Host/Switch) objects
@@ -177,38 +163,6 @@ class Mininet( object ):
if topo and build:
self.build()
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.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():
info( '%s ' % switch )
remaining.remove( switch )
if not remaining:
info( '\n' )
return True
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.copy():
if not switch.connected():
warn( 'Warning: %s is not connected to a controller\n'
% switch.name )
else:
remaining.remove( switch )
return not remaining
def addHost( self, name, cls=None, **params ):
"""Add host.
name: name of host to add
@@ -221,7 +175,7 @@ class Mininet( object ):
prefixLen=self.prefixLen ) +
'/%s' % self.prefixLen }
if self.autoSetMacs:
defaults[ 'mac' ] = macColonHex( self.nextIP )
defaults[ 'mac'] = macColonHex( self.nextIP )
if self.autoPinCpus:
defaults[ 'cores' ] = self.nextCore
self.nextCore = ( self.nextCore + 1 ) % self.numCores
@@ -234,24 +188,6 @@ class Mininet( object ):
self.nameToNode[ name ] = h
return h
def delNode( self, node, nodes=None):
"""Delete node
node: node to delete
nodes: optional list to delete from (e.g. self.hosts)"""
if nodes is None:
nodes = ( self.hosts if node in self.hosts else
( self.switches if node in self.switches else
( self.controllers if node in self.controllers else
[] ) ) )
node.stop( deleteIntfs=True )
node.terminate()
nodes.remove( node )
del self.nameToNode[ node.name ]
def delHost( self, host ):
"Delete a host"
self.delNode( host, nodes=self.hosts )
def addSwitch( self, name, cls=None, **params ):
"""Add switch.
name: name of switch to add
@@ -270,10 +206,6 @@ class Mininet( object ):
self.nameToNode[ name ] = sw
return sw
def delSwitch( self, switch ):
"Delete a switch"
self.delNode( switch, nodes=self.switches )
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
@@ -281,12 +213,12 @@ class Mininet( object ):
if not controller:
controller = self.controller
# Construct new controller if one is not given
if isinstance( name, Controller ):
if isinstance(name, Controller):
controller_new = name
# Pylint thinks controller is a str()
# pylint: disable=maybe-no-member
# pylint: disable=E1103
name = controller_new.name
# pylint: enable=maybe-no-member
# pylint: enable=E1103
else:
controller_new = controller( name, **params )
# Add new controller to net
@@ -295,36 +227,6 @@ class Mininet( object ):
self.nameToNode[ name ] = controller_new
return controller_new
def delController( self, controller ):
"""Delete a controller
Warning - does not reconfigure switches, so they
may still attempt to connect to it!"""
self.delNode( controller )
def addNAT( self, name='nat0', connect=True, inNamespace=False,
**params):
"""Add a NAT to the Mininet network
name: name of NAT node
connect: switch to connect to | True (s1) | None
inNamespace: create in a network namespace
params: other NAT node params, notably:
ip: used as default gateway address"""
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
subnet=self.ipBase, **params )
# find first switch and create link
if connect:
if not isinstance( connect, Node ):
# Use first switch if not specified
connect = self.switches[ 0 ]
# Connect the nat to the switch
self.addLink( nat, connect )
# Set the default route on hosts
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
for host in self.hosts:
if host.inNamespace:
host.setDefaultRoute( 'via %s' % natIP )
return nat
# BL: We now have four ways to look up nodes
# This may (should?) be cleaned up in the future.
def getNodeByName( self, *args ):
@@ -339,13 +241,9 @@ class Mininet( object ):
# Even more convenient syntax for node lookup and iteration
def __getitem__( self, key ):
"net[ name ] operator: Return node with given name"
"""net [ name ] operator: Return node(s) with given name(s)"""
return self.nameToNode[ key ]
def __delitem__( self, key ):
"del net[ name ] operator - delete node with given name"
self.delNode( self.nameToNode[ key ] )
def __iter__( self ):
"return iterator over node names"
for node in chain( self.hosts, self.switches, self.controllers ):
@@ -372,64 +270,21 @@ class Mininet( object ):
"return (key,value) tuple list for every node in net"
return zip( self.keys(), self.values() )
@staticmethod
def randMac():
"Return a random, non-multicast MAC address"
return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
0x020000000000 )
def addLink( self, node1, node2, port1=None, port2=None,
cls=None, **params ):
""""Add a link from node1 to node2
node1: source node (or name)
node2: dest node (or name)
port1: source port (optional)
port2: dest port (optional)
cls: link class (optional)
params: additional link params (optional)
node1: source node
node2: dest node
port1: source port
port2: dest port
returns: link object"""
# Accept node objects or names
node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
options = dict( params )
# Port is optional
if port1 is not None:
options.setdefault( 'port1', port1 )
if port2 is not None:
options.setdefault( 'port2', port2 )
if self.intf is not None:
options.setdefault( 'intf', self.intf )
# Set default MAC - this should probably be in Link
options.setdefault( 'addr1', self.randMac() )
options.setdefault( 'addr2', self.randMac() )
cls = self.link if cls is None else cls
link = cls( node1, node2, **options )
self.links.append( link )
return link
def delLink( self, link ):
"Remove a link from this network"
link.delete()
self.links.remove( link )
def linksBetween( self, node1, node2 ):
"Return Links between node1 and node2"
return [ link for link in self.links
if ( node1, node2 ) in (
( link.intf1.node, link.intf2.node ),
( link.intf2.node, link.intf1.node ) ) ]
def delLinkBetween( self, node1, node2, index=0, allLinks=False ):
"""Delete link(s) between node1 and node2
index: index of link to delete if multiple links (0)
allLinks: ignore index and delete all such links (False)
returns: deleted link(s)"""
links = self.linksBetween( node1, node2 )
if not allLinks:
links = [ links[ index ] ]
for link in links:
self.delLink( link )
return links
defaults = { 'port1': port1,
'port2': port2,
'intf': self.intf }
defaults.update( params )
if not cls:
cls = self.link
return cls( node1, node2, **defaults )
def configHosts( self ):
"Configure a set of hosts."
@@ -443,10 +298,11 @@ class Mininet( object ):
host.configDefault( ip=None, mac=None )
# You're low priority, dude!
# BL: do we want to do this here or not?
# May not make sense if we have CPU limiting...
# May not make sense if we have CPU lmiting...
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
# This may not be the right place to do this, but
# it needs to be done somewhere.
host.cmd( 'ifconfig lo up' )
info( '\n' )
def buildFromTopo( self, topo=None ):
@@ -465,14 +321,10 @@ class Mininet( object ):
# Add a default controller
info( '*** Adding controller\n' )
classes = self.controller
if not isinstance( classes, list ):
if type( classes ) is not list:
classes = [ classes ]
for i, cls in enumerate( classes ):
# Allow Controller objects because nobody understands partial()
if isinstance( cls, Controller ):
self.addController( cls )
else:
self.addController( 'c%d' % i, cls )
self.addController( 'c%d' % i, cls )
info( '*** Adding hosts:\n' )
for hostName in topo.hosts():
@@ -481,19 +333,16 @@ class Mininet( object ):
info( '\n*** Adding switches:\n' )
for switchName in topo.switches():
# A bit ugly: add batch parameter if appropriate
params = topo.nodeInfo( switchName)
cls = params.get( 'cls', self.switch )
if hasattr( cls, 'batchStartup' ):
params.setdefault( 'batch', True )
self.addSwitch( switchName, **params )
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
info( switchName + ' ' )
info( '\n*** Adding links:\n' )
for srcName, dstName, params in topo.links(
sort=True, withInfo=True ):
self.addLink( **params )
info( '(%s, %s) ' % ( srcName, dstName ) )
for srcName, dstName in topo.links(sort=True):
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
params = topo.linkInfo( srcName, dstName )
srcPort, dstPort = topo.port( srcName, dstName )
self.addLink( src, dst, srcPort, dstPort, **params )
info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' )
@@ -506,7 +355,7 @@ class Mininet( object ):
"Build mininet."
if self.topo:
self.buildFromTopo( self.topo )
if self.inNamespace:
if ( self.inNamespace ):
self.configureControlNetwork()
info( '*** Configuring hosts\n' )
self.configHosts()
@@ -546,63 +395,32 @@ class Mininet( object ):
self.build()
info( '*** Starting controller\n' )
for controller in self.controllers:
info( controller.name + ' ')
controller.start()
info( '\n' )
info( '*** Starting %s switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ')
switch.start( self.controllers )
started = {}
for swclass, switches in groupby(
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches )
started.update( { s: s for s in success } )
info( '\n' )
if self.waitConn:
self.waitConnected( self.waitConn )
def stop( self ):
"Stop the controller(s), switches and hosts"
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
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()
info( '*** Stopping %i links\n' % len( self.links ) )
for link in self.links:
info( '.' )
link.stop()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {}
for swclass, switches in groupby(
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches )
stopped.update( { s: s for s in success } )
for switch in self.switches:
info( switch.name + ' ' )
if switch not in stopped:
switch.stop()
switch.terminate()
switch.stop()
info( '\n' )
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( host.name + ' ' )
host.terminate()
info( '\n' )
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
info( controller.name + ' ' )
controller.stop()
info( '\n*** Done\n' )
def run( self, test, *args, **kwargs ):
@@ -622,13 +440,13 @@ class Mininet( object ):
if hosts is None:
hosts = self.hosts
poller = select.poll()
h1 = hosts[ 0 ] # so we can call class method fdToNode
Node = hosts[ 0 ] # so we can call class method fdToNode
for host in hosts:
poller.register( host.stdout )
while True:
ready = poller.poll( timeoutms )
for fd, event in ready:
host = h1.fdToNode( fd )
host = Node.fdToNode( fd )
if event & select.POLLIN:
line = host.readline()
if line is not None:
@@ -645,13 +463,13 @@ class Mininet( object ):
"Parse ping output and return packets sent, received."
# Check for downed link
if 'connect: Network is unreachable' in pingOutput:
return 1, 0
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
return (1, 0)
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return 1, 0
return (1, 0)
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
return sent, received
@@ -674,12 +492,8 @@ class Mininet( object ):
opts = ''
if timeout:
opts = '-W %s' % timeout
if dest.intfs:
result = node.cmd( 'LANG=C ping -c1 %s %s' %
(opts, dest.IP()) )
sent, received = self._parsePing( result )
else:
sent, received = 0, 0
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
sent, received = self._parsePing( result )
packets += sent
if received > sent:
error( '*** Error: received too many packets' )
@@ -690,7 +504,7 @@ class Mininet( object ):
output( ( '%s ' % dest.name ) if received else 'X ' )
output( '\n' )
if packets > 0:
ploss = 100.0 * lost / packets
ploss = 100 * lost / packets
received = packets - lost
output( "*** Results: %i%% dropped (%d/%d received)\n" %
( ploss, received, packets ) )
@@ -708,7 +522,7 @@ class Mininet( object ):
m = re.search( r, pingOutput )
if m is not None:
return errorTuple
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -719,8 +533,6 @@ class Mininet( object ):
r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
m = re.search( r, pingOutput )
if m is None:
if received == 0:
return errorTuple
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return errorTuple
@@ -763,10 +575,10 @@ class Mininet( object ):
(rttmin, rttavg, rttmax, rttdev) )
return all_outputs
def pingAll( self, timeout=None ):
def pingAll( self ):
"""Ping between all hosts.
returns: ploss packet loss percentage"""
return self.ping( timeout=timeout )
return self.ping()
def pingPair( self ):
"""Ping between first two hosts, useful for testing.
@@ -786,78 +598,60 @@ class Mininet( object ):
return self.pingFull( hosts=hosts )
@staticmethod
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
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 ''
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
seconds=5, port=5001):
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses first and last hosts
hosts: list of hosts; if None, uses opposite hosts
l4Type: string, one of [ TCP, UDP ]
udpBw: bandwidth target for UDP test
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
note: send() is buffered, so client rate can be much higher than
the actual transmission rate; on an unloaded system, server
rate should be much closer to the actual receive rate"""
hosts = hosts or [ self.hosts[ 0 ], self.hosts[ -1 ] ]
assert len( hosts ) == 2
returns: results two-element array of server and client speeds"""
if not quietRun( 'which telnet' ):
error( 'Cannot find telnet in $PATH - required for iperf test' )
return
if not hosts:
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else:
assert len( hosts ) == 2
client, server = hosts
output( '*** Iperf: testing', l4Type, 'bandwidth between',
client, 'and', server, '\n' )
output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
output( "%s and %s\n" % ( client.name, server.name ) )
server.cmd( 'killall -9 iperf' )
# Note: CSV mode
iperfArgs = 'iperf -y C -p %d ' % port
iperfArgs = 'iperf '
bwArgs = ''
if l4Type == 'UDP':
iperfArgs += '-u '
bwArgs = '-b ' + udpBw + ' '
server.sendCmd( iperfArgs + '-s' )
serverip = server.IP()
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
server.sendCmd( iperfArgs + '-s', printPid=True )
servout = ''
while server.lastPid is None:
servout += server.monitor()
if l4Type == 'TCP':
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 )
cvals = self._iperfVals( cliout, serverip )
debug( 'iperf client output:', cliout, cvals )
serverout = ''
# Wait for output from the client session
while True:
serverout += server.monitor( timeoutms=5000 )
svals = self._iperfVals( serverout, serverip )
# Check for the client's source/output port
if ( svals and cvals[ 'sport' ] == svals[ 'sport' ]
and int( svals[ 'rate' ] ) > 0 ):
break
debug( 'iperf server output:', serverout, svals )
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
output('waiting for iperf to start up...')
sleep(.5)
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs )
debug( 'Client output: %s\n' % cliout )
server.sendInt()
serverout += server.waitOutput()
result = [ fmtBps( svals[ 'rate'], fmt ),
fmtBps( cvals[ 'rate' ], fmt ) ]
servout += server.waitOutput()
debug( 'Server output: %s\n' % servout )
result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
if l4Type == 'UDP':
result.insert( 0, udpBw )
output( '*** Results: %s\n' % result )
@@ -866,45 +660,36 @@ class Mininet( object ):
def runCpuLimitTest( self, cpu, duration=5 ):
"""run CPU limit test with 'while true' processes.
cpu: desired CPU fraction of each host
duration: test duration in seconds (integer)
duration: test duration in seconds
returns a single list of measured CPU fractions as floats.
"""
pct = cpu * 100
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
hosts = self.hosts
cores = int( quietRun( 'nproc' ) )
# number of processes to run a while loop on per host
num_procs = int( ceil( cores * cpu ) )
pids = {}
for h in hosts:
pids[ h ] = []
for _core in range( num_procs ):
h.cmd( 'while true; do a=1; done &' )
pids[ h ].append( h.cmd( 'echo $!' ).strip() )
outputs = {}
time = {}
# get the initial cpu time for each host
for host in hosts:
outputs[ host ] = []
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
host, 'r' ) as f:
time[ host ] = float( f.read() )
h.cmd( 'while true; do a=1; done &' )
pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
pids_str = ",".join(["%s" % pid for pid in pids])
cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
# It's a shame that this is what pylint prefers
outputs = []
for _ in range( duration ):
sleep( 1 )
for host in hosts:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
host, 'r' ) as f:
readTime = float( f.read() )
outputs[ host ].append( ( ( readTime - time[ host ] )
/ 1000000000 ) / cores * 100 )
time[ host ] = readTime
for h, pids in pids.items():
for pid in pids:
h.cmd( 'kill -9 %s' % pid )
outputs.append( quietRun( cmd ).strip() )
for h in hosts:
h.cmd( 'kill %1' )
cpu_fractions = []
for _host, outputs in outputs.items():
for pct in outputs:
cpu_fractions.append( pct )
for test_output in outputs:
# Split by line. Ignore first line, which looks like this:
# PID %CPU COMMAND\n
for line in test_output.split('\n')[1:]:
r = r'\d+\s*(\d+\.\d+)'
m = re.search( r, line )
if m is None:
error( '*** Error: could not extract CPU fraction: %s\n' %
line )
return None
cpu_fractions.append( float( m.group( 1 ) ) )
output( '*** Results: %s\n' % cpu_fractions )
return cpu_fractions
@@ -920,8 +705,10 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
src = self.nameToNode[ src ]
dst = self.nameToNode[ dst ]
if type( src ) is str:
src = self.nameToNode[ src ]
if type( dst ) is str:
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
+302 -673
View File
File diff suppressed because it is too large Load Diff
-154
View File
@@ -1,154 +0,0 @@
"""
Node Library for Mininet
This contains additional Node types which you may find to be useful.
"""
from mininet.node import Node, Switch
from mininet.log import info, warn
from mininet.moduledeps import pathCheck
from mininet.util import quietRun
class LinuxBridge( Switch ):
"Linux Bridge (with optional spanning tree)"
nextPrio = 100 # next bridge priority for spanning tree
def __init__( self, name, stp=False, prio=None, **kwargs ):
"""stp: use spanning tree protocol? (default False)
prio: optional explicit bridge priority for STP"""
self.stp = stp
if prio:
self.prio = prio
else:
self.prio = LinuxBridge.nextPrio
LinuxBridge.nextPrio += 1
Switch.__init__( self, name, **kwargs )
def connected( self ):
"Are we forwarding yet?"
if self.stp:
return 'forwarding' in self.cmd( 'brctl showstp', self )
else:
return True
def start( self, _controllers ):
"Start Linux bridge"
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
self.cmd( 'brctl addbr', self )
if self.stp:
self.cmd( 'brctl setbridgeprio', self.prio )
self.cmd( 'brctl stp', self, 'on' )
for i in self.intfList():
if self.name in i.name:
self.cmd( 'brctl addif', self, i )
self.cmd( 'ifconfig', self, 'up' )
def stop( self, deleteIntfs=True ):
"""Stop Linux bridge
deleteIntfs: delete interfaces? (True)"""
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
super( LinuxBridge, self ).stop( deleteIntfs )
def dpctl( self, *args ):
"Run brctl command"
return self.cmd( 'brctl', *args )
@classmethod
def setup( cls ):
"Check dependencies and warn about firewalling"
pathCheck( 'brctl', moduleName='bridge-utils' )
# Disable Linux bridge firewalling so that traffic can flow!
for table in 'arp', 'ip', 'ip6':
cmd = 'sysctl net.bridge.bridge-nf-call-%stables' % table
out = quietRun( cmd ).strip()
if out.endswith( '1' ):
warn( 'Warning: Linux bridge may not work with', out, '\n' )
class NAT( Node ):
"NAT: Provides connectivity to external network"
def __init__( self, name, subnet='10.0/8',
localIntf=None, flush=False, **params):
"""Start NAT/forwarding between Mininet and external network
subnet: Mininet subnet (default 10.0/8)
flush: flush iptables before installing NAT rules"""
super( NAT, self ).__init__( name, **params )
self.subnet = subnet
self.localIntf = localIntf
self.flush = flush
self.forwardState = self.cmd( 'sysctl -n net.ipv4.ip_forward' ).strip()
def setManualConfig( self, intf ):
"""Prevent network-manager/networkd from messing with our interface
by specifying manual configuration in /etc/network/interfaces"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
try:
with open( cfile ) as f:
config = f.read()
except IOError:
config = ''
if ( line ) not in config:
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network manager to be safe -
# hopefully this won't disconnect you
self.cmd( 'service network-manager restart || netplan apply' )
# pylint: disable=arguments-differ
def config( self, **params ):
"""Configure the NAT and iptables"""
if not self.localIntf:
self.localIntf = self.defaultIntf()
self.setManualConfig( self.localIntf )
# Now we can configure manually without interference
super( NAT, self).config( **params )
if self.flush:
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
self.cmd( 'iptables -P INPUT ACCEPT' )
self.cmd( 'iptables -P OUTPUT ACCEPT' )
self.cmd( 'iptables -P FORWARD DROP' )
# Install NAT rules
self.cmd( 'iptables -I FORWARD',
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -A FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -A FORWARD',
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING',
'-s', self.subnet, "'!'", '-d', self.subnet,
'-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
def terminate( self ):
"Stop NAT/forwarding between Mininet and external network"
# Remote NAT rules
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -D FORWARD',
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -D POSTROUTING',
'-s', self.subnet, '\'!\'', '-d', self.subnet,
'-j MASQUERADE' )
# Put the forwarding state back to what it was
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
super( NAT, self ).terminate()
+5 -6
View File
@@ -32,16 +32,16 @@ def tunnelX11( node, display=None):
port = 6000 + int( float( screen ) )
connection = r'TCP\:%s\:%s' % ( host, port )
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
def makeTerm( node, title='Node', term='xterm', display=None ):
"""Create an X11 tunnel to the node and start up a terminal.
node: Node object
title: base title
term: 'xterm' or 'gterm'
returns: two Popen objects, tunnel and terminal"""
title = '"%s: %s"' % ( title, node.name )
title += ': ' + node.name
if not node.inNamespace:
title += ' (root)'
cmds = {
@@ -50,12 +50,11 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
return None
return
display, tunnel = tunnelX11( node, display )
if display is None:
return []
term = node.popen( cmds[ term ] +
[ display, '-e', 'env TERM=ansi %s' % cmd ] )
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
+6 -9
View File
@@ -6,7 +6,7 @@ Run all mininet core tests
-quick : skip tests that take more than ~30 seconds
"""
from unittest import defaultTestLoader, TextTestRunner
import unittest
import os
import sys
from mininet.util import ensureRoot
@@ -19,16 +19,13 @@ def runTests( testDir, verbosity=1 ):
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = defaultTestLoader.discover( testDir )
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
success = ( TextTestRunner( verbosity=verbosity )
.run( testSuite ).wasSuccessful() )
sys.exit( 0 if success else 1 )
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
setLogLevel( 'warning' )
# get the directory containing example tests
thisdir = os.path.dirname( os.path.realpath( __file__ ) )
vlevel = 2 if '-v' in sys.argv else 1
runTests( testDir=thisdir, verbosity=vlevel )
testDir = os.path.dirname( os.path.realpath( __file__ ) )
verbosity = 2 if '-v' in sys.argv else 1
runTests( testDir, verbosity )
+28 -140
View File
@@ -4,7 +4,6 @@
Test creation and pings for topologies with link and/or CPU options."""
import unittest
import sys
from functools import partial
from mininet.net import Mininet
@@ -14,7 +13,6 @@ from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Number of hosts for each test
N = 2
@@ -40,160 +38,72 @@ class testOptionsTopoCommon( object ):
"""Verify ability to create networks with host and link options
(common code)."""
switchClass = None # overridden in subclasses
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info() != ( None, None, None ):
cleanup()
def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
"Generic topology-with-options test runner."
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
lopts=lopts ),
host=CPULimitedHost, link=TCLink,
switch=self.switchClass, waitConnected=True )
switch=self.switchClass )
dropped = mn.run( mn.ping )
hoptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in hopts.items() )
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg += ( '%s%% of pings were dropped during mininet.ping().\n'
'Topo = SingleSwitchTopo, %s hosts\n'
'hopts = %s\n'
'lopts = %s\n'
'host = CPULimitedHost\n'
'link = TCLink\n'
'Switch = %s\n'
% ( dropped, n, hoptsStr, loptsStr, self.switchClass ) )
self.assertEqual( dropped, 0 )
self.assertEqual( dropped, 0, msg=msg )
def assertWithinTolerance( self, measured, expected, tolerance_frac, msg ):
def assertWithinTolerance(self, measured, expected, tolerance_frac):
"""Check that a given value is within a tolerance of expected
tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
"""
upperBound = ( float( expected ) + ( 1 - tolerance_frac ) *
float( expected ) )
lowerBound = float( expected ) * tolerance_frac
info = ( 'measured value is out of bounds\n'
'expected value: %s\n'
'measured value: %s\n'
'failure tolerance: %s\n'
'upper bound: %s\n'
'lower bound: %s\n'
% ( expected, measured, tolerance_frac,
upperBound, lowerBound ) )
msg += info
self.assertGreaterEqual( float( measured ), lowerBound, msg=msg )
self.assertLessEqual( float( measured ), upperBound, msg=msg )
self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
def testCPULimits( self ):
"Verify topology creation with CPU limits set for both schedulers."
CPU_FRACTION = 0.1
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
hopts = { 'cpu': CPU_FRACTION }
# self.runOptionsTopoTest( N, hopts=hopts )
#self.runOptionsTopoTest( N, hopts=hopts )
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
host=CPULimitedHost, switch=self.switchClass,
waitConnected=True )
host=CPULimitedHost, switch=self.switchClass )
mn.start()
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop()
hostUsage = '\n'.join( 'h%s: %s' %
( n + 1,
results[ (n - 1) * 5 : (n * 5) - 1 ] )
for n in range( N ) )
hoptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in hopts.items() )
msg = ( '\nTesting cpu limited to %d%% of cpu per host\n'
'cpu usage percent per host:\n%s\n'
'Topo = SingleSwitchTopo, %s hosts\n'
'hopts = %s\n'
'host = CPULimitedHost\n'
'Switch = %s\n'
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
self.switchClass ) )
for pct in results:
# divide cpu by 100 to convert from percentage to fraction
self.assertWithinTolerance( pct/100, CPU_FRACTION,
CPU_TOLERANCE, msg )
for cpu in results:
self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE )
def testLinkBandwidth( self ):
"Verify that link bandwidths are accurate within a bound."
if self.switchClass is UserSwitch:
self.skipTest( 'UserSwitch has very poor performance -'
' skipping for now' )
BW = 5 # Mbps
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first;
lopts = { 'bw': BW, 'use_htb': True }
# Also verify correctness of limit limiting within a bound.
# Also verify correctness of limit limitng within a bound.
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass,
waitConnected=True )
bw_strs = mn.run( mn.iperf, fmt='m' )
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting link bandwidth limited to %d Mbps per link\n'
'iperf results[ client, server ]: %s\n'
'Topo = SingleSwitchTopo, %s hosts\n'
'Link = TCLink\n'
'lopts = %s\n'
'host = default\n'
'switch = %s\n'
% ( BW, bw_strs, N, loptsStr, self.switchClass ) )
# On the client side, iperf doesn't wait for ACKs - it simply
# reports how long it took to fill up the TCP send buffer.
# As long as the kernel doesn't wait a long time before
# delivering bytes to the iperf server, its reported data rate
# should be close to the actual receive rate.
serverRate, _clientRate = bw_strs
bw = float( serverRate.split(' ')[0] )
self.assertWithinTolerance( bw, BW, BW_TOLERANCE, msg )
link=TCLink, switch=self.switchClass )
bw_strs = mn.run( mn.iperf )
for bw_str in bw_strs:
bw = float( bw_str.split(' ')[0] )
self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
def testLinkDelay( self ):
"Verify that link delays are accurate within a bound."
DELAY_MS = 15
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
REPS = 3
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass, autoStaticArp=True,
waitConnected=True )
mn.start()
for _ in range( REPS ):
ping_delays = mn.pingFull()
mn.stop()
link=TCLink, switch=self.switchClass )
ping_delays = mn.run( mn.pingFull )
test_outputs = ping_delays[0]
# Ignore unused variables below
# pylint: disable=W0612
# pylint: disable-msg=W0612
node, dest, ping_outputs = test_outputs
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
pingFailMsg = 'sent %s pings, only received %s' % ( sent, received )
self.assertEqual( sent, received, msg=pingFailMsg )
# pylint: enable=W0612
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting Link Delay of %s ms\n'
'ping results across 4 links:\n'
'(Sent, Received, rttmin, rttavg, rttmax, rttdev)\n'
'%s\n'
'Topo = SingleSwitchTopo, %s hosts\n'
'Link = TCLink\n'
'lopts = %s\n'
'host = default'
'switch = %s\n'
% ( DELAY_MS, ping_outputs, N, loptsStr, self.switchClass ) )
self.assertEqual( sent, received )
# pylint: enable-msg=W0612
for rttval in [rttmin, rttavg, rttmax]:
# Multiply delay by 4 to cover there & back on two links
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
DELAY_TOLERANCE, msg )
DELAY_TOLERANCE)
def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate."
@@ -202,8 +112,7 @@ class testOptionsTopoCommon( object ):
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
host=CPULimitedHost, link=TCLink,
switch=self.switchClass,
waitConnected=True )
switch=self.switchClass )
# Drops are probabilistic, but the chance of no dropped packets is
# 1 in 100 million with 4 hops for a link w/99% loss.
dropped_total = 0
@@ -211,59 +120,38 @@ class testOptionsTopoCommon( object ):
for _ in range(REPS):
dropped_total += mn.ping(timeout='1')
mn.stop()
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting packet loss with %d%% loss rate\n'
'number of dropped pings during mininet.ping(): %s\n'
'expected number of dropped packets: 1\n'
'Topo = SingleSwitchTopo, %s hosts\n'
'Link = TCLink\n'
'lopts = %s\n'
'host = default\n'
'switch = %s\n'
% ( LOSS_PERCENT, dropped_total, N, loptsStr,
self.switchClass ) )
self.assertGreater( dropped_total, 0, msg )
self.assertTrue(dropped_total > 0)
def testMostOptions( self ):
"Verify topology creation with most link options and CPU limits."
lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
hopts = { 'cpu': 0.5 / N }
msg = '\nTesting many cpu and link options\n'
self.runOptionsTopoTest( N, msg, hopts=hopts, lopts=lopts )
self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )
# pylint: enable=E1101
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(OVS kernel switch)."""
longMessage = True
switchClass = OVSSwitch
@unittest.skip( 'Skipping OVS user switch test for now' )
class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(OVS user switch)."""
longMessage = True
switchClass = partial( OVSSwitch, datapath='user' )
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
"Verify ability to create networks with host and link options (IVS)."
longMessage = True
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
'Reference user switch is not installed' )
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(UserSwitch)."""
longMessage = True
"Verify ability to create networks with host and link options (UserSwitch)."
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+6 -16
View File
@@ -4,7 +4,6 @@
Test creation and all-pairs ping for each included mininet topo type."""
import unittest
import sys
from functools import partial
from mininet.net import Mininet
@@ -13,7 +12,6 @@ from mininet.node import UserSwitch, OVSSwitch, IVSSwitch
from mininet.topo import SingleSwitchTopo, LinearTopo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
@@ -21,25 +19,18 @@ from mininet.clean import cleanup
class testSingleSwitchCommon( object ):
"Test ping with single switch topology (common code)."
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info() != ( None, None, None ):
cleanup()
switchClass = None # overridden in subclasses
def testMinimal( self ):
"Ping test on minimal topology"
mn = Mininet( SingleSwitchTopo(), self.switchClass, Host, Controller,
waitConnected=True )
mn = Mininet( SingleSwitchTopo(), self.switchClass, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
def testSingle5( self ):
"Ping test on 5-host single-switch topology"
mn = Mininet( SingleSwitchTopo( k=5 ), self.switchClass, Host,
Controller, waitConnected=True )
Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
@@ -59,7 +50,7 @@ class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ):
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
'Reference user switch is not installed' )
class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (Userspace switch)."
switchClass = UserSwitch
@@ -71,12 +62,11 @@ class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
class testLinearCommon( object ):
"Test all-pairs ping with LinearNet (common code)."
switchClass = None # overridden in subclasses
switchClass = None # overridden in subclasses
def testLinear5( self ):
"Ping test on a 5-switch topology"
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host,
Controller, waitConnected=True )
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python
"""
Regression test for pty leak in Node()
"""
import unittest
from mininet.net import Mininet
from mininet.clean import cleanup
from mininet.topo import SingleSwitchTopo
class TestPtyLeak( unittest.TestCase ):
"Verify that there is no pty leakage"
@staticmethod
def testPtyLeak():
"Test for pty leakage"
net = Mininet( SingleSwitchTopo() )
net.start()
host = net[ 'h1' ]
for _ in range( 0, 10 ):
oldptys = host.slave, host.master
net.delHost( host )
host = net.addHost( 'h1' )
assert ( host.slave, host.master ) == oldptys
net.stop()
if __name__ == '__main__':
unittest.main()
cleanup()
-102
View File
@@ -1,102 +0,0 @@
#!/usr/bin/env python
"""Package: mininet
Regression tests for switch dpid assignment."""
import unittest
import sys
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"Verify Switch dpid assignment."
switchClass = OVSSwitch # overridden in subclasses
def tearDown( self ):
"Clean up if necessary"
# satisfy pylint
assert self
if sys.exc_info() != ( None, None, None ):
cleanup()
def testDefaultDpid( self ):
"""Verify that the default dpid is assigned using a valid provided
canonical switchname if no dpid is passed in switch creation."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
net.stop()
def dpidFrom( self, num ):
"Compute default dpid from number"
fmt = ( '%0' + str( self.switchClass.dpidLen ) + 'x' )
return fmt % num
def testActualDpidAssignment( self ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation."""
dpid = self.dpidFrom( 0xABCD )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid )
net.stop()
def testDefaultDpidAssignmentFailure( self ):
"""Verify that Default dpid assignment raises an Exception if the
name of the switch does not contain a digit. Also verify the
exception message."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
with self.assertRaises( Exception ) as raises_cm:
net.addSwitch( 'A' )
self.assertTrue( 'Unable to derive '
'default datapath ID - please either specify a dpid '
'or use a canonical switch name such as s23.'
in str( raises_cm.exception ) )
net.stop()
def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch
name) 0's followed by hex of first string of contiguous digits passed
in switch name."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
net.stop()
class OVSUser( OVSSwitch):
"OVS User Switch convenience class"
def __init__( self, *args, **kwargs ):
kwargs.update( datapath='user' )
OVSSwitch.__init__( self, *args, **kwargs )
class testSwitchOVSUser( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of OVS User Switch."
switchClass = OVSUser
@unittest.skipUnless( quietRun( 'which ivs-ctl' ),
'IVS switch is not installed' )
class testSwitchIVS( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of IVS switch."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of Userspace switch."
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
cleanup()
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env python
"""Package: mininet
Test functions defined in mininet.util."""
import unittest
from mininet.util import quietRun
class testQuietRun( unittest.TestCase ):
"""Test quietRun that runs a command and returns its merged output from
STDOUT and STDIN"""
@staticmethod
def getEchoCmd( n ):
"Return a command that will print n characters"
return "echo -n " + "x" * n
def testEmpty( self ):
"Run a command that prints nothing"
output = quietRun(testQuietRun.getEchoCmd( 0 ) )
self.assertEqual( 0, len( output ) )
def testOneRead( self ):
"""Run a command whose output is entirely read on the first call if
each call reads at most 1024 characters
"""
for n in [ 42, 1024 ]:
output = quietRun( testQuietRun.getEchoCmd( n ) )
self.assertEqual( n, len( output ) )
def testMultipleReads( self ):
"Run a command whose output is not entirely read on the first read"
for n in [ 1025, 4242 ]:
output = quietRun(testQuietRun.getEchoCmd( n ) )
self.assertEqual( n, len( output ) )
if __name__ == "__main__":
unittest.main()
+48 -109
View File
@@ -6,35 +6,15 @@ Tests for the Mininet Walkthrough
TODO: missing xterm test
"""
import os
import re
import unittest
from sys import stdout
from mininet.util import quietRun, pexpect, StrictVersion
from mininet.clean import cleanup
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
return versionMatch[ 0 ]
# pylint doesn't understand pexpect.match, unfortunately!
# pylint:disable=maybe-no-member
import pexpect
import os
from mininet.util import quietRun
class testWalkthrough( unittest.TestCase ):
"Test Mininet walkthrough"
prompt = 'mininet>'
@staticmethod
def setup():
"Be paranoid and run cleanup() before each test"
cleanup()
# PART 1
def testHelp( self ):
"Check the usage message"
@@ -44,24 +24,16 @@ class testWalkthrough( unittest.TestCase ):
def testWireshark( self ):
"Use tshark to test the of dissector"
# Satisfy pylint
assert self
if StrictVersion( tsharkVersion() ) < StrictVersion( '1.12.0' ):
tshark = pexpect.spawn( 'tshark -i lo -R of' )
else:
tshark = pexpect.spawn( 'tshark -i lo -Y openflow_v1' )
tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback" ] )
tshark = pexpect.spawn( 'tshark -i lo -R of' )
tshark.expect( 'Capturing on lo' )
mn = pexpect.spawn( 'mn --test pingall' )
mn.expect( '0% dropped' )
tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
tshark.expect( 'OFP 74 Hello' )
tshark.sendintr()
mn.expect( pexpect.EOF )
tshark.expect( 'aptured' ) # 'xx packets captured'
tshark.expect( pexpect.EOF )
def testBasic( self ):
"Test basic CLI commands (help, nodes, net, dump)"
p = pexpect.spawn( 'mn -w' )
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# help command
p.sendline( 'help' )
@@ -69,13 +41,13 @@ class testWalkthrough( unittest.TestCase ):
self.assertEqual( index, 0, 'No output for "help" command')
# nodes command
p.sendline( 'nodes' )
p.expect( r'([chs]\d ?){4}' )
p.expect( '([chs]\d ?){4}' )
nodes = p.match.group( 0 ).split()
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
p.expect( self.prompt )
# net command
p.sendline( 'net' )
expected = list( nodes )
expected = [ x for x in nodes ]
while len( expected ) > 0:
index = p.expect( expected )
node = p.match.group( 0 )
@@ -85,33 +57,29 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
# dump command
p.sendline( 'dump' )
expected = [ r'<\w+ (%s)' % n for n in nodes ]
expected = [ '<\w+ (%s)' % n for n in nodes ]
actual = []
for _ in nodes:
index = p.expect( expected )
node = p.match.group( 1 )
actual.append( node )
p.expect( '\n' )
self.assertEqual( actual.sort(), nodes.sort(),
'"nodes" and "dump" differ' )
self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testHostCommands( self ):
"Test ifconfig and ps on h1 and s1"
p = pexpect.spawn( 'mn -w' )
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
self.prompt ]
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index in (0, 3):
if index == 0 or index == 3:
ifcount += 1
elif index == 1:
self.fail( 's1 interface displayed in "h1 ifconfig"' )
@@ -127,32 +95,29 @@ class testWalkthrough( unittest.TestCase ):
index = p.expect( interfaces )
if index == 0:
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
elif index in (1, 2, 3):
elif index == 1 or index == 2 or index == 3:
ifcount += 1
else:
break
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.sendline( 'h1 ps -a' )
p.expect( self.prompt )
h1Output = p.before
# s1 ps
p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
p.sendline( 's1 ps -a' )
p.expect( self.prompt )
s1Output = p.before
# strip command from ps output and compute diffs
h1Output = h1Output.split( '\n' )[ 1: ]
s1Output = s1Output.split( '\n' )[ 1: ]
diffs = set( h1Output ).difference( set( s1Output ) )
# allow up to two diffs to account for daemons, etc.
self.assertTrue( len( diffs ) <= 2,
'h1 and s1 "ps" output differ too much: %s' % diffs )
# strip command from ps output
h1Output = h1Output.split( '\n', 1 )[ 1 ]
s1Output = s1Output.split( '\n', 1 )[ 1 ]
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
p.sendline( 'exit' )
p.wait()
def testConnectivity( self ):
"Test ping and pingall"
p = pexpect.spawn( 'mn -w' )
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 h2' )
p.expect( '1 packets transmitted, 1 received' )
@@ -165,20 +130,9 @@ class testWalkthrough( unittest.TestCase ):
def testSimpleHTTP( self ):
"Start an HTTP server on h1 and wget from h2"
if 'Python 2' in quietRun( 'python --version' ):
httpserver = 'SimpleHTTPServer'
else:
httpserver = 'http.server'
p = pexpect.spawn( 'mn -w', logfile=stdout )
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 python -m %s 80 >& /dev/null &' % httpserver )
p.expect( self.prompt )
# The walkthrough doesn't specify a delay here, and
# we also don't read the output (also a possible problem),
# but for now let's wait a number of seconds to make
# it less likely to fail due to the race condition.
p.sendline( 'px from mininet.util import waitListening;'
'waitListening(h1, port=80, timeout=30)' )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
p.expect( self.prompt )
p.sendline( ' h2 wget -O - h1' )
p.expect( '200 OK' )
@@ -197,7 +151,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( pexpect.EOF )
# test iperf
p = pexpect.spawn( 'mn --test iperf' )
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
@@ -206,7 +160,7 @@ class testWalkthrough( unittest.TestCase ):
"Test pingall on single,3 and linear,4 topos"
# testing single,3
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
p.expect( r'(\d+)/(\d+) received')
p.expect( '(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
@@ -214,7 +168,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( pexpect.EOF )
# testing linear,4
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
p.expect( r'(\d+)/(\d+) received')
p.expect( '(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
@@ -223,24 +177,21 @@ class testWalkthrough( unittest.TestCase ):
def testLinkChange( self ):
"Test TCLink bw and delay"
p = pexpect.spawn( 'mn -w --link tc,bw=10,delay=10ms' )
p.expect( self.prompt )
p.sendline( 'h1 route && ping -c1 h2' )
p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
# test bw
p.expect( self.prompt )
p.sendline( 'iperf' )
p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
p.expect( "Results: \['([\d\.]+) Mbits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
p.expect( self.prompt )
# test delay
p.sendline( 'h1 ping -c 4 h2' )
p.expect( r'rtt min/avg/max/mdev = '
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay >= 40, 'Delay < 40ms' )
self.assertTrue( delay <= 50, 'Delay > 50ms' )
self.assertTrue( delay > 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@@ -257,17 +208,14 @@ class testWalkthrough( unittest.TestCase ):
p = pexpect.spawn( 'mn -v debug --test none' )
p.expect( pexpect.EOF )
lines = p.before.split( '\n' )
self.assertTrue( len( lines ) > 70, "Debug output is too short" )
self.assertTrue( len( lines ) > 100, "Debug output is too short" )
def testCustomTopo( self ):
"Start Mininet using a custom topo, then run pingall"
# Satisfy pylint
assert self
custom = os.path.dirname( os.path.realpath( __file__ ) )
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
custom = os.path.normpath( custom )
p = pexpect.spawn(
'mn --custom %s --topo mytopo --test pingall' % custom )
p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom )
p.expect( '0% dropped' )
p.expect( pexpect.EOF )
@@ -277,17 +225,15 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
for i in range( 1, 3 ):
p.sendline( 'h%d ifconfig' % i )
p.expect( r'\s00:00:00:00:00:0%d\s' % i )
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
p.expect( self.prompt )
p.sendline( 'exit' )
p.expect( pexpect.EOF )
def testSwitches( self ):
"Run iperf test using user and ovsk switches"
switches = [ 'user', 'ovsk' ]
for sw in switches:
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
@@ -295,7 +241,7 @@ class testWalkthrough( unittest.TestCase ):
def testBenchmark( self ):
"Run benchmark and verify that it takes less than 2 seconds"
p = pexpect.spawn( 'mn --test none' )
p.expect( r'completed in ([\d\.]+) seconds' )
p.expect( 'completed in ([\d\.]+) seconds' )
time = float( p.match.group( 1 ) )
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
@@ -303,14 +249,12 @@ class testWalkthrough( unittest.TestCase ):
"Test running user switch in its own namespace"
p = pexpect.spawn( 'mn --innamespace --switch user' )
p.expect( self.prompt )
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
self.prompt ]
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
p.sendline( 's1 ifconfig -a' )
ifcount = 0
while True:
index = p.expect( interfaces )
if index in (1, 3):
if index == 1 or index == 3:
ifcount += 1
elif index == 0:
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
@@ -321,7 +265,7 @@ class testWalkthrough( unittest.TestCase ):
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
# verify that all hosts a reachable
p.sendline( 'pingall' )
p.expect( r'(\d+)% dropped' )
p.expect( '(\d+)% dropped' )
dropped = int( p.match.group( 1 ) )
self.assertEqual( dropped, 0, 'pingall failed')
p.expect( self.prompt )
@@ -331,7 +275,7 @@ class testWalkthrough( unittest.TestCase ):
# PART 3
def testPythonInterpreter( self ):
"Test py and px by checking IP for h1 and adding h3"
p = pexpect.spawn( 'mn -w' )
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# test host IP
p.sendline( 'py h1.IP()' )
@@ -353,7 +297,7 @@ class testWalkthrough( unittest.TestCase ):
def testLink( self ):
"Test link CLI command using ping"
p = pexpect.spawn( 'mn -w' )
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'link s1 h1 down' )
p.expect( self.prompt )
@@ -373,20 +317,15 @@ class testWalkthrough( unittest.TestCase ):
'Github is not reachable; cannot download Pox' )
def testRemoteController( self ):
"Test Mininet using Pox controller"
# Satisfy pylint
assert self
if not os.path.exists( '/tmp/pox' ):
p = pexpect.spawn(
'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p.expect( pexpect.EOF )
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
net = pexpect.spawn(
'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net.expect( '0% dropped' )
net.expect( pexpect.EOF )
pox.sendintr()
pox.wait()
if __name__ == '__main__':
unittest.main()
unittest.main()
+163 -252
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
"""@package topo
'''@package topo
Network topology creation.
@@ -9,350 +9,261 @@ This package includes code to represent network topologies.
A Topo object can be a topology database for NOX, can represent a physical
setup for testing, and can even be emulated with the Mininet package.
"""
'''
from mininet.util import irange, natural, naturalSeq
# pylint: disable=too-many-arguments
class MultiGraph( object ):
"Utility class to track nodes and edges - replaces networkx.MultiGraph"
"Utility class to track nodes and edges - replaces networkx.Graph"
def __init__( self ):
self.node = {}
self.edge = {}
self.data = {}
def add_node( self, node, attr_dict=None, **attrs):
"""Add node to graph
attr_dict: attribute dict (optional)
attrs: more attributes (optional)
warning: updates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node[ node ] = attr_dict
def add_node( self, node ):
"Add node to graph"
self.data.setdefault( node, [] )
def add_edge( self, src, dst, key=None, attr_dict=None, **attrs ):
"""Add edge to graph
key: optional key
attr_dict: optional attribute dict
attrs: more attributes
warning: updates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node.setdefault( src, {} )
self.node.setdefault( dst, {} )
self.edge.setdefault( src, {} )
self.edge.setdefault( dst, {} )
self.edge[ src ].setdefault( dst, {} )
entry = self.edge[ dst ][ src ] = self.edge[ src ][ dst ]
# If no key, pick next ordinal number
if key is None:
keys = [ k for k in entry.keys() if isinstance( k, int ) ]
key = max( [ 0 ] + keys ) + 1
entry[ key ] = attr_dict
return key
def add_edge( self, src, dest ):
"Add edge to graph"
src, dest = sorted( ( src, dest ) )
self.add_node( src )
self.add_node( dest )
self.data[ src ].append( dest )
def nodes( self, data=False):
"""Return list of graph nodes
data: return list of ( node, attrs)"""
return self.node.items() if data else self.node.keys()
def nodes( self ):
"Return list of graph nodes"
return self.data.keys()
def edges_iter( self, data=False, keys=False ):
"Iterator: return graph edges, optionally with data and keys"
for src, entry in self.edge.items():
for dst, entrykeys in entry.items():
if src > dst:
# Skip duplicate edges
continue
for k, attrs in entrykeys.items():
if data:
if keys:
yield( src, dst, k, attrs )
else:
yield( src, dst, attrs )
else:
if keys:
yield( src, dst, k )
else:
yield( src, dst )
def edges( self, data=False, keys=False ):
"Return list of graph edges"
return list( self.edges_iter( data=data, keys=keys ) )
def edges( self ):
"Iterator: return graph edges"
for src in self.data.keys():
for dest in self.data[ src ]:
yield ( src, dest )
def __getitem__( self, node ):
"Return link dict for given src node"
return self.edge[ node ]
def __len__( self ):
"Return the number of nodes"
return len( self.node )
def convertTo( self, cls, data=False, keys=False ):
"""Convert to a new object of networkx.MultiGraph-like class cls
data: include node and edge data
keys: include edge keys as well as edge data"""
g = cls()
g.add_nodes_from( self.nodes( data=data ) )
g.add_edges_from( self.edges( data=( data or keys ), keys=keys ) )
return g
"Return link dict for the given node"
return self.data[node]
class Topo( object ):
class Topo(object):
"Data center network representation for structured multi-trees."
def __init__( self, *args, **params ):
"""Topo object.
Optional named parameters:
def __init__(self, hopts=None, sopts=None, lopts=None):
"""Topo object:
hinfo: default host options
sopts: default switch options
lopts: default link options
calls build()"""
lopts: default link options"""
self.g = MultiGraph()
self.hopts = params.pop( 'hopts', {} )
self.sopts = params.pop( 'sopts', {} )
self.lopts = params.pop( 'lopts', {} )
# ports[src][dst][sport] is port on dst that connects to src
self.ports = {}
self.build( *args, **params )
self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = {} if hopts is None else hopts
self.sopts = {} if sopts is None else sopts
self.lopts = {} if lopts is None else lopts
self.ports = {} # ports[src][dst] is port on src that connects to dst
def build( self, *args, **params ):
"Override this method to build your topology."
pass
def addNode( self, name, **opts ):
def addNode(self, name, **opts):
"""Add Node to graph.
name: name
opts: node options
returns: node name"""
self.g.add_node( name, **opts )
self.g.add_node(name)
self.node_info[name] = opts
return name
def addHost( self, name, **opts ):
def addHost(self, name, **opts):
"""Convenience method: Add host to graph.
name: host name
opts: host options
returns: host name"""
if not opts and self.hopts:
opts = self.hopts
return self.addNode( name, **opts )
return self.addNode(name, **opts)
def addSwitch( self, name, **opts ):
def addSwitch(self, name, **opts):
"""Convenience method: Add switch to graph.
name: switch name
opts: switch options
returns: switch name"""
if not opts and self.sopts:
opts = self.sopts
result = self.addNode( name, isSwitch=True, **opts )
result = self.addNode(name, isSwitch=True, **opts)
return result
def addLink( self, node1, node2, port1=None, port2=None,
key=None, **opts ):
def addLink(self, node1, node2, port1=None, port2=None,
**opts):
"""node1, node2: nodes to link together
port1, port2: ports (optional)
opts: link options (optional)
returns: link info key"""
if not opts and self.lopts:
opts = self.lopts
port1, port2 = self.addPort( node1, node2, port1, port2 )
opts = dict( opts )
opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
return self.g.add_edge(node1, node2, key, opts )
self.addPort(node1, node2, port1, port2)
key = tuple(self.sorted([node1, node2]))
self.link_info[key] = opts
self.g.add_edge(*key)
return key
def nodes( self, sort=True ):
def addPort(self, src, dst, sport=None, dport=None):
'''Generate port mapping for new edge.
@param src source switch name
@param dst destination switch name
'''
self.ports.setdefault(src, {})
self.ports.setdefault(dst, {})
# New port: number of outlinks + base
src_base = 1 if self.isSwitch(src) else 0
dst_base = 1 if self.isSwitch(dst) else 0
if sport is None:
sport = len(self.ports[src]) + src_base
if dport is None:
dport = len(self.ports[dst]) + dst_base
self.ports[src][dst] = sport
self.ports[dst][src] = dport
def nodes(self, sort=True):
"Return nodes in graph"
if sort:
return self.sorted( self.g.nodes() )
else:
return self.g.nodes()
def isSwitch( self, n ):
"Returns true if node is a switch."
return self.g.node[ n ].get( 'isSwitch', False )
def isSwitch(self, n):
'''Returns true if node is a switch.'''
info = self.node_info[n]
return info and info.get('isSwitch', False)
def switches( self, sort=True ):
"""Return switches.
sort: sort switches alphabetically
returns: dpids list of dpids"""
return [ n for n in self.nodes( sort ) if self.isSwitch( n ) ]
def switches(self, sort=True):
'''Return switches.
sort: sort switches alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if self.isSwitch(n)]
def hosts( self, sort=True ):
"""Return hosts.
sort: sort hosts alphabetically
returns: list of hosts"""
return [ n for n in self.nodes( sort ) if not self.isSwitch( n ) ]
def hosts(self, sort=True):
'''Return hosts.
sort: sort hosts alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
def iterLinks( self, withKeys=False, withInfo=False ):
"""Return links (iterator)
withKeys: return link keys
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
for _src, _dst, key, info in self.g.edges_iter( data=True, keys=True ):
node1, node2 = info[ 'node1' ], info[ 'node2' ]
if withKeys:
if withInfo:
yield( node1, node2, key, info )
else:
yield( node1, node2, key )
else:
if withInfo:
yield( node1, node2, info )
else:
yield( node1, node2 )
def links( self, sort=False, withKeys=False, withInfo=False ):
"""Return links
sort: sort links alphabetically, preserving (src, dst) order
withKeys: return link keys
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
links = list( self.iterLinks( withKeys, withInfo ) )
def links(self, sort=True):
'''Return links.
sort: sort links alphabetically
@return links list of name pairs
'''
if not sort:
return links
# Ignore info when sorting
tupleSize = 3 if withKeys else 2
return sorted( links, key=( lambda l: naturalSeq( l[ :tupleSize ] ) ) )
return self.g.edges()
else:
links = [tuple(self.sorted(e)) for e in self.g.edges()]
return sorted( links, key=naturalSeq )
# This legacy port management mechanism is clunky and will probably
# be removed at some point.
def port(self, src, dst):
'''Get port number.
def addPort( self, src, dst, sport=None, dport=None ):
"""Generate port mapping for new edge.
src: source switch name
dst: destination switch name"""
# Initialize if necessary
ports = self.ports
ports.setdefault( src, {} )
ports.setdefault( dst, {} )
# New port: number of outlinks + base
if sport is None:
src_base = 1 if self.isSwitch( src ) else 0
sport = len( ports[ src ] ) + src_base
if dport is None:
dst_base = 1 if self.isSwitch( dst ) else 0
dport = len( ports[ dst ] ) + dst_base
ports[ src ][ sport ] = ( dst, dport )
ports[ dst ][ dport ] = ( src, sport )
return sport, dport
@param src source switch name
@param dst destination switch name
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
if src in self.ports and dst in self.ports[src]:
assert dst in self.ports and src in self.ports[dst]
return (self.ports[src][dst], self.ports[dst][src])
def port( self, src, dst ):
"""Get port numbers.
src: source switch name
dst: destination switch name
sport: optional source port (otherwise use lowest src port)
returns: tuple (sport, dport), where
sport = port on source switch leading to the destination switch
dport = port on destination switch leading to the source switch
Note that you can also look up ports using linkInfo()"""
# A bit ugly and slow vs. single-link implementation ;-(
ports = [ ( sport, entry[ 1 ] )
for sport, entry in self.ports[ src ].items()
if entry[ 0 ] == dst ]
return ports if len( ports ) != 1 else ports[ 0 ]
def linkInfo( self, src, dst ):
"Return link metadata"
src, dst = self.sorted([src, dst])
return self.link_info[(src, dst)]
def _linkEntry( self, src, dst, key=None ):
"Helper function: return link entry and key"
entry = self.g[ src ][ dst ]
if key is None:
key = min( entry )
return entry, key
def linkInfo( self, src, dst, key=None ):
"Return link metadata dict"
entry, key = self._linkEntry( src, dst, key )
return entry[ key ]
def setlinkInfo( self, src, dst, info, key=None ):
"Set link metadata dict"
entry, key = self._linkEntry( src, dst, key )
entry[ key ] = info
def setlinkInfo( self, src, dst, info ):
"Set link metadata"
src, dst = self.sorted([src, dst])
self.link_info[(src, dst)] = info
def nodeInfo( self, name ):
"Return metadata (dict) for node"
return self.g.node[ name ]
info = self.node_info[ name ]
return info if info is not None else {}
def setNodeInfo( self, name, info ):
"Set metadata (dict) for node"
self.g.node[ name ] = info
def convertTo( self, cls, data=True, keys=True ):
"""Convert to a new object of networkx.MultiGraph-like class cls
data: include node and edge data (default True)
keys: include edge keys as well as edge data (default True)"""
return self.g.convertTo( cls, data=data, keys=keys )
self.node_info[ name ] = info
@staticmethod
def sorted( items ):
"Items sorted in natural (i.e. alphabetical) order"
return sorted( items, key=natural )
return sorted(items, key=natural)
class SingleSwitchTopo(Topo):
'''Single switch connected to k hosts.'''
# Our idiom defines additional parameters in build(param...)
# pylint: disable=arguments-differ
def __init__(self, k=2, **opts):
'''Init.
class SingleSwitchTopo( Topo ):
"Single switch connected to k hosts."
@param k number of hosts
@param enable_all enables all nodes and switches?
'''
super(SingleSwitchTopo, self).__init__(**opts)
def build( self, k=2, **_opts ):
"k: number of hosts"
self.k = k
switch = self.addSwitch( 's1' )
for h in irange( 1, k ):
host = self.addHost( 'h%s' % h )
self.addLink( host, switch )
switch = self.addSwitch('s1')
for h in irange(1, k):
host = self.addHost('h%s' % h)
self.addLink(host, switch)
class SingleSwitchReversedTopo( Topo ):
"""Single switch connected to k hosts, with reversed ports.
The lowest-numbered host is connected to the highest-numbered port.
Useful to verify that Mininet properly handles custom port
numberings."""
class SingleSwitchReversedTopo(Topo):
'''Single switch connected to k hosts, with reversed ports.
def build( self, k=2 ):
"k: number of hosts"
The lowest-numbered host is connected to the highest-numbered port.
Useful to verify that Mininet properly handles custom port numberings.
'''
def __init__(self, k=2, **opts):
'''Init.
@param k number of hosts
@param enable_all enables all nodes and switches?
'''
super(SingleSwitchReversedTopo, self).__init__(**opts)
self.k = k
switch = self.addSwitch( 's1' )
for h in irange( 1, k ):
host = self.addHost( 'h%s' % h )
self.addLink( host, switch,
port1=0, port2=( k - h + 1 ) )
switch = self.addSwitch('s1')
for h in irange(1, k):
host = self.addHost('h%s' % h)
self.addLink(host, switch,
port1=0, port2=(k - h + 1))
class MinimalTopo( SingleSwitchTopo ):
"Minimal topology with two hosts and one switch"
def build( self ):
return SingleSwitchTopo.build( self, k=2 )
class LinearTopo( Topo ):
class LinearTopo(Topo):
"Linear topology of k switches, with n hosts per switch."
def build( self, k=2, n=1, **_opts):
"""k: number of switches
n: number of hosts per switch"""
def __init__(self, k=2, n=1, **opts):
"""Init.
k: number of switches
n: number of hosts per switch
hconf: host configuration options
lconf: link configuration options"""
super(LinearTopo, self).__init__(**opts)
self.k = k
self.n = n
if n == 1:
def genHostName( i, _j ):
return 'h%s' % i
genHostName = lambda i, j: 'h%s' % i
else:
def genHostName( i, j ):
return 'h%ss%d' % ( j, i )
genHostName = lambda i, j: 'h%ss%d' % (j, i)
lastSwitch = None
for i in irange( 1, k ):
for i in irange(1, k):
# Add switch
switch = self.addSwitch( 's%s' % i )
switch = self.addSwitch('s%s' % i)
# Add hosts to switch
for j in irange( 1, n ):
host = self.addHost( genHostName( i, j ) )
self.addLink( host, switch )
for j in irange(1, n):
host = self.addHost(genHostName(i, j))
self.addLink(host, switch)
# Connect switch to previous
if lastSwitch:
self.addLink( switch, lastSwitch )
self.addLink(switch, lastSwitch)
lastSwitch = switch
# pylint: enable=arguments-differ
+2 -50
View File
@@ -3,13 +3,11 @@
from mininet.topo import Topo
from mininet.net import Mininet
# The build() method is expected to do this:
# pylint: disable=arguments-differ
class TreeTopo( Topo ):
"Topology for a tree network with a given depth and fanout."
def build( self, depth=1, fanout=2 ):
def __init__( self, depth=1, fanout=2 ):
super( TreeTopo, self ).__init__()
# Numbering: h1..N, s1..M
self.hostNum = 1
self.switchNum = 1
@@ -36,49 +34,3 @@ def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout )
return Mininet( topo, **kwargs )
class TorusTopo( Topo ):
"""2-D Torus topology
WARNING: this topology has LOOPS and WILL NOT WORK
with the default controller or any Ethernet bridge
without STP turned on! It can be used with STP, e.g.:
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
def build( self, x, y, n=1 ):
"""x: dimension of torus in x-direction
y: dimension of torus in y-direction
n: number of hosts per switch"""
if x < 3 or y < 3:
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
if n == 1:
def genHostName( loc, _k ):
return 'h%s' % ( loc )
else:
def genHostName( loc, k ):
return 'h%sx%d' % ( loc, k )
hosts, switches, dpid = {}, {}, 0
# Create and wire interior
for i in range( 0, x ):
for j in range( 0, y ):
loc = '%dx%d' % ( i + 1, j + 1 )
# dpid cannot be zero for OVS
dpid = ( i + 1 ) * 256 + ( j + 1 )
switch = switches[ i, j ] = self.addSwitch(
's' + loc, dpid='%x' % dpid )
for k in range( 0, n ):
host = hosts[ i, j, k ] = self.addHost(
genHostName( loc, k + 1 ) )
self.addLink( host, switch )
# Connect switches
for i in range( 0, x ):
for j in range( 0, y ):
sw1 = switches[ i, j ]
sw2 = switches[ i, ( j + 1 ) % y ]
sw3 = switches[ ( i + 1 ) % x, j ]
self.addLink( sw1, sw2 )
self.addLink( sw1, sw3 )
# pylint: enable=arguments-differ
+123 -346
View File
@@ -1,86 +1,15 @@
"Utility functions for Mininet."
import codecs
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
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
from select import poll, POLLIN, POLLHUP
from subprocess import call, check_call, Popen, PIPE, STDOUT
from sys import exit # pylint: disable=redefined-builtin
from time import sleep
from mininet.log import output, info, error, warn, debug
# pylint: disable=too-many-arguments
# Python 2/3 compatibility
Python3 = sys.version_info[0] == 3
BaseString = str if Python3 else getattr( str, '__base__' )
Encoding = 'utf-8' if Python3 else None
class NullCodec( object ):
"Null codec for Python 2"
@staticmethod
def decode( buf ):
"Null decode"
return buf
@staticmethod
def encode( buf ):
"Null encode"
return buf
if Python3:
def decode( buf ):
"Decode buffer for Python 3"
return buf.decode( Encoding )
def encode( buf ):
"Encode buffer for Python 3"
return buf.encode( Encoding )
getincrementaldecoder = codecs.getincrementaldecoder( Encoding )
else:
decode, encode = NullCodec.decode, NullCodec.encode
def getincrementaldecoder():
"Return null codec for Python 2"
return NullCodec
try:
import packaging.version # replacement for distutils.version
StrictVersion = packaging.version.parse
except ImportError: # python2.7 lacks ModuleNotFoundError
import distutils.version # pylint: disable=deprecated-module
StrictVersion = distutils.version.StrictVersion
try:
oldpexpect = None
import pexpect as oldpexpect # pylint: disable=import-error
class Pexpect( object ):
"Custom pexpect that is compatible with str"
@staticmethod
def spawn( *args, **kwargs):
"pexpect.spawn that is compatible with str"
if Python3 and 'encoding' not in kwargs:
kwargs.update( encoding='utf-8' )
return oldpexpect.spawn( *args, **kwargs )
def __getattr__( self, name ):
return getattr( oldpexpect, name )
pexpect = Pexpect()
except ImportError:
pass
from time import sleep
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
from select import poll, POLLIN, POLLHUP
from subprocess import call, check_call, Popen, PIPE, STDOUT
import re
from fcntl import fcntl, F_GETFL, F_SETFL
from os import O_NONBLOCK
import os
# Command execution support
@@ -95,21 +24,20 @@ def checkRun( cmd ):
return check_call( cmd.split( ' ' ) )
# pylint doesn't understand explicit type checking
# pylint: disable=maybe-no-member
# pylint: disable-msg=E1103
def oldQuietRun( *cmd ):
"""Run a command, routing stderr to stdout, and return the output.
cmd: list of command params"""
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, BaseString ):
if isinstance( cmd, str ):
cmd = cmd.split( ' ' )
out = ''
popen = Popen( # pylint: disable=consider-using-with
cmd, stdout=PIPE, stderr=STDOUT )
popen = Popen( 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:
@@ -127,15 +55,18 @@ 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
cmd: string or list of command and args
stderr: STDOUT to merge stderr with stdout
shell: run command using shell
echo: monitor output to console"""
# Allow passing in a list or a string
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, str ):
cmd = cmd.split( ' ' )
cmd = [ str( arg ) for arg in cmd ]
# By default we separate stderr, don't run in a shell, and don't echo
stderr = kwargs.get( 'stderr', PIPE )
shell = kwargs.get( 'shell', False )
@@ -143,63 +74,35 @@ def errRun( *cmd, **kwargs ):
if echo:
# cmd goes to stderr, output goes to stdout
info( cmd, '\n' )
if len( cmd ) == 1:
cmd = cmd[ 0 ]
# Allow passing in a list or a string
if isinstance( cmd, BaseString ) and not shell:
cmd = cmd.split( ' ' )
cmd = [ str( arg ) for arg in cmd ]
elif isinstance( cmd, list ) and shell:
cmd = " ".join( arg for arg in cmd )
debug( '*** errRun:', cmd, '\n' )
# pylint: disable=consider-using-with
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
# We use poll() because select() doesn't work with large fd numbers,
# and thus communicate() doesn't work either
out, err = '', ''
poller = poll()
poller.register( popen.stdout, POLLIN )
fdToFile = { popen.stdout.fileno(): popen.stdout }
fdToDecoder = { popen.stdout.fileno(): getincrementaldecoder() }
fdtofile = { popen.stdout.fileno(): popen.stdout }
outDone, errDone = False, True
if popen.stderr:
fdToFile[ popen.stderr.fileno() ] = popen.stderr
fdToDecoder[ popen.stderr.fileno() ] = getincrementaldecoder()
fdtofile[ popen.stderr.fileno() ] = popen.stderr
poller.register( popen.stderr, POLLIN )
errDone = False
while not outDone or not errDone:
readable = poller.poll()
for fd, event in readable:
f = fdToFile[ fd ]
decoder = fdToDecoder[ fd ]
if event & ( POLLIN | POLLHUP ):
data = decoder.decode( f.read( 1024 ) )
if echo:
output( data )
if f == popen.stdout:
out += data
if data == '':
outDone = True
elif f == popen.stderr:
err += data
if data == '':
errDone = True
else: # something unexpected
if f == popen.stdout:
for fd, _event in readable:
f = fdtofile[ fd ]
data = f.read( 1024 )
if echo:
output( data )
if f == popen.stdout:
out += data
if data == '':
outDone = True
elif f == popen.stderr:
elif f == popen.stderr:
err += data
if data == '':
errDone = True
poller.unregister( fd )
returncode = popen.wait()
# Python 3 complains if we don't explicitly close these
popen.stdout.close()
if stderr == PIPE:
popen.stderr.close()
debug( out, err, returncode )
return CmdResult( out, err, returncode )
# pylint: enable=too-many-branches
return out, err, returncode
def errFail( *cmd, **kwargs ):
"Run a command using errRun and raise exception on nonzero exit"
@@ -207,31 +110,27 @@ def errFail( *cmd, **kwargs ):
if ret:
raise Exception( "errFail: %s failed with return code %s: %s"
% ( cmd, ret, err ) )
return CmdResult( out, err, ret )
return out, err, ret
def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs ).out
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
def which(cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
out, _, ret = errRun( ["which", cmd], stderr=STDOUT, **kwargs )
return out.rstrip() if ret == 0 else None
# pylint: enable=maybe-no-member
# pylint: enable-msg=E1103
# pylint: disable-msg=E1101
def isShellBuiltin( cmd ):
"Return True if cmd is a bash builtin."
if isShellBuiltin.builtIns is None:
isShellBuiltin.builtIns = set(quietRun( 'bash -c enable' ).split())
isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
space = cmd.find( ' ' )
if space > 0:
cmd = cmd[ :space]
return cmd in isShellBuiltin.builtIns
isShellBuiltin.builtIns = None
# pylint: enable-msg=E1101
# Interface management
#
@@ -246,41 +145,17 @@ isShellBuiltin.builtIns = None
# live in the root namespace and thus do not have to be
# explicitly moved.
def makeIntfPair( intf1, intf2, addr1=None, addr2=None, node1=None, node2=None,
deleteIntfs=True, runCmd=None ):
"""Make a veth pair connnecting new interfaces intf1 and intf2
intf1: name for interface 1
intf2: name for interface 2
addr1: MAC address for interface 1 (optional)
addr2: MAC address for interface 2 (optional)
node1: home node for interface 1 (optional)
node2: home node for interface 2 (optional)
deleteIntfs: delete intfs before creating them
runCmd: function to run shell commands (quietRun)
raises Exception on failure"""
if not runCmd:
runCmd = quietRun if not node1 else node1.cmd
runCmd2 = quietRun if not node2 else node2.cmd
if deleteIntfs:
# Delete any old interfaces with the same names
runCmd( 'ip link del ' + intf1 )
runCmd2( 'ip link del ' + intf2 )
def makeIntfPair( intf1, intf2 ):
"""Make a veth pair connecting intf1 and intf2.
intf1: string, interface
intf2: string, interface
returns: success boolean"""
# Delete any old interfaces with the same names
quietRun( 'ip link del ' + intf1 )
quietRun( 'ip link del ' + intf2 )
# Create new pair
netns = 1 if not node2 else node2.pid
if addr1 is None and addr2 is None:
cmdOutput = runCmd( 'ip link add name %s '
'type veth peer name %s '
'netns %s' % ( intf1, intf2, netns ) )
else:
cmdOutput = runCmd( 'ip link add name %s '
'address %s '
'type veth peer name %s '
'address %s '
'netns %s' %
( intf1, addr1, intf2, addr2, netns ) )
if cmdOutput:
raise Exception( "Error creating interface pair (%s,%s): %s " %
( intf1, intf2, cmdOutput ) )
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
return quietRun( cmd )
def retry( retries, delaySecs, fn, *args, **keywords ):
"""Try something several times before giving up.
@@ -296,32 +171,35 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
error( "*** gave up after %i retries\n" % tries )
exit( 1 )
def moveIntfNoRetry( intf, dstNode, printError=False ):
def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
"""Move interface to node, without retrying.
intf: string, interface
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
cmdOutput = quietRun( cmd )
# If ip link set does not produce any output, then we can assume
# that the link has been moved successfully.
if cmdOutput:
if srcNode:
srcNode.cmd( cmd )
else:
quietRun( cmd )
links = dstNode.cmd( 'ip link show' )
if not ( ' %s:' % intf ) in links:
if printError:
error( '*** Error: moveIntf: ' + intf +
' not successfully moved to ' + dstNode.name + ':\n',
cmdOutput )
' not successfully moved to ' + dstNode.name + '\n' )
return False
return True
def moveIntf( intf, dstNode, printError=True,
retries=3, delaySecs=0.001 ):
def moveIntf( intf, dstNode, srcNode=None, printError=False,
retries=3, delaySecs=0.001 ):
"""Move interface to node, retrying on failure.
intf: string, interface
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
printError=printError )
srcNode=srcNode, printError=printError )
# Support for dumping network
@@ -349,15 +227,6 @@ def dumpNetConnections( net ):
nodes = net.controllers + net.switches + net.hosts
dumpNodeConnections( nodes )
def dumpPorts( switches ):
"dump interface to openflow port mappings for each switch"
for switch in switches:
output( '%s ' % switch.name )
for intf in switch.intfList():
port = switch.ports[ intf ]
output( '%s:%d ' % ( intf, port ) )
output( '\n' )
# IP and Mac address formatting and parsing
def _colonHex( val, bytecount ):
@@ -400,7 +269,7 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
ipBaseNum: option base IP address as int
returns IP address as string"""
imax = 0xffffffff >> prefixLen
assert i <= imax, 'Not enough IP addresses in the subnet'
assert i <= imax
mask = 0xffffffff ^ imax
ipnum = ( ipBaseNum & mask ) + i
return ipStr( ipnum )
@@ -408,8 +277,6 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
def ipParse( ip ):
"Parse an IP address and return an unsigned int."
args = [ int( arg ) for arg in ip.split( '.' ) ]
while len(args) < 4:
args.insert( len(args) - 1, 0 )
return ipNum( *args )
def netParse( ipstr ):
@@ -419,10 +286,6 @@ def netParse( ipstr ):
if '/' in ipstr:
ip, pf = ipstr.split( '/' )
prefixLen = int( pf )
# if no prefix is specified, set the prefix to 24
else:
ip = ipstr
prefixLen = 24
return ipParse( ip ), prefixLen
def checkInt( s ):
@@ -462,34 +325,30 @@ def pmonitor(popens, timeoutms=500, readline=True,
terminates: when all EOFs received"""
poller = poll()
fdToHost = {}
fdToDecoder = {}
for host, popen in popens.items():
for host, popen in popens.iteritems():
fd = popen.stdout.fileno()
fdToHost[ fd ] = host
fdToDecoder[ fd ] = getincrementaldecoder()
poller.register( fd, POLLIN )
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
# pylint: disable=too-many-nested-blocks
if not readline:
# Use non-blocking reads
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
while popens:
fds = poller.poll( timeoutms )
if fds:
for fd, event in fds:
host = fdToHost[ fd ]
decoder = fdToDecoder[ fd ]
popen = popens[ host ]
if event & ( POLLIN | POLLHUP ):
while True:
try:
f = popen.stdout
line = decoder.decode( f.readline() if readline
else f.read( readmax ) )
except IOError:
line = ''
if line == '':
break
yield host, line
if event & POLLHUP:
if event & POLLIN:
if readline:
# Attempt to read a line of output
# This blocks until we receive a newline!
line = popen.stdout.readline()
else:
line = popen.stdout.read( readmax )
yield host, line
# Check for EOF
elif event & POLLHUP:
poller.unregister( fd )
del popens[ host ]
else:
@@ -498,19 +357,19 @@ def pmonitor(popens, timeoutms=500, readline=True,
# Other stuff we use
def sysctlTestAndSet( name, limit ):
"Helper function to set sysctl limits"
# convert non-directory names into directory names
#convert non-directory names into directory names
if '/' not in name:
name = '/proc/sys/' + name.replace( '.', '/' )
# read limit
#read limit
with open( name, 'r' ) as readFile:
oldLimit = readFile.readline()
if isinstance( limit, int ):
# compare integer limits before overriding
if type( limit ) is int:
#compare integer limits before overriding
if int( oldLimit ) < limit:
with open( name, 'w' ) as writeFile:
writeFile.write( "%d" % limit )
else:
# overwrite non-integer limits
#overwrite non-integer limits
with open( name, 'w' ) as writeFile:
writeFile.write( limit )
@@ -527,54 +386,44 @@ def fixLimits():
try:
rlimitTestAndSet( RLIMIT_NPROC, 8192 )
rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
# Increase open file limit
#Increase open file limit
sysctlTestAndSet( 'fs.file-max', 10000 )
# Increase network buffer space
#Increase network buffer space
sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
# Increase arp cache size
#Increase arp cache size
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
# Increase routing table size
#Increase routing table size
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
# Increase number of PTYs for nodes
#Increase number of PTYs for nodes
sysctlTestAndSet( 'kernel.pty.max', 20000 )
# pylint: disable=broad-except
except Exception:
except:
warn( "*** Error setting resource limits. "
"Mininet's performance may be affected.\n" )
# pylint: enable=broad-except
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 mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'cat /proc/mounts' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if ('cgroup %s' % cgdir not in mounts and
'cgroups %s' % cgdir not in mounts):
raise Exception( "cgroups not mounted on " + cgdir )
if 'cpuset %s' % csdir not in mounts:
errRun( 'mkdir -p ' + csdir )
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
def natural( text ):
"To sort sanely/alphabetically: sorted( l, key=natural )"
def num( s ):
"Convert text segment to int if necessary"
return int( s ) if s.isdigit() else s
return [ num( s ) for s in re.split( r'(\d+)', str( text ) ) ]
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
def naturalSeq( t ):
"Natural sort key function for sequences"
@@ -623,54 +472,31 @@ def splitArgs( argstr ):
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
def customClass( classes, argStr ):
"""Return customized class based on argStr
The args and key/val pairs in argStr will be automatically applied
when the generated class is later used.
def customConstructor( constructors, argStr ):
"""Return custom constructor based on argStr
The args and key/val pairs in argsStr will be automatically applied
when the generated constructor is later used.
"""
cname, args, kwargs = splitArgs( argStr )
cls = classes.get( cname, None )
if not cls:
cname, newargs, kwargs = splitArgs( argStr )
constructor = constructors.get( cname, None )
if not constructor:
raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, classes.keys() ) )
if not args and not kwargs:
return cls
( cname, constructors.keys() ) )
return specialClass( cls, append=args, defaults=kwargs )
def specialClass( cls, prepend=None, append=None,
defaults=None, override=None ):
"""Like functools.partial, but it returns a class
prepend: arguments to prepend to argument list
append: arguments to append to argument list
defaults: default values for keyword arguments
override: keyword arguments to override"""
if prepend is None:
prepend = []
if append is None:
append = []
if defaults is None:
defaults = {}
if override is None:
override = {}
class CustomClass( cls ):
"Customized subclass with preset args/params"
def __init__( self, *args, **params ):
newparams = defaults.copy()
newparams.update( params )
newparams.update( override )
cls.__init__( self, *( list( prepend ) + list( args ) +
list( append ) ),
**newparams )
CustomClass.__name__ = '%s%s' % ( cls.__name__, defaults )
return CustomClass
def customized( name, *args, **params ):
"Customized constructor, useful for Node, Link, and other classes"
params = params.copy()
params.update( kwargs )
if not newargs:
return constructor( name, *args, **params )
if args:
warn( 'warning: %s replacing %s with %s\n' % (
constructor, args, newargs ) )
return constructor( name, *newargs, **params )
customized.__name__ = 'customConstructor(%s)' % argStr
return customized
def buildTopo( topos, topoStr ):
"""Create topology from string with format (object, arg1, arg2,...).
@@ -687,55 +513,6 @@ def ensureRoot():
Probably we should only sudo when needed as per Big Switch's patch.
"""
if os.getuid() != 0:
error( '*** Mininet must run as root.\n' )
print "*** Mininet must run as root."
exit( 1 )
def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
"""Wait until server is listening on port.
returns True if server is listening"""
runCmd = ( client.cmd if client else
partial( quietRun, shell=True ) )
if not runCmd( 'which telnet' ):
raise Exception('Could not find telnet' )
# pylint: disable=maybe-no-member
serverIP = server if isinstance( server, BaseString ) else server.IP()
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
time = 0
result = runCmd( cmd )
while 'Connected' not in result:
if 'No route' in result:
rtable = runCmd( 'route' )
error( 'no route to %s:\n%s' % ( server, rtable ) )
return False
if timeout and time >= timeout:
error( 'could not connect to %s on port %d\n' % ( server, port ) )
return False
debug( 'waiting for', server, 'to listen on port', port, '\n' )
info( '.' )
sleep( .5 )
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)
return
+12 -57
View File
@@ -5,7 +5,7 @@
*
* - closing all file descriptors except stdin/out/error
* - detaching from a controlling tty using setsid
* - running in network and mount namespaces
* - running in a network namespace
* - printing out the pid of a process so we can identify it later
* - attaching to a namespace and cgroup
* - setting RT scheduling
@@ -23,25 +23,21 @@
#include <stdlib.h>
#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)"
#endif
void usage(char *name)
void usage(char *name)
{
printf("Execution utility for Mininet\n\n"
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Options:\n"
" -c: close all file descriptors except stdin/out/error\n"
" -d: detach from tty by calling setsid()\n"
" -n: run in new network and mount namespaces\n"
" -n: run in new network namespace\n"
" -p: print ^A + pid\n"
" -a pid: attach to pid's network and mount namespaces\n"
" -a pid: attach to pid's network namespace\n"
" -g group: add to cgroup\n"
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
" -v: print version\n",
@@ -99,26 +95,16 @@ 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 */
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);
for (fd = getdtablesize(); fd > 2; fd--)
close(fd);
break;
case 'd':
/* detach from tty */
@@ -136,26 +122,11 @@ int main(int argc, char *argv[])
setsid();
break;
case 'n':
/* run in network and mount namespaces */
if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
/* run in network namespace */
if (unshare(CLONE_NEWNET) == -1) {
perror("unshare");
return 1;
}
/* Mark our whole hierarchy recursively as private, so that our
* mounts do not propagate to other processes.
*/
if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1) {
perror("remount");
return 1;
}
/* mount sysfs to pick up the new network namespace */
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
perror("mount");
return 1;
}
break;
case 'p':
/* print pid */
@@ -163,9 +134,9 @@ int main(int argc, char *argv[])
fflush(stdout);
break;
case 'a':
/* Attach to pid's network namespace and mount namespace */
/* Attach to pid's network namespace */
pid = atoi(optarg);
sprintf(path, "/proc/%d/ns/net", pid);
sprintf(path, "/proc/%d/ns/net", pid );
nsid = open(path, O_RDONLY);
if (nsid < 0) {
perror(path);
@@ -175,22 +146,6 @@ int main(int argc, char *argv[])
perror("setns");
return 1;
}
/* Plan A: call setns() to attach to mount namespace */
sprintf(path, "/proc/%d/ns/mnt", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0 || setns(nsid, 0) != 0) {
/* Plan B: chroot/chdir into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
return 1;
}
}
/* chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
}
break;
case 'g':
/* Attach to cgroup */
@@ -212,7 +167,7 @@ int main(int argc, char *argv[])
exit(0);
default:
usage(argv[0]);
exit(1);
exit(1);
}
if (optind < argc) {
@@ -220,7 +175,7 @@ int main(int argc, char *argv[])
perror(argv[optind]);
return 1;
}
usage(argv[0]);
return 0;
+1 -1
View File
@@ -2,7 +2,7 @@
"Setuptools params"
from setuptools import setup
from setuptools import setup, find_packages
from os.path import join
# Get version number from source tree
-182
View File
@@ -1,182 +0,0 @@
#!/usr/bin/env bash
# Mininet ssh authentication script for cluster edition
# This script will create a single key pair, which is then
# propagated throughout the entire cluster.
# There are two options for setup; temporary setup
# persistent setup. If no options are specified, and the script
# is only given ip addresses or host names, it will default to
# the temporary setup. An ssh directory is then created in
# /tmp/mn/ssh on each node, and mounted with the keys over the
# user's ssh directory. This setup can easily be torn down by running
# clustersetup with the -c option.
# If the -p option is used, the setup will be persistent. In this
# case, the key pair will be be distributed directly to each node's
# ssh directory, but will be called cluster_key. An option to
# specify this key for use will be added to the config file in each
# user's ssh directory.
set -e
num_options=0
persistent=false
showHelp=false
clean=false
declare -a hosts=()
user=$(whoami)
SSHDIR=/tmp/mn/ssh
USERDIR=$HOME/.ssh
usage="./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
Authenticate yourself and other cluster nodes to each other
via ssh for mininet cluster edition. By default, we use a
temporary ssh setup. An ssh directory is mounted over
$USERDIR on each machine in the cluster.
-h: display this help
-p: create a persistent ssh setup. This will add
new ssh keys and known_hosts to each nodes
$USERDIR directory
-c: method to clean up a temporary ssh setup.
Any hosts taken as arguments will be cleaned
"
persistentSetup() {
echo "***creating key pair"
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f $USERDIR/cluster_key -N '' &> /dev/null
cat $USERDIR/cluster_key.pub >> $USERDIR/authorized_keys
echo "***configuring ssh"
echo "IdentityFile $USERDIR/cluster_key" >> $USERDIR/config
echo "IdentityFile $USERDIR/id_rsa" >> $USERDIR/config
for host in $hosts; do
echo "***copying public key to $host"
ssh-copy-id -i $USERDIR/cluster_key.pub $user@$host &> /dev/null
echo "***copying key pair to remote host"
scp $USERDIR/cluster_key $user@$host:$USERDIR
scp $USERDIR/cluster_key.pub $user@$host:$USERDIR
echo "***configuring remote host"
ssh -o ForwardAgent=yes $user@$host "
echo 'IdentityFile $USERDIR/cluster_key' >> $USERDIR/config
echo 'IdentityFile $USERDIR/id_rsa' >> $USERDIR/config"
done
for host in $hosts; do
echo "***copying known_hosts to $host"
scp $USERDIR/known_hosts $user@$host:$USERDIR/cluster_known_hosts
ssh $user@$host "
cat $USERDIR/cluster_known_hosts >> $USERDIR/known_hosts
rm $USERDIR/cluster_known_hosts"
done
}
tempSetup() {
echo "***creating temporary ssh directory"
mkdir -p $SSHDIR
echo "***creating key pair"
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f $SSHDIR/id_rsa -N '' &> /dev/null
echo "***mounting temporary ssh directory"
sudo mount --bind $SSHDIR $USERDIR
cp $SSHDIR/id_rsa.pub $SSHDIR/authorized_keys
for host in $hosts; do
echo "***copying public key to $host"
ssh-copy-id $user@$host &> /dev/null
echo "***mounting remote temporary ssh directory for $host"
ssh -o ForwardAgent=yes $user@$host "
mkdir -p $SSHDIR
cp $USERDIR/authorized_keys $SSHDIR/authorized_keys
sudo mount --bind $SSHDIR $USERDIR"
echo "***copying key pair to $host"
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying known_hosts to $host"
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
done
}
cleanup() {
for host in $hosts; do
echo "***cleaning up $host"
ssh $user@$host "sudo umount $USERDIR
sudo rm -rf $SSHDIR"
done
echo "**unmounting local directories"
sudo umount $USERDIR
echo "***removing temporary ssh directory"
sudo rm -rf $SSHDIR
echo "done!"
}
if [ $# -eq 0 ]; then
echo "ERROR: No Arguments"
echo "$usage"
exit
else
while getopts 'hpc' OPTION
do
((num_options+=1))
case $OPTION in
h) showHelp=true;;
p) persistent=true;;
c) clean=true;;
?) showHelp=true;;
esac
done
shift $(($OPTIND - 1))
fi
if [ "$num_options" -gt 1 ]; then
echo "ERROR: Too Many Options"
echo "$usage"
exit
fi
if $showHelp; then
echo "$usage"
exit
fi
for i in "$@"; do
output=$(getent ahostsv4 "$i")
if [ -z "$output" ]; then
echo '***WARNING: could not find hostname "$i"'
echo ""
else
hosts+="$i "
fi
done
if $clean; then
cleanup
exit
fi
echo "***authenticating to:"
for host in $hosts; do
echo "$host"
done
echo
if $persistent; then
echo '***Setting up persistent SSH configuration between all nodes'
persistentSetup
echo $'\n*** Successfully set up ssh throughout the cluster!'
else
echo '*** Setting up temporary SSH configuration between all nodes'
tempSetup
echo $'\n***Finished temporary setup. When you are done with your cluster'
echo $' session, tear down the SSH connections with'
echo $' ./clustersetup.sh -c '$hosts''
fi
echo

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