Compare commits

..

5 Commits

Author SHA1 Message Date
Brandon Heller 6ca49a78de OVS Install on 10.04 2012-11-01 13:32:13 -07:00
Brandon Heller c703bafd9c Don't try to look for cgroups in mount on 10.04 - oddly not there. 2012-11-01 13:32:00 -07:00
Brandon Heller a2f659de57 Work around issues with cgroup-bin processes for 10.04 2012-11-01 13:31:37 -07:00
Brandon Heller 4a08ccdf5a Demote fast-link requests to warning 2012-11-01 13:29:40 -07:00
Brandon Heller 6525cd90bd Add /mng/cgroups killer, specific to 10.04 2012-11-01 13:29:02 -07:00
114 changed files with 1828 additions and 11995 deletions
-1
View File
@@ -1 +0,0 @@
*.py diff=python
+2 -8
View File
@@ -1,14 +1,8 @@
mnexec
*.pyc
*~
*.1
*.xcodeproj
*.xcworkspace
\#*\#
mininet.egg-info
build
dist
doc/html
doc/latex
trunk
build/*
dist/*
+5 -9
View File
@@ -41,19 +41,16 @@ load-plugins=
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
locally-disabled
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
[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 message's id in outpu
include-ids=yes
# Put messages in a separate file for each module / package specified on the
@@ -194,7 +191,7 @@ additional-builtins=
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:
@@ -267,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).
-50
View File
@@ -1,50 +0,0 @@
Mininet Contributors
Mininet is an open source project and we gratefully acknowledge
the many contributions to the project! If you have contributed
code into the project and are not on this list, please let us know
or send a pull request.
Contributors include:
Mininet Core Team (and alumni)
Bob Lantz
Brandon Heller
Nikhil Handigol
Vimal Jeyakumar
Brian O'Connor
Cody Burkard
Additional Mininet Contributors
Tomasz Buchert
Gustavo Pantuza Coelho Pinto
Fernando Cappi
Ryan Cox
Shaun Crampton
David Erickson
Glen Gibb
Andrew Ferguson
Eder Leao Fernandes
Gregory Gee
Jon Hall
Vitaly Ivanov
Rich Lane
Zi Shen Lim
Murphy McCauley
José Pedro Oliveira
James Page
Rich Lane
Rémy Léone
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Andreas Wundsam
Isaku Yamahata
Baohua Yang
Thanks also to everyone who has submitted issues and pull
requests on github, and to our friendly mininet-discuss
mailing list!
+212 -118
View File
@@ -1,179 +1,273 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.2.0
Mininet 1.0.0
---
The supported installation methods for Mininet are 1) using a
pre-built VM image, and 2) native installation on Ubuntu. You can also
easily create your own Mininet VM image (4).
The supported installation methods for Mininet are 1) using
a pre-built VM image, and 2) native installation on Ubuntu or Debian.
(Other distributions may be supported in the future - if you would
like to contribute an installation script, we would welcome it!)
1. Easiest "installation" - use our pre-built VM image!
1. Easiest "install" - use our pre-built VM image!
The easiest way to get Mininet running is to start with one of our
pre-built virtual machine images from <http://mininet.org/>
The easiest way to get Mininet running is to start with one of our pre-built
virtual machine images from http://openflow.org/mininet
Boot up the VM image, log in, and follow the instructions on the
Mininet web site.
Boot up the VM image, log in, and follow the instructions on the wiki page.
One advantage of using the VM image is that it doesn't mess with
your native OS installation or damage it in any way.
An additional advantage of using the VM image is that it doesn't mess with
your native OS installation or damage it in any way.
Although a single Mininet instance can simulate multiple networks
with multiple controllers, only one Mininet instance may currently
be run at a time, and Mininet requires root access in the machine
it's running on. Therefore, if you have a multiuser system, you
may wish to consider running Mininet in a VM.
2. Native installation (experimental!) for Ubuntu 10.04 LTS
2. Next-easiest option: use our Ubuntu package!
If you are running Ubuntu 10.04 LTS (or possibly Debian 5), you may be
able to use our handy install.sh script, which is in mininet/util.
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
12.10+:
WARNING: USE AT YOUR OWN RISK!
sudo apt-get install mininet
install.sh is a bit intrusive and may possibly damage your OS and/or
home directory, by creating/modifying several directories such as
mininet, openflow, openvswitch and noxcore. Although we hope it won't
do anything completely terrible, you may want to look at the script
before you run it, and you should make sure your system and home
directory are backed up just in case!
Note: if you are upgrading from an older version of Mininet, make
sure you remove the old OVS from `/usr/local`:
To install ALL of the software which we use for OpenFlow tutorials,
you may use
sudo rm /usr/local/bin/ovs*
sudo rm /usr/local/sbin/ovs*
$ mininet/util/install.sh
3. Native installation from source
This takes about 20-30 minutes.
3.1. Native installation from source on Ubuntu 12.04+
Alternately, you can install just the pieces you need.
If you're reading this, you've probably already done so, but the
command to download the Mininet source code is:
We recommend the following steps, in order:
git clone git://github.com/mininet/mininet.git
[a) On Debian 5, first install a Mininet-compatible kernel:
$ mininet/util/install.sh -k
Reboot and run 'uname -r' to make sure you're running the new kernel.]
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
b) Install mininet and its dependencies:
$ mininet/util/install.sh -n
c) Install OpenFlow 1.0 and associated useful software
$ mininet/util/install.sh -f
cd mininet
git tag
d) Install Open vSwitch and its kernel module
$ mininet/util/install.sh -vm
and then
e) If you wish to install the version of NOX we use in the tutorial:
$ mininet/util/install.sh -x
git checkout <release tag>
Note: NOX development is progressing over time, so after you complete
the tutorial you may wish to install the latest and greatest NOX from
noxrepo.org.
where <release tag> is the release you want to check out.
Good luck! Some additional installation notes are provided below, for
the brave and/or Linux-savvy, or those who are trying to understand what
is installed and why.
If you are running Ubuntu, Debian, or Fedora, you may be able to use
our handy `install.sh` script, which is in `mininet/util`.
p.s. Note that only one instance of Mininet is currently supported on a single
machine - that's one reason we recommend using a VM to run it.
*WARNING: USE AT YOUR OWN RISK!*
---
`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.
Mininet Manual Installation Notes
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!
These installation notes assume you understand how to do things like
compile kernels, apply patches, configure networks, write code, etc.. If
this is unfamiliar territory, or if you run into trouble, we recommend
using one of our pre-built virtual machine images (see above.)
To install Mininet itself, the OpenFlow reference implementation, and
Open vSwitch, you may use:
If you wish to try to create a VM to run Mininet, you may also wish
to look at the Wiki page:
mininet/util/install.sh -fnv
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
This should be reasonably quick, and the following command should
work after the installation:
0. Obtaining Mininet
sudo mn --test pingall
If you're reading this, you've already done it, but the command to
download mininet is:
git clone git://openflow.org/mininet.git
1. Core Mininet installation
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:
The core Mininet installation requires gcc, make, python,
and setuptools. On Ubuntu and Debian you may install them with:
# aptitude install gcc make python setuptools
mininet/util/install.sh -a
To install Mininet itself, with root privileges:
# cd mininet
# make install
This takes about 4 minutes on our test system.
This places the mininet package in /usr/lib/python-*/site-packages/,
so that 'import mininet' will work, and installs the primary mn
script (mn) as well as its helper utility (mnexec.)
On Ubuntu and Debian, Mininet's dependencies and core files may also be
installed using mininet/util/install.sh -n
You can change the directory where the dependencies are installed using
the -s <directory> flag.
2. Installation script for Ubuntu/Debian Lenny
mininet/util/install.sh -s <directory> -a
If you are running Ubuntu 10.04 or Debian Lenny, you may be able to use the
util/install.sh script to install a compatible Linux kernel as well as
other software including the OpenFlow reference implementation, the Open
vSwitch switch implementation, and the NOX OpenFlow controller.
3.2. Native installation from source on Fedora 18+.
Many different installation options are possible by passing different
options to install.sh; install.sh -h lists them all.
Assuming the mininet source tree is installed in ~/mininet, the steps to run
install.sh to install EVERYTHING we use for OpenFlow tutorials are:
% cd
% time ~/mininet/util/install.sh # installs tons of stuff
% sudo reboot # to load new kernel
% ~/mininet/util/install.sh -c # to clean out unneeded kernel stuff
As root execute the following operations:
This installs a lot of useful software, but it will take a while (30
minutes or more, depending on your network connection, computer, etc..)
Probably the minimal semi-useful configuration would be to install
Mininet itself, kernel support if necessary, and either the
reference OpenFlow switch or Open vSwitch. This could be installed
as follows:
% sudo ~/mininet/util/install.sh -knvm
Respectively, this installs kernel support, core mininet dependencies,
Open vSwitch, and the Open vSwitch kernel module. If a new kernel was
installed, then a reboot may be required.
* install git
If install.sh cannot be used for some reason (e.g. you're on Fedora
or some other Linux - please don't say CentOS) or if you don't want to
install all of these components (they're useful!), the kernel and
OpenFlow software requirements are described in steps [3] and [4],
which follow.
yum install git
If you successfully used install.sh, congratulations! You're basically
done. Proceed to step [6] for additional advice.
3. Linux Kernel requirements
* create an user account (e.g. mininet) and add it to the wheel group
Mininet requires a kernel built with network namespace support enabled,
i.e. with CONFIG_NET_NS=Y, such as the kernel shipped with
Ubuntu 10.04 LTS, currently 2.6.32. On Ubuntu 10.04, you should not need
to install or build a custom kernel, although 2.6.33+ is faster at
tearing down virtual ethernet pairs.
useradd [...] mininet
usermod -a -G wheel mininet
For Ubuntu and Debian, we provide a 2.6.33 kernel package which you may be
able to install using "util/install.sh -k". Note our kernel package
requires an ext2 or ext3 root file system, so it won't work if you have
a default Ubuntu install, which uses ext4.
If your kernel wasn't compiled with CONFIG_NET_NS=Y, you will need to
build and install a kernel that does! >= 2.6.33 works better, but may
be harder to get working, depending on your Linux distribution.
A script for building Debian packages for 2.6.33.1 is provided in
mininet/util/kbuild. You may wish to read it, as it applies patches
to enable 2.6.33.1 to build under debian-stable, and to enable the
tun driver to work correctly with Mininet.
Earlier kernels (e.g. 2.6.29) work with CONFIG_NET_NS enabled and no
additional patches, but are much slower at removing veth interfaces,
resulting in much slower switch shutdown.
* change the SElinux setting to permissive. It can be done
temporarily with:
For scalable configurations, you might need to increase some of your
kernel limits. Sample params are in util/sysctl_addon, which can be
appended to /etc/sysctl.conf (and modified as necessary for your
desired configuration):
setenforce 0
sudo su -c "cat sysctl_addon >> /etc/sysctl.conf"
then login with the new account (e.g. mininet) and do the following:
To save the config change, run:
* clone the Mininet repository
sudo sysctl -p
4. OpenFlow software and configuration requirements
git clone git://github.com/mininet/mininet.git
Mininet requires either the reference OpenFlow switch implementation
(from openflowswitch.org) or Open vSwitch (openvswitch.org) to be
installed. "make test" requires the reference user space
implementations as well as Open vSwitch. Note the reference kernel
implementation is not currently included in OpenFlow 1.0.
On Ubuntu and Debian, the install.sh script may be used with the '-f'
option to install the OpenFlow reference implementation, the '-v' option
to build Open vSwitch, and the '-m' option to install the Open vSwitch
kernel module into /lib/modules (note: you must build Open vSwitch first!)
Mininet will automatically load and remove kernel module dependencies
for supported switch types, using modprobe and rmmod - but these
modules must be in a location where modprobe can find them (e.g.
something like /lib/modules/`uname -r`/kernel/drivers/net/)
* install Mininet, the OpenFlow reference implementation, and
Open vSwitch
The reference OpenFlow controller (controller(8)) only supports 16
switches by default! If you wish to run a network with more than 16
switches, please recompile controller(8) with larger limits, or use a
different controller such as nox. A patch to controller(8) is included
as util/openflow-patches/controller.patch.
5. Other software dependencies
mininet/util/install.sh -fnv
On Ubuntu and Debian, other Mininet dependencies may be installed using
the '-n' option of the install.sh script.
* enable and start openvswitch
To run the iperf test, you need to install iperf:
sudo systemctl enable openvswitch
sudo systemctl start openvswitch
sudo aptitude/yum install iperf
* test the mininet installation
We assume you already have ping installed. ;-)
To use xterm or sshd with Mininet, you need the following:
sudo mn --test pingall
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:
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
time install-mininet-vm.sh
Finally, verify that Mininet is installed and working in the VM:
sudo mn --test pingall
5. Installation on other Linux distributions
Although we don't support other Linux distributions directly, it
should be possible to install and run Mininet with some degree of
manual effort.
In general, you must have:
* A Linux kernel compiled with network namespace support enabled
* An compatible software switch such as Open vSwitch or
the Linux bridge.
* Python, `bash`, `ping`, `iperf`, etc.
* Root privileges (required for network device access)
We encourage contribution of patches to the `install.sh` script to
support other Linux distributions.
sudo aptitude/yum install sshd xterm screen
Some examples may have additional requirements - consult the specific
example file for details.
The install.sh script has an '-x' option to install the version of
NOX from the OpenFlow tutorial.
6. Other notes and recommendations
If you did not install certain useful packages and you wish to later,
it may be possible to install them using install.sh.
Mininet should be run either on a machine with
no other important processes, or on a virtual machine (recommended!)
Multiple concurrent Mininet instances are not supported!
Good luck!
Mininet Team
---
Historical information on OpenFlow 0.8.9 and the reference kernel module:
The kernel reference implementation has been deprecated, but it may
be possible to get it work with Mininet.
To switch to the most recent OpenFlow 0.8.9 release branch (the most
recent one with full NOX support and kernel datapath support) in your
OpenFlow git tree:
git checkout -b release/0.8.9 remotes/origin/release/0.8.9
A patch to enable datapath.c to compile with recent kernels
is included in util/openflow-patches/datapath.patch.
In OpenFlow 1.0, switch port numbering starts at 1 (for better or for worse.)
To run with previous versions of OpenFlow, it may be necessary
to change SWITCH_PORT_BASE from 1 to 0 in node.py.
+2 -6
View File
@@ -1,10 +1,6 @@
Mininet 2.2.0 License
Mininet 1.0.0 License
Copyright (c) 2013-2014 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
Original authors: Bob Lantz and Brandon Heller
Copyright (c) 2009-2011 Bob Lantz and Brandon Heller
We are making Mininet available for public use and benefit with the
expectation that others will use, modify and enhance the Software and
+11 -45
View File
@@ -1,31 +1,21 @@
MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
BIN = $(MN)
EXAMPLES = examples/*.py
BIN = bin/mn
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
DOCDIRS = doc/html doc/latex
PDF = doc/latex/refman.pdf
CFLAGS += -Wall -Wextra
P8IGN = E251,E201,E302,E202
all: codecheck test
clean:
rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS)
rm -rf build dist *.egg-info *.pyc $(MNEXEC)
codecheck: $(PYSRC)
-echo "Running code check"
util/versioncheck.py
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"
@@ -35,39 +25,15 @@ errcheck: $(PYSRC)
test: $(MININET) $(TEST)
-echo "Running tests"
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=. $(MN) --version`\" $< -o $@
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
install: $(MNEXEC)
install $(MNEXEC) /usr/local/bin/
python setup.py install
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
develop: $(MNEXEC)
install $(MNEXEC) /usr/local/bin/
python setup.py develop
man: $(MANPAGES)
doc:
doxygen doxygen.cfg
mn.1: $(MN)
PYTHONPATH=. help2man -N -n "create a Mininet network." \
--no-discard-stderr $< -o $@
mnexec.1: mnexec
help2man -N -n "execution utility for Mininet." \
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
.PHONY: doc
doc: man
doxygen doc/doxygen.cfg
make -C doc/latex
+86
View File
@@ -0,0 +1,86 @@
Mininet: A Simple Virtual Testbed for OpenFlow/SDN
or
How to Squeeze a 1024-node OpenFlow Network onto your Laptop
Mininet 1.0.0
---
Welcome to Mininet!
Mininet creates OpenFlow test networks by using process-based
virtualization and network namespaces.
Simulated hosts (as well as switches and controllers with the user
datapath) are created as processes in separate network namespaces. This
allows a complete OpenFlow network to be simulated on top of a single
Linux kernel.
Mininet may be invoked directly from the command line, and also provides a
handy Python API for creating networks of varying sizes and topologies.
Mininet is currently in *limited alpha release*. We encourage you to
experiment with it and hope that you will provide us with feedback on
features, documentation, and how you're using it. We plan to make it
available publicly via a GPL or BSD license (probably in April), but please
don't distribute the code or URLs yet! The feedback you provide will help
us improve Mininet for general release.
In order to run Mininet, you must have:
* A Linux 2.6.26 or greater kernel compiled with network namespace support
enabled (see INSTALL for additional information.)
* An OpenFlow implementation (either the reference user or kernel
space implementations, or Open vSwitch.) Appropriate kernel modules
(e.g. tun and ofdatapath for the reference kernel implementation) must
be loaded.
* Python, bash, ping, iperf, etc.
* Root privileges (required for network device access)
Currently Mininet includes:
- A simple node infrastructure (Host, Switch, Controller classes) for
creating virtual OpenFlow networks
- A simple network infrastructure (Mininet class) supporting parametrized
topologies (Topo subclasses.) For example, a tree network may be created
with the command
# mn --topo tree,depth=2,fanout=3
- Basic tests, including connectivity (ping) and bandwidth (iperf)
- A command-line interface (CLI class) which provides useful
diagnostic commands, as well as the ability to send a command to a
node. For example,
mininet> h11 ifconfig -a
tells host h11 to run the command 'ifconfig -a'
- A 'cleanup' command to get rid of junk (interfaces, processes, files in
/tmp, etc.) which might be left around by Mininet or Linux. Try this if
things stop working!
# mn -c
- Examples (in the examples/ directory) to help you get started.
Batteries are not included (yet!)
However, some preliminary installation notes are included in the INSTALL
file.
Additionally, much useful information, including a Mininet tutorial,
is available on the Mininet wiki:
http://openflow.org/mininet
Enjoy, and good luck!
---
Bob Lantz
rlantz@cs.stanford.edu
-153
View File
@@ -1,153 +0,0 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.2.0
### What is Mininet?
Mininet emulates a complete network of hosts, links, and switches
on a single machine. To create a sample two-host, one-switch network,
just run:
`sudo mn`
Mininet is useful for interactive development, testing, and demos,
especially those using OpenFlow and SDN. OpenFlow-based network
controllers prototyped in Mininet can usually be transferred to
hardware with minimal changes for full line-rate execution.
### How does it work?
Mininet creates virtual networks using process-based virtualization
and network namespaces - features that are available in recent Linux
kernels. In Mininet, hosts are emulated as `bash` processes running in
a network namespace, so any code that would normally run on a Linux
server (like a web server or client program) should run just fine
within a Mininet "Host". The Mininet "Host" will have its own private
network interface and can only see its own processes. Switches in
Mininet are software-based switches like Open vSwitch or the OpenFlow
reference switch. Links are virtual ethernet pairs, which live in the
Linux kernel and connect our emulated switches to emulated hosts
(processes).
### Features
Mininet includes:
* A command-line launcher (`mn`) to instantiate networks.
* A handy Python API for creating networks of varying sizes and
topologies.
* Examples (in the `examples/` directory) to help you get started.
* Full API documentation via Python `help()` docstrings, as well as
the ability to generate PDF/HTML documentation with `make doc`.
* Parametrized topologies (`Topo` subclasses) using the Mininet
object. For example, a tree network may be created with the
command:
`mn --topo tree,depth=2,fanout=3`
* A command-line interface (`CLI` class) which provides useful
diagnostic commands (like `iperf` and `ping`), as well as the
ability to run a command to a node. For example,
`mininet> h11 ifconfig -a`
tells host h11 to run the command `ifconfig -a`
* A "cleanup" command to get rid of junk (interfaces, processes, files
in /tmp, etc.) which might be left around by Mininet or Linux. Try
this if things stop working!
`mn -c`
### New features in this release
This release provides a number of bug fixes as well as
several new features, including:
* Improved OpenFlow 1.3 support
- `mn --switch ovs,protocols=openflow13` starts OVS in 1.3 mode
- `install.sh -w` installs a 1.3-compatible Wireshark dissector using
Loxigen
- `install.sh -y` installs the Ryu 1.3-compatible controller
* A new `nodelib.py` node library, and new `Node` types including
`LinuxBridge`, `OVSBridge`, `LinuxRouter` (see `examples/`)
and `NAT`
* A `--nat` option which connects a Mininet network to your LAN using NAT
(For this to work correctly, Mininet's `--ipbase` subnet should not
overlap with any external or internet IP addresses you wish to use)
* An improved MiniEdit GUI (`examples/miniedit.py`) - thanks to
Gregory Gee
* Support for multiple `--custom` arguments to `mn`
* Experimental cluster support - consult the
[documentation](http://docs.mininet.org) for details -
as well as `examples/cluster.py` and an experimental `--cluster`
option for topologies built with the default `Host` and `OVSSwitch`
classes:
`mn --cluster localhost,server1,server2`
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!
A number of bugs have also been fixed, most notably multiple link
support in `Topo()`. See github issues and the release notes on
the Mininet wiki for additional information.
### Installation
See `INSTALL` for installation instructions and details.
### Documentation
In addition to the API documentation (`make doc`), much useful
information, including a Mininet walkthrough and an introduction
to the Python API, is available on the
[Mininet Web Site](http://mininet.org).
There is also a wiki which you are encouraged to read and to
contribute to, particularly the Frequently Asked Questions (FAQ.)
### Support
Mininet is community-supported. We encourage you to join the
Mininet mailing list, `mininet-discuss` at:
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
### Join Us
Mininet is an open source project and is currently hosted
at <https://github.com/mininet>. You are encouraged to download
the code, examine it, modify it, and submit bug reports, bug fixes,
feature requests, new features and other issues and pull requests.
Thanks to everyone who has contributed code to the Mininet project
(see CONTRIBUTORS for more info!) It is because of everyone's
hard work that Mininet continues to grow and improve.
### Enjoy Mininet
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
The Mininet Core Team:
* Bob Lantz
* Brian O'Connor
* Cody Burkard
Thanks again to all of the Mininet contributors, particularly Gregory
Gee for his work on MiniEdit.
+65 -208
View File
@@ -12,72 +12,47 @@ Example to pull custom params (topo, switch, etc.) from a file:
"""
from optparse import OptionParser
import os
import os.path
import sys
import time
# Fix setuptools' evil madness, and open up (more?) security holes
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
from mininet.clean import cleanup
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, warn, error
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.log import lg, LEVELS, info, warn
from mininet.net import Mininet, MininetWithControlNet
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
RYU, NOX, RemoteController, findController,
DefaultController,
UserSwitch, OVSSwitch, OVSBridge,
OVSLegacyKernelSwitch, IVSSwitch )
from mininet.nodelib import LinuxBridge
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
OVSLegacyKernelSwitch )
from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import customConstructor, splitArgs
from mininet.topolib import TreeTopo
from mininet.util import makeNumeric, custom, customConstructor, splitArgs
from mininet.util import buildTopo
from functools import partial
# Experimental! cluster edition prototype
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
RemoteOVSSwitch, RemoteLink,
SwitchBinPlacer, RandomPlacer )
from mininet.examples.clustercli import ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
# built in topologies, created only when run
TOPODEF = 'minimal'
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo,
'torus': TorusTopo }
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo }
SWITCHDEF = 'default'
SWITCHDEF = 'ovsk'
SWITCHES = { 'user': UserSwitch,
'ovs': OVSSwitch,
'ovsbr' : OVSBridge,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch,
'lxbr': LinuxBridge,
'default': OVSSwitch }
'ovsk': OVSKernelSwitch,
'ovsl': OVSLegacyKernelSwitch }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': partial( CPULimitedHost, sched='rt' ),
'cfs': partial( CPULimitedHost, sched='cfs' ) }
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'default'
CONTROLLERDEF = 'ref'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'ryu': RYU,
'default': DefaultController, # Note: replaced below
'none': lambda name: None }
LINKDEF = 'default'
@@ -87,12 +62,10 @@ LINKS = { 'default': Link,
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
'none' ]
'none' ]
ALTSPELLING = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
@@ -104,20 +77,14 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
help: string"""
if default not in choicesDict:
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
( default, name ) )
if not helpStr:
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
help = helpStr )
def version( *_args ):
"Print Mininet version and exit"
print "%s" % VERSION
exit()
type='string',
default = default,
help = helpStr )
class MininetRunner( object ):
@@ -133,29 +100,6 @@ 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 ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
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' ):
@@ -169,32 +113,28 @@ 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"
"invoke the Mininet CLI, and run tests." )
usage = ( '%prog [options]\n'
'(type %prog -h for details)' )
opts = OptionParser( description=desc, usage=usage )
opts = OptionParser()
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
@@ -202,67 +142,42 @@ class MininetRunner( object ):
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)'
)
default=False, help='clean and exit' )
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 ) )
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
default=False, help='spawn xterms for each node' )
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
help='base IP address for hosts' )
opts.add_option( '--mac', action='store_true',
default=False, help='automatically set host MACs' )
default=False, help='automatically set host MACs' )
opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' )
default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice',
choices=LEVELS.keys(), default = 'info',
help = '|'.join( LEVELS.keys() ) )
choices=LEVELS.keys(), default = 'info',
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6634,
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6635,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening " +
"port")
default=False, help="don't use passive listening port")
opts.add_option( '--pre', type='string', default=None,
help='CLI script to run before tests' )
help='CLI script to run before tests' )
opts.add_option( '--post', type='string', default=None,
help='CLI script to run after tests' )
help='CLI script to run after tests' )
opts.add_option( '--prefixlen', type='int', default=8,
help='prefix length (e.g. /8) for automatic '
'network configuration' )
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="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( '--cluster', type='string', default=None,
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
opts.add_option( '--placement', type='choice',
choices=PLACEMENT.keys(), default='block',
metavar='block|random',
help=( 'node placement for --cluster '
'(experimental!) ' ) )
self.options, self.args = opts.parse_args()
# We don't accept extra arguments after the options
if self.args:
opts.print_help()
exit()
def setup( self ):
"Setup and validate environment."
@@ -274,9 +189,6 @@ class MininetRunner( object ):
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# Maybe we'll reorganize this someday...
# pylint: disable=too-many-branches,too-many-statements
def begin( self ):
"Create and run mininet."
@@ -286,23 +198,6 @@ class MininetRunner( object ):
start = time.time()
if self.options.controller == 'default':
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
if CONTROLLERS[ 'default' ] is None:
if self.options.switch == 'default':
info( '*** No default OpenFlow controller found '
'for default switch!\n' )
info( '*** Falling back to OVS Bridge\n' )
self.options.switch = 'ovsbr'
self.options.controller = 'none'
elif self.options.switch in ( 'ovsbr', 'lxbr' ):
self.options.controller = 'none'
else:
raise Exception( "Could not find a default controller "
"for switch %s" %
self.options.switch )
topo = buildTopo( TOPOS, self.options.topo )
switch = customConstructor( SWITCHES, self.options.switch )
host = customConstructor( HOSTS, self.options.host )
@@ -312,6 +207,8 @@ class MininetRunner( object ):
if self.validate:
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
@@ -320,22 +217,6 @@ class MininetRunner( object ):
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
# Handle inNamespace, cluster options
inNamespace = self.options.innamespace
cluster = self.options.cluster
if inNamespace and cluster:
print "Please specify --innamespace OR --cluster"
exit()
Net = MininetWithControlNet if inNamespace else Mininet
cli = ClusterCLI if cluster else CLI
if cluster:
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
Net = partial( MininetCluster, servers=cluster.split( ',' ),
placement=PLACEMENT[ self.options.placement ] )
mn = Net( topo=topo,
switch=switch, host=host, controller=controller,
link=link,
@@ -345,13 +226,8 @@ class MininetRunner( object ):
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
if self.options.ensure_value( 'nat', False ):
nat = mn.addNAT( *self.options.nat_args,
**self.options.nat_kwargs )
nat.configDefault()
if self.options.pre:
cli( mn, script=self.options.pre )
CLI( mn, script=self.options.pre )
test = self.options.test
test = ALTSPELLING.get( test, test )
@@ -361,18 +237,16 @@ class MininetRunner( object ):
if test == 'none':
pass
elif test == 'all':
mn.waitConnected()
mn.start()
mn.ping()
mn.iperf()
elif test == 'cli':
cli( mn )
CLI( mn )
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
if self.options.post:
cli( mn, script=self.options.post )
CLI( mn, script=self.options.post )
mn.stop()
@@ -381,21 +255,4 @@ class MininetRunner( object ):
if __name__ == "__main__":
try:
MininetRunner()
except KeyboardInterrupt:
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
cleanup()
except Exception:
# Print exception
type_, val_, trace_ = sys.exc_info()
errorMsg = ( "-"*80 + "\n" +
"Caught exception. Cleaning up...\n\n" +
"%s: %s\n" % ( type_.__name__, val_ ) +
"-"*80 + "\n" )
error( errorMsg )
# Print stack trace to debug log
import traceback
stackTrace = traceback.format_exc()
debug( stackTrace + "\n" )
cleanup()
MininetRunner()
+24 -13
View File
@@ -1,5 +1,7 @@
"""Custom topology example
author: Brandon Heller (brandonh@stanford.edu)
Two directly connected switches plus a host for each switch:
host --- switch --- switch --- host
@@ -8,27 +10,36 @@ Adding the 'topos' dict with a key/value pair to generate our newly defined
topology enables one to pass in '--topo=mytopo' from the command line.
"""
from mininet.topo import Topo
from mininet.topo import Topo, Node
class MyTopo( Topo ):
"Simple topology example."
def __init__( self ):
def __init__( self, enable_all = True ):
"Create custom topo."
# Initialize topology
Topo.__init__( self )
# Add default members to class.
super( MyTopo, self ).__init__()
# Add hosts and switches
leftHost = self.addHost( 'h1' )
rightHost = self.addHost( 'h2' )
leftSwitch = self.addSwitch( 's3' )
rightSwitch = self.addSwitch( 's4' )
# Set Node IDs for hosts and switches
leftHost = 1
leftSwitch = 2
rightSwitch = 3
rightHost = 4
# Add links
self.addLink( leftHost, leftSwitch )
self.addLink( leftSwitch, rightSwitch )
self.addLink( rightSwitch, rightHost )
# Add nodes
self.add_node( leftSwitch, Node( is_switch=True ) )
self.add_node( rightSwitch, Node( is_switch=True ) )
self.add_node( leftHost, Node( is_switch=False ) )
self.add_node( rightHost, Node( is_switch=False ) )
# Add edges
self.add_edge( leftHost, leftSwitch )
self.add_edge( leftSwitch, rightSwitch )
self.add_edge( rightSwitch, rightHost )
# Consider all switches and hosts 'on'
self.enable_all()
topos = { 'mytopo': ( lambda: MyTopo() ) }
-33
View File
@@ -1,33 +0,0 @@
mininet (2.1.0-0ubuntu1) saucy; urgency=low
* Add 2.1.0 final packaging
-- Bob Lantz <rlantz@cs.stanford.edu> Wed, 18 Sep 2013 22:43:47 -0700
mininet (2.1.0~rc1-0ubuntu1) saucy; urgency=low
* New upstream release candidate:
- d/control: Drop dependency on python-networkx, add iperf, socat
and cgroup-bin to Depends.
-- James Page <james.page@ubuntu.com> Wed, 28 Aug 2013 10:10:20 +0100
mininet (2.0.0-0ubuntu1) raring; urgency=low
* New upstream release.
-- James Page <james.page@ubuntu.com> Wed, 19 Dec 2012 15:48:01 +0000
mininet (2.0.0~rc1-0ubuntu1) quantal; urgency=low
* New upstream release.
* Update copyright to match upstream release
* Fix this message
-- Bob Lantz <rlantz@cs.stanford.edu> Sun, 18 Nov 2012 00:15:09 -0800
mininet (2.0.0~d4-0ubuntu1) quantal; urgency=low
* Initial release.
-- Bob Lantz <rlantz@cs.stanford.edu> Tue, 07 Aug 2012 14:11:27 -0700
-1
View File
@@ -1 +0,0 @@
9
-31
View File
@@ -1,31 +0,0 @@
Source: mininet
Section: net
Priority: extra
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
XSBC-Original-Maintainer: Bob Lantz <rlantz@cs.stanford.edu>
Standards-Version: 3.9.3
Build-Depends:
debhelper (>= 9~),
help2man,
python-dev,
python-pkg-resources,
python-setuptools
Homepage: http://openflow.org/mininet
Package: mininet
Architecture: any
Depends:
openvswitch-switch,
telnet,
socat,
iperf,
cgroup-bin,
${misc:Depends},
${python:Depends},
${shlibs:Depends}
Recommends: openvswitch-controller
Description: Process-based network emulator
Mininet is a network emulator which uses lightweight
virtualization to create virtual networks for rapid
prototyping of Software-Defined Network (SDN) designs
using OpenFlow.
-37
View File
@@ -1,37 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
Upstream-Name: mininet
Source: https://github.com/mininet/mininet
Files: *
Copyright: 2012-2013 Open Networking Laboratory,
2009-2012 Bob Lantz,
2009-2012 The Board of Trustees of the Leland Stanford Junior
University
License:
Original authors: Bob Lantz and Brandon Heller
.
We are making Mininet available for public use and benefit with the
expectation that others will use, modify and enhance the Software and
contribute those enhancements back to the community. However, since we
would like to make the Software available for broadest use, with as few
restrictions as possible permission is hereby granted, free of charge, to
any person obtaining a copy of this Software to deal in the Software
under the copyrights without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
.
The name and trademarks of copyright holder(s) may NOT be used in
advertising or publicity pertaining to the Software or any derivatives
without specific, written prior permission.
Vendored
-1
View File
@@ -1 +0,0 @@
README.md
-1
View File
@@ -1 +0,0 @@
examples/*
-1
View File
@@ -1 +0,0 @@
mnexec /usr/bin
-1
View File
@@ -1 +0,0 @@
*.1
-12
View File
@@ -1,12 +0,0 @@
#!/usr/bin/make -f
%:
dh $@ --buildsystem=python_distutils --with=python2
override_dh_auto_build:
make man
make mnexec
dh_auto_build
get-orig-source:
uscan --force-download --rename
-1
View File
@@ -1 +0,0 @@
3.0 (quilt)
-3
View File
@@ -1,3 +0,0 @@
version=3
opts=filenamemangle=s/(.*)\/archive/$1/,uversionmangle=s/([abdr].*)\.tar\.gz/~$1/ \
https://github.com/mininet/mininet/tags .*/archive\/(\d.*\.tar\.gz)
+3 -3
View File
@@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = "Mininet Python API Reference Manual"
PROJECT_NAME = Mininet
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
@@ -114,7 +114,7 @@ FULL_PATH_NAMES = YES
# If left blank the directory from which doxygen is run is used as the
# path to strip.
STRIP_FROM_PATH =
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
# the path mentioned in the documentation of a class, which tells
@@ -919,7 +919,7 @@ COMPACT_LATEX = NO
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = letter
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
+84
View File
@@ -0,0 +1,84 @@
Mininet Examples
These examples are intended to help you get started using
Mininet's Python API.
---
baresshd.py:
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
consoles.py:
This example creates a grid of console windows, one for each node,
and allows interaction with and monitoring of each console, including
graphical monitoring.
controllers.py:
This example creates a network and adds multiple controllers to it.
emptynet.py:
This example demonstrates creating an empty network (i.e. with no
topology object) and adding nodes to it.
linearbandwidth.py:
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
miniedit.py:
This example demonstrates creating a network via a graphical editor.
multiping.py:
This example demonstrates one method for
monitoring output from multiple hosts, using node.monitor().
multipoll.py:
This example demonstrates monitoring output files from multiple hosts.
multitest.py:
This example creates a network and runs multiple tests on it.
popenpoll.py:
This example demonstrates monitoring output from multiple hosts using
the node.popen() interface (which returns Popen objects) and pmonitor().
scratchnet.py, scratchnetuser.py:
These two examples demonstrate how to create a network by using the lowest-
level Mininet functions. Generally the higher-level API is easier to use,
but scratchnet shows what is going on behind the scenes.
simpleperf.py:
A simple example of configuring network and CPU bandwidth limits.
sshd.py:
This example shows how to run an sshd process in each host, allowing
you to log in via ssh. This requires connecting the Mininet data network
to an interface in the root namespace (generaly the control network
already lives in the root namespace, so it does not need to be explicitly
connected.)
treeping64.py:
This example creates a 64-host tree network, and attempts to check full
connectivity using ping, for different switch/datapath types.
tree1024.py:
This example attempts to create a 1024-host network, and then runs the
CLI on it. It may run into scalability limits, depending on available
memory and sysctl configuration (see INSTALL.)
-179
View File
@@ -1,179 +0,0 @@
Mininet Examples
========================================================
These examples are intended to help you get started using
Mininet's Python API.
========================================================
#### baresshd.py:
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
#### 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,
and allows interaction with and monitoring of each console, including
graphical monitoring.
#### controllers.py:
This example creates a network with multiple controllers, by
using a custom `Switch()` subclass.
#### controllers2.py:
This example creates a network with multiple controllers by
creating an empty network, adding nodes to it, and manually
starting the switches.
#### controlnet.py:
This examples shows how you can model the control network as well
as the data network, by actually creating two Mininet objects.
#### cpu.py:
This example tests iperf bandwidth for varying CPU limits.
#### emptynet.py:
This example demonstrates creating an empty network (i.e. with no
topology object) and adding nodes to it.
#### hwintf.py:
This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
#### 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.
#### linearbandwidth.py:
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
#### 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
monitoring output from multiple hosts, using `node.monitor()`.
#### multipoll.py:
This example demonstrates monitoring output files from multiple hosts.
#### multitest.py:
This example creates a network and runs multiple tests on it.
#### nat.py:
This example shows how to connect a Mininet network to the Internet
using NAT. It also answers the eternal question "why can't I ping
`google.com`?"
#### 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
`pmonitor()`.
#### popenpoll.py:
This example demonstrates monitoring output from multiple hosts using
the `node.popen()` interface (which returns `Popen` objects) and `pmonitor()`.
#### scratchnet.py, scratchnetuser.py:
These two examples demonstrate how to create a network by using the lowest-
level Mininet functions. Generally the higher-level API is easier to use,
but scratchnet shows what is going on behind the scenes.
#### simpleperf.py:
A simple example of configuring network and CPU bandwidth limits.
#### sshd.py:
This example shows how to run an `sshd` process in each host, allowing
you to log in via `ssh`. This requires connecting the Mininet data network
to an interface in the root namespace (generaly the control network
already lives in the root namespace, so it does not need to be explicitly
connected.)
#### tree1024.py:
This example attempts to create a 1024-host network, and then runs the
CLI on it. It may run into scalability limits, depending on available
memory and `sysctl` configuration (see `INSTALL`.)
#### treeping64.py:
This example creates a 64-host tree network, and attempts to check full
connectivity using `ping`, for different switch/datapath types.
#### vlanhost.py:
An example of how to subclass Host to use a VLAN on its primary interface.
-4
View File
@@ -1,4 +0,0 @@
"""
Mininet Examples
See README for details
"""
+2 -16
View File
@@ -2,12 +2,7 @@
"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
ensureRoot()
timeout = 5
print "*** Creating nodes"
h1 = Host( 'h1' )
@@ -29,15 +24,6 @@ f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
f.close()
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 )
h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
if listening:
print "*** You may now ssh into", h1.name, "at", h1.IP()
else:
print ( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ) )
print "*** You may now ssh into", h1.name, "at", h1.IP()
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/python
"""
bind.py: Bind mount example
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'
"""
from mininet.net import Mininet
from mininet.node import Host
from mininet.cli import CLI
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from functools import partial
# Sample usage
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 )
net.start()
directories = [ directory[ 0 ] if isinstance( directory, tuple )
else directory for directory in privateDirs ]
info( 'Private Directories:', directories, '\n' )
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
testHostWithPrivateDirs()
info( 'Done.\n')
-865
View File
@@ -1,865 +0,0 @@
#!/usr/bin/python
"""
cluster.py: prototyping/experimentation for distributed Mininet,
aka Mininet: Cluster Edition
Author: Bob Lantz
Core classes:
RemoteNode: a Node() running on a remote server
RemoteOVSSwitch(): an OVSSwitch() running on a remote server
RemoteLink: a Link() on a remote server
Tunnel: a Link() between a local Node() and a RemoteNode()
These are largely interoperable with local objects.
- One Mininet to rule them all
It is important that the same topologies, APIs, and CLI can be used
with minimal or no modification in both local and distributed environments.
- Multiple placement models
Placement should be as easy as possible. We should provide basic placement
support and also allow for explicit placement.
Questions:
What is the basic communication mechanism?
To start with? Probably a single multiplexed ssh connection between each
pair of mininet servers that needs to communicate.
How are tunnels created?
We have several options including ssh, GRE, OF capsulator, socat, VDE, l2tp,
etc.. It's not clear what the best one is. For now, we use ssh tunnels since
they are encrypted and semi-automatically shared. We will probably want to
support GRE as well because it's very easy to set up with OVS.
How are tunnels destroyed?
They are destroyed when the links are deleted in Mininet.stop()
How does RemoteNode.popen() work?
It opens a shared ssh connection to the remote server and attaches to
the namespace using mnexec -a -g.
Is there any value to using Paramiko vs. raw ssh?
Maybe, but it doesn't seem to support L2 tunneling.
Should we preflight the entire network, including all server-to-server
connections?
Yes! We don't yet do this with remote server-to-server connections yet.
Should we multiplex the link ssh connections?
Yes, this is done automatically with ControlMaster=auto.
Note on ssh and DNS:
Please add UseDNS: no to your /etc/ssh/sshd_config!!!
Things to do:
- asynchronous/pipelined/parallel startup
- ssh debugging/profiling
- make connections into real objects
- support for other tunneling schemes
- tests and benchmarks
- hifi support (e.g. delay compensation)
"""
from mininet.node import Node, Host, OVSSwitch, Controller
from mininet.link import Link, Intf
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, makeIntfPair, errRun, retry
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from signal import signal, SIGINT, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
import os
from random import randrange
import sys
import re
from distutils.version import StrictVersion
# BL note: so little code is required for remote nodes,
# we will probably just want to update the main Node()
# class to enable it for remote access! However, there
# are a large number of potential failure conditions with
# remote nodes which we may want to detect and handle.
# Another interesting point is that we could put everything
# in a mix-in class and easily add cluster mode to 2.0.
class RemoteMixin( object ):
"A mix-in class to turn local nodes into remote nodes"
# ssh base command
# -q: don't print stupid diagnostic messages
# BatchMode yes: don't ask for password
# ForwardAgent yes: forward authentication credentials
sshbase = [ 'ssh', '-q',
'-o', 'BatchMode=yes',
'-o', 'ForwardAgent=yes', '-tt' ]
def __init__( self, name, server='localhost', user=None, serverIP=None,
controlPath=False, splitInit=False, **kwargs):
"""Instantiate a remote node
name: name of remote node
server: remote server (optional)
user: user on remote server (optional)
controlPath: specify shared ssh control path (optional)
splitInit: split initialization?
**kwargs: see Node()"""
# We connect to servers by IP address
self.server = server if server else 'localhost'
self.serverIP = ( serverIP if serverIP
else self.findServerIP( self.server ) )
self.user = user if user else self.findUser()
if controlPath is True:
# Set a default control path for shared SSH connections
controlPath = '/tmp/mn-%r@%h:%p'
self.controlPath = controlPath
self.splitInit = splitInit
if self.user and self.server != 'localhost':
self.dest = '%s@%s' % ( self.user, self.serverIP )
self.sshcmd = [ 'sudo', '-E', '-u', self.user ] + self.sshbase
if self.controlPath:
self.sshcmd += [ '-o', 'ControlPath=' + self.controlPath,
'-o', 'ControlMaster=auto',
'-o', 'ControlPersist=' + '1' ]
self.sshcmd = self.sshcmd + [ self.dest ]
self.isRemote = True
else:
self.dest = None
self.sshcmd = []
self.isRemote = False
# Satisfy pylint
self.shell, self.pid = None, None
super( RemoteMixin, self ).__init__( name, **kwargs )
@staticmethod
def findUser():
"Try to return logged-in (usually non-root) user"
return (
# If we're running sudo
os.environ.get( 'SUDO_USER', False ) or
# Logged-in user (if we have a tty)
( quietRun( 'who am i' ).split() or [ False ] )[ 0 ] or
# Give up and return effective user
quietRun( 'whoami' ) )
# Determine IP address of local host
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
@classmethod
def findServerIP( cls, server ):
"Return our server's IP address"
# First, check for an IP address
ipmatch = cls._ipMatchRegex.findall( server )
if ipmatch:
return ipmatch[ 0 ]
# Otherwise, look up remote server
output = quietRun( 'getent ahostsv4 %s' % server )
ips = cls._ipMatchRegex.findall( output )
ip = ips[ 0 ] if ips else None
return ip
# Command support via shell process in namespace
def startShell( self, *args, **kwargs ):
"Start a shell process for running commands"
if self.isRemote:
kwargs.update( mnopts='-c' )
super( RemoteMixin, self ).startShell( *args, **kwargs )
# Optional split initialization
self.sendCmd( 'echo $$' )
if not self.splitInit:
self.finishInit()
def finishInit( self ):
"Wait for split initialization to complete"
self.pid = int( self.waitOutput() )
def rpopen( self, *cmd, **opts ):
"Return a Popen object on underlying server in root namespace"
params = { 'stdin': PIPE,
'stdout': PIPE,
'stderr': STDOUT,
'sudo': True }
params.update( opts )
return self._popen( *cmd, **params )
def rcmd( self, *cmd, **opts):
"""rcmd: run a command on underlying server
in root namespace
args: string or list of strings
returns: stdout and stderr"""
popen = self.rpopen( *cmd, **opts )
# print 'RCMD: POPEN:', popen
# These loops are tricky to get right.
# Once the process exits, we can read
# EOF twice if necessary.
result = ''
while True:
poll = popen.poll()
result += popen.stdout.read()
if poll is not None:
break
return result
@staticmethod
def _ignoreSignal():
"Detach from process group to ignore all signals"
os.setpgrp()
def _popen( self, cmd, sudo=True, tt=True, **params):
"""Spawn a process on a remote node
cmd: remote command to run (list)
**params: parameters to Popen()
returns: Popen() object"""
if type( cmd ) is str:
cmd = cmd.split()
if self.isRemote:
if sudo:
cmd = [ 'sudo', '-E' ] + cmd
if tt:
cmd = self.sshcmd + cmd
else:
# Hack: remove -tt
sshcmd = list( self.sshcmd )
sshcmd.remove( '-tt' )
cmd = sshcmd + cmd
else:
if self.user and not sudo:
# Drop privileges
cmd = [ 'sudo', '-E', '-u', self.user ] + cmd
params.update( preexec_fn=self._ignoreSignal )
debug( '_popen', ' '.join(cmd), params )
popen = super( RemoteMixin, self )._popen( cmd, **params )
return popen
def popen( self, *args, **kwargs ):
"Override: disable -tt"
return super( RemoteMixin, self).popen( *args, tt=False, **kwargs )
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
return super( RemoteMixin,
self).addIntf( *args,
moveIntfFn=RemoteLink.moveIntf,
**kwargs )
def cleanup( self ):
"Help python collect its garbage."
# Intfs may end up in root NS
for intfName in self.intfNames():
if self.name in intfName:
self.rcmd( 'ip link del ' + intfName )
self.shell = None
class RemoteNode( RemoteMixin, Node ):
"A node on a remote server"
pass
class RemoteHost( RemoteNode ):
"A RemoteHost is simply a RemoteNode"
pass
class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
"Remote instance of Open vSwitch"
OVSVersions = {}
def isOldOVS( self ):
"Is remote switch using an old OVS version?"
cls = type( self )
if self.server not in cls.OVSVersions:
# pylint: disable=not-callable
vers = self.cmd( 'ovs-vsctl --version' )
# pylint: enable=not-callable
cls.OVSVersions[ self.server ] = re.findall(
r'\d+\.\d+', vers )[ 0 ]
return ( StrictVersion( cls.OVSVersions[ self.server ] ) <
StrictVersion( '1.10' ) )
class RemoteLink( Link ):
"A RemoteLink is a link between nodes which may be on different servers"
def __init__( self, node1, node2, **kwargs ):
"""Initialize a RemoteLink
see Link() for parameters"""
# Create links on remote node
self.node1 = node1
self.node2 = node2
self.tunnel = None
kwargs.setdefault( 'params1', {} )
kwargs.setdefault( 'params2', {} )
self.cmd = None # satisfy pylint
Link.__init__( self, node1, node2, **kwargs )
def stop( self ):
"Stop this link"
if self.tunnel:
self.tunnel.terminate()
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
node1, node2 = self.node1, self.node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == 'localhost' and server2 == 'localhost':
# Local link
return makeIntfPair( intfname1, intfname2, addr1, addr2 )
elif server1 == server2:
# Remote link on same remote server
return makeIntfPair( intfname1, intfname2, addr1, addr2,
runCmd=node1.rcmd )
# Otherwise, make a tunnel
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
addr1, addr2 )
return self.tunnel
@staticmethod
def moveIntf( intf, node, printError=True ):
"""Move remote interface from root ns to node
intf: string, interface
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
node.rcmd( cmd )
links = node.cmd( 'ip link show' )
if not ' %s:' % intf in links:
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
return True
def makeTunnel( self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None ):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != 'localhost' or node2.server != 'localhost'
# And we can't ssh into this server remotely as 'localhost',
# so try again swappping node1 and node2
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
# 1. Create tap interfaces
for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename
node.rcmd( 'ip link delete tap9', stderr=PIPE )
cmd = 'ip tuntap add dev tap9 mode tap user ' + node.user
node.rcmd( cmd )
links = node.rcmd( 'ip link show' )
# print 'after add, links =', links
assert 'tap9' in links
# 2. Create ssh tunnel between tap interfaces
# -n: close stdin
dest = '%s@%s' % ( node2.user, node2.serverIP )
cmd = [ 'ssh', '-n', '-o', 'Tunnel=Ethernet', '-w', '9:9',
dest, 'echo @' ]
self.cmd = cmd
tunnel = node1.rpopen( cmd, sudo=False )
# When we receive the character '@', it means that our
# tunnel should be set up
debug( 'Waiting for tunnel to come up...\n' )
ch = tunnel.stdout.read( 1 )
if ch != '@':
error( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
tunnel.terminate()
tunnel.wait()
error( ch + tunnel.stdout.read() )
error( tunnel.stderr.read() )
sys.exit( 1 )
# 3. Move interfaces if necessary
for node in node1, node2:
if node.inNamespace:
retry( 3, .01, RemoteLink.moveIntf, 'tap9', node )
# 4. Rename tap interfaces to desired names
for node, intf, addr in ( ( node1, intfname1, addr1 ),
( node2, intfname2, addr2 ) ):
if not addr:
node.cmd( 'ip link set tap9 name', intf )
else:
node.cmd( 'ip link set tap9 name', intf, 'address', addr )
for node, intf in ( ( node1, intfname1 ), ( node2, intfname2 ) ):
assert intf in node.cmd( 'ip link show' )
return tunnel
def status( self ):
"Detailed representation of link"
if self.tunnel:
if self.tunnel.poll() is not None:
status = "Tunnel EXITED %s" % self.tunnel.returncode
else:
status = "Tunnel Running (%s: %s)" % (
self.tunnel.pid, self.cmd )
else:
status = "OK"
result = "%s %s" % ( Link.status( self ), status )
return result
# Some simple placement algorithms for MininetCluster
class Placer( object ):
"Node placement algorithm for MininetCluster"
def __init__( self, servers=None, nodes=None, hosts=None,
switches=None, controllers=None, links=None ):
"""Initialize placement object
servers: list of servers
nodes: list of all nodes
hosts: list of hosts
switches: list of switches
controllers: list of controllers
links: list of links
(all arguments are optional)
returns: server"""
self.servers = servers or []
self.nodes = nodes or []
self.hosts = hosts or []
self.switches = switches or []
self.controllers = controllers or []
self.links = links or []
def place( self, node ):
"Return server for a given node"
assert self, node # satisfy pylint
# Default placement: run locally
return 'localhost'
class RandomPlacer( Placer ):
"Random placement"
def place( self, nodename ):
"""Random placement function
nodename: node name"""
assert nodename # please pylint
# This may be slow with lots of servers
return self.servers[ randrange( 0, len( self.servers ) ) ]
class RoundRobinPlacer( Placer ):
"""Round-robin placement
Note this will usually result in cross-server links between
hosts and switches"""
def __init__( self, *args, **kwargs ):
Placer.__init__( self, *args, **kwargs )
self.next = 0
def place( self, nodename ):
"""Round-robin placement function
nodename: node name"""
assert nodename # please pylint
# This may be slow with lots of servers
server = self.servers[ self.next ]
self.next = ( self.next + 1 ) % len( self.servers )
return server
class SwitchBinPlacer( Placer ):
"""Place switches (and controllers) into evenly-sized bins,
and attempt to co-locate hosts and switches"""
def __init__( self, *args, **kwargs ):
Placer.__init__( self, *args, **kwargs )
# Easy lookup for servers and node sets
self.servdict = dict( enumerate( self.servers ) )
self.hset = frozenset( self.hosts )
self.sset = frozenset( self.switches )
self.cset = frozenset( self.controllers )
# Server and switch placement indices
self.placement = self.calculatePlacement()
@staticmethod
def bin( nodes, servers ):
"Distribute nodes evenly over servers"
# Calculate base bin size
nlen = len( nodes )
slen = len( servers )
# Basic bin size
quotient = int( nlen / slen )
binsizes = { server: quotient for server in servers }
# Distribute remainder
remainder = nlen % slen
for server in servers[ 0 : remainder ]:
binsizes[ server ] += 1
# Create binsize[ server ] tickets for each server
tickets = sum( [ binsizes[ server ] * [ server ]
for server in servers ], [] )
# And assign one ticket to each node
return { node: ticket for node, ticket in zip( nodes, tickets ) }
def calculatePlacement( self ):
"Pre-calculate node placement"
placement = {}
# Create host-switch connectivity map,
# associating host with last switch that it's
# connected to
switchFor = {}
for src, dst in self.links:
if src in self.hset and dst in self.sset:
switchFor[ src ] = dst
if dst in self.hset and src in self.sset:
switchFor[ dst ] = src
# Place switches
placement = self.bin( self.switches, self.servers )
# Place controllers and merge into placement dict
placement.update( self.bin( self.controllers, self.servers ) )
# Co-locate hosts with their switches
for h in self.hosts:
if h in placement:
# Host is already placed - leave it there
continue
if h in switchFor:
placement[ h ] = placement[ switchFor[ h ] ]
else:
raise Exception(
"SwitchBinPlacer: cannot place isolated host " + h )
return placement
def place( self, node ):
"""Simple placement algorithm:
place switches into evenly sized bins,
and place hosts near their switches"""
return self.placement[ node ]
class HostSwitchBinPlacer( Placer ):
"""Place switches *and hosts* into evenly-sized bins
Note that this will usually result in cross-server
links between hosts and switches"""
def __init__( self, *args, **kwargs ):
Placer.__init__( self, *args, **kwargs )
# Calculate bin sizes
scount = len( self.servers )
self.hbin = max( int( len( self.hosts ) / scount ), 1 )
self.sbin = max( int( len( self.switches ) / scount ), 1 )
self.cbin = max( int( len( self.controllers ) / scount ), 1 )
info( 'scount:', scount )
info( 'bins:', self.hbin, self.sbin, self.cbin, '\n' )
self.servdict = dict( enumerate( self.servers ) )
self.hset = frozenset( self.hosts )
self.sset = frozenset( self.switches )
self.cset = frozenset( self.controllers )
self.hind, self.sind, self.cind = 0, 0, 0
def place( self, nodename ):
"""Simple placement algorithm:
place nodes into evenly sized bins"""
# Place nodes into bins
if nodename in self.hset:
server = self.servdict[ self.hind / self.hbin ]
self.hind += 1
elif nodename in self.sset:
server = self.servdict[ self.sind / self.sbin ]
self.sind += 1
elif nodename in self.cset:
server = self.servdict[ self.cind / self.cbin ]
self.cind += 1
else:
info( 'warning: unknown node', nodename )
server = self.servdict[ 0 ]
return server
# The MininetCluster class is not strictly necessary.
# However, it has several purposes:
# 1. To set up ssh connection sharing/multiplexing
# 2. To pre-flight the system so that everything is more likely to work
# 3. To allow connection/connectivity monitoring
# 4. To support pluggable placement algorithms
class MininetCluster( Mininet ):
"Cluster-enhanced version of Mininet class"
# Default ssh command
# BatchMode yes: don't ask for password
# ForwardAgent yes: forward authentication credentials
sshcmd = [ 'ssh', '-o', 'BatchMode=yes', '-o', 'ForwardAgent=yes' ]
def __init__( self, *args, **kwargs ):
"""servers: a list of servers to use (note: include
localhost or None to use local system as well)
user: user name for server ssh
placement: Placer() subclass"""
params = { 'host': RemoteHost,
'switch': RemoteOVSSwitch,
'link': RemoteLink,
'precheck': True }
params.update( kwargs )
servers = params.pop( 'servers', [ 'localhost' ] )
servers = [ s if s else 'localhost' for s in servers ]
self.servers = servers
self.serverIP = params.pop( 'serverIP', {} )
if not self.serverIP:
self.serverIP = { server: RemoteMixin.findServerIP( server )
for server in self.servers }
self.user = params.pop( 'user', RemoteMixin.findUser() )
if params.pop( 'precheck' ):
self.precheck()
self.connections = {}
self.placement = params.pop( 'placement', SwitchBinPlacer )
# Make sure control directory exists
self.cdir = os.environ[ 'HOME' ] + '/.ssh/mn'
errRun( [ 'mkdir', '-p', self.cdir ] )
Mininet.__init__( self, *args, **params )
def popen( self, cmd ):
"Popen() for server connections"
assert self # please pylint
old = signal( SIGINT, SIG_IGN )
conn = Popen( cmd, stdin=PIPE, stdout=PIPE, close_fds=True )
signal( SIGINT, old )
return conn
def baddLink( self, *args, **kwargs ):
"break addlink for testing"
pass
def precheck( self ):
"""Pre-check to make sure connection works and that
we can call sudo without a password"""
result = 0
info( '*** Checking servers\n' )
for server in self.servers:
ip = self.serverIP[ server ]
if not server or server == 'localhost':
continue
info( server, '' )
dest = '%s@%s' % ( self.user, ip )
cmd = [ 'sudo', '-E', '-u', self.user ]
cmd += self.sshcmd + [ '-n', dest, 'sudo true' ]
debug( ' '.join( cmd ), '\n' )
_out, _err, code = errRun( cmd )
if code != 0:
error( '\nstartConnection: server connection check failed '
'to %s using command:\n%s\n'
% ( server, ' '.join( cmd ) ) )
result |= code
if result:
error( '*** Server precheck failed.\n'
'*** Make sure that the above ssh command works'
' correctly.\n'
'*** You may also need to run mn -c on all nodes, and/or\n'
'*** use sudo -E.\n' )
sys.exit( 1 )
info( '\n' )
def modifiedaddHost( self, *args, **kwargs ):
"Slightly modify addHost"
assert self # please pylint
kwargs[ 'splitInit' ] = True
return Mininet.addHost( *args, **kwargs )
def placeNodes( self ):
"""Place nodes on servers (if they don't have a server), and
start shell processes"""
if not self.servers or not self.topo:
# No shirt, no shoes, no service
return
nodes = self.topo.nodes()
placer = self.placement( servers=self.servers,
nodes=self.topo.nodes(),
hosts=self.topo.hosts(),
switches=self.topo.switches(),
links=self.topo.links() )
for node in nodes:
config = self.topo.nodeInfo( node )
# keep local server name consistent accross nodes
if 'server' in config.keys() and config[ 'server' ] is None:
config[ 'server' ] = 'localhost'
server = config.setdefault( 'server', placer.place( node ) )
if server:
config.setdefault( 'serverIP', self.serverIP[ server ] )
info( '%s:%s ' % ( node, server ) )
key = ( None, server )
_dest, cfile, _conn = self.connections.get(
key, ( None, None, None ) )
if cfile:
config.setdefault( 'controlPath', cfile )
def addController( self, *args, **kwargs ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
# Update IP address for controller that may not be local
if ( isinstance( controller, Controller)
and controller.IP() == '127.0.0.1'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
return controller
def buildFromTopo( self, *args, **kwargs ):
"Start network"
info( '*** Placing nodes\n' )
self.placeNodes()
info( '\n' )
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels():
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=RemoteLink )
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
net.stop()
# Manual topology creation with net.add*()
#
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2' ):
"Test remote Node classes"
print '*** Remote Node Test'
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=RemoteLink )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
print "*** Creating local h1"
h1 = net.addHost( 'h1' )
print "*** Creating remote h2"
h2 = net.addHost( 'h2', server=remote )
print "*** Creating local s1"
s1 = net.addSwitch( 's1' )
print "*** Creating remote s2"
s2 = net.addSwitch( 's2', server=remote )
print "*** Adding links"
net.addLink( h1, s1 )
net.addLink( s1, s2 )
net.addLink( h2, s2 )
net.start()
print 'Mininet is running on', quietRun( 'hostname' ).strip()
for node in c0, h1, h2, s1, s2:
print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip()
net.pingAll()
CLI( net )
net.stop()
# High-level/Topo API example
#
# This shows how existing Mininet topologies may be used in cluster
# mode by creating node placement functions and a controller which
# can be accessed remotely. This implements a very compatible version
# of cluster edition with a minimum of code!
remoteHosts = [ 'h2' ]
remoteSwitches = [ 's2' ]
remoteServer = 'ubuntu2'
def HostPlacer( name, *args, **params ):
"Custom Host() constructor which places hosts on servers"
if name in remoteHosts:
return RemoteHost( name, *args, server=remoteServer, **params )
else:
return Host( name, *args, **params )
def SwitchPlacer( name, *args, **params ):
"Custom Switch() constructor which places switches on servers"
if name in remoteSwitches:
return RemoteOVSSwitch( name, *args, server=remoteServer, **params )
else:
return RemoteOVSSwitch( name, *args, **params )
def ClusterController( *args, **kwargs):
"Custom Controller() constructor which updates its eth0 IP address"
controller = Controller( *args, **kwargs )
# Find out its IP address so that cluster switches can connect
Intf( 'eth0', node=controller ).updateIP()
return controller
def testRemoteTopo():
"Test remote Node classes using Mininet()/Topo() API"
topo = LinearTopo( 2 )
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
link=RemoteLink, controller=ClusterController )
net.start()
net.pingAll()
net.stop()
# Need to test backwards placement, where each host is on
# a server other than its switch!! But seriously we could just
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches():
"Test with local hosts and remote switches"
servers = [ 'localhost', 'ubuntu2']
topo = TreeTopo( depth=4, fanout=2 )
net = MininetCluster( topo=topo, servers=servers,
placement=RoundRobinPlacer )
net.start()
net.pingAll()
net.stop()
#
# For testing and demo purposes it would be nice to draw the
# network graph and color it based on server.
# The MininetCluster() class integrates pluggable placement
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster():
"Test MininetCluster()"
servers = [ 'localhost', 'ubuntu2' ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers,
placement=SwitchBinPlacer )
net.start()
net.pingAll()
net.stop()
def signalTest():
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server='ubuntu1' )
h.shell.send_signal( SIGINT )
h.shell.poll()
if h.shell.returncode is None:
print 'OK: ', h, 'has not exited'
else:
print 'FAILURE:', h, 'exited with code', h.shell.returncode
h.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
# testRemoteTopo()
# testRemoteNet()
# testMininetCluster()
# testRemoteSwitches()
signalTest()
-22
View File
@@ -1,22 +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()
-98
View File
@@ -1,98 +0,0 @@
#!/usr/bin/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
if not nx:
try:
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
plt = pyplot # satisfiy pylint
import pygraphviz
assert pygraphviz # silence pyflakes
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, hosts, switches = mn.servers, 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 = nx.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 ) ) )
-22
View File
@@ -1,22 +0,0 @@
#!/usr/bin/python
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
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,
placement=SwitchBinPlacer )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
demo()
+15 -22
View File
@@ -74,11 +74,11 @@ class Console( Frame ):
"Pop up a new terminal window for a node."
net.terms += makeTerms( [ node ], title )
label = Button( self, text=self.node.name, command=newTerm,
**self.buttonStyle )
**self.buttonStyle )
label.pack( side='top', fill='x' )
text = Text( self, wrap='word', **self.textStyle )
ybar = Scrollbar( self, orient='vertical', width=7,
command=text.yview )
command=text.yview )
text.configure( yscrollcommand=ybar.set )
text.pack( side='left', expand=True, fill='both' )
ybar.pack( side='right', fill='y' )
@@ -95,7 +95,7 @@ class Console( Frame ):
# way to trigger a file event handler from Tk's
# event loop!
self.tk.createfilehandler( self.node.stdout, READABLE,
self.handleReadable )
self.handleReadable )
# We're not a terminal (yet?), so we ignore the following
# control characters other than [\b\n\r]
@@ -169,8 +169,11 @@ class Graph( Frame ):
"Graph that we can add bars to over time."
def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
barwidth=10, ymax=3.5,):
def __init__( self, parent=None,
bg = 'white',
gheight=200, gwidth=500,
barwidth=10,
ymax=3.5,):
Frame.__init__( self, parent )
@@ -192,7 +195,7 @@ class Graph( Frame ):
width = 25
ymax = self.ymax
scale = Canvas( self, width=width, height=height,
background=self.bg )
background=self.bg )
opts = { 'fill': 'red' }
# Draw scale line
scale.create_line( width - 1, height, width - 1, 0, **opts )
@@ -208,7 +211,7 @@ class Graph( Frame ):
ofs = 20
height = self.gheight + ofs
self.graph.configure( scrollregion=( 0, -ofs,
self.xpos * self.barwidth, height ) )
self.xpos * self.barwidth, height ) )
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
def yview( self, *args ):
@@ -228,7 +231,7 @@ class Graph( Frame ):
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
ybar = Scrollbar( self, orient='vertical', command=self.yview )
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
scrollregion=(0, 0, width, height ) )
scrollregion=(0, 0, width, height ) )
scale.configure( yscrollcommand=ybar.set )
# Layout
@@ -249,7 +252,7 @@ class Graph( Frame ):
x1 = x0 + self.barwidth
y0 = self.gheight
y1 = ( 1 - percent ) * self.gheight
c.create_rectangle( x0, y0, x1, y1, fill='green' )
c.create_rectangle( x0 , y0, x1, y1, fill='green' )
self.xpos += 1
self.updateScrollRegions()
self.graph.xview( 'moveto', '1.0' )
@@ -315,19 +318,11 @@ class ConsoleApp( Frame ):
def updateGraph( self, _console, output ):
"Update our graph."
m = re.search( r'(\d+.?\d*) ([KMG]?bits)/sec', output )
m = re.search( r'(\d+) Mbits/sec', output )
if not m:
return
val, units = float( m.group( 1 ) ), m.group( 2 )
#convert to Gbps
if units[0] == 'M':
val *= 10 ** -3
elif units[0] == 'K':
val *= 10 ** -6
elif units[0] == 'b':
val *= 10 ** -9
self.updates += 1
self.bw += val
self.bw += .001 * float( m.group( 1 ) )
if self.updates >= self.hostCount:
self.graph.addBar( self.bw )
self.bw = 0
@@ -418,9 +413,7 @@ class ConsoleApp( Frame ):
count = len( consoles )
self.setOutputHook( self.updateGraph )
for console in consoles:
# Sometimes iperf -sD doesn't return,
# so we run it in the background instead
console.node.cmd( 'iperf -s &' )
console.node.cmd( 'iperf -sD' )
i = 0
for console in consoles:
i = ( i + 1 ) % count
+52 -24
View File
@@ -1,36 +1,64 @@
#!/usr/bin/python
"""
Create a network where different switches are connected to
different controllers, by creating a custom Switch() subclass.
This example creates a multi-controller network from
semi-scratch; note a topo object could also be used and
would be passed into the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import OVSSwitch, Controller, RemoteController
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.node import Controller, OVSKernelSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel
setLogLevel( 'info' )
Switch = OVSKernelSwitch
# Two local and one "external" controller (which is actually c0)
# Ignore the warning message that the remote isn't (yet) running
c0 = Controller( 'c0', port=6633 )
c1 = Controller( 'c1', port=6634 )
c2 = RemoteController( 'c2', ip='127.0.0.1' )
def addHost( net, N ):
"Create host hN and add to net."
name = 'h%d' % N
ip = '10.0.0.%d' % N
return net.addHost( name, ip=ip )
cmap = { 's1': c0, 's2': c1, 's3': c2 }
def multiControllerNet():
"Create a network with multiple controllers."
class MultiSwitch( OVSSwitch ):
"Custom Switch() subclass that connects to different controllers"
def start( self, controllers ):
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
net = Mininet( controller=Controller, switch=Switch)
topo = TreeTopo( depth=2, fanout=2 )
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
for c in [ c0, c1 ]:
net.addController(c)
net.build()
net.start()
CLI( net )
net.stop()
print "*** Creating controllers"
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
print "*** Creating switches"
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
print "*** Creating hosts"
hosts1 = [ addHost( net, n ) for n in 3, 4 ]
hosts2 = [ addHost( net, n ) for n in 5, 6 ]
print "*** Creating links"
for h in hosts1:
s1.linkTo( h )
for h in hosts2:
s2.linkTo( h )
s1.linkTo( s2 )
print "*** Starting network"
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
print "*** Testing network"
net.pingAll()
print "*** Running CLI"
CLI( net )
print "*** Stopping network"
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' ) # for CLI output
multiControllerNet()
-61
View File
@@ -1,61 +0,0 @@
#!/usr/bin/python
"""
This example creates a multi-controller network from semi-scratch by
using the net.add*() API and manually starting the switches and controllers.
This is the "mid-level" API, which is an alternative to the "high-level"
Topo() API which supports parametrized topology classes.
Note that one could also create a custom switch class and pass it into
the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel
def multiControllerNet():
"Create a network from semi-scratch with multiple controllers."
net = Mininet( controller=Controller, switch=OVSSwitch )
print "*** Creating (reference) controllers"
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
print "*** Creating switches"
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
print "*** Creating hosts"
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
print "*** Creating links"
for h in hosts1:
net.addLink( s1, h )
for h in hosts2:
net.addLink( s2, h )
net.addLink( s1, s2 )
print "*** Starting network"
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
print "*** Testing network"
net.pingAll()
print "*** Running CLI"
CLI( net )
print "*** Stopping network"
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' ) # for CLI output
multiControllerNet()
-151
View File
@@ -1,151 +0,0 @@
#!/usr/bin/python
"""
controlnet.py: Mininet with a custom control network
We create two Mininet() networks, a control network
and a data network, running four DataControllers on the
control network to control the data network.
Since we're using UserSwitch on the data network,
it should correctly fail over to a backup controller.
We also use a Mininet Facade to talk to both the
control and data networks from a single CLI.
"""
from functools import partial
from mininet.net import Mininet
from mininet.node import Controller, UserSwitch
from mininet.cli import CLI
from mininet.topo import Topo
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel, info
# Some minor hacks
class DataController( Controller ):
"""Data Network Controller.
patched to avoid checkListening error"""
def checkListening( self ):
"Ignore spurious error"
pass
class MininetFacade( object ):
"""Mininet object facade that allows a single CLI to
talk to one or more networks"""
def __init__( self, net, *args, **kwargs ):
"""Create MininetFacade object.
net: Primary Mininet object
args: unnamed networks passed as arguments
kwargs: named networks passed as arguments"""
self.net = net
self.nets = [ net ] + list( args ) + kwargs.values()
self.nameToNet = kwargs
self.nameToNet['net'] = net
def __getattr__( self, name ):
"returns attribute from Primary Mininet object"
return getattr( self.net, name )
def __getitem__( self, key ):
"returns primary/named networks or node from any net"
#search kwargs for net named key
if key in self.nameToNet:
return self.nameToNet[ key ]
#search each net for node named key
for net in self.nets:
if key in net:
return net[ key ]
def __iter__( self ):
"Iterate through all nodes in all Mininet objects"
for net in self.nets:
for node in net:
yield node
def __len__( self ):
"returns aggregate number of nodes in all nets"
count = 0
for net in self.nets:
count += len(net)
return count
def __contains__( self, key ):
"returns True if node is a member of any net"
return key in self.keys()
def keys( self ):
"returns a list of all node names in all networks"
return list( self )
def values( self ):
"returns a list of all nodes in all networks"
return [ self[ key ] for key in self ]
def items( self ):
"returns (key,value) tuple list for every node in all networks"
return zip( self.keys(), self.values() )
# A real control network!
class ControlNetwork( Topo ):
"Control Network Topology"
def __init__( self, n, dataController=DataController, **kwargs ):
"""n: number of data network controller nodes
dataController: class for data network controllers"""
Topo.__init__( self, **kwargs )
# Connect everything to a single switch
cs0 = self.addSwitch( 'cs0' )
# Add hosts which will serve as data network controllers
for i in range( 0, n ):
c = self.addHost( 'c%s' % i, cls=dataController,
inNamespace=True )
self.addLink( c, cs0 )
# Connect switch to root namespace so that data network
# switches will be able to talk to us
root = self.addHost( 'root', inNamespace=False )
self.addLink( root, cs0 )
# Make it Happen!!
def run():
"Create control and data networks, and invoke the CLI"
info( '* Creating Control Network\n' )
ctopo = ControlNetwork( n=4, dataController=DataController )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
info( '* Adding Control Network Controller\n')
cnet.addController( 'cc0', controller=Controller )
info( '* Starting Control Network\n')
cnet.start()
info( '* Creating Data Network\n' )
topo = TreeTopo( depth=2, fanout=2 )
# UserSwitch so we can easily test failover
sw = partial( UserSwitch, opts='--inactivity-probe=1 --max-backoff=1' )
net = Mininet( topo=topo, switch=sw, controller=None )
info( '* Adding Controllers to Data Network\n' )
for host in cnet.hosts:
if isinstance(host, Controller):
net.addController( host )
info( '* Starting Data Network\n')
net.start()
mn = MininetFacade( net, cnet=cnet )
CLI( mn )
info( '* Stopping Data Network\n' )
net.stop()
info( '* Stopping Control Network\n' )
cnet.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
run()
+18 -10
View File
@@ -1,14 +1,27 @@
#!/usr/bin/python
"""
cpu.py: test iperf bandwidth for varying cpu limits
cpu.py: test iperf bandwidth for varying cpu limtis
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, waitListening
from mininet.log import setLogLevel, info
from mininet.util import custom
from mininet.log import setLogLevel, output
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 ):
@@ -25,12 +38,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
host = custom( CPULimitedHost, sched=sched,
period_us=period_us,
cpu=cpu )
try:
net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except
except:
info( '*** Skipping host %s\n' % sched )
break
net = Mininet( topo=topo, host=host )
net.start()
net.pingAll()
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
@@ -38,7 +46,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
server.cmd( 'iperf -s -p 5001 &' )
waitListening( client, server, 5001 )
result = client.cmd( 'iperf -yc -t %s -c %s' % (
seconds, server.IP() ) ).split( ',' )
seconds, server.IP() ) ).split( ',' )
bps = float( result[ -1 ] )
server.cmdPrint( 'kill %iperf' )
net.stop()
+2 -2
View File
@@ -27,8 +27,8 @@ def emptyNet():
s3 = net.addSwitch( 's3' )
info( '*** Creating links\n' )
net.addLink( h1, s3 )
net.addLink( h2, s3 )
h1.linkTo( s3 )
h2.linkTo( s3 )
info( '*** Starting network\n')
net.start()
+6 -15
View File
@@ -6,12 +6,10 @@ hardware interface) to a network after the network is created.
"""
import re
import sys
from mininet.cli import CLI
from mininet.log import setLogLevel, info, error
from mininet.net import Mininet
from mininet.link import Intf
from mininet.topolib import TreeTopo
from mininet.util import quietRun
@@ -23,29 +21,22 @@ def checkIntf( intf ):
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
if ips:
error( 'Error:', intf, 'has an IP address,'
'and is probably in use!\n' )
'and is probably in use!\n' )
exit( 1 )
if __name__ == '__main__':
setLogLevel( 'info' )
# try to get hw intf from the command line; by default, use eth1
intfName = sys.argv[ 1 ] if len( sys.argv ) > 1 else 'eth1'
info( '*** Connecting to hw intf: %s' % intfName )
info( '*** Checking', intfName, '\n' )
checkIntf( intfName )
newIntf = 'eth1'
info( '*** Checking', newIntf, '\n' )
checkIntf( newIntf )
info( '*** Creating network\n' )
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
switch = net.switches[ 0 ]
info( '*** Adding hardware interface', intfName, 'to switch',
switch.name, '\n' )
_intf = Intf( intfName, node=switch )
info( '*** Note: you may need to reconfigure the interfaces for '
'the Mininet hosts:\n', net.hosts, '\n' )
info( '*** Adding', newIntf, 'to switch', switch.name, '\n' )
switch.addIntf( newIntf )
net.start()
CLI( net )
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/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 )
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 5 Mb\n' )
link1.intf1.config( bw=5 )
info( '\n*** Running iperf to test\n' )
net.iperf()
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()
+24 -15
View File
@@ -9,35 +9,44 @@ 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.log import setLogLevel
from time import sleep
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 ):
def testCpuLimit( net, cpu ):
"run CPU limit test"
pct = cpu * 100
print '*** Testing CPU %.0f%% bandwidth limit' % pct
h1, h2 = net.hosts
h1.cmd( 'while true; do a=1; done &' )
h2.cmd( 'while true; do a=1; done &' )
pid1 = h1.cmd( 'echo $!' ).strip()
pid2 = h2.cmd( 'echo $!' ).strip()
cmd = 'ps -p %s,%s -o pid,%%cpu,args' % ( pid1, pid2 )
# It's a shame that this is what pylint prefers
for _ in range( 5 ):
sleep( 1 )
print quietRun( cmd ).strip()
h1.cmd( 'kill %1')
h2.cmd( 'kill %1')
def limit( bw=10, cpu=.4 ):
"""Example/test of link and CPU bandwidth limits
bw: interface bandwidth limit in Mbps
cpu: cpu limit as fraction of overall CPU time"""
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 )
net.start()
testLinkLimit( net, bw=bw )
net.runCpuLimitTest( cpu=cpu )
testCpuLimit( net, cpu=cpu )
net.stop()
def verySimpleLimit( bw=150 ):
+10 -26
View File
@@ -24,12 +24,10 @@ of switches, this example demonstrates:
"""
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.node import UserSwitch, OVSKernelSwitch
from mininet.topo import Topo
from mininet.log import lg
from mininet.util import irange, quietRun
from mininet.link import TCLink
from functools import partial
from mininet.util import irange
import sys
flush = sys.stdout.flush
@@ -43,22 +41,22 @@ class LinearTestTopo( Topo ):
Topo.__init__( self, **params )
# Create switches and hosts
hosts = [ self.addHost( 'h%s' % h )
hosts = [ self.add_host( 'h%s' % h )
for h in irange( 1, N ) ]
switches = [ self.addSwitch( 's%s' % s )
switches = [ self.add_switch( 's%s' % s )
for s in irange( 1, N - 1 ) ]
# Wire up switches
last = None
for switch in switches:
if last:
self.addLink( last, switch )
self.add_link( last, switch )
last = switch
# Wire up hosts
self.addLink( hosts[ 0 ], switches[ 0 ] )
self.add_link( hosts[ 0 ], switches[ 0 ] )
for host, switch in zip( hosts[ 1: ], switches ):
self.addLink( host, switch )
self.add_link( host, switch )
def linearBandwidthTest( lengths ):
@@ -70,26 +68,15 @@ def linearBandwidthTest( lengths ):
hostCount = switchCount + 1
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' ]
'Open vSwitch kernel': OVSKernelSwitch }
topo = LinearTestTopo( hostCount )
# Select TCP Reno
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath in switches.keys():
print "*** testing", datapath, "datapath"
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='1ms' )
net = Mininet( topo=topo, switch=Switch,
controller=Controller, waitConnected=True,
link=link )
net = Mininet( topo=topo, switch=Switch )
net.start()
print "*** testing basic connectivity"
for n in lengths:
@@ -97,11 +84,8 @@ def linearBandwidthTest( lengths ):
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' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ], seconds=10 )
bandwidth = net.iperf( [ src, dst ] )
print bandwidth
flush()
results[ datapath ] += [ ( n, bandwidth ) ]
-74
View File
@@ -1,74 +0,0 @@
#!/usr/bin/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 topology contains a router with three IP subnets:
- 192.168.1.0/24 (interface IP: 192.168.1.1)
- 172.16.0.0/12 (interface IP: 172.16.0.1)
- 10.0.0.0/8 (interface IP: 10.0.0.1)
It also contains three hosts, one in each subnet:
- h1 (IP: 192.168.1.100)
- h2 (IP: 172.16.0.100)
- h3 (IP: 10.0.0.100)
Routing entries can be added to the routing tables of the
hosts or router using the "ip route add" or "route add" command.
See the man pages for more details.
"""
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."
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 simple topology of a router with three subnets (one host in each)."
def build( self, **_opts ):
router = self.addNode( 'r0', cls=LinuxRouter, ip='192.168.1.1/24' )
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' )
self.addLink( h1, router, intfName2='r0-eth1',
params2={ 'ip' : '192.168.1.1/24' } )
self.addLink( h2, router, intfName2='r0-eth2',
params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( h3, router, intfName2='r0-eth3',
params2={ 'ip' : '10.0.0.1/8' } )
def run():
"Test linux router"
topo = NetworkTopo()
net = Mininet( topo=topo, controller=None ) # no controller needed
net.start()
info( '*** Routing Table on Router\n' )
print net[ 'r0' ].cmd( 'route' )
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
run()
+101 -2982
View File
File diff suppressed because it is too large Load Diff
-134
View File
@@ -1,134 +0,0 @@
#!/usr/bin/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 mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.log import output, warn
from random import randint
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 ]
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"
print '* Simple mobility test'
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
print '* Starting network:'
net.start()
printConnections( net.switches )
print '* Testing network'
net.pingAll()
print '* Identifying switch interface for h1'
h1, old = net.get( 'h1', 's1' )
for s in 2, 3, 1:
new = net[ 's%d' % s ]
port = randint( 10, 20 )
print '* Moving', h1, 'from', old, 'to', new, 'port', port
hintf, sintf = moveHost( h1, old, new, newPort=port )
print '*', hintf, 'is now connected to', sintf
print '* Clearing out old flows'
for sw in net.switches:
sw.dpctl( 'del-flows' )
print '* New network:'
printConnections( net.switches )
print '* Testing connectivity:'
net.pingAll()
old = new
net.stop()
if __name__ == '__main__':
mobilityTest()
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/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 )
net.start()
CLI( net )
net.stop()
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
def __init__( self, n, **kwargs ):
Topo.__init__( self, **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()
+7 -4
View File
@@ -18,13 +18,18 @@ from time import time
def chunks( l, n ):
"Divide list l into chunks of size n - thanks Stackoverflow"
return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
return [ l[ i : i + n ] for i in range( 0, len( l ), n ) ]
def startpings( host, targetips ):
"Tell host to repeatedly ping targets"
targetips.append( '10.0.0.200' )
targetips = ' '.join( targetips )
# BL: Not sure why loopback intf isn't up!
host.cmd( 'ifconfig lo up' )
# Simple ping loop
cmd = ( 'while true; do '
' for ip in %s; do ' % targetips +
@@ -35,7 +40,7 @@ def startpings( host, targetips ):
'done &' )
print ( '*** Host %s (%s) will be pinging ips: %s' %
( host.name, host.IP(), targetips ) )
( host.name, host.IP(), targetips ) )
host.cmd( cmd )
@@ -58,8 +63,6 @@ def multiping( netsize, chunksize, seconds):
# Start pings
for subnet in subnets:
ips = [ host.IP() for host in subnet ]
#adding bogus to generate packet loss
ips.append( '10.0.0.200' )
for host in subnet:
startpings( host, ips )
+1 -1
View File
@@ -19,7 +19,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.iteritems():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
tails[ h ] = tail
fdToFile[ fd ] = tail.stdout
+1 -1
View File
@@ -22,7 +22,7 @@ if __name__ == '__main__':
info( "*** Initializing Mininet and kernel modules\n" )
OVSKernelSwitch.setup()
info( "*** Creating network\n" )
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch)
info( "*** Starting network\n" )
network.start()
info( "*** Running ping test\n" )
-112
View File
@@ -1,112 +0,0 @@
#!/usr/bin/python
"""
Example to create a Mininet topology and connect it to the internet via NAT
through eth0 on the host.
Glen Gibb, February 2011
(slight modifications by BL, 5/13)
"""
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeNet
#################################
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
"""Start NAT/forwarding between Mininet and external network
root: node to access iptables from
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
# Identify the interface connecting to the mininet network
localIntf = root.defaultIntf()
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
root.cmd( 'iptables -P INPUT ACCEPT' )
root.cmd( 'iptables -P OUTPUT ACCEPT' )
root.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
def stopNAT( root ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
def fixNetworkManager( root, intf ):
"""Prevent network-manager from messing with our interface,
by specifying manual configuration in /etc/network/interfaces
root: a node in the root namespace (for running commands)
intf: interface name"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if line not in config:
print '*** Adding', line.strip(), 'to', cfile
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
root.cmd( 'service network-manager restart' )
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
"""Connect the network to the internet
switch: switch to connect to root namespace
rootip: address for interface in root namespace
subnet: Mininet subnet"""
switch = network.get( switch )
prefixLen = subnet.split( '/' )[ 1 ]
# Create a node in root namespace
root = Node( 'root', inNamespace=False )
# Prevent network-manager from interfering with our interface
fixNetworkManager( root, 'root-eth0' )
# Create link between root NS and switch
link = network.addLink( root, switch )
link.intf1.setIP( rootip, prefixLen )
# Start network that now includes link to root namespace
network.start()
# Start NAT and establish forwarding
startNAT( root )
# Establish routes from end hosts
for host in network.hosts:
host.cmd( 'ip route flush root 0/0' )
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
host.cmd( 'route add default gw', rootip )
return root
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
# Configure and start NATted connectivity
rootnode = connectToInternet( net )
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
CLI( net )
# Shut down NAT
stopNAT( rootnode )
net.stop()
-69
View File
@@ -1,69 +0,0 @@
#!/usr/bin/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."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
# 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)
net.start()
CLI(net)
net.stop()
if __name__ == '__main__':
setLogLevel('info')
run()
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/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 )
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' )
print '\n'
# test the network with pingall
net.pingAll()
print '\n'
info( '*** Stopping network' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
testPortNumbering()
+1 -1
View File
@@ -23,7 +23,7 @@ def pmonitorTest( N=3, seconds=10 ):
endTime = time() + seconds
for h, line in pmonitor( popens, timeoutms=500 ):
if h:
print '<%s>: %s' % ( h.name, line ),
print '%s: %s' % ( h.name, line ),
if time() >= endTime:
for p in popens.values():
p.send_signal( SIGINT )
+4 -4
View File
@@ -38,12 +38,12 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
h1intf, sintf2 = linkIntfs( h1, switch )
info( '*** Configuring control network\n' )
controller.setIP( '10.0.123.1/24', intf=cintf )
switch.setIP( '10.0.123.2/24', intf=sintf)
controller.setIP( '10.0.123.1/24', cintf )
switch.setIP( '10.0.123.2/24', sintf)
info( '*** Configuring hosts\n' )
h0.setIP( '192.168.123.1/24', intf=h0intf )
h1.setIP( '192.168.123.2/24', intf=h1intf )
h0.setIP( '192.168.123.1/24', h0intf )
h1.setIP( '192.168.123.2/24', h1intf )
info( '*** Network state:\n' )
for node in controller, switch, h0, h1:
Executable → Regular
+11 -15
View File
@@ -2,11 +2,6 @@
"""
Simple example of setting network and CPU parameters
NOTE: link params limit BW, add latency, and loss.
There is a high chance that pings WILL fail and that
iperf will hang indefinitely if the TCP handshake fails
to complete.
"""
from mininet.topo import Topo
@@ -20,27 +15,28 @@ class SingleSwitchTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
switch = self.addSwitch('s1')
switch = self.add_switch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
host = self.addHost('h%s' % (h + 1),
cpu=.5 / n)
host = self.add_host('h%s' % (h + 1),
cpu=.5 / n)
# 10 Mbps, 5ms delay, 10% loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
self.add_link(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
def perfTest():
"Create network and run simple performance test"
topo = SingleSwitchTopo( n=4 )
net = Mininet( topo=topo,
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
topo = SingleSwitchTopo(n=4)
net = Mininet(topo=topo,
host=CPULimitedHost, link=TCLink)
net.start()
print "Dumping host connections"
dumpNodeConnections(net.hosts)
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' )
net.iperf((h1, h4))
net.stop()
if __name__ == '__main__':
+12 -27
View File
@@ -16,54 +16,43 @@ demonstrates:
- running server processes (sshd in this case) on hosts
"""
import sys
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node
from mininet.node import Node, OVSKernelSwitch
from mininet.topolib import TreeTopo
from mininet.link import Link
from mininet.util import waitListening
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout )
return Mininet( topo, **kwargs )
def connectToRootNS( network, switch, ip, routes ):
def connectToRootNS( network, switch, ip, prefixLen, routes ):
"""Connect hosts to root namespace via switch. Starts network.
network: Mininet() network object
switch: switch to connect to root namespace
ip: IP address for root namespace node
prefixLen: IP address prefix length (e.g. 8, 16, 24)
routes: host networks to route to"""
# Create a node in root namespace and link to switch 0
root = Node( 'root', inNamespace=False )
intf = Link( root, switch ).intf1
root.setIP( ip, intf=intf )
root.setIP( ip, prefixLen, intf )
# Start network that now includes link to root namespace
network.start()
# Add routes from root ns to hosts
for route in routes:
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
ip='10.123.123.1/32', routes=None, switch=None ):
"""Start a network, connect it to root ns, and run sshd on all hosts.
ip: root-eth0 IP address in root namespace (10.123.123.1/32)
routes: Mininet host networks to route to (10.0/24)
switch: Mininet switch to connect to root namespace (s1)"""
if not switch:
switch = network[ 's1' ] # switch to use
if not routes:
routes = [ '10.0.0.0/24' ]
connectToRootNS( network, switch, ip, routes )
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
"Start a network, connect it to root ns, and run sshd on all hosts."
switch = network.switches[ 0 ] # switch to use
ip = '10.123.123.1' # our IP address on host network
routes = [ '10.0.0.0/8' ] # host networks to route to
connectToRootNS( network, switch, ip, 8, routes )
for host in network.hosts:
host.cmd( cmd + ' ' + opts + '&' )
print "*** Waiting for ssh daemons to start"
for server in network.hosts:
waitListening( server=server, port=22, timeout=5 )
print
print "*** Hosts are running sshd at the following addresses:"
print
@@ -78,9 +67,5 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
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 (
'-D -o UseDNS=no -u0' )
sshd( net, opts=argvopts )
net = TreeNet( depth=1, fanout=4, switch=OVSKernelSwitch )
sshd( net )
-41
View File
@@ -1,41 +0,0 @@
#!/usr/bin/env python
"""
Run all mininet.examples tests
-v : verbose output
-quick : skip tests that take more than ~30 seconds
"""
import unittest
import os
import sys
from mininet.util import ensureRoot
from mininet.clean import cleanup
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
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
MininetTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
# get the directory containing example tests
testDir = os.path.dirname( os.path.realpath( __file__ ) )
verbosity = 2 if '-v' in sys.argv else 1
runTests( testDir, verbosity )
-64
View File
@@ -1,64 +0,0 @@
#!/usr/bin/env python
"""
Tests for baresshd.py
"""
import unittest
import pexpect
from mininet.clean import cleanup, sh
class testBareSSHD( unittest.TestCase ):
opts = [ 'Welcome to h1', pexpect.EOF, pexpect.TIMEOUT ]
def connected( self ):
"Log into ssh server, check banner, then exit"
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
while True:
index = p.expect( self.opts )
if index == 0:
return True
else:
return False
def setUp( self ):
# verify that sshd is not running
self.assertFalse( self.connected() )
# create public key pair for testing
sh( 'rm -rf /tmp/ssh' )
sh( 'mkdir /tmp/ssh' )
sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" )
sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' )
# run example with custom sshd args
cmd = ( 'python -m mininet.examples.baresshd '
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
'-o StrictModes=no' )
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' )
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()
self.assertTrue( result )
def tearDown( self ):
# kill the ssh process
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
cleanup()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
if __name__ == '__main__':
unittest.main()
-66
View File
@@ -1,66 +0,0 @@
#!/usr/bin/env python
"""
Tests for bind.py
"""
import unittest
import pexpect
class testBind( unittest.TestCase ):
prompt = 'mininet>'
def setUp( self ):
self.net = pexpect.spawn( 'python -m mininet.examples.bind' )
self.net.expect( "Private Directories: \[([\w\s,'/]+)\]" )
self.directories = []
# parse directories from mn output
for d in self.net.match.group(1).split(', '):
self.directories.append( d.strip("'") )
self.net.expect( self.prompt )
self.assertTrue( len( self.directories ) > 0 )
def testCreateFile( self ):
"Create a file, a.txt, in the first private directory and verify"
fileName = 'a.txt'
directory = self.directories[ 0 ]
path = directory + '/' + fileName
self.net.sendline( 'h1 touch %s; ls %s' % ( path, directory ) )
index = self.net.expect( [ fileName, self.prompt ] )
self.assertTrue( index == 0 )
self.net.expect( self.prompt )
self.net.sendline( 'h1 rm %s' % path )
self.net.expect( self.prompt )
def testIsolation( self ):
"Create a file in two hosts and verify that contents are different"
fileName = 'b.txt'
directory = self.directories[ 0 ]
path = directory + '/' + fileName
contents = { 'h1' : '1', 'h2' : '2' }
# Verify file doesn't exist, then write private copy of file
for host in contents:
value = contents[ host ]
self.net.sendline( '%s cat %s' % ( host, path ) )
self.net.expect( 'No such file' )
self.net.expect( self.prompt )
self.net.sendline( '%s echo %s > %s' % ( host, value, path ) )
self.net.expect( self.prompt )
# Verify file contents
for host in contents:
value = contents[ host ]
self.net.sendline( '%s cat %s' % ( host, path ) )
self.net.expect( value )
self.net.expect( self.prompt )
self.net.sendline( '%s rm %s' % ( host, path ) )
self.net.expect( self.prompt )
# TODO: need more tests
def tearDown( self ):
self.net.sendline( 'exit' )
self.net.wait()
if __name__ == '__main__':
unittest.main()
-27
View File
@@ -1,27 +0,0 @@
#!/usr/bin/env python
'''
A simple sanity check test for cluster edition
'''
import unittest
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( '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()
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env python
"""
Tests for controllers.py and controllers2.py
"""
import unittest
import pexpect
class testControllers( unittest.TestCase ):
prompt = 'mininet>'
def connectedTest( self, name, cmap ):
"Verify that switches are connected to the controller specified by cmap"
p = pexpect.spawn( 'python -m %s' % name )
p.expect( self.prompt )
# but first a simple ping test
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# verify connected controller
for switch in cmap:
p.sendline( 'sh ovs-vsctl get-controller %s' % switch )
p.expect( 'tcp:([\d.:]+)')
actual = p.match.group(1)
expected = cmap[ switch ]
self.assertEqual( actual, expected )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testControllers( self ):
c0 = '127.0.0.1:6633'
c1 = '127.0.0.1:6634'
cmap = { 's1': c0, 's2': c1, 's3': c0 }
self.connectedTest( 'mininet.examples.controllers', cmap )
def testControllers2( self ):
c0 = '127.0.0.1:6633'
c1 = '127.0.0.1:6634'
cmap = { 's1': c0, 's2': c1 }
self.connectedTest( 'mininet.examples.controllers2', cmap )
if __name__ == '__main__':
unittest.main()
-47
View File
@@ -1,47 +0,0 @@
#!/usr/bin/env python
"""
Test for controlnet.py
"""
import unittest
import pexpect
class testControlNet( unittest.TestCase ):
prompt = 'mininet>'
def testPingall( self ):
"Simple pingall test that verifies 0% packet drop in data network"
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testFailover( self ):
"Kill controllers and verify that switch, s1, fails over properly"
count = 1
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
p.expect( self.prompt )
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' )
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
ip = int( lp.match.group( 1 ) )
self.assertEqual( count, ip )
count += 1
for c in [ 'c0', 'c1' ]:
p.sendline( '%s ifconfig %s-eth0 down' % ( c, c) )
p.expect( self.prompt )
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
ip = int( lp.match.group( 1 ) )
self.assertEqual( count, ip )
count += 1
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
-51
View File
@@ -1,51 +0,0 @@
#!/usr/bin/env python
"""
Test for cpu.py
results format:
sched cpu client MB/s
cfs 45.00% 13254.669841
cfs 40.00% 11822.441399
cfs 30.00% 5112.963009
cfs 20.00% 3449.090009
cfs 10.00% 2271.741564
"""
import unittest
import pexpect
import sys
class testCPU( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testCPU( self ):
"Verify that CPU utilization is monotonically decreasing for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
# matches each line from results( shown above )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
bw = float( p.match.group( 3 ) )
if sched not in scheds:
scheds.append( sched )
else:
self.assertTrue( bw < previous_bw )
previous_bw = bw
else:
break
self.assertTrue( len( scheds ) > 0 )
if __name__ == '__main__':
unittest.main()
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python
"""
Test for emptynet.py
"""
import unittest
import pexpect
class testEmptyNet( unittest.TestCase ):
prompt = 'mininet>'
def testEmptyNet( self ):
"Run simple CLI tests: pingall (verify 0% drop) and iperf (sanity)"
p = pexpect.spawn( 'python -m mininet.examples.emptynet' )
p.expect( self.prompt )
# pingall test
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# iperf test
p.sendline( 'iperf' )
p.expect( "Results: \['[\d.]+ .bits/sec', '[\d.]+ .bits/sec'\]" )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
-65
View File
@@ -1,65 +0,0 @@
#!/usr/bin/env python
"""
Test for hwintf.py
"""
import unittest
import re
import pexpect
from mininet.log import setLogLevel
from mininet.node import Node
from mininet.link import Link
class testHwintf( unittest.TestCase ):
prompt = 'mininet>'
def setUp( self ):
self.h3 = Node( 't0', ip='10.0.0.3/8' )
self.n0 = Node( 't1', inNamespace=False )
Link( self.h3, self.n0 )
self.h3.configDefault()
def testLocalPing( self ):
"Verify connectivity between virtual hosts using pingall"
p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() )
p.expect( self.prompt )
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def testExternalPing( self ):
"Verify connnectivity between virtual host and virtual-physical 'external' host "
p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() )
p.expect( self.prompt )
# test ping external to internal
expectStr = '(\d+) packets transmitted, (\d+) received'
m = re.search( expectStr, self.h3.cmd( 'ping -v -c 1 10.0.0.1' ) )
tx = m.group( 1 )
rx = m.group( 2 )
self.assertEqual( tx, rx )
# test ping internal to external
p.sendline( 'h1 ping -c 1 10.0.0.3')
p.expect( expectStr )
tx = p.match.group( 1 )
rx = p.match.group( 2 )
self.assertEqual( tx, rx )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
def tearDown( self ):
self.h3.terminate()
self.n0.terminate()
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
-42
View File
@@ -1,42 +0,0 @@
#!/usr/bin/env python
"""
Test for intfOptions.py
"""
import unittest
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 = .8
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 = float( p.match.group( 1 ) )
self.assertGreaterEqual( bw, float( 5 * tolerance ) )
self.assertLessEqual( bw, float( 5 + 5 * ( 1 - tolerance ) ) )
elif index == 1:
BW = 10
measuredBw = float( p.match.group( 1 ) )
loss = ( measuredBw / BW ) * 100
self.assertGreaterEqual( loss, 50 * tolerance )
self.assertLessEqual( loss, 50 + 50 * ( 1 - tolerance ) )
elif index == 2:
delay = float( p.match.group( 6 ) )
self.assertGreaterEqual( delay, 15 * tolerance )
self.assertLessEqual( delay, 15 + 15 * ( 1 - tolerance ) )
else:
break
if __name__ == '__main__':
unittest.main()
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env python
"""
Test for limit.py
"""
import unittest
import pexpect
import sys
class testLimit( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testLimit( self ):
"Verify that CPU limits are within a 2% tolerance of limit for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.limit' )
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
pexpect.EOF ]
count = 0
bw = 0
tolerance = 2
while True:
index = p.expect( opts )
if index == 0:
bw = float( p.match.group( 1 ) )
count += 1
elif index == 1:
results = p.match.group( 1 )
for x in results.split( ',' ):
result = float( x )
self.assertTrue( result < bw + tolerance )
self.assertTrue( result > bw - tolerance )
else:
break
self.assertTrue( count > 0 )
if __name__ == '__main__':
unittest.main()
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python
"""
Test for linearbandwidth.py
"""
import unittest
import pexpect
import sys
class testLinearBandwidth( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testLinearBandwidth( self ):
"Verify that bandwidth is monotonically decreasing as # of hops increases"
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
count = 0
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
count += 1
elif index == 1:
n = int( p.match.group( 1 ) )
bw = float( p.match.group( 2 ) )
unit = p.match.group( 3 )
if unit[ 0 ] == 'K':
bw *= 10 ** 3
elif unit[ 0 ] == 'M':
bw *= 10 ** 6
elif unit[ 0 ] == 'G':
bw *= 10 ** 9
# check that we have a previous result to compare to
if n != 1:
info = ( 'bw: %d bits/s across %d switches, '
'previous: %d bits/s across %d switches' %
( bw, n, previous_bw, previous_n ) )
self.assertTrue( bw < previous_bw, info )
previous_bw, previous_n = bw, n
else:
break
# verify that we received results from at least one switch
self.assertTrue( count > 0 )
if __name__ == '__main__':
unittest.main()
-52
View File
@@ -1,52 +0,0 @@
#!/usr/bin/env python
"""
Test for linuxrouter.py
"""
import unittest
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
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()
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env python
"""
Test for multiping.py
"""
import unittest
import pexpect
from collections import defaultdict
class testMultiPing( unittest.TestCase ):
def testMultiPing( self ):
"""Verify that each target is pinged at least once, and
that pings to 'real' targets are successful and unknown targets fail"""
p = pexpect.spawn( 'python -m mininet.examples.multiping' )
opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)",
"(h\d+): ([\d.]+) -> ([\d.]+) \d packets transmitted, (\d) received",
pexpect.EOF ]
pings = defaultdict( list )
while True:
index = p.expect( opts )
if index == 0:
name = p.match.group(1)
ip = p.match.group(2)
targets = p.match.group(3).split()
pings[ name ] += targets
elif index == 1:
name = p.match.group(1)
ip = p.match.group(2)
target = p.match.group(3)
received = int( p.match.group(4) )
if target == '10.0.0.200':
self.assertEqual( received, 0 )
else:
self.assertEqual( received, 1 )
try:
pings[ name ].remove( target )
except:
pass
else:
break
self.assertTrue( len( pings ) > 0 )
for t in pings.values():
self.assertEqual( len( t ), 0 )
if __name__ == '__main__':
unittest.main()
-38
View File
@@ -1,38 +0,0 @@
#!/usr/bin/env python
"""
Test for multipoll.py
"""
import unittest
import pexpect
class testMultiPoll( unittest.TestCase ):
def testMultiPoll( self ):
"Verify that we receive one ping per second per host"
p = pexpect.spawn( 'python -m mininet.examples.multipoll' )
opts = [ "\*\*\* (h\d) :" ,
"(h\d+): \d+ bytes from",
"Monitoring output for (\d+) seconds",
pexpect.EOF ]
pings = {}
while True:
index = p.expect( opts )
if index == 0:
name = p.match.group( 1 )
pings[ name ] = 0
elif index == 1:
name = p.match.group( 1 )
pings[ name ] += 1
elif index == 2:
seconds = int( p.match.group( 1 ) )
else:
break
self.assertTrue( len( pings ) > 0 )
# make sure we have received at least one ping per second
for count in pings.values():
self.assertTrue( count >= seconds )
if __name__ == '__main__':
unittest.main()
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python
"""
Test for multitest.py
"""
import unittest
import pexpect
class testMultiTest( unittest.TestCase ):
prompt = 'mininet>'
def testMultiTest( self ):
"Verify pingall (0% dropped) and hX-eth0 interface for each host (ifconfig)"
p = pexpect.spawn( 'python -m mininet.examples.multitest' )
p.expect( '(\d+)% dropped' )
dropped = int( p.match.group( 1 ) )
self.assertEqual( dropped, 0 )
ifCount = 0
while True:
index = p.expect( [ 'h\d-eth0', self.prompt ] )
if index == 0:
ifCount += 1
elif index == 1:
p.sendline( 'exit' )
break
p.wait()
self.assertEqual( ifCount, 4 )
if __name__ == '__main__':
unittest.main()
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python
"""
Test for nat.py
"""
import unittest
import pexpect
from mininet.util import quietRun
destIP = '8.8.8.8' # Google DNS
class testNAT( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
'Destination IP is not reachable' )
def testNAT( self ):
"Attempt to ping an IP on the Internet and verify 0% packet loss"
p = pexpect.spawn( 'python -m mininet.examples.nat' )
p.expect( self.prompt )
p.sendline( 'h1 ping -c 1 %s' % destIP )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
if __name__ == '__main__':
unittest.main()
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/env python
"""
Test for natnet.py
"""
import unittest
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
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()
-45
View File
@@ -1,45 +0,0 @@
#!/usr/bin/env python
"""
Test for popen.py and popenpoll.py
"""
import unittest
import pexpect
class testPopen( unittest.TestCase ):
def pingTest( self, name ):
"Verify that there are no dropped packets for each host"
p = pexpect.spawn( 'python -m %s' % name )
opts = [ "<(h\d+)>: PING ",
"<(h\d+)>: (\d+) packets transmitted, (\d+) received",
pexpect.EOF ]
pings = {}
while True:
index = p.expect( opts )
if index == 0:
name = p.match.group(1)
pings[ name ] = 0
elif index == 1:
name = p.match.group(1)
transmitted = p.match.group(2)
received = p.match.group(3)
# verify no dropped packets
self.assertEqual( received, transmitted )
pings[ name ] += 1
else:
break
self.assertTrue( len(pings) > 0 )
# verify that each host has gotten results
for count in pings.values():
self.assertEqual( count, 1 )
def testPopen( self ):
self.pingTest( 'mininet.examples.popen' )
def testPopenPoll( self ):
self.pingTest( 'mininet.examples.popenpoll' )
if __name__ == '__main__':
unittest.main()
-27
View File
@@ -1,27 +0,0 @@
#!/usr/bin/env python
"""
Test for scratchnet.py
"""
import unittest
import pexpect
class testScratchNet( unittest.TestCase ):
opts = [ "1 packets transmitted, 1 received, 0% packet loss", pexpect.EOF ]
def pingTest( self, name ):
"Verify that no ping packets were dropped"
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts )
self.assertEqual( index, 0 )
def testPingKernel( self ):
self.pingTest( 'mininet.examples.scratchnet' )
def testPingUser( self ):
self.pingTest( 'mininet.examples.scratchnetuser' )
if __name__ == '__main__':
unittest.main()
-34
View File
@@ -1,34 +0,0 @@
#!/usr/bin/env python
"""
Test for simpleperf.py
"""
import unittest
import pexpect
import sys
from mininet.log import setLogLevel
from mininet.examples.simpleperf import SingleSwitchTopo
class testSimplePerf( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testE2E( self ):
"Run the example and verify iperf results"
BW = 10
TOLERANCE = .8
expectedBw = BW * TOLERANCE
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
measuredBw = float( p.match.group( 1 ) )
lowerBound = expectedBw * TOLERANCE
upperBound = expectedBw + expectedBw * ( 1 - TOLERANCE )
self.assertGreaterEqual( measuredBw, lowerBound )
self.assertLessEqual( measuredBw, upperBound )
p.wait()
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
-60
View File
@@ -1,60 +0,0 @@
#!/usr/bin/env python
"""
Test for sshd.py
"""
import unittest
import pexpect
from mininet.clean import sh
class testSSHD( unittest.TestCase ):
opts = [ '\(yes/no\)\?', 'refused', 'Welcome|\$|#', pexpect.EOF, pexpect.TIMEOUT ]
def connected( self, ip ):
"Log into ssh server, check banner, then exit"
# Note: this test will fail if "Welcome" is not in the sshd banner
# and '#'' or '$'' are not in the prompt
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
while True:
index = p.expect( self.opts )
if index == 0:
print p.match.group(0)
p.sendline( 'yes' )
elif index == 1:
return False
elif index == 2:
p.sendline( 'exit' )
p.wait()
return True
else:
return False
def setUp( self ):
# create public key pair for testing
sh( 'rm -rf /tmp/ssh' )
sh( 'mkdir /tmp/ssh' )
sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" )
sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' )
cmd = ( 'python -m mininet.examples.sshd -D '
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
'-o StrictModes=no -o UseDNS=no -u0' )
# run example with custom sshd args
self.net = pexpect.spawn( cmd )
self.net.expect( 'mininet>' )
def testSSH( self ):
"Verify that we can ssh into all hosts (h1 to h4)"
for h in range( 1, 5 ):
self.assertTrue( self.connected( '10.0.0.%d' % h ) )
def tearDown( self ):
self.net.sendline( 'exit' )
self.net.wait()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
if __name__ == '__main__':
unittest.main()
-29
View File
@@ -1,29 +0,0 @@
#!/usr/bin/env python
"""
Test for tree1024.py
"""
import unittest
import pexpect
import sys
class testTree1024( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testTree1024( self ):
"Run the example and do a simple ping test from h1 to h1024"
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
p.sendline( 'h1 ping -c 1 h1024' )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
if __name__ == '__main__':
unittest.main()
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python
"""
Test for treeping64.py
"""
import unittest
import pexpect
import sys
class testTreePing64( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testTreePing64( self ):
"Run the example and verify ping results"
p = pexpect.spawn( 'python -m mininet.examples.treeping64' )
p.expect( 'Tree network ping results:', timeout=6000 )
count = 0
while True:
index = p.expect( [ '(\d+)% packet loss', pexpect.EOF ] )
if index == 0:
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
count += 1
else:
break
self.assertTrue( count > 0 )
if __name__ == '__main__':
unittest.main()
-50
View File
@@ -1,50 +0,0 @@
#!/usr/bin/env python
"""
Test for vlanhost.py
"""
import unittest
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()
+2 -2
View File
@@ -11,8 +11,8 @@ def treePing64():
results = {}
switches = { # 'reference kernel': KernelSwitch,
'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
'reference user': UserSwitch,
'Open vSwitch kernel': OVSKernelSwitch }
for name in switches:
print "*** Testing", name, "datapath"
-124
View File
@@ -1,124 +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 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"
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 )
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() )
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()
+29 -44
View File
@@ -10,33 +10,35 @@ It may also get rid of 'false positives', but hopefully
nothing irreplaceable!
"""
from subprocess import ( Popen, PIPE, check_output as co,
CalledProcessError )
import time
from subprocess import Popen, PIPE
from mininet.log import info
from mininet.term import cleanUpScreens
import os
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
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 )
CGROUPS_LOC='/mnt/cgroups'
def kill_cgroups(cgroups = None):
"""cgroups is a list of cgroup names."""
if not cgroups:
cpudir = os.path.join(CGROUPS_LOC, 'cpu')
if not os.path.exists(cpudir):
return
else:
break
cgroups = os.listdir(cpudir)
info( "killing cgroups: %s" % " ".join(cgroups) )
for g in cgroups:
if 'sysdefault' in g:
continue
for resource in os.listdir(CGROUPS_LOC):
cgdir = "%s/%s/%s" % (CGROUPS_LOC, resource, g)
if os.path.exists(cgdir):
sh( "sudo rmdir %s" % cgdir )
def cleanup():
"""Clean up junk which might be left over from old runs;
@@ -45,53 +47,36 @@ def 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'
zombies += 'ovs-openflowd udpbwtest'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing old X11 tunnels\n" )
info( "*** Removing old screen sessions\n" )
cleanUpScreens()
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).splitlines()
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' )
for dp in dps:
if dp:
if dp != '':
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths" )
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()
dps = sh("ovs-vsctl list-br").split( '\n' )
for dp in dps:
sh( 'ovs-vsctl del-br ' + dp )
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()
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
for link in links:
if link:
if link != '':
sh( "ip link del " + link )
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/*' )
kill_cgroups()
info( "*** Cleanup complete.\n" )
+58 -149
View File
@@ -31,13 +31,10 @@ from os import isatty
from select import poll, POLLIN
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.term import makeTerms
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
@@ -46,8 +43,13 @@ class CLI( Cmd ):
def __init__( self, mininet, stdin=sys.stdin, script=None ):
self.mn = mininet
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
self.nodemap = {} # map names to Node objects
for node in self.nodelist:
self.nodemap[ node.name ] = node
# Local variable bindings for py command
self.locals = { 'net': mininet }
self.locals.update( self.nodemap )
# Attempt to handle input
self.stdin = stdin
self.inPoller = poll()
@@ -55,30 +57,18 @@ class CLI( Cmd ):
self.inputFile = script
Cmd.__init__( self )
info( '*** Starting CLI:\n' )
# Set up history if readline is available
try:
import readline
except ImportError:
pass
else:
history_path = os.path.expanduser('~/.mininet_history')
if os.path.isfile(history_path):
readline.read_history_file(history_path)
atexit.register(lambda: readline.write_history_file(history_path))
if self.inputFile:
self.do_source( self.inputFile )
return
while True:
try:
# Make sure no nodes are still waiting
for node in self.mn.values():
for node in self.nodelist:
while node.waiting:
node.sendInt()
node.waitOutput()
node.monitor()
if self.isatty():
quietRun( 'stty echo sane intr "^C"' )
quietRun( 'stty sane' )
self.cmdloop()
break
except KeyboardInterrupt:
@@ -88,10 +78,10 @@ class CLI( Cmd ):
"Don't repeat last command when you hit return."
pass
def getLocals( self ):
"Local variable bindings for py command"
self.locals.update( self.mn )
return self.locals
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
@@ -120,31 +110,25 @@ class CLI( Cmd ):
def do_nodes( self, _line ):
"List all nodes."
nodes = ' '.join( sorted( self.mn ) )
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
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() )
dumpNodeConnections( self.nodelist )
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
# do_py() needs to catch any exception during eval()
# pylint: disable-msg=W0703
def do_py( self, line ):
"""Evaluate a Python expression.
Node names may be used, e.g.: py h1.cmd('ls')"""
Node names may be used, e.g.: h1.cmd('ls')"""
try:
result = eval( line, globals(), self.getLocals() )
result = eval( line, globals(), self.locals )
if not result:
return
elif isinstance( result, str ):
@@ -154,38 +138,18 @@ class CLI( Cmd ):
except Exception, e:
output( str( e ) + '\n' )
# We are in fact using the exec() pseudo-function
# pylint: disable=exec-used
# pylint: enable-msg=W0703
def do_px( self, line ):
"""Execute a Python statement.
Node names may be used, e.g.: px print h1.cmd('ls')"""
try:
exec( line, globals(), self.getLocals() )
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable=broad-except,exec-used
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."
self.mn.pingAllFull()
def do_pingpairfull( self, _line ):
"Ping between first two hosts, returns all ping results."
self.mn.pingPairFull()
def do_iperf( self, line ):
"""Simple iperf TCP test between two (optionally specified) hosts.
Usage: iperf node1 node2"""
"Simple iperf TCP test between two (optionally specified) hosts."
args = line.split()
if not args:
self.mn.iperf()
@@ -193,19 +157,18 @@ class CLI( Cmd ):
hosts = []
err = False
for arg in args:
if arg not in self.mn:
if arg not in self.nodemap:
err = True
error( "node '%s' not in network\n" % arg )
else:
hosts.append( self.mn[ arg ] )
hosts.append( self.nodemap[ arg ] )
if not err:
self.mn.iperf( hosts )
else:
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' )
@@ -214,11 +177,11 @@ class CLI( Cmd ):
hosts = []
err = False
for arg in args[ 1:3 ]:
if arg not in self.mn:
if arg not in self.nodemap:
err = True
error( "node '%s' not in network\n" % arg )
else:
hosts.append( self.mn[ arg ] )
hosts.append( self.nodemap[ arg ] )
if not err:
self.mn.iperf( hosts, l4Type='UDP', udpBw=udpBw )
else:
@@ -227,18 +190,17 @@ class CLI( Cmd ):
def do_intfs( self, _line ):
"List interfaces."
for node in self.mn.values():
for node in self.nodelist:
output( '%s: %s\n' %
( node.name, ','.join( node.intfNames() ) ) )
( node.name, ','.join( node.intfNames() ) ) )
def do_dump( self, _line ):
"Dump node info."
for node in self.mn.values():
for node in self.nodelist:
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' )
@@ -248,39 +210,24 @@ 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 )
else:
for arg in args:
if arg not in self.mn:
if arg not in self.nodemap:
error( "node '%s' not in network\n" % arg )
else:
node = self.mn[ arg ]
node = self.nodemap[ arg ]
self.mn.terms += makeTerms( [ node ], term = term )
def do_x( self, line ):
"""Create an X11 tunnel to the given node,
optionally starting a client.
Usage: x node [cmd args]"""
args = line.split()
if not args:
error( 'usage: x node [cmd args]...\n' )
else:
node = self.mn[ args[ 0 ] ]
cmd = args[ 1: ]
self.mn.terms += runX11( node, cmd )
def do_gterm( self, line ):
"""Spawn gnome-terminal(s) for the given node(s).
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 ):
@@ -297,8 +244,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 )
@@ -306,8 +252,7 @@ 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' )
@@ -322,12 +267,10 @@ class CLI( Cmd ):
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 command on all switches."
args = line.split()
if len(args) < 1:
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
@@ -343,33 +286,6 @@ 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:
print link, link.status()
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 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.
@@ -377,27 +293,30 @@ class CLI( Cmd ):
corresponding IP addrs."""
first, args, line = self.parseline( line )
if not args:
return
if args and len(args) > 0 and args[ -1 ] == '\n':
args = args[ :-1 ]
rest = args.split( ' ' )
if first in self.mn:
if not args:
print "*** Enter a command for node: %s <cmd>" % first
return
node = self.mn[ first ]
rest = args.split( ' ' )
if first in self.nodemap:
node = self.nodemap[ first ]
# Substitute IP addresses for node names in command
# If updateIP() returns None, then use node name
rest = [ self.mn[ arg ].defaultIntf().updateIP() or arg
if arg in self.mn else arg
for arg in rest ]
rest = [ self.nodemap[ arg ].IP()
if arg in self.nodemap else arg
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 )
error( '*** Unknown command: %s\n' % first )
# 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 )
@@ -415,7 +334,7 @@ class CLI( Cmd ):
if False and self.inputFile:
key = self.inputFile.read( 1 )
if key is not '':
node.write( key )
node.write(key)
else:
self.inputFile = None
if isReadable( self.inPoller ):
@@ -427,18 +346,8 @@ 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()
def precmd( self, line ):
"allow for comments in the cli"
if '#' in line:
line = line.split( '#' )[ 0 ]
return line
# Helper functions
def isReadable( poller ):
-1
View File
@@ -1 +0,0 @@
../examples
+76 -153
View File
@@ -25,15 +25,15 @@ Link: basic link class for creating veth pairs
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair, quietRun
from mininet.util import makeIntfPair
from time import sleep
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
@@ -41,13 +41,7 @@ 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.mac, self.ip, self.prefixLen = None, None, None
# Add to node (and move ourselves if necessary )
node.addIntf( self, port=port )
# Save params for future reference
@@ -70,9 +64,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 ) )
@@ -89,10 +80,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
@@ -104,19 +92,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
@@ -128,15 +103,8 @@ 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"
@@ -162,9 +130,9 @@ class Intf( object ):
f = getattr( self, method, None )
if not f or value is None:
return
if isinstance( value, list ):
if type( value ) is list:
result = f( *value )
elif isinstance( value, dict ):
elif type( value ) is dict:
result = f( **value )
else:
result = f( value )
@@ -187,22 +155,15 @@ 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 )
if self.node.inNamespace:
# Link may have been dumped into root NS
quietRun( 'ip link del ' + self.name )
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"
# Does it help to sleep to let things run?
sleep( 0.001 )
def __repr__( self ):
return '<%s %s>' % ( self.__class__.__name__, self.name )
@@ -217,13 +178,13 @@ class TCIntf( Intf ):
as well as delay, loss and max queue length"""
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False ):
enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
if bw and ( bw < 0 or bw > 10000 ):
warn( 'Bandwidth', bw, 'is outside range 0..10000 Mbps\n' )
elif bw is not None:
# BL: this seems a bit brittle...
@@ -235,63 +196,58 @@ class TCIntf( Intf ):
# are specifying the correct sizes. For now I have used
# the same settings we had in the mininet-hifi code.
if use_hfsc:
cmds += [ '%s qdisc add dev %s root handle 5:0 hfsc default 1',
'%s class add dev %s parent 5:0 classid 5:1 hfsc sc '
cmds += [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
'%s class add dev %s parent 1:0 classid 1:1 hfsc sc '
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
if latency_ms is None:
latency_ms = 15 * 8 / bw
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
latency_us = 10 * 1500 * 8 / bw
cmds += ['%s qdisc add dev %s root handle 1: tbf ' +
'rate %fMbit burst 15000 latency %fus' %
( bw, latency_us ) ]
else:
cmds += [ '%s qdisc add dev %s root handle 5:0 htb default 1',
'%s class add dev %s parent 5:0 classid 5:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 5:1 '
cmds += [ '%s qdisc add dev %s root handle 1:0 htb default 1',
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 1:1 '
# ECN or RED
if enable_ecn:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 6: red limit 1000000 ' +
'handle 10: red limit 1000000 ' +
'min 30000 max 35000 avpkt 1500 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1 ecn' % bw ]
parent = ' parent 6: '
parent = ' parent 10: '
elif enable_red:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 6: red limit 1000000 ' +
'handle 10: red limit 1000000 ' +
'min 30000 max 35000 avpkt 1500 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1' % bw ]
parent = ' parent 6: '
parent = ' parent 10: '
return cmds, parent
@staticmethod
def delayCmds( parent, delay=None, jitter=None,
loss=None, max_queue_size=None ):
def delayCmds( parent, delay=None, loss=None,
max_queue_size=None ):
"Internal method: return tc commands for delay and loss"
cmds = []
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
elif jitter and jitter < 0:
error( 'Negative jitter', jitter, '\n' )
elif loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# Delay/jitter/loss/max queue size
netemargs = '%s%s%s%s' % (
# Delay/loss/max queue size
netemargs = '%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'%s ' % jitter if jitter is not None else '',
'loss %d ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
else '' )
if netemargs:
cmds = [ '%s qdisc add dev %s ' + parent +
' handle 10: netem ' +
netemargs ]
parent = ' parent 10:1 '
return cmds, parent
netemargs ]
return cmds
def tc( self, cmd, tc='tc' ):
"Execute tc command for our interface"
@@ -299,10 +255,9 @@ class TCIntf( Intf ):
debug(" *** executing command: %s\n" % c)
return self.cmd( c )
def config( self, bw=None, delay=None, jitter=None, loss=None,
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 ):
def config( self, bw=None, delay=None, loss=None, disable_gro=True,
speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False,
enable_red=False, max_queue_size=None, **params ):
"Configure the port and set its properties."
result = Intf.config( self, **params)
@@ -318,46 +273,34 @@ class TCIntf( Intf ):
return
# Clear existing configuration
tcoutput = self.tc( '%s qdisc show dev %s' )
if "priomap" 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,
use_hfsc=use_hfsc, use_tbf=use_tbf,
latency_ms=latency_ms,
enable_ecn=enable_ecn,
enable_red=enable_red )
use_hfsc=use_hfsc, use_tbf=use_tbf,
enable_ecn=enable_ecn,
enable_red=enable_red )
cmds += bwcmds
# Delay/jitter/loss/max_queue_size using netem
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter,
loss=loss,
max_queue_size=max_queue_size,
parent=parent )
cmds += delaycmds
# Delay/loss/max_queue_size using netem
cmds += self.delayCmds( delay=delay, loss=loss,
max_queue_size=max_queue_size,
parent=parent )
# Ugly but functional: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
# 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
result[ 'parent' ] = parent
return result
@@ -368,7 +311,7 @@ class Link( object ):
Other types of links could be tunnels, link emulators, etc.."""
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 ):
"""Create veth link to another node, making two new interfaces.
@@ -383,85 +326,65 @@ class Link( object ):
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.
if params1 is None:
params1 = {}
if params2 is None:
params2 = {}
# Allow passing in params1=params2
if params2 is params1:
params2 = dict( params1 )
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 )
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
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 ):
def makeIntfPair( cls, intf1, intf2 ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(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 )
makeIntfPair( intf1, intf2 )
def delete( self ):
"Delete this link"
self.intf1.delete()
self.intf2.delete()
def stop( self ):
"Override to stop and clean up link as needed"
pass
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 TCLink( Link ):
"Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None,
addr1=None, addr2=None, **params ):
intfName1=None, intfName2=None, **params ):
Link.__init__( self, node1, node2, port1=port1, port2=port2,
intfName1=intfName1, intfName2=intfName2,
cls1=TCIntf,
cls2=TCIntf,
addr1=addr1, addr2=addr2,
params1=params,
params2=params )
params2=params)
+20 -18
View File
@@ -11,11 +11,11 @@ import types
OUTPUT = 25
LEVELS = { 'debug': logging.DEBUG,
'info': logging.INFO,
'output': OUTPUT,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL }
'info': logging.INFO,
'output': OUTPUT,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL }
# change this to logging.INFO to get printouts when running unit tests
LOGLEVELDEFAULT = OUTPUT
@@ -57,19 +57,21 @@ class StreamHandlerNoNewline( logging.StreamHandler ):
class Singleton( type ):
"""Singleton pattern from Wikipedia
See http://en.wikipedia.org/wiki/Singleton_Pattern
See http://en.wikipedia.org/wiki/SingletonPattern#Python
Intended to be used as a __metaclass_ param, as shown for the class
below."""
below.
def __init__( cls, name, bases, dict_ ):
super( Singleton, cls ).__init__( name, bases, dict_ )
cls.instance = None
Changed cls first args to mcs to satisfy pylint."""
def __call__( cls, *args, **kw ):
if cls.instance is None:
cls.instance = super( Singleton, cls ).__call__( *args, **kw )
return cls.instance
def __init__( mcs, name, bases, dict_ ):
super( Singleton, mcs ).__init__( name, bases, dict_ )
mcs.instance = None
def __call__( mcs, *args, **kw ):
if mcs.instance is None:
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
return mcs.instance
class MininetLogger( Logger, object ):
@@ -115,7 +117,7 @@ class MininetLogger( Logger, object ):
Convenience function to support lowercase names.
levelName: level name from LEVELS"""
level = LOGLEVELDEFAULT
if levelname is not None:
if levelname != None:
if levelname not in LEVELS:
raise Exception( 'unknown levelname seen in setLogLevel' )
else:
@@ -124,8 +126,8 @@ class MininetLogger( Logger, object ):
self.setLevel( level )
self.handlers[ 0 ].setLevel( level )
# pylint: disable=method-hidden
# "An attribute inherited from mininet.log hide this method" (sic)
# 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()
@@ -142,7 +144,7 @@ class MininetLogger( Logger, object ):
if self.isEnabledFor( OUTPUT ):
self._log( OUTPUT, msg, args, kwargs )
# pylint: enable=method-hidden
# pylint: enable-msg=E0202
lg = MininetLogger()
+7 -7
View File
@@ -19,7 +19,7 @@ def modprobe( mod ):
return quietRun( [ 'modprobe', mod ] )
OF_KMOD = 'ofdatapath'
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
OVS_KMOD = 'openvswitch_mod'
TUN = 'tun'
def moduleDeps( subtract=None, add=None ):
@@ -28,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():
@@ -48,8 +48,8 @@ def moduleDeps( subtract=None, add=None ):
modprobeOutput = modprobe( mod )
if modprobeOutput:
error( 'Error inserting ' + mod +
' - is it installed and available via modprobe?\n' +
'Error was: "%s"\n' % modprobeOutput )
' - is it installed and available via modprobe?\n' +
'Error was: "%s"\n' % modprobeOutput )
if mod not in lsmod():
error( 'Failed to insert ' + mod + ' - quitting.\n' )
exit( 1 )
@@ -63,6 +63,6 @@ def pathCheck( *args, **kwargs ):
for arg in args:
if not quietRun( 'which ' + arg ):
error( 'Cannot find required executable %s.\n' % arg +
'Please make sure that %s is installed ' % moduleName +
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
'Please make sure that %s is installed ' % moduleName +
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
exit( 1 )
+79 -359
View File
@@ -90,34 +90,25 @@ import os
import re
import select
import signal
import random
from time import sleep
from itertools import chain, groupby
from math import ceil
from mininet.cli import CLI
from mininet.log import info, error, debug, output, warn
from mininet.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
from mininet.util import quietRun, fixLimits, numCores
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.2.0"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
controller=DefaultController, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None, waitConnected=False ):
controller=Controller, link=Link, intf=Intf,
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
inNamespace=False,
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
listenPort=None ):
"""Create Mininet object.
topo: Topo (topology) object or None
switch: default Switch class
@@ -153,12 +144,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
@@ -170,36 +159,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,
up to 5 seconds
timeout: time to wait, or None 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
remaining = list( self.switches )
while True:
for switch in tuple( remaining ):
if switch.connected():
info( '%s ' % switch )
remaining.remove( switch )
if not remaining:
info( '\n' )
return True
if time > timeout and timeout is not None:
break
sleep( delay )
time += delay
warn( 'Timed out after %d seconds\n' % time )
for switch in remaining:
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
@@ -212,7 +171,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
@@ -246,50 +205,16 @@ class Mininet( object ):
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
# Get controller class
if not controller:
controller = self.controller
# Construct new controller if one is not given
if isinstance( name, Controller ):
controller_new = name
# Pylint thinks controller is a str()
# pylint: disable=maybe-no-member
name = controller_new.name
# pylint: enable=maybe-no-member
else:
controller_new = controller( name, **params )
# Add new controller to net
controller_new = controller( name, **params )
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
return controller_new
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, self.switches[ 0 ] )
# 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.
# BL: is this better than just using nameToNode[] ?
# Should it have a better name?
def getNodeByName( self, *args ):
"Return node(s) with given name(s)"
if len( args ) == 1:
@@ -300,69 +225,21 @@ class Mininet( object ):
"Convenience alias for getNodeByName"
return self.getNodeByName( *args )
# Even more convenient syntax for node lookup and iteration
def __getitem__( self, key ):
"""net [ name ] operator: Return node(s) with given name(s)"""
return self.nameToNode[ key ]
def __iter__( self ):
"return iterator over node names"
for node in chain( self.hosts, self.switches, self.controllers ):
yield node.name
def __len__( self ):
"returns number of nodes in net"
return ( len( self.hosts ) + len( self.switches ) +
len( self.controllers ) )
def __contains__( self, item ):
"returns True if net contains named node"
return item in self.nameToNode
def keys( self ):
"return a list of all node names or net's keys"
return list( self )
def values( self ):
"return a list of all nodes or net's values"
return [ self[name] for name in self ]
def items( self ):
"return (key,value) tuple list for every node in net"
return zip( self.keys(), self.values() )
@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 )
# 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
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."
@@ -370,7 +247,7 @@ class Mininet( object ):
info( host.name + ' ' )
intf = host.defaultIntf()
if intf:
host.configDefault()
host.configDefault( defaultRoute=intf )
else:
# Don't configure nonexistent intf
host.configDefault( ip=None, mac=None )
@@ -380,6 +257,7 @@ class Mininet( object ):
# 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 ):
@@ -394,18 +272,10 @@ class Mininet( object ):
info( '*** Creating network\n' )
if not self.controllers and self.controller:
if not self.controllers:
# Add a default controller
info( '*** Adding controller\n' )
classes = self.controller
if not isinstance( classes, list ):
classes = [ classes ]
for i, cls in enumerate( classes ):
# Allow Controller objects because nobody understands currying
if isinstance( cls, Controller ):
self.addController( cls )
else:
self.addController( 'c%d' % i, cls )
self.addController( 'c0' )
info( '*** Adding hosts:\n' )
for hostName in topo.hosts():
@@ -418,23 +288,25 @@ class Mininet( object ):
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' )
def configureControlNetwork( self ):
"Control net config hook: override in subclass"
raise Exception( 'configureControlNetwork: '
'should be overriden in subclass', self )
'should be overriden in subclass', self )
def build( self ):
"Build mininet."
if self.topo:
self.buildFromTopo( self.topo )
if self.inNamespace:
if ( self.inNamespace ):
self.configureControlNetwork()
info( '*** Configuring hosts\n' )
self.configHosts()
@@ -446,9 +318,6 @@ class Mininet( object ):
def startTerms( self ):
"Start a terminal for each node."
if 'DISPLAY' not in os.environ:
error( "Error starting terms: Cannot connect to display\n" )
return
info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
cleanUpScreens()
self.terms += makeTerms( self.controllers, 'controller' )
@@ -474,45 +343,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 )
info( '\n' )
if self.waitConn:
self.waitConnected()
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' )
if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
info( '*** Stopping %i switches\n' % len( self.switches ) )
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
if hasattr( swclass, 'batchShutdown' ):
swclass.batchShutdown( switches )
for switch in self.switches:
info( switch.name + ' ' )
switch.stop()
switch.terminate()
info( '\n' )
info( '*** Stopping %i links\n' % len( self.links ) )
for link in self.links:
link.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 switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ' )
switch.stop()
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 ):
@@ -532,13 +388,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:
@@ -555,20 +411,19 @@ 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
return (1, 0)
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
if m == None:
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return 1, 0
pingOutput )
return (1, 0)
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
return sent, received
def ping( self, hosts=None, timeout=None ):
def ping( self, hosts=None ):
"""Ping between all specified hosts.
hosts: list of hosts
timeout: time to wait for a response, as string
returns: ploss packet loss percentage"""
# should we check if running?
packets = 0
@@ -581,15 +436,8 @@ class Mininet( object ):
output( '%s -> ' % node.name )
for dest in hosts:
if node != dest:
opts = ''
if timeout:
opts = '-W %s' % timeout
if dest.intfs:
result = node.cmd( 'ping -c1 %s %s' %
(opts, dest.IP()) )
sent, received = self._parsePing( result )
else:
sent, received = 0, 0
result = node.cmd( 'ping -c1 ' + dest.IP() )
sent, received = self._parsePing( result )
packets += sent
if received > sent:
error( '*** Error: received too many packets' )
@@ -599,84 +447,15 @@ class Mininet( object ):
lost += sent - received
output( ( '%s ' % dest.name ) if received else 'X ' )
output( '\n' )
if packets > 0:
ploss = 100.0 * lost / packets
received = packets - lost
output( "*** Results: %i%% dropped (%d/%d received)\n" %
( ploss, received, packets ) )
else:
ploss = 0
output( "*** Warning: No packets sent\n" )
ploss = 100 * lost / packets
output( "*** Results: %i%% dropped (%d/%d lost)\n" %
( ploss, lost, packets ) )
return ploss
@staticmethod
def _parsePingFull( pingOutput ):
"Parse ping output and return all data."
errorTuple = (1, 0, 0, 0, 0, 0)
# Check for downed link
r = r'[uU]nreachable'
m = re.search( r, pingOutput )
if m is not None:
return errorTuple
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return errorTuple
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
r = r'rtt min/avg/max/mdev = '
r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
m = re.search( r, pingOutput )
if m is None:
if received == 0:
return errorTuple
error( '*** Error: could not parse ping output: %s\n' %
pingOutput )
return errorTuple
rttmin = float( m.group( 1 ) )
rttavg = float( m.group( 2 ) )
rttmax = float( m.group( 3 ) )
rttdev = float( m.group( 4 ) )
return sent, received, rttmin, rttavg, rttmax, rttdev
def pingFull( self, hosts=None, timeout=None ):
"""Ping between all specified hosts and return all data.
hosts: list of hosts
timeout: time to wait for a response, as string
returns: all ping data; see function body."""
# should we check if running?
# Each value is a tuple: (src, dsd, [all ping outputs])
all_outputs = []
if not hosts:
hosts = self.hosts
output( '*** Ping: testing ping reachability\n' )
for node in hosts:
output( '%s -> ' % node.name )
for dest in hosts:
if node != dest:
opts = ''
if timeout:
opts = '-W %s' % timeout
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
outputs = self._parsePingFull( result )
sent, received, rttmin, rttavg, rttmax, rttdev = outputs
all_outputs.append( (node, dest, outputs) )
output( ( '%s ' % dest.name ) if received else 'X ' )
output( '\n' )
output( "*** Results: \n" )
for outputs in all_outputs:
src, dest, ping_outputs = outputs
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
(rttmin, rttavg, rttmax, rttdev) )
return all_outputs
def pingAll( self, 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.
@@ -684,17 +463,6 @@ class Mininet( object ):
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
return self.ping( hosts=hosts )
def pingAllFull( self ):
"""Ping between all hosts.
returns: ploss packet loss percentage"""
return self.pingFull()
def pingPairFull( self ):
"""Ping between first two hosts, useful for testing.
returns: ploss packet loss percentage"""
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
return self.pingFull( hosts=hosts )
@staticmethod
def _parseIperf( iperfOutput ):
"""Parse iperf output and return bandwidth.
@@ -711,23 +479,18 @@ class Mininet( object ):
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
seconds=5):
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: iperf format argument if any
seconds: iperf time to transmit
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"""
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
hosts = hosts or [ self.hosts[ 0 ], self.hosts[ -1 ] ]
assert len( hosts ) == 2
if not hosts:
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else:
assert len( hosts ) == 2
client, server = hosts
output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
output( "%s and %s\n" % ( client.name, server.name ) )
@@ -739,19 +502,17 @@ class Mininet( object ):
bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if fmt:
iperfArgs += '-f %s ' % fmt
server.sendCmd( iperfArgs + '-s', printPid=True )
servout = ''
while server.lastPid is None:
servout += server.monitor()
if l4Type == 'TCP':
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
info( 'Waiting for iperf to start up...' )
'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 %d -c ' % seconds +
server.IP() + ' ' + bwArgs )
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs )
debug( 'Client output: %s\n' % cliout )
server.sendInt()
servout += server.waitOutput()
@@ -762,52 +523,6 @@ class Mininet( object ):
output( '*** Results: %s\n' % result )
return result
def runCpuLimitTest( self, cpu, duration=5 ):
"""run CPU limit test with 'while true' processes.
cpu: desired CPU fraction of each host
duration: test duration in seconds (integer)
returns a single list of measured CPU fractions as floats.
"""
cores = int( quietRun( 'nproc' ) )
pct = cpu * 100
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
hosts = self.hosts
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() )
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 )
cpu_fractions = []
for _host, outputs in outputs.items():
for pct in outputs:
cpu_fractions.append( pct )
output( '*** Results: %s\n' % cpu_fractions )
return cpu_fractions
# BL: I think this can be rewritten now that we have
# a real link class.
def configLinkStatus( self, src, dst, status ):
@@ -820,9 +535,9 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
if isinstance( src, basestring ):
if type( src ) is str:
src = self.nameToNode[ src ]
if isinstance( dst, basestring ):
if type( dst ) is str:
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
@@ -849,7 +564,12 @@ class Mininet( object ):
"Initialize Mininet"
if cls.inited:
return
ensureRoot()
if os.getuid() != 0:
# Note: this script must be run as root
# Probably we should only sudo when we need
# to as per Big Switch's patch
print "*** Mininet must run as root."
exit( 1 )
fixLimits()
cls.inited = True
@@ -890,7 +610,7 @@ class MininetWithControlNet( Mininet ):
# in the control network location.
def configureRoutedControlNetwork( self, ip='192.168.123.1',
prefixLen=16 ):
prefixLen=16 ):
"""Configure a routed control network on controller and switches.
For use with the user datapath only right now."""
controller = self.controllers[ 0 ]
+227 -598
View File
File diff suppressed because it is too large Load Diff
-148
View File
@@ -1,148 +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
import re
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 ):
"Make sure our class dependencies are available"
pathCheck( 'brctl', moduleName='bridge-utils' )
class NAT( Node ):
"NAT: Provides connectivity to external network"
def __init__( self, name, inetIntf=None, subnet='10.0/8',
localIntf=None, **params):
"""Start NAT/forwarding between Mininet and external network
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
super( NAT, self ).__init__( name, **params )
self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
self.subnet = subnet
self.localIntf = localIntf
def config( self, **params ):
"""Configure the NAT and iptables"""
super( NAT, self).config( **params )
if not self.localIntf:
self.localIntf = self.defaultIntf()
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
# Flush any currently active rules
# TODO: is this safe?
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' )
# Configure NAT
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',
'-i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING',
'-o', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
# Prevent network-manager from messing with our interface
# by specifying manual configuration in /etc/network/interfaces
intf = self.localIntf
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if ( line ) not in config:
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
self.cmd( 'service network-manager restart' )
def getGatewayIntf( self, fallback='eth0' ):
"""Return gateway interface name
fallback: default device to fall back to"""
routes = self.cmd( 'ip route show' )
match = re.search( r'default via \S+ dev (\S+)', routes )
if match:
return match.group( 1 )
else:
warn( 'There is no default route set.',
'Using', fallback, 'as gateway interface...\n' )
return fallback
def terminate( self ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
# TODO: is this safe?
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
super( NAT, self ).terminate()
+33 -53
View File
@@ -1,80 +1,60 @@
"""
Terminal creation and cleanup.
Utility functions to run a terminal (connected via socat(1)) on each host.
Utility functions to run a term (connected via screen(1)) on each host.
Requires socat(1) and xterm(1).
Requires GNU screen(1) and xterm(1).
Optionally uses gnome-terminal.
"""
from os import environ
import re
from subprocess import Popen
from mininet.log import error
from mininet.util import quietRun, errRun
from mininet.util import quietRun
def tunnelX11( node, display=None):
"""Create an X11 tunnel from node:6000 to the root host
display: display on root host (optional)
returns: node $DISPLAY, Popen object for tunnel"""
if display is None and 'DISPLAY' in environ:
display = environ[ 'DISPLAY' ]
if display is None:
error( "Error: Cannot connect to display\n" )
return None, None
host, screen = display.split( ':' )
# Unix sockets should work
if not host or host == 'unix':
# GDM3 doesn't put credentials in .Xauthority,
# so allow root to just connect
quietRun( 'xhost +si:localuser:root' )
return display, None
else:
# Create a tunnel for the TCP connection
port = 6000 + int( float( screen ) )
connection = r'TCP\:%s\:%s' % ( host, port )
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
return 'localhost:' + screen, node.popen( cmd )
def quoteArg( arg ):
"Quote an argument if it contains spaces."
return repr( arg ) if ' ' in arg else arg
def makeTerm( node, title='Node', term='xterm', display=None ):
"""Create an X11 tunnel to the node and start up a terminal.
def makeTerm( node, title='Node', term='xterm' ):
"""Run screen on a node, and hook 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 )
returns: process created"""
title += ': ' + node.name
if not node.inNamespace:
title += ' (root)'
cmds = {
'xterm': [ 'xterm', '-title', title, '-display' ],
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
'xterm': [ 'xterm', '-title', title, '-e' ],
'gterm': [ 'gnome-terminal', '--title', title, '-e' ]
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
return
display, tunnel = tunnelX11( node, display )
if display is None:
return []
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
"Run an X11 client on a node"
_display, tunnel = tunnelX11( node )
if _display is None:
return []
popen = node.popen( cmd )
return [ tunnel, popen ]
if not node.execed:
node.cmd( 'screen -dmS ' + 'mininet.' + node.name)
args = [ 'screen', '-D', '-RR', '-S', 'mininet.' + node.name ]
else:
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
if term == 'gterm':
# Compress these for gnome-terminal, which expects one token
# to follow the -e option
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
return Popen( cmds[ term ] + args )
def cleanUpScreens():
"Remove moldy socat X11 tunnels."
errRun( "pkill -9 -f mnexec.*socat" )
"Remove moldy old screen sessions."
r = r'(\d+\.mininet\.[hsc]\d+)'
output = quietRun( 'screen -ls' ).split( '\n' )
for line in output:
m = re.search( r, line )
if m:
quietRun( 'screen -S ' + m.group( 1 ) + ' -X quit' )
def makeTerms( nodes, title='Node', term='xterm' ):
"""Create terminals.
nodes: list of Node objects
title: base title for each
returns: list of created tunnel/terminal processes"""
terms = []
for node in nodes:
terms += makeTerm( node, title, term )
return terms
returns: list of created terminal processes"""
return [ makeTerm( node, title, term ) for node in nodes ]
-31
View File
@@ -1,31 +0,0 @@
#!/usr/bin/env python
"""
Run all mininet core tests
-v : verbose output
-quick : skip tests that take more than ~30 seconds
"""
from unittest import defaultTestLoader, TextTestRunner
import os
import sys
from mininet.util import ensureRoot
from mininet.clean import cleanup
from mininet.log import setLogLevel
def runTests( testDir, verbosity=1 ):
"discover and run all tests in testDir"
# ensure root and cleanup before starting tests
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = defaultTestLoader.discover( testDir )
# run tests
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 )
-268
View File
@@ -1,268 +0,0 @@
#!/usr/bin/env python
"""Package: mininet
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
from mininet.node import OVSSwitch, UserSwitch, IVSSwitch
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Number of hosts for each test
N = 2
class SingleSwitchOptionsTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, hopts=None, lopts=None):
if not hopts:
hopts = {}
if not lopts:
lopts = {}
Topo.__init__(self, hopts=hopts, lopts=lopts)
switch = self.addSwitch('s1')
for h in range(n):
host = self.addHost('h%s' % (h + 1))
self.addLink(host, switch)
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
class testOptionsTopoCommon( object ):
"""Verify ability to create networks with host and link options
(common code)."""
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
cleanup()
def runOptionsTopoTest( self, n, msg, 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 )
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, msg=msg )
def assertWithinTolerance( self, measured, expected, tolerance_frac, msg ):
"""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 )
def testCPULimits( self ):
"Verify topology creation with CPU limits set for both schedulers."
CPU_FRACTION = 0.1
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
hopts = { 'cpu': CPU_FRACTION }
#self.runOptionsTopoTest( N, hopts=hopts )
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
host=CPULimitedHost, switch=self.switchClass,
waitConnected=True )
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 )
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 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 )
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()
test_outputs = ping_delays[0]
# Ignore unused variables below
# pylint: disable=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 ) )
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 )
def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate."
LOSS_PERCENT = 99
REPS = 1
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
host=CPULimitedHost, link=TCLink,
switch=self.switchClass,
waitConnected=True )
# Drops are probabilistic, but the chance of no dropped packets is
# 1 in 100 million with 4 hops for a link w/99% loss.
dropped_total = 0
mn.start()
for _ in range(REPS):
dropped_total += mn.ping(timeout='1')
mn.stop()
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 )
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 )
# 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' )
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(UserSwitch)."""
longMessage = True
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+23 -81
View File
@@ -4,103 +4,45 @@
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
from mininet.node import Host, Controller
from mininet.node import UserSwitch, OVSSwitch, IVSSwitch
from mininet.node import UserSwitch, OVSKernelSwitch
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
SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch,
}
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()
class testSingleSwitch( unittest.TestCase ):
"For each datapath type, test ping with single switch topologies."
def testMinimal( self ):
"Ping test on minimal topology"
mn = Mininet( SingleSwitchTopo(), self.switchClass, Host, Controller,
waitConnected=True )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
"Ping test with both datapaths on minimal topology"
for switch in SWITCHES.values():
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
def testSingle5( self ):
"Ping test on 5-host single-switch topology"
mn = Mininet( SingleSwitchTopo( k=5 ), self.switchClass, Host,
Controller, waitConnected=True )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
# pylint: enable=E1101
class testSingleSwitchOVSKernel( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (OVS kernel switch)."
switchClass = OVSSwitch
class testSingleSwitchOVSUser( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (OVS user switch)."
switchClass = partial( OVSSwitch, datapath='user' )
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (IVS switch)."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (Userspace switch)."
switchClass = UserSwitch
"Ping test with both datapaths on 5-host single-switch topology"
for switch in SWITCHES.values():
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
class testLinearCommon( object ):
"Test all-pairs ping with LinearNet (common code)."
switchClass = None # overridden in subclasses
class testLinear( unittest.TestCase ):
"For each datapath type, test all-pairs ping with LinearNet."
def testLinear5( self ):
"Ping test on a 5-switch topology"
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host,
Controller, waitConnected=True )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
# pylint: enable=E1101
class testLinearOVSKernel( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (OVS kernel switch)."
switchClass = OVSSwitch
class testLinearOVSUser( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (OVS user switch)."
switchClass = partial( OVSSwitch, datapath='user' )
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
class testLinearIVS( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (IVS switch)."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testLinearUserspace( testLinearCommon, unittest.TestCase ):
"Test all-pairs ping with LinearNet (Userspace switch)."
switchClass = UserSwitch
"Ping test with both datapaths on a 5-switch topology"
for switch in SWITCHES.values():
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
if __name__ == '__main__':
-104
View File
@@ -1,104 +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, OVSLegacyKernelSwitch,
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."""
switch = Mininet( Topo(),
self.switchClass,
Host, Controller ).addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
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 )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch(
's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid )
def testDefaultDpidAssignmentFailure( self ):
"""Verify that Default dpid assignment raises an Exception if the
name of the switch does not contin a digit. Also verify the
exception message."""
with self.assertRaises( Exception ) as raises_cm:
Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 'A' )
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
'default datapath ID - please either specify a dpid '
'or use a canonical switch name such as s23.')
def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch
name) 0's followed by hex of first string of contiguous digits passed
in switch name."""
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
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 assignnment of OVS User Switch."
switchClass = OVSUser
@unittest.skipUnless( quietRun( 'which ovs-openflowd' ),
'OVS Legacy Kernel switch is not installed' )
class testSwitchOVSLegacyKernel( TestSwitchDpidAssignmentOVS ):
"Test dpid assignnment of OVS Legacy Kernel Switch."
switchClass = OVSLegacyKernelSwitch
@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()

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