Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8137a9219 | ||
|
|
891ea4854d | ||
|
|
2e0e2d97ec | ||
|
|
71b3128122 | ||
|
|
5ca1fb7e0f | ||
|
|
18a0b7e4ac | ||
|
|
e27673aeec | ||
|
|
035d4d20fe | ||
|
|
550ee2469e | ||
|
|
d8d9035242 | ||
|
|
f1aca0d9a6 |
No files matched your search
@@ -1 +0,0 @@
|
||||
*.py diff=python
|
||||
+2
-8
@@ -1,14 +1,8 @@
|
||||
mnexec
|
||||
*.pyc
|
||||
*~
|
||||
*.1
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
\#*\#
|
||||
mininet.egg-info
|
||||
build
|
||||
dist
|
||||
doc/html
|
||||
doc/latex
|
||||
trunk
|
||||
build/*
|
||||
dist/*
|
||||
|
||||
@@ -1,39 +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
|
||||
|
||||
Bob Lantz
|
||||
Brandon Heller
|
||||
Nikhil Handigol
|
||||
Vimal Jeyakumar
|
||||
Brian O'Connor
|
||||
|
||||
Additional Mininet Contributors
|
||||
|
||||
Gustavo Pantuza Coelho Pinto
|
||||
Ryan Cox
|
||||
Shaun Crampton
|
||||
David Erickson
|
||||
Glen Gibb
|
||||
Andrew Ferguson
|
||||
Eder Leao Fernandes
|
||||
Vitaly Ivanov
|
||||
Rich Lane
|
||||
Murphy McCauley
|
||||
José Pedro Oliveira
|
||||
James Page
|
||||
Angad Singh
|
||||
Piyush Srivastava
|
||||
Ed Swierk
|
||||
Isaku Yamahata
|
||||
|
||||
Thanks also to everyone who has submitted issues and pull
|
||||
requests on github, and to our friendly mininet-discuss
|
||||
mailing list!
|
||||
@@ -1,173 +1,273 @@
|
||||
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.1.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, use:
|
||||
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
|
||||
|
||||
git clone git://github.com/mininet/mininet
|
||||
git checkout -b 2.1.0 2.1.0
|
||||
d) Install Open vSwitch and its kernel module
|
||||
$ mininet/util/install.sh -vm
|
||||
|
||||
If you are running Ubuntu, you may be able to use our handy
|
||||
`install.sh` script, which is in `mininet/util`.
|
||||
e) If you wish to install the version of NOX we use in the tutorial:
|
||||
$ mininet/util/install.sh -x
|
||||
|
||||
*WARNING: USE AT YOUR OWN RISK!*
|
||||
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.
|
||||
|
||||
`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..
|
||||
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!
|
||||
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.
|
||||
|
||||
To install Mininet itself, the OpenFlow reference implementation, and
|
||||
Open vSwitch, you may use:
|
||||
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.
|
||||
|
||||
mininet/util/install.sh -fnv
|
||||
---
|
||||
|
||||
This should be reasonably quick, and the following command should
|
||||
work after the installation:
|
||||
Mininet Manual Installation Notes
|
||||
|
||||
sudo mn --test pingall
|
||||
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 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:
|
||||
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 -a
|
||||
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
|
||||
|
||||
This takes about 4 minutes on our test system.
|
||||
0. Obtaining Mininet
|
||||
|
||||
You can change the directory where the dependencies are installed using
|
||||
the -s <directory> flag.
|
||||
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
|
||||
|
||||
mininet/util/install.sh -s <directory> -a
|
||||
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
|
||||
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
To install Mininet itself, with root privileges:
|
||||
|
||||
# cd mininet
|
||||
# make install
|
||||
|
||||
As root execute the following operations:
|
||||
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
|
||||
|
||||
* install git
|
||||
2. Installation script for Ubuntu/Debian Lenny
|
||||
|
||||
yum install git
|
||||
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.
|
||||
|
||||
* create an user account (e.g. mininet) and add it to the wheel group
|
||||
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
|
||||
|
||||
useradd [...] mininet
|
||||
usermod -a -G wheel mininet
|
||||
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.
|
||||
|
||||
* change the SElinux setting to permissive. It can be done
|
||||
temporarily with:
|
||||
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.
|
||||
|
||||
setenforce 0
|
||||
If you successfully used install.sh, congratulations! You're basically
|
||||
done. Proceed to step [6] for additional advice.
|
||||
|
||||
3. Linux Kernel requirements
|
||||
|
||||
then login with the new account (e.g. mininet) and do the following:
|
||||
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.
|
||||
|
||||
* clone the Mininet repository
|
||||
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.
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
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):
|
||||
|
||||
* install Mininet, the OpenFlow reference implementation, and
|
||||
Open vSwitch
|
||||
sudo su -c "cat sysctl_addon >> /etc/sysctl.conf"
|
||||
|
||||
mininet/util/install.sh -fnv
|
||||
To save the config change, run:
|
||||
|
||||
* enable and start openvswitch
|
||||
sudo sysctl -p
|
||||
|
||||
4. OpenFlow software and configuration requirements
|
||||
|
||||
sudo systemctl enable openvswitch
|
||||
sudo systemctl start openvswitch
|
||||
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/)
|
||||
|
||||
* test the mininet installation
|
||||
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
|
||||
|
||||
sudo mn --test pingall
|
||||
On Ubuntu and Debian, other Mininet dependencies may be installed using
|
||||
the '-n' option of the install.sh script.
|
||||
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||
To run the iperf test, you need to install iperf:
|
||||
|
||||
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:
|
||||
sudo aptitude/yum install iperf
|
||||
|
||||
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
|
||||
time install-mininet-vm.sh
|
||||
We assume you already have ping installed. ;-)
|
||||
|
||||
To use xterm or sshd with Mininet, you need the following:
|
||||
|
||||
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 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)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
Mininet 2.1.0+ License
|
||||
Mininet 1.0.0 License
|
||||
|
||||
Copyright (c) 2013 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
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
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
|
||||
BINDIR = /usr/bin
|
||||
MANDIR = /usr/share/man/man1
|
||||
DOCDIRS = doc/html doc/latex
|
||||
PDF = doc/latex/refman.pdf
|
||||
|
||||
CFLAGS += -Wall -Wextra
|
||||
|
||||
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)
|
||||
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
|
||||
@@ -34,34 +25,15 @@ errcheck: $(PYSRC)
|
||||
test: $(MININET) $(TEST)
|
||||
-echo "Running tests"
|
||||
mininet/test/test_nets.py
|
||||
mininet/test/test_hifi.py
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -1,133 +0,0 @@
|
||||
Mininet: Rapid Prototyping for Software Defined Networks
|
||||
========================================================
|
||||
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
Version 2.1.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 2.1.0+
|
||||
|
||||
Mininet 2.1.0+ provides a number of bug fixes as well as
|
||||
several new features, including:
|
||||
|
||||
* Convenient access to `Mininet()` as a dict of nodes
|
||||
* X11 tunneling (wireshark in Mininet hosts, finally!)
|
||||
* Accurate reflection of the `Mininet()` object in the CLI
|
||||
* Automatically detecting and adjusting resource limits
|
||||
* Automatic cleanup on failure of the `mn` command
|
||||
* Preliminary support for running OVS in user space mode
|
||||
* Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch
|
||||
* support for installing the OpenFlow 1.3 versions of the reference
|
||||
user switch and NOX from CPqD
|
||||
* The ability to import modules from `mininet.examples`
|
||||
|
||||
We have provided several new examples (which can easily be
|
||||
imported to provide useful functionality) including:
|
||||
|
||||
* Modeling separate control and data networks: `mininet.examples.controlnet`
|
||||
* Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat`
|
||||
* Creating per-host custom directories using bind mounts: `mininet.examples.bind`
|
||||
|
||||
Note that examples contain experimental features which might
|
||||
"graduate" into mainline Mininet in the future, but they should
|
||||
not be considered a stable part of the Mininet API!
|
||||
|
||||
### Installation
|
||||
|
||||
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>
|
||||
|
||||
### Contributing
|
||||
|
||||
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 to the project
|
||||
(see CONTRIBUTORS for more info!)
|
||||
|
||||
Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
|
||||
### Credits
|
||||
|
||||
The Mininet 2.1.0+ Team:
|
||||
|
||||
* Bob Lantz
|
||||
* Brian O'Connor
|
||||
@@ -12,59 +12,68 @@ 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, 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,
|
||||
NOX, RemoteController, DefaultController,
|
||||
UserSwitch, OVSSwitch,
|
||||
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 custom, customConstructor
|
||||
from mininet.util import buildTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import makeNumeric, custom
|
||||
|
||||
|
||||
def customNode( constructors, argStr ):
|
||||
"Return custom Node constructor based on argStr"
|
||||
cname, newargs, kwargs = splitArgs( argStr )
|
||||
constructor = constructors.get( cname, None )
|
||||
|
||||
if not constructor:
|
||||
raise Exception( "error: %s is unknown - please specify one of %s" %
|
||||
( cname, constructors.keys() ) )
|
||||
|
||||
def customized( name, *args, **params ):
|
||||
"Customized Node constructor"
|
||||
params.update( kwargs )
|
||||
if not newargs:
|
||||
return constructor( name, *args, **params )
|
||||
if args:
|
||||
warn( 'warning: %s replacing %s with %s\n' % (
|
||||
constructor, args, newargs ) )
|
||||
return constructor( name, *newargs, **params )
|
||||
|
||||
return customized
|
||||
|
||||
|
||||
# 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 = 'ovsk'
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
'ovs': OVSSwitch,
|
||||
# Keep ovsk for compatibility with 2.0
|
||||
'ovsk': OVSSwitch,
|
||||
'ovsl': OVSLegacyKernelSwitch,
|
||||
'ivs': IVSSwitch,
|
||||
'lxbr': LinuxBridge }
|
||||
'ovsk': OVSKernelSwitch,
|
||||
'ovsl': OVSLegacyKernelSwitch }
|
||||
|
||||
HOSTDEF = 'proc'
|
||||
HOSTS = { 'proc': Host,
|
||||
'rt': custom( CPULimitedHost, sched='rt' ),
|
||||
'cfs': custom( CPULimitedHost, sched='cfs' ) }
|
||||
|
||||
CONTROLLERDEF = 'default'
|
||||
CONTROLLERDEF = 'ref'
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'default': DefaultController,
|
||||
'none': lambda name: None }
|
||||
|
||||
LINKDEF = 'default'
|
||||
@@ -74,12 +83,35 @@ 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 splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
split = argstr.split( ',' )
|
||||
fn = split[ 0 ]
|
||||
params = split[ 1: ]
|
||||
# Convert int and float args; removes the need for function
|
||||
# to be flexible with input arg formats.
|
||||
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||
kwargs = {}
|
||||
for s in [ p for p in params if '=' in p ]:
|
||||
key, val = s.split( '=' )
|
||||
kwargs[ key ] = makeNumeric( val )
|
||||
return fn, args, kwargs
|
||||
|
||||
|
||||
def buildTopo( topoStr ):
|
||||
"Create topology from string with format (object, arg1, arg2,...)."
|
||||
topo, args, kwargs = splitArgs( topoStr )
|
||||
if topo not in TOPOS:
|
||||
raise Exception( 'Invalid topo name %s' % topo )
|
||||
return TOPOS[ topo ]( *args, **kwargs )
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
@@ -91,21 +123,16 @@ 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 )
|
||||
type='string',
|
||||
default = default,
|
||||
help = helpStr )
|
||||
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
print "%s" % VERSION
|
||||
exit()
|
||||
|
||||
class MininetRunner( object ):
|
||||
"Build, setup, and run Mininet."
|
||||
|
||||
@@ -153,14 +180,7 @@ class MininetRunner( object ):
|
||||
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' )
|
||||
@@ -168,50 +188,45 @@ class MininetRunner( object ):
|
||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||
|
||||
opts.add_option( '--clean', '-c', action='store_true',
|
||||
default=False, help='clean and exit' )
|
||||
default=False, help='clean and exit' )
|
||||
opts.add_option( '--custom', type='string', default=None,
|
||||
help='read custom topo and node params from .py' +
|
||||
'file' )
|
||||
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( '--ip', type='string', default='127.0.0.1',
|
||||
help='ip address as a dotted decimal string for a'
|
||||
'remote controller' )
|
||||
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='store_true',
|
||||
default=False, help="adds a NAT to the topology "
|
||||
"that connects Mininet to the physical network" )
|
||||
opts.add_option( '--version', action='callback', callback=version )
|
||||
|
||||
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."
|
||||
|
||||
@@ -232,11 +247,11 @@ class MininetRunner( object ):
|
||||
|
||||
start = time.time()
|
||||
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customConstructor( SWITCHES, self.options.switch )
|
||||
host = customConstructor( HOSTS, self.options.host )
|
||||
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||
link = customConstructor( LINKS, self.options.link )
|
||||
topo = buildTopo( self.options.topo )
|
||||
switch = customNode( SWITCHES, self.options.switch )
|
||||
host = customNode( HOSTS, self.options.host )
|
||||
controller = customNode( CONTROLLERS, self.options.controller )
|
||||
link = customNode( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( self.options )
|
||||
@@ -260,10 +275,6 @@ class MininetRunner( object ):
|
||||
autoStaticArp=arp, autoPinCpus=pin,
|
||||
listenPort=listenPort )
|
||||
|
||||
if self.options.nat:
|
||||
nat = mn.addNAT()
|
||||
nat.configDefault()
|
||||
|
||||
if self.options.pre:
|
||||
CLI( mn, script=self.options.pre )
|
||||
|
||||
@@ -275,14 +286,12 @@ class MininetRunner( object ):
|
||||
if test == 'none':
|
||||
pass
|
||||
elif test == 'all':
|
||||
mn.waitConnected()
|
||||
mn.start()
|
||||
mn.ping()
|
||||
mn.iperf()
|
||||
elif test == 'cli':
|
||||
CLI( mn )
|
||||
elif test != 'build':
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )()
|
||||
|
||||
if self.options.post:
|
||||
@@ -295,21 +304,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
@@ -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() ) }
|
||||
Vendored
-33
@@ -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
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
9
|
||||
Vendored
-31
@@ -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.
|
||||
Vendored
-37
@@ -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
@@ -1 +0,0 @@
|
||||
README.md
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
examples/*
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
mnexec /usr/bin
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
*.1
|
||||
Vendored
-12
@@ -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
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
||||
Vendored
-3
@@ -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)
|
||||
@@ -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.
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
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.
|
||||
|
||||
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.)
|
||||
|
||||
@@ -1,129 +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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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`?"
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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
|
||||
@@ -1,4 +0,0 @@
|
||||
"""
|
||||
Mininet Examples
|
||||
See README for details
|
||||
"""
|
||||
@@ -2,11 +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
|
||||
|
||||
ensureRoot()
|
||||
|
||||
print "*** Creating nodes"
|
||||
h1 = Host( 'h1' )
|
||||
@@ -28,10 +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 )
|
||||
h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
|
||||
|
||||
print "*** You may now ssh into", h1.name, "at", h1.IP()
|
||||
@@ -1,72 +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, HostWithPrivateDirs
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info, debug
|
||||
|
||||
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( HostWithPrivateDirs,
|
||||
privateDirs=privateDirs )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
net.start()
|
||||
directories = []
|
||||
for directory in privateDirs:
|
||||
directories.append( directory[ 0 ]
|
||||
if isinstance( directory, tuple )
|
||||
else directory )
|
||||
info( 'Private Directories:', directories, '\n' )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testHostWithPrivateDirs()
|
||||
info( 'Done.\n')
|
||||
|
||||
|
||||
+15
-22
@@ -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
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/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
|
||||
@@ -46,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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
+7
-15
@@ -5,12 +5,11 @@ This example shows how to add an interface (for example a real
|
||||
hardware interface) to a network after the network is created.
|
||||
"""
|
||||
|
||||
import re, sys
|
||||
import re
|
||||
|
||||
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
|
||||
|
||||
@@ -22,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 )
|
||||
|
||||
+20
-4
@@ -8,17 +8,33 @@ from mininet.net import Mininet
|
||||
from mininet.link import TCIntf
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom
|
||||
from mininet.util import custom, quietRun
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from time import sleep
|
||||
|
||||
def testLinkLimit( net, bw ):
|
||||
"Run bandwidth limit test"
|
||||
print '*** Testing network %.2f Mbps bandwidth limit' % bw
|
||||
net.iperf( )
|
||||
|
||||
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=.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"""
|
||||
@@ -30,7 +46,7 @@ def limit( bw=10, cpu=.1 ):
|
||||
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 ):
|
||||
|
||||
@@ -24,7 +24,7 @@ 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
|
||||
@@ -41,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 ):
|
||||
@@ -68,7 +68,7 @@ def linearBandwidthTest( lengths ):
|
||||
hostCount = switchCount + 1
|
||||
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
topo = LinearTestTopo( hostCount )
|
||||
|
||||
@@ -76,7 +76,7 @@ def linearBandwidthTest( lengths ):
|
||||
print "*** testing", datapath, "datapath"
|
||||
Switch = switches[ datapath ]
|
||||
results[ datapath ] = []
|
||||
net = Mininet( topo=topo, switch=Switch, controller=Controller, waitConnected=True )
|
||||
net = Mininet( topo=topo, switch=Switch )
|
||||
net.start()
|
||||
print "*** testing basic connectivity"
|
||||
for n in lengths:
|
||||
|
||||
+99
-2975
File diff suppressed because it is too large.
Load diff
@@ -1,136 +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.util import quietRun
|
||||
from mininet.log import output, warn
|
||||
|
||||
from random import randint
|
||||
from re import findall
|
||||
|
||||
|
||||
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()
|
||||
@@ -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 )
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
@@ -1,70 +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, h=1, **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()
|
||||
|
||||
@@ -1,76 +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
|
||||
from mininet.node import Node
|
||||
|
||||
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 net():
|
||||
|
||||
"Create a network with 5 hosts."
|
||||
|
||||
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 )
|
||||
net.addLink( h5, s1, port1 = 1, port2 = 9 ) # specify a different port to connect host 5 to on the switch.
|
||||
|
||||
root = Node( 'root', inNamespace=False )
|
||||
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' )
|
||||
net()
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example monitors a number of hosts using host.popen() and
|
||||
pmonitor()
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import custom, pmonitor
|
||||
|
||||
def monitorhosts( hosts=5, sched='cfs' ):
|
||||
"Start a bunch of pings and monitor them using popen"
|
||||
mytopo = SingleSwitchTopo( hosts )
|
||||
cpu = .5 / hosts
|
||||
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
|
||||
net = Mininet( topo=mytopo, host=myhost )
|
||||
net.start()
|
||||
# Start a bunch of pings
|
||||
popens = {}
|
||||
last = net.hosts[ -1 ]
|
||||
for host in net.hosts:
|
||||
popens[ host ] = host.popen( "ping -c5 %s" % last.IP() )
|
||||
last = host
|
||||
# Monitor them and print output
|
||||
for host, line in pmonitor( popens ):
|
||||
if host:
|
||||
print "<%s>: %s" % ( host.name, line.strip() )
|
||||
# Done
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
monitorhosts( hosts=5 )
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"Monitor multiple hosts using popen()/pmonitor()"
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.util import pmonitor
|
||||
from time import time
|
||||
from signal import SIGINT
|
||||
|
||||
def pmonitorTest( N=3, seconds=10 ):
|
||||
"Run pings and monitor multiple hosts using pmonitor"
|
||||
topo = SingleSwitchTopo( N )
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
popens = {}
|
||||
for h in hosts:
|
||||
popens[ h ] = h.popen('ping', server.IP() )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
endTime = time() + seconds
|
||||
for h, line in pmonitor( popens, timeoutms=500 ):
|
||||
if h:
|
||||
print '<%s>: %s' % ( h.name, line ),
|
||||
if time() >= endTime:
|
||||
for p in popens.values():
|
||||
p.send_signal( SIGINT )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pmonitorTest()
|
||||
@@ -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
+5
-10
@@ -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,14 +15,14 @@ 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"
|
||||
|
||||
+12
-22
@@ -16,12 +16,10 @@ 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
|
||||
|
||||
@@ -30,33 +28,29 @@ def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||
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
|
||||
@@ -73,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
|
||||
opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
|
||||
'-D -o UseDNS=no -u0' )
|
||||
sshd( net, opts=opts )
|
||||
net = TreeNet( depth=1, fanout=4, switch=OVSKernelSwitch )
|
||||
sshd( net )
|
||||
@@ -1,29 +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
|
||||
|
||||
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
|
||||
unittest.TextTestRunner( 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 )
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for baresshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from time import sleep
|
||||
from mininet.clean import cleanup, sh
|
||||
|
||||
class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
opts = [ '\(yes/no\)\?', 'Welcome to h1', 'refused', pexpect.EOF, pexpect.TIMEOUT ]
|
||||
|
||||
def connected( self ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -i /tmp/ssh/test_rsa exit' )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
p.sendline( 'yes' )
|
||||
elif index == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def setUp( self ):
|
||||
# verify that sshd is not running
|
||||
self.assertFalse( self.connected() )
|
||||
# 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' )
|
||||
sh( cmd )
|
||||
|
||||
def testSSH( self ):
|
||||
"Simple test to verify that we can ssh into h1"
|
||||
result = False
|
||||
# try to connect up to 3 times; sshd can take a while to start
|
||||
for _ in range( 3 ):
|
||||
result = self.connected()
|
||||
if result:
|
||||
break
|
||||
else:
|
||||
sleep( 1 )
|
||||
self.assertTrue( result )
|
||||
|
||||
def tearDown( self ):
|
||||
# kill the ssh process
|
||||
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
|
||||
cleanup()
|
||||
# remove public key pair
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for cpu.py
|
||||
"""
|
||||
|
||||
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' )
|
||||
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 )
|
||||
previous_bw = 10 ** 4 # 10 GB/s
|
||||
self.assertTrue( bw < previous_bw )
|
||||
previous_bw = bw
|
||||
else:
|
||||
break
|
||||
|
||||
self.assertTrue( len( scheds ) > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -1,44 +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:
|
||||
previous_bw = 10 ** 10 # 10 Gbits
|
||||
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
|
||||
self.assertTrue( bw < previous_bw )
|
||||
previous_bw = bw
|
||||
else:
|
||||
break
|
||||
|
||||
self.assertTrue( count > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for simpleperf.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import re
|
||||
import sys
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.link import TCLink
|
||||
|
||||
from mininet.examples.simpleperf import SingleSwitchTopo
|
||||
|
||||
class testSimplePerf( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testE2E( self ):
|
||||
"Run the example and verify ping and iperf results"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
|
||||
# check ping results
|
||||
p.expect( "Results: (\d+)% dropped", timeout=120 )
|
||||
loss = int( p.match.group( 1 ) )
|
||||
self.assertTrue( 0 < loss < 100 )
|
||||
# check iperf results
|
||||
p.expect( "Results: \['([\d\.]+) .bits/sec", timeout=480 )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw > 0 )
|
||||
p.wait()
|
||||
|
||||
def testTopo( self ):
|
||||
"""Import SingleSwitchTopo from example and test connectivity between two hosts
|
||||
Note: this test may fail very rarely because it is non-deterministic
|
||||
i.e. links are configured with 10% packet loss, but if we get unlucky and
|
||||
none or all of the packets are dropped, the test will fail"""
|
||||
topo = SingleSwitchTopo( n=4 )
|
||||
net = Mininet( topo=topo, host=CPULimitedHost, link=TCLink )
|
||||
net.start()
|
||||
h1, h4 = net.get( 'h1', 'h4' )
|
||||
# have h1 ping h4 ten times
|
||||
expectStr = '(\d+) packets transmitted, (\d+) received, (\d+)% packet loss'
|
||||
output = h1.cmd( 'ping -c 10 %s' % h4.IP() )
|
||||
m = re.search( expectStr, output )
|
||||
loss = int( m.group( 3 ) )
|
||||
net.stop()
|
||||
self.assertTrue( 0 < loss < 100 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
|
||||
+10
-36
@@ -10,8 +10,7 @@ It may also get rid of 'false positives', but hopefully
|
||||
nothing irreplaceable!
|
||||
"""
|
||||
|
||||
from subprocess import Popen, PIPE, check_output as co
|
||||
import time
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from mininet.log import info
|
||||
from mininet.term import cleanUpScreens
|
||||
@@ -28,59 +27,34 @@ 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" )
|
||||
sh( 'pkill -9 -f mininet:' )
|
||||
# Make sure they are gone
|
||||
while True:
|
||||
try:
|
||||
pids = co( 'pgrep -f mininet:'.split() )
|
||||
except:
|
||||
pids = ''
|
||||
if pids:
|
||||
sh( 'pkill -f 9 mininet:' )
|
||||
sleep( .5 )
|
||||
else:
|
||||
break
|
||||
|
||||
info( "*** Cleanup complete.\n" )
|
||||
+44
-100
@@ -30,12 +30,9 @@ from cmd import Cmd
|
||||
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.term import makeTerms
|
||||
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
|
||||
|
||||
class CLI( Cmd ):
|
||||
@@ -45,8 +42,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()
|
||||
@@ -54,30 +56,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.monitor()
|
||||
if self.isatty():
|
||||
quietRun( 'stty echo sane intr "^C"' )
|
||||
quietRun( 'stty sane' )
|
||||
self.cmdloop()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
@@ -87,11 +77,6 @@ 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
|
||||
@@ -124,25 +109,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_net( self, _line ):
|
||||
"List network connections."
|
||||
dumpNodeConnections( self.mn.values() )
|
||||
dumpNodeConnections( self.nodelist )
|
||||
|
||||
def do_sh( self, line ):
|
||||
"Run an external shell command"
|
||||
call( line, shell=True )
|
||||
|
||||
# do_py() and do_px() need to catch any exception during eval()/exec()
|
||||
# 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 ):
|
||||
@@ -152,35 +137,16 @@ class CLI( Cmd ):
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# We are in fact using the exec() pseudo-function
|
||||
# pylint: disable-msg=W0122
|
||||
# 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-msg=W0703,W0122
|
||||
|
||||
def do_pingall( self, line ):
|
||||
def do_pingall( self, _line ):
|
||||
"Ping between all hosts."
|
||||
self.mn.pingAll( line )
|
||||
self.mn.pingAll()
|
||||
|
||||
def do_pingpair( self, _line ):
|
||||
"Ping between first two hosts, useful for testing."
|
||||
self.mn.pingPair()
|
||||
|
||||
def do_pingallfull( self, _line ):
|
||||
"Ping between all hosts, returns all ping results."
|
||||
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."
|
||||
args = line.split()
|
||||
@@ -190,18 +156,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."
|
||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||
args = line.split()
|
||||
if not args:
|
||||
self.mn.iperf( l4Type='UDP' )
|
||||
@@ -210,11 +176,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:
|
||||
@@ -223,13 +189,13 @@ 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 ):
|
||||
@@ -249,23 +215,12 @@ class CLI( Cmd ):
|
||||
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."""
|
||||
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)."
|
||||
self.do_xterm( line, term='gterm' )
|
||||
@@ -311,11 +266,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."
|
||||
"Run dpctl command on all switches."
|
||||
args = line.split()
|
||||
if len(args) < 1:
|
||||
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
|
||||
@@ -324,13 +278,6 @@ class CLI( Cmd ):
|
||||
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
|
||||
output( sw.dpctl( *args ) )
|
||||
|
||||
def do_time( self, line ):
|
||||
"Measure time taken for any command in Mininet."
|
||||
start = time.time()
|
||||
self.onecmd(line)
|
||||
elapsed = time.time() - start
|
||||
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
|
||||
|
||||
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.
|
||||
@@ -338,35 +285,36 @@ 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 )
|
||||
bothPoller = poll()
|
||||
bothPoller.register( self.stdin, POLLIN )
|
||||
bothPoller.register( node.stdout, POLLIN )
|
||||
bothPoller.register( self.stdin )
|
||||
bothPoller.register( node.stdout )
|
||||
if self.isatty():
|
||||
# Buffer by character, so that interactive
|
||||
# commands sort of work
|
||||
@@ -378,7 +326,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 ):
|
||||
@@ -390,12 +338,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()
|
||||
|
||||
|
||||
# Helper functions
|
||||
|
||||
def isReadable( poller ):
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../examples
|
||||
+43
-59
@@ -25,7 +25,8 @@ 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 ):
|
||||
@@ -161,9 +162,8 @@ class Intf( object ):
|
||||
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 )
|
||||
# Does it help to sleep to let things run?
|
||||
sleep( 0.001 )
|
||||
|
||||
def __repr__( self ):
|
||||
return '<%s %s>' % ( self.__class__.__name__, self.name )
|
||||
@@ -178,7 +178,7 @@ 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 '
|
||||
@@ -196,63 +196,59 @@ 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 ' +
|
||||
'min 30000 max 35000 avpkt 1500 ' +
|
||||
cmds = [ '%s qdisc add dev %s' + parent +
|
||||
'handle 10: red limit 1000000 ' +
|
||||
'min 20000 max 25000 avpkt 1000 ' +
|
||||
'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 ' +
|
||||
'min 30000 max 35000 avpkt 1500 ' +
|
||||
cmds = [ '%s qdisc add dev %s' + parent +
|
||||
'handle 10: red limit 1000000 ' +
|
||||
'min 20000 max 25000 avpkt 1000 ' +
|
||||
'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"
|
||||
@@ -260,10 +256,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)
|
||||
@@ -279,45 +274,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
|
||||
|
||||
|
||||
+17
-15
@@ -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:
|
||||
|
||||
@@ -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 ):
|
||||
@@ -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 )
|
||||
@@ -0,0 +1,145 @@
|
||||
from time import sleep, time
|
||||
from subprocess import Popen, PIPE
|
||||
from multiprocessing import Process
|
||||
import re
|
||||
import os
|
||||
|
||||
from mininet.log import info, error, debug, output
|
||||
from mininet.util import quietRun
|
||||
|
||||
class Monitor(object):
|
||||
|
||||
def __init__(self, output_dir='/tmp'):
|
||||
self.monitors = []
|
||||
self.output_dir = output_dir
|
||||
|
||||
# Add general process monitors
|
||||
# Bandwidth monitor
|
||||
self.monitors.append(Process(target=self.monitor_devs_ng,
|
||||
args=('%s/bwm.txt' % self.output_dir, 1.0)))
|
||||
|
||||
# CPU monitor
|
||||
self.monitors.append(Process(target=self.monitor_cpu,
|
||||
args=('%s/cpu.txt' % self.output_dir, )))
|
||||
|
||||
# cwnd monitor: tcp_probe
|
||||
self.monitors.append(Process(target=self.monitor_cwnd,
|
||||
args=('%s/tcp_probe.txt' % self.output_dir, )))
|
||||
|
||||
def start(self):
|
||||
'''Start all the system monitors'''
|
||||
|
||||
# Set output directory
|
||||
self.set_output_dir(self.output_dir)
|
||||
|
||||
# Start the monitors
|
||||
for m in self.monitors:
|
||||
m.start()
|
||||
|
||||
def stop(self):
|
||||
'''Terminate all the system monitors'''
|
||||
# Stop the monitors
|
||||
for m in self.monitors:
|
||||
m.terminate()
|
||||
self.monitors = []
|
||||
Popen("killall -9 bwm-ng top", shell=True).wait()
|
||||
Popen("killall -9 cat; rmmod tcp_probe > /dev/null 2>&1;", shell=True).wait()
|
||||
|
||||
def set_output_dir(self, output_dir):
|
||||
# Create output directory if it doesn't exist already
|
||||
self.output_dir = output_dir
|
||||
debug('Monitoring output dir: %s' % self.output_dir)
|
||||
if not os.path.isdir(self.output_dir):
|
||||
os.makedirs(self.output_dir)
|
||||
|
||||
def monitor_qlen(self, iface, interval_sec = 0.01, fname='%s/qlen.txt' % '.'):
|
||||
pat_queued = re.compile(r'backlog\s[^\s]+\s([\d]+)p')
|
||||
cmd = "tc -s qdisc show dev %s" % (iface)
|
||||
ret = []
|
||||
open(fname, 'w').write('')
|
||||
while 1:
|
||||
p = Popen(cmd, shell=True, stdout=PIPE)
|
||||
output = p.stdout.read()
|
||||
# Not quite right, but will do for now
|
||||
matches = pat_queued.findall(output)
|
||||
if matches and len(matches) > 1:
|
||||
ret.append(matches[1])
|
||||
t = "%f" % time()
|
||||
open(fname, 'a').write(t + ',' + matches[1] + '\n')
|
||||
sleep(interval_sec)
|
||||
return
|
||||
|
||||
def monitor_cwnd(self, fname='%s/tcp_probe.txt' % '.'):
|
||||
Popen("rmmod tcp_probe > /dev/null 2>&1; modprobe tcp_probe;", shell=True).wait()
|
||||
Popen("cat /proc/net/tcpprobe > %s" % fname, shell=True).wait()
|
||||
|
||||
def monitor_devs_ng(self, fname="%s/txrate.txt" % '.', interval_sec=0.01):
|
||||
"""Uses bwm-ng tool to collect iface tx rate stats. Very reliable."""
|
||||
cmd = "sleep 1; bwm-ng -t %s -o csv -u bits -T rate -C ',' > %s" % (interval_sec * 1000, fname)
|
||||
Popen(cmd, shell=True).wait()
|
||||
|
||||
def monitor_cpu(self, fname="%s/cpu.txt" % '.'):
|
||||
cmd = "(top -b -p 1 -d 1 | grep --line-buffered \"^Cpu\") > %s" % fname
|
||||
Popen(cmd, shell=True).wait()
|
||||
|
||||
def monitor_cpuacct(self, hosts, fname="%s/cpuacct.txt" % '.', interval_sec=1.0):
|
||||
prereqs = ['cgget']
|
||||
for p in prereqs:
|
||||
if not quietRun('which ' + p):
|
||||
error('Could not find %s... not monitoring cpuacct' % p)
|
||||
return
|
||||
hnames = ' '.join([h.name for h in hosts])
|
||||
cpuacct_cmd = 'cgget -g cpuacct %s >> %s' % (hnames, fname)
|
||||
prev_time = time()
|
||||
while 1:
|
||||
sleep(interval_sec - (time() - prev_time))
|
||||
prev_time = time()
|
||||
cpu_usage = Popen(cpuacct_cmd, shell=True).wait()
|
||||
return
|
||||
|
||||
# Obsolete: Use bwm-ng instead (monitor_devs_ng), for reliable stats
|
||||
def monitor_count(self, ipt_args="--src 10.0.0.0/8", interval_sec=0.01, fname='%s/bytes_sent.txt' % '.', chain="OUTPUT"):
|
||||
cmd = "iptables -I %(chain)s 1 %(filter)s -j RETURN" % {
|
||||
"filter": ipt_args,
|
||||
"chain": chain,
|
||||
}
|
||||
# We always erase the first rule; will fix this later
|
||||
Popen("iptables -D %s 1" % chain, shell=True).wait()
|
||||
# Add our rule
|
||||
Popen(cmd, shell=True).wait()
|
||||
open(fname, 'w').write('')
|
||||
cmd = "iptables -vnL %s 1 -Z" % (chain)
|
||||
while 1:
|
||||
p = Popen(cmd, shell=True, stdout=PIPE)
|
||||
output = p.stdout.read().strip()
|
||||
values = output.split(' ')
|
||||
if len(values) > 2:
|
||||
t = "%f" % time()
|
||||
pkts, bytes = values[0], values[1]
|
||||
open(fname, 'a').write(','.join([t, pkts, bytes]) + '\n')
|
||||
sleep(interval_sec)
|
||||
return
|
||||
|
||||
# Obsolete: Use bwm-ng instead (monitor_devs_ng), for reliable stats
|
||||
def monitor_devs(self, dev_pattern='^sw', fname="%s/bytes_sent.txt" % '.', interval_sec=0.01):
|
||||
"""Aggregates (sums) all txed bytes and rate (in Mbps) from devices whose name
|
||||
matches @dev_pattern and writes to @fname"""
|
||||
pat = re.compile(dev_pattern)
|
||||
spaces = re.compile('\s+')
|
||||
open(fname, 'w').write('')
|
||||
prev_tx = {}
|
||||
while 1:
|
||||
lines = open('/proc/net/dev').read().split('\n')
|
||||
t = str(time())
|
||||
total = 0
|
||||
for line in lines:
|
||||
line = spaces.split(line.strip())
|
||||
iface = line[0]
|
||||
if pat.match(iface) and len(line) > 9:
|
||||
tx_bytes = int(line[9])
|
||||
total += tx_bytes - prev_tx.get(iface, tx_bytes)
|
||||
prev_tx[iface] = tx_bytes
|
||||
open(fname, 'a').write(','.join([t, str(total * 8 / interval_sec / 1e6), str(total)]) + "\n")
|
||||
sleep(interval_sec)
|
||||
return
|
||||
|
||||
+74
-286
@@ -90,31 +90,28 @@ import os
|
||||
import re
|
||||
import select
|
||||
import signal
|
||||
import copy
|
||||
from time import sleep
|
||||
from itertools import chain, groupby
|
||||
from datetime import datetime
|
||||
from multiprocessing import Process
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import info, error, debug, output, warn
|
||||
from mininet.node import 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.1.0+"
|
||||
from mininet.monitor import Monitor
|
||||
|
||||
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
|
||||
@@ -150,7 +147,7 @@ class Mininet( object ):
|
||||
self.numCores = numCores()
|
||||
self.nextCore = 0 # next core for pinning hosts to CPUs
|
||||
self.listenPort = listenPort
|
||||
self.waitConn = waitConnected
|
||||
self.monitoring = None
|
||||
|
||||
self.hosts = []
|
||||
self.switches = []
|
||||
@@ -166,36 +163,12 @@ 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 set_debug(self, output_dir=None):
|
||||
'''Enable debugging, with output to the specified directory'''
|
||||
dt = datetime.now()
|
||||
if not output_dir:
|
||||
output_dir = '/tmp/mininet-%s-%s' % (str(dt.date()), str(dt.time()))
|
||||
self.monitoring = Monitor(output_dir=output_dir)
|
||||
|
||||
def addHost( self, name, cls=None, **params ):
|
||||
"""Add host.
|
||||
@@ -206,10 +179,9 @@ class Mininet( object ):
|
||||
# Default IP and MAC addresses
|
||||
defaults = { 'ip': ipAdd( self.nextIP,
|
||||
ipBaseNum=self.ipBaseNum,
|
||||
prefixLen=self.prefixLen ) +
|
||||
'/%s' % self.prefixLen }
|
||||
prefixLen=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
|
||||
@@ -243,81 +215,22 @@ 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=E1103
|
||||
name = controller_new.name
|
||||
# pylint: enable=E1103
|
||||
else:
|
||||
controller_new = controller( name, **params )
|
||||
# Add new controller to net
|
||||
if controller_new: # allow controller-less setups
|
||||
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 ):
|
||||
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
|
||||
subnet=self.ipBase, **params )
|
||||
# find first switch and create link
|
||||
if connect:
|
||||
# connect the nat to the first 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:
|
||||
return self.nameToNode[ args[ 0 ] ]
|
||||
return [ self.nameToNode[ n ] for n in args ]
|
||||
|
||||
def get( self, *args ):
|
||||
"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() )
|
||||
|
||||
def addLink( self, node1, node2, port1=None, port2=None,
|
||||
cls=None, **params ):
|
||||
""""Add a link from node1 to node2
|
||||
@@ -338,12 +251,7 @@ class Mininet( object ):
|
||||
"Configure a set of hosts."
|
||||
for host in self.hosts:
|
||||
info( host.name + ' ' )
|
||||
intf = host.defaultIntf()
|
||||
if intf:
|
||||
host.configDefault()
|
||||
else:
|
||||
# Don't configure nonexistent intf
|
||||
host.configDefault( ip=None, mac=None )
|
||||
host.configDefault( defaultRoute=host.defaultIntf() )
|
||||
# You're low priority, dude!
|
||||
# BL: do we want to do this here or not?
|
||||
# May not make sense if we have CPU lmiting...
|
||||
@@ -365,18 +273,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 type( classes ) is not 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():
|
||||
@@ -401,13 +301,13 @@ class Mininet( object ):
|
||||
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()
|
||||
@@ -419,9 +319,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' )
|
||||
@@ -453,31 +350,37 @@ class Mininet( object ):
|
||||
info( switch.name + ' ')
|
||||
switch.start( self.controllers )
|
||||
info( '\n' )
|
||||
if self.waitConn:
|
||||
self.waitConnected()
|
||||
info( '*** Starting system monitor\n' )
|
||||
if self.monitoring:
|
||||
m = self.monitoring
|
||||
m.monitors.append(Process(target=m.monitor_cpuacct,
|
||||
args=(self.hosts, '%s/cpuacct.txt' % m.output_dir)))
|
||||
m.start()
|
||||
info( 'Logging monitoring info in: %s\n' % m.output_dir )
|
||||
|
||||
def stop( self ):
|
||||
"Stop the controller(s), switches and hosts"
|
||||
info( '*** Stopping system monitor\n' )
|
||||
if self.monitoring:
|
||||
self.monitoring.stop()
|
||||
if self.terms:
|
||||
info( '*** Stopping %i terms\n' % len( self.terms ) )
|
||||
self.stopXterms()
|
||||
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' )
|
||||
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()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
||||
for host in self.hosts:
|
||||
info( host.name + ' ' )
|
||||
host.terminate()
|
||||
info( '\n*** Done\n' )
|
||||
|
||||
def run( self, test, *args, **kwargs ):
|
||||
@@ -520,20 +423,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
|
||||
@@ -546,10 +448,7 @@ class Mininet( object ):
|
||||
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()) )
|
||||
result = node.cmd( 'ping -c1 ' + dest.IP() )
|
||||
sent, received = self._parsePing( result )
|
||||
packets += sent
|
||||
if received > sent:
|
||||
@@ -560,82 +459,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:
|
||||
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.
|
||||
@@ -643,17 +475,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.
|
||||
@@ -670,7 +491,7 @@ class Mininet( object ):
|
||||
|
||||
# XXX This should be cleaned up
|
||||
|
||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ):
|
||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
|
||||
"""Run iperf between two hosts.
|
||||
hosts: list of hosts; if None, uses opposite hosts
|
||||
l4Type: string, one of [ TCP, UDP ]
|
||||
@@ -693,19 +514,17 @@ class Mininet( object ):
|
||||
bwArgs = '-b ' + udpBw + ' '
|
||||
elif l4Type != 'TCP':
|
||||
raise Exception( 'Unexpected l4 type: %s' % l4Type )
|
||||
if format:
|
||||
iperfArgs += '-f %s ' %format
|
||||
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 5 -c ' + server.IP() + ' ' +
|
||||
bwArgs )
|
||||
bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
server.sendInt()
|
||||
servout += server.waitOutput()
|
||||
@@ -716,42 +535,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
|
||||
returns a single list of measured CPU fractions as floats.
|
||||
"""
|
||||
pct = cpu * 100
|
||||
info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
|
||||
hosts = self.hosts
|
||||
for h in hosts:
|
||||
h.cmd( 'while true; do a=1; done &' )
|
||||
pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
|
||||
pids_str = ",".join(["%s" % pid for pid in pids])
|
||||
cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
|
||||
# It's a shame that this is what pylint prefers
|
||||
outputs = []
|
||||
for _ in range( duration ):
|
||||
sleep( 1 )
|
||||
outputs.append( quietRun( cmd ).strip() )
|
||||
for h in hosts:
|
||||
h.cmd( 'kill %1' )
|
||||
cpu_fractions = []
|
||||
for test_output in outputs:
|
||||
# Split by line. Ignore first line, which looks like this:
|
||||
# PID %CPU COMMAND\n
|
||||
for line in test_output.split('\n')[1:]:
|
||||
r = r'\d+\s*(\d+\.\d+)'
|
||||
m = re.search( r, line )
|
||||
if m is None:
|
||||
error( '*** Error: could not extract CPU fraction: %s\n' %
|
||||
line )
|
||||
return None
|
||||
cpu_fractions.append( float( m.group( 1 ) ) )
|
||||
output( '*** Results: %s\n' % cpu_fractions )
|
||||
return cpu_fractions
|
||||
|
||||
# BL: I think this can be rewritten now that we have
|
||||
# a real link class.
|
||||
def configLinkStatus( self, src, dst, status ):
|
||||
@@ -793,7 +576,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
|
||||
|
||||
@@ -834,7 +622,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 ]
|
||||
|
||||
+118
-482
@@ -16,11 +16,6 @@ Host: a virtual host. By default, a host is simply a shell; commands
|
||||
CPULimitedHost: a virtual host whose CPU bandwidth is limited by
|
||||
RT or CFS bandwidth limiting.
|
||||
|
||||
HostWithPrivateDirs: a virtual host that has user-specified private
|
||||
directories. These may be temporary directories stored as a tmpfs,
|
||||
or persistent directories that are mounted from another directory in
|
||||
the root filesystem.
|
||||
|
||||
Switch: superclass for switch nodes.
|
||||
|
||||
UserSwitch: a switch using the user-space switch from the OpenFlow
|
||||
@@ -50,21 +45,16 @@ Future enhancements:
|
||||
"""
|
||||
|
||||
import os
|
||||
import pty
|
||||
import re
|
||||
import signal
|
||||
import select
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
from operator import or_
|
||||
from time import sleep
|
||||
|
||||
from mininet.log import info, error, warn, debug
|
||||
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
|
||||
numCores, retry, mountCgroups )
|
||||
numCores, retry, mountCgroups )
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
|
||||
from mininet.link import Link, Intf, TCIntf
|
||||
from re import findall
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
class Node( object ):
|
||||
"""A virtual network node is simply a shell in a network namespace.
|
||||
@@ -93,8 +83,8 @@ class Node( object ):
|
||||
|
||||
# Make pylint happy
|
||||
( self.shell, self.execed, self.pid, self.stdin, self.stdout,
|
||||
self.lastPid, self.lastCmd, self.pollOut ) = (
|
||||
None, None, None, None, None, None, None, None )
|
||||
self.lastPid, self.lastCmd, self.pollOut ) = (
|
||||
None, None, None, None, None, None, None, None )
|
||||
self.waiting = False
|
||||
self.readbuf = ''
|
||||
|
||||
@@ -124,22 +114,15 @@ class Node( object ):
|
||||
return
|
||||
# mnexec: (c)lose descriptors, (d)etach from tty,
|
||||
# (p)rint pid, and run in (n)amespace
|
||||
opts = '-cd'
|
||||
opts = '-cdp'
|
||||
if self.inNamespace:
|
||||
opts += 'n'
|
||||
# bash -m: enable job control, i: force interactive
|
||||
# -s: pass $* to shell, and make process easy to find in ps
|
||||
# prompt is set to sentinel chr( 127 )
|
||||
os.environ[ 'PS1' ] = chr( 127 )
|
||||
cmd = [ 'mnexec', opts, 'bash', '--norc', '-mis', 'mininet:' + self.name ]
|
||||
# Spawn a shell subprocess in a pseudo-tty, to disable buffering
|
||||
# in the subprocess and insulate it from signals (e.g. SIGINT)
|
||||
# received by the parent
|
||||
master, slave = pty.openpty()
|
||||
self.shell = Popen( cmd, stdin=slave, stdout=slave, stderr=slave,
|
||||
close_fds=False )
|
||||
self.stdin = os.fdopen( master )
|
||||
self.stdout = self.stdin
|
||||
# bash -m: enable job control
|
||||
cmd = [ 'mnexec', opts, 'bash', '-m' ]
|
||||
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
|
||||
close_fds=True )
|
||||
self.stdin = self.shell.stdin
|
||||
self.stdout = self.shell.stdout
|
||||
self.pid = self.shell.pid
|
||||
self.pollOut = select.poll()
|
||||
self.pollOut.register( self.stdout )
|
||||
@@ -152,25 +135,16 @@ class Node( object ):
|
||||
self.lastCmd = None
|
||||
self.lastPid = None
|
||||
self.readbuf = ''
|
||||
# Wait for prompt
|
||||
while True:
|
||||
data = self.read( 1024 )
|
||||
if data[ -1 ] == chr( 127 ):
|
||||
break
|
||||
self.pollOut.poll()
|
||||
self.waiting = False
|
||||
self.cmd( 'stty -echo' )
|
||||
|
||||
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:
|
||||
quietRun( 'ip link del ' + intfName )
|
||||
if not self.inNamespace:
|
||||
for intfName in self.intfNames():
|
||||
if self.name in intfName:
|
||||
quietRun( 'ip link del ' + intfName )
|
||||
self.shell = None
|
||||
|
||||
# Subshell I/O, commands and control
|
||||
|
||||
def read( self, maxbytes=1024 ):
|
||||
"""Buffered read from node, non-blocking.
|
||||
maxbytes: maximum number of bytes to return"""
|
||||
@@ -193,7 +167,7 @@ class Node( object ):
|
||||
if '\n' not in self.readbuf:
|
||||
return None
|
||||
pos = self.readbuf.find( '\n' )
|
||||
line = self.readbuf[ 0: pos ]
|
||||
line = self.readbuf[ 0 : pos ]
|
||||
self.readbuf = self.readbuf[ pos + 1: ]
|
||||
return line
|
||||
|
||||
@@ -204,8 +178,7 @@ class Node( object ):
|
||||
|
||||
def terminate( self ):
|
||||
"Send kill signal to Node and clean up after it."
|
||||
if self.shell:
|
||||
os.killpg( self.pid, signal.SIGHUP )
|
||||
os.kill( self.pid, signal.SIGKILL )
|
||||
self.cleanup()
|
||||
|
||||
def stop( self ):
|
||||
@@ -238,32 +211,36 @@ class Node( object ):
|
||||
# Replace empty commands with something harmless
|
||||
cmd = 'echo -n'
|
||||
self.lastCmd = cmd
|
||||
if printPid and not isShellBuiltin( cmd ):
|
||||
if len( cmd ) > 0 and cmd[ -1 ] == '&':
|
||||
# print ^A{pid}\n so monitor() can set lastPid
|
||||
cmd += ' printf "\\001%d\n" $! \n'
|
||||
else:
|
||||
printPid = printPid and not isShellBuiltin( cmd )
|
||||
if len( cmd ) > 0 and cmd[ -1 ] == '&':
|
||||
# print ^A{pid}\n{sentinel}
|
||||
cmd += ' printf "\\001%d\n\\177" $! \n'
|
||||
else:
|
||||
# print sentinel
|
||||
cmd += '; printf "\\177"'
|
||||
if printPid and not isShellBuiltin( cmd ):
|
||||
cmd = 'mnexec -p ' + cmd
|
||||
self.write( cmd + '\n' )
|
||||
self.lastPid = None
|
||||
self.waiting = True
|
||||
|
||||
def sendInt( self, intr=chr( 3 ) ):
|
||||
def sendInt( self, sig=signal.SIGINT ):
|
||||
"Interrupt running command."
|
||||
self.write( intr )
|
||||
if self.lastPid:
|
||||
try:
|
||||
os.kill( self.lastPid, sig )
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def monitor( self, timeoutms=None, findPid=True ):
|
||||
def monitor( self, timeoutms=None ):
|
||||
"""Monitor and return the output of a command.
|
||||
Set self.waiting to False if command has completed.
|
||||
timeoutms: timeout in ms or None to wait indefinitely."""
|
||||
self.waitReadable( timeoutms )
|
||||
data = self.read( 1024 )
|
||||
# Look for PID
|
||||
marker = chr( 1 ) + r'\d+\r\n'
|
||||
if findPid and chr( 1 ) in data:
|
||||
# Marker can be read in chunks; continue until all of it is read
|
||||
while not re.findall( marker, data ):
|
||||
data += self.read( 1024 )
|
||||
marker = chr( 1 ) + r'\d+\n'
|
||||
if chr( 1 ) in data:
|
||||
markers = re.findall( marker, data )
|
||||
if markers:
|
||||
self.lastPid = int( markers[ 0 ][ 1: ] )
|
||||
@@ -305,43 +282,6 @@ class Node( object ):
|
||||
cmd: string"""
|
||||
return self.cmd( *args, **{ 'verbose': True } )
|
||||
|
||||
def popen( self, *args, **kwargs ):
|
||||
"""Return a Popen() object in our namespace
|
||||
args: Popen() args, single list, or string
|
||||
kwargs: Popen() keyword args"""
|
||||
defaults = { 'stdout': PIPE, 'stderr': PIPE,
|
||||
'mncmd':
|
||||
[ 'mnexec', '-da', str( self.pid ) ] }
|
||||
defaults.update( kwargs )
|
||||
if len( args ) == 1:
|
||||
if type( args[ 0 ] ) is list:
|
||||
# popen([cmd, arg1, arg2...])
|
||||
cmd = args[ 0 ]
|
||||
elif type( args[ 0 ] ) is str:
|
||||
# popen("cmd arg1 arg2...")
|
||||
cmd = args[ 0 ].split()
|
||||
else:
|
||||
raise Exception( 'popen() requires a string or list' )
|
||||
elif len( args ) > 0:
|
||||
# popen( cmd, arg1, arg2... )
|
||||
cmd = list( args )
|
||||
# Attach to our namespace using mnexec -a
|
||||
mncmd = defaults[ 'mncmd' ]
|
||||
del defaults[ 'mncmd' ]
|
||||
cmd = mncmd + cmd
|
||||
# Shell requires a string, not a list!
|
||||
if defaults.get( 'shell', False ):
|
||||
cmd = ' '.join( cmd )
|
||||
return Popen( cmd, **defaults )
|
||||
|
||||
def pexec( self, *args, **kwargs ):
|
||||
"""Execute a command using popen
|
||||
returns: out, err, exitcode"""
|
||||
popen = self.popen( *args, **kwargs)
|
||||
out, err = popen.communicate()
|
||||
exitcode = popen.wait()
|
||||
return out, err, exitcode
|
||||
|
||||
# Interface management, configuration, and routing
|
||||
|
||||
# BL notes: This might be a bit redundant or over-complicated.
|
||||
@@ -378,23 +318,17 @@ class Node( object ):
|
||||
return self.intfs[ min( ports ) ]
|
||||
else:
|
||||
warn( '*** defaultIntf: warning:', self.name,
|
||||
'has no interfaces\n' )
|
||||
'has no interfaces\n' )
|
||||
|
||||
def intf( self, intf='' ):
|
||||
"""Return our interface object with given string name,
|
||||
default intf if name is falsy (None, empty string, etc).
|
||||
or the input intf arg.
|
||||
|
||||
Having this fcn return its arg for Intf objects makes it
|
||||
easier to construct functions with flexible input args for
|
||||
interfaces (those that accept both string names and Intf objects).
|
||||
"""
|
||||
"""Return our interface object with given name,
|
||||
or default intf if name is empty"""
|
||||
if not intf:
|
||||
return self.defaultIntf()
|
||||
elif type( intf ) is str:
|
||||
elif type( intf) is str:
|
||||
return self.nameToIntf[ intf ]
|
||||
else:
|
||||
return intf
|
||||
return None
|
||||
|
||||
def connectionsTo( self, node):
|
||||
"Return [ intf1, intf2... ] for all intfs that connect self to node."
|
||||
@@ -410,19 +344,16 @@ class Node( object ):
|
||||
connections += [ ( intf, link.intf1 ) ]
|
||||
return connections
|
||||
|
||||
def deleteIntfs( self, checkName=True ):
|
||||
"""Delete all of our interfaces.
|
||||
checkName: only delete interfaces that contain our name"""
|
||||
def deleteIntfs( self ):
|
||||
"Delete all of our interfaces."
|
||||
# In theory the interfaces should go away after we shut down.
|
||||
# However, this takes time, so we're better off removing them
|
||||
# explicitly so that we won't get errors if we run before they
|
||||
# have been removed by the kernel. Unfortunately this is very slow,
|
||||
# at least with Linux kernels before 2.6.33
|
||||
for intf in self.intfs.values():
|
||||
# Protect against deleting hardware interfaces
|
||||
if ( self.name in intf.name ) or ( not checkName ):
|
||||
intf.delete()
|
||||
info( '.' )
|
||||
intf.delete()
|
||||
info( '.' )
|
||||
|
||||
# Routing support
|
||||
|
||||
@@ -441,14 +372,11 @@ class Node( object ):
|
||||
|
||||
def setDefaultRoute( self, intf=None ):
|
||||
"""Set the default route to go through intf.
|
||||
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
|
||||
# Note setParam won't call us if intf is none
|
||||
if type( intf ) is str and ' ' in intf:
|
||||
params = intf
|
||||
else:
|
||||
params = 'dev %s' % intf
|
||||
self.cmd( 'ip route del default' )
|
||||
return self.cmd( 'ip route add default', params )
|
||||
intf: string, interface name"""
|
||||
if not intf:
|
||||
intf = self.defaultIntf()
|
||||
self.cmd( 'ip route flush root 0/0' )
|
||||
return self.cmd( 'route add default %s' % intf )
|
||||
|
||||
# Convenience and configuration methods
|
||||
|
||||
@@ -460,7 +388,7 @@ class Node( object ):
|
||||
|
||||
def setIP( self, ip, prefixLen=8, intf=None ):
|
||||
"""Set the IP address for an interface.
|
||||
intf: intf or intf name
|
||||
intf: interface name
|
||||
ip: IP address as a string
|
||||
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
|
||||
# This should probably be rethought
|
||||
@@ -474,7 +402,7 @@ class Node( object ):
|
||||
|
||||
def MAC( self, intf=None ):
|
||||
"Return MAC address of a node or specific interface."
|
||||
return self.intf( intf ).MAC()
|
||||
return self.intf( intf ).IP()
|
||||
|
||||
def intfIsUp( self, intf=None ):
|
||||
"Check if an interface is up."
|
||||
@@ -519,7 +447,7 @@ class Node( object ):
|
||||
r = {}
|
||||
self.setParam( r, 'setMAC', mac=mac )
|
||||
self.setParam( r, 'setIP', ip=ip )
|
||||
self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute )
|
||||
self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
|
||||
# This should be examined
|
||||
self.cmd( 'ifconfig lo ' + lo )
|
||||
return r
|
||||
@@ -548,9 +476,9 @@ class Node( object ):
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
for i in self.intfList() ] ) )
|
||||
for i in self.intfList() ] ) )
|
||||
return '<%s %s: %s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
|
||||
def __str__( self ):
|
||||
"Abbreviated string representation"
|
||||
@@ -601,7 +529,6 @@ class CPULimitedHost( Host ):
|
||||
# still does better with larger period values.
|
||||
self.period_us = kwargs.get( 'period_us', 100000 )
|
||||
self.sched = sched
|
||||
self.rtprio = 20
|
||||
|
||||
def cgroupSet( self, param, value, resource='cpu' ):
|
||||
"Set a cgroup parameter and return its value"
|
||||
@@ -626,25 +553,13 @@ class CPULimitedHost( Host ):
|
||||
_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
|
||||
return exitcode != 0
|
||||
|
||||
def popen( self, *args, **kwargs ):
|
||||
"""Return a Popen() object in node's namespace
|
||||
args: Popen() args, single list, or string
|
||||
kwargs: Popen() keyword args"""
|
||||
# Tell mnexec to execute command in our cgroup
|
||||
mncmd = [ 'mnexec', '-da', str( self.pid ),
|
||||
'-g', self.name ]
|
||||
if self.sched == 'rt':
|
||||
mncmd += [ '-r', str( self.rtprio ) ]
|
||||
return Host.popen( self, *args, mncmd=mncmd, **kwargs )
|
||||
|
||||
def cleanup( self ):
|
||||
"Clean up Node, then clean up our cgroup"
|
||||
super( CPULimitedHost, self ).cleanup()
|
||||
"Clean up our cgroup"
|
||||
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
|
||||
|
||||
def chrt( self ):
|
||||
def chrt( self, prio=20 ):
|
||||
"Set RT scheduling priority"
|
||||
quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
|
||||
quietRun( 'chrt -p %s %s' % ( prio, self.pid ) )
|
||||
result = quietRun( 'chrt -p %s' % self.pid )
|
||||
firstline = result.split( '\n' )[ 0 ]
|
||||
lastword = firstline.split( ' ' )[ -1 ]
|
||||
@@ -703,7 +618,7 @@ class CPULimitedHost( Host ):
|
||||
self.cgroupSet( qstr, quota )
|
||||
if sched == 'rt':
|
||||
# Set RT priority if necessary
|
||||
self.chrt()
|
||||
self.chrt( prio=20 )
|
||||
info( '(%s %d/%dus) ' % ( sched, quota, period ) )
|
||||
|
||||
def setCPUs( self, cores, mems=0 ):
|
||||
@@ -719,7 +634,7 @@ class CPULimitedHost( Host ):
|
||||
# We have to do this here after we've specified
|
||||
# cpus and mems
|
||||
errFail( 'cgclassify -g cpuset:/%s %s' % (
|
||||
self.name, self.pid ) )
|
||||
self.name, self.pid ) )
|
||||
|
||||
def config( self, cpu=None, cores=None, **params ):
|
||||
"""cpu: desired overall system CPU fraction
|
||||
@@ -740,33 +655,6 @@ class CPULimitedHost( Host ):
|
||||
mountCgroups()
|
||||
cls.inited = True
|
||||
|
||||
class HostWithPrivateDirs( Host ):
|
||||
"Host with private directories"
|
||||
|
||||
def __init__( self, name, *args, **kwargs ):
|
||||
"privateDirs: list of private directory strings or tuples"
|
||||
self.name = name
|
||||
self.privateDirs = kwargs.pop( 'privateDirs', [] )
|
||||
Host.__init__( self, name, *args, **kwargs )
|
||||
self.mountPrivateDirs()
|
||||
|
||||
def mountPrivateDirs( self ):
|
||||
"mount private directories"
|
||||
for directory in self.privateDirs:
|
||||
if isinstance( directory, tuple ):
|
||||
# mount given private directory
|
||||
privateDir = directory[ 1 ] % self.__dict__
|
||||
mountPoint = directory[ 0 ]
|
||||
self.cmd( 'mkdir -p %s' % privateDir )
|
||||
self.cmd( 'mkdir -p %s' % mountPoint )
|
||||
self.cmd( 'mount --bind %s %s' %
|
||||
( privateDir, mountPoint ) )
|
||||
else:
|
||||
# mount temporary filesystem on directory
|
||||
self.cmd( 'mkdir -p %s' % directory )
|
||||
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
|
||||
|
||||
|
||||
|
||||
# Some important things to note:
|
||||
#
|
||||
@@ -793,35 +681,24 @@ class Switch( Node ):
|
||||
an OpenFlow switch."""
|
||||
|
||||
portBase = 1 # Switches start with port 1 in OpenFlow
|
||||
dpidLen = 16 # digits in dpid passed to switch
|
||||
|
||||
def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
|
||||
"""dpid: dpid hex string (or None to derive from name, e.g. s1 -> 1)
|
||||
"""dpid: dpid for switch (or None for default)
|
||||
opts: additional switch options
|
||||
listenPort: port to listen on for dpctl connections"""
|
||||
Node.__init__( self, name, **params )
|
||||
self.dpid = self.defaultDpid( dpid )
|
||||
self.dpid = dpid if dpid else self.defaultDpid()
|
||||
self.opts = opts
|
||||
self.listenPort = listenPort
|
||||
if not self.inNamespace:
|
||||
self.controlIntf = Intf( 'lo', self, port=0 )
|
||||
|
||||
def defaultDpid( self, dpid=None ):
|
||||
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
|
||||
if dpid:
|
||||
# Remove any colons and make sure it's a good hex number
|
||||
dpid = dpid.translate( None, ':' )
|
||||
assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
|
||||
else:
|
||||
# Use hex of the first number in the switch name
|
||||
nums = re.findall( r'\d+', self.name )
|
||||
if nums:
|
||||
dpid = hex( int( nums[ 0 ] ) )[ 2: ]
|
||||
else:
|
||||
raise Exception( 'Unable to derive default datapath ID - '
|
||||
'please either specify a dpid or use a '
|
||||
'canonical switch name such as s23.' )
|
||||
return '0' * ( self.dpidLen - len( dpid ) ) + dpid
|
||||
def defaultDpid( self ):
|
||||
"Derive dpid from switch name, s1 -> 1"
|
||||
dpid = int( re.findall( '\d+', self.name )[ 0 ] )
|
||||
dpid = hex( dpid )[ 2: ]
|
||||
dpid = '0' * ( 16 - len( dpid ) ) + dpid
|
||||
return dpid
|
||||
|
||||
def defaultIntf( self ):
|
||||
"Return control interface"
|
||||
@@ -838,37 +715,26 @@ class Switch( Node ):
|
||||
return Node.sendCmd( self, *cmd, **kwargs )
|
||||
else:
|
||||
error( '*** Error: %s has execed and cannot accept commands' %
|
||||
self.name )
|
||||
|
||||
def connected( self ):
|
||||
"Is the switch connected to a controller? (override this method)"
|
||||
return False and self # satisfy pylint
|
||||
self.name )
|
||||
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
for i in self.intfList() ] ) )
|
||||
for i in self.intfList() ] ) )
|
||||
return '<%s %s: %s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
|
||||
class UserSwitch( Switch ):
|
||||
"User-space switch."
|
||||
|
||||
dpidLen = 12
|
||||
|
||||
def __init__( self, name, dpopts='--no-slicing', **kwargs ):
|
||||
def __init__( self, name, **kwargs ):
|
||||
"""Init.
|
||||
name: name for the switch
|
||||
dpopts: additional arguments to ofdatapath (--no-slicing)"""
|
||||
name: name for the switch"""
|
||||
Switch.__init__( self, name, **kwargs )
|
||||
pathCheck( 'ofdatapath', 'ofprotocol',
|
||||
moduleName='the OpenFlow reference user switch' +
|
||||
'(openflow.org)' )
|
||||
moduleName='the OpenFlow reference user switch (openflow.org)' )
|
||||
if self.listenPort:
|
||||
self.opts += ' --listen=ptcp:%i ' % self.listenPort
|
||||
else:
|
||||
self.opts += ' --listen=punix:/tmp/%s.listen' % self.name
|
||||
self.dpopts = dpopts
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
@@ -878,68 +744,28 @@ class UserSwitch( Switch ):
|
||||
|
||||
def dpctl( self, *args ):
|
||||
"Run dpctl command"
|
||||
listenAddr = None
|
||||
if not self.listenPort:
|
||||
listenAddr = 'unix:/tmp/%s.listen' % self.name
|
||||
else:
|
||||
listenAddr = 'tcp:127.0.0.1:%i' % self.listenPort
|
||||
return "can't run dpctl without passive listening port"
|
||||
return self.cmd( 'dpctl ' + ' '.join( args ) +
|
||||
' ' + listenAddr )
|
||||
|
||||
def connected( self ):
|
||||
"Is the switch connected to a controller?"
|
||||
return 'remote.is-connected=true' in self.dpctl( 'status' )
|
||||
|
||||
@staticmethod
|
||||
def TCReapply( intf ):
|
||||
"""Unfortunately user switch and Mininet are fighting
|
||||
over tc queuing disciplines. To resolve the conflict,
|
||||
we re-create the user switch's configuration, but as a
|
||||
leaf of the TCIntf-created configuration."""
|
||||
if type( intf ) is TCIntf:
|
||||
ifspeed = 10000000000 # 10 Gbps
|
||||
minspeed = ifspeed * 0.001
|
||||
|
||||
res = intf.config( **intf.params )
|
||||
|
||||
if res is None: # link may not have TC parameters
|
||||
return
|
||||
|
||||
# Re-add qdisc, root, and default classes user switch created, but
|
||||
# with new parent, as setup by Mininet's TCIntf
|
||||
parent = res['parent']
|
||||
intf.tc( "%s qdisc add dev %s " + parent +
|
||||
" handle 1: htb default 0xfffe" )
|
||||
intf.tc( "%s class add dev %s classid 1:0xffff parent 1: htb rate "
|
||||
+ str(ifspeed) )
|
||||
intf.tc( "%s class add dev %s classid 1:0xfffe parent 1:0xffff " +
|
||||
"htb rate " + str(minspeed) + " ceil " + str(ifspeed) )
|
||||
' tcp:127.0.0.1:%i' % self.listenPort )
|
||||
|
||||
def start( self, controllers ):
|
||||
"""Start OpenFlow reference user datapath.
|
||||
Log to /tmp/sN-{ofd,ofp}.log.
|
||||
controllers: list of controller objects"""
|
||||
# Add controllers
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
controller = controllers[ 0 ]
|
||||
ofdlog = '/tmp/' + self.name + '-ofd.log'
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
|
||||
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
|
||||
' punix:/tmp/' + self.name + ' -d %s ' % self.dpid +
|
||||
self.dpopts +
|
||||
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||
' punix:/tmp/' + self.name + ' -d ' + self.dpid +
|
||||
' --no-slicing ' +
|
||||
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
|
||||
' ' + clist +
|
||||
' --fail=closed ' + self.opts +
|
||||
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||
if "no-slicing" not in self.dpopts:
|
||||
# Only TCReapply if slicing is enable
|
||||
sleep(1) # Allow ofdatapath to start before re-arranging qdisc's
|
||||
for intf in self.intfList():
|
||||
if not intf.IP():
|
||||
self.TCReapply( intf )
|
||||
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
|
||||
' --fail=closed ' + self.opts +
|
||||
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||
|
||||
def stop( self ):
|
||||
"Stop OpenFlow reference user datapath."
|
||||
@@ -962,14 +788,14 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
self.intf = self.dp
|
||||
if self.inNamespace:
|
||||
error( "OVSKernelSwitch currently only works"
|
||||
" in the root namespace.\n" )
|
||||
" in the root namespace.\n" )
|
||||
exit( 1 )
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Ensure any dependencies are loaded; if not, try to load them."
|
||||
pathCheck( 'ovs-dpctl', 'ovs-openflowd',
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
|
||||
def start( self, controllers ):
|
||||
@@ -983,13 +809,12 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
|
||||
self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
|
||||
# Run protocol daemon
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
controller = controllers[ 0 ]
|
||||
self.cmd( 'ovs-openflowd ' + self.dp +
|
||||
' ' + clist +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --datapath-id=' + self.dpid +
|
||||
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --datapath-id=' + self.dpid +
|
||||
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||
self.execed = False
|
||||
|
||||
def stop( self ):
|
||||
@@ -1002,27 +827,19 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
class OVSSwitch( Switch ):
|
||||
"Open vSwitch switch. Depends on ovs-vsctl."
|
||||
|
||||
def __init__( self, name, failMode='secure', datapath='kernel',
|
||||
inband=False, protocols=None, **params ):
|
||||
def __init__( self, name, failMode='secure', **params ):
|
||||
"""Init.
|
||||
name: name for switch
|
||||
failMode: controller loss behavior (secure|open)
|
||||
datapath: userspace or kernel mode (kernel|user)
|
||||
inband: use in-band control (False)"""
|
||||
failMode: controller loss behavior (secure|open)"""
|
||||
Switch.__init__( self, name, **params )
|
||||
self.failMode = failMode
|
||||
self.datapath = datapath
|
||||
self.inband = inband
|
||||
self.protocols = protocols
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Make sure Open vSwitch is installed and working"
|
||||
pathCheck( 'ovs-vsctl',
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
# This should no longer be needed, and it breaks
|
||||
# with OVS 1.7 which has renamed the kernel module:
|
||||
# moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
|
||||
if exitcode:
|
||||
error( out + err +
|
||||
@@ -1034,24 +851,10 @@ class OVSSwitch( Switch ):
|
||||
'You may wish to try '
|
||||
'"service openvswitch-switch start".\n' )
|
||||
exit( 1 )
|
||||
info = quietRun( 'ovs-vsctl --version' )
|
||||
cls.OVSVersion = findall( '\d+\.\d+', info )[ 0 ]
|
||||
|
||||
@classmethod
|
||||
def isOldOVS( cls ):
|
||||
return ( StrictVersion( cls.OVSVersion ) <
|
||||
StrictVersion( '1.10' ) )
|
||||
|
||||
@classmethod
|
||||
def batchShutdown( cls, switches ):
|
||||
"Call ovs-vsctl del-br on all OVSSwitches in a list"
|
||||
quietRun( 'ovs-vsctl ' +
|
||||
' -- '.join( '--if-exists del-br %s' % s
|
||||
for s in switches ) )
|
||||
|
||||
def dpctl( self, *args ):
|
||||
"Run ovs-ofctl command"
|
||||
return self.cmd( 'ovs-ofctl', args[ 0 ], self, *args[ 1: ] )
|
||||
"Run ovs-dpctl command"
|
||||
return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
|
||||
|
||||
@staticmethod
|
||||
def TCReapply( intf ):
|
||||
@@ -1071,23 +874,6 @@ class OVSSwitch( Switch ):
|
||||
"Disconnect a data port"
|
||||
self.cmd( 'ovs-vsctl del-port', self, intf )
|
||||
|
||||
def controllerUUIDs( self ):
|
||||
"Return ovsdb UUIDs for our controllers"
|
||||
uuids = []
|
||||
controllers = self.cmd( 'ovs-vsctl -- get Bridge', self,
|
||||
'Controller' ).strip()
|
||||
if controllers.startswith( '[' ) and controllers.endswith( ']' ):
|
||||
controllers = controllers[ 1 : -1 ]
|
||||
uuids = [ c.strip() for c in controllers.split( ',' ) ]
|
||||
return uuids
|
||||
|
||||
def connected( self ):
|
||||
"Are we connected to at least one of our controllers?"
|
||||
results = [ 'true' in self.cmd( 'ovs-vsctl -- get Controller',
|
||||
uuid, 'is_connected' )
|
||||
for uuid in self.controllerUUIDs() ]
|
||||
return reduce( or_, results, False )
|
||||
|
||||
def start( self, controllers ):
|
||||
"Start up a new OVS OpenFlow switch using ovs-vsctl"
|
||||
if self.inNamespace:
|
||||
@@ -1098,168 +884,38 @@ class OVSSwitch( Switch ):
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
# Annoyingly, --if-exists option seems not to work
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
int( self.dpid, 16 ) # DPID must be a hex string
|
||||
# Interfaces and controllers
|
||||
intfs = ' '.join( '-- add-port %s %s ' % ( self, intf ) +
|
||||
'-- set Interface %s ' % intf +
|
||||
'ofport_request=%s ' % self.ports[ intf ]
|
||||
for intf in self.intfList() if not intf.IP() )
|
||||
clist = ' '.join( '%s:%s:%d' % ( c.protocol, c.IP(), c.port )
|
||||
for c in controllers )
|
||||
if self.listenPort:
|
||||
clist += ' ptcp:%s' % self.listenPort
|
||||
# Construct big ovs-vsctl command for new versions of OVS
|
||||
if not self.isOldOVS():
|
||||
cmd = ( 'ovs-vsctl add-br %s ' % self +
|
||||
'-- set Bridge %s ' % self +
|
||||
'other_config:datapath-id=%s ' % self.dpid +
|
||||
'-- set-fail-mode %s %s ' % ( self, self.failMode ) +
|
||||
intfs +
|
||||
'-- set-controller %s %s ' % ( self, clist ) )
|
||||
# Construct ovs-vsctl commands for old versions of OVS
|
||||
else:
|
||||
self.cmd( 'ovs-vsctl add-br', self )
|
||||
for intf in self.intfList():
|
||||
if not intf.IP():
|
||||
self.cmd( 'ovs-vsctl add-port', self, intf )
|
||||
cmd = ( 'ovs-vsctl set Bridge %s ' % self +
|
||||
'other_config:datapath-id=%s ' % self.dpid +
|
||||
'-- set-fail-mode %s %s ' % ( self, self.failMode ) +
|
||||
'-- set-controller %s %s ' % ( self, clist ) )
|
||||
if not self.inband:
|
||||
cmd += ( '-- set bridge %s '
|
||||
'other-config:disable-in-band=true ' % self )
|
||||
if self.datapath == 'user':
|
||||
cmd += '-- set bridge %s datapath_type=netdev ' % self
|
||||
if self.protocols:
|
||||
cmd += '-- set bridge %s protocols=%s' % ( self, self.protocols )
|
||||
# Reconnect quickly to controllers (1s vs. 15s max_backoff)
|
||||
for uuid in self.controllerUUIDs():
|
||||
if uuid.count( '-' ) != 4:
|
||||
# Doesn't look like a UUID
|
||||
continue
|
||||
uuid = uuid.strip()
|
||||
cmd += '-- set Controller %smax_backoff=1000 ' % uuid
|
||||
# Do it!!
|
||||
self.cmd( cmd )
|
||||
self.cmd( 'ovs-vsctl add-br', self )
|
||||
self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode )
|
||||
for intf in self.intfList():
|
||||
self.TCReapply( intf )
|
||||
|
||||
if not intf.IP():
|
||||
self.attach( intf )
|
||||
# Add controllers
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
self.cmd( 'ovs-vsctl set-controller', self, clist )
|
||||
|
||||
def stop( self ):
|
||||
"Terminate OVS switch."
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
if self.datapath == 'user':
|
||||
self.cmd( 'ip link del', self )
|
||||
self.deleteIntfs()
|
||||
|
||||
OVSKernelSwitch = OVSSwitch
|
||||
|
||||
|
||||
class IVSSwitch(Switch):
|
||||
"""IVS virtual switch"""
|
||||
|
||||
def __init__( self, name, verbose=True, **kwargs ):
|
||||
Switch.__init__( self, name, **kwargs )
|
||||
self.verbose = verbose
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Make sure IVS is installed"
|
||||
pathCheck( 'ivs-ctl', 'ivs',
|
||||
moduleName="Indigo Virtual Switch (projectfloodlight.org)" )
|
||||
out, err, exitcode = errRun( 'ivs-ctl show' )
|
||||
if exitcode:
|
||||
error( out + err +
|
||||
'ivs-ctl exited with code %d\n' % exitcode +
|
||||
'*** The openvswitch kernel module might '
|
||||
'not be loaded. Try modprobe openvswitch.\n' )
|
||||
exit( 1 )
|
||||
|
||||
@classmethod
|
||||
def batchShutdown( cls, switches ):
|
||||
"Kill each IVS switch, to be waited on later in stop()"
|
||||
for switch in switches:
|
||||
switch.cmd( 'kill %ivs' )
|
||||
|
||||
def start( self, controllers ):
|
||||
"Start up a new IVS switch"
|
||||
args = ['ivs']
|
||||
args.extend( ['--name', self.name] )
|
||||
args.extend( ['--dpid', self.dpid] )
|
||||
if self.verbose:
|
||||
args.extend( ['--verbose'] )
|
||||
for intf in self.intfs.values():
|
||||
if not intf.IP():
|
||||
args.extend( ['-i', intf.name] )
|
||||
for c in controllers:
|
||||
args.extend( ['-c', '%s:%d' % (c.IP(), c.port)] )
|
||||
if self.listenPort:
|
||||
args.extend( ['--listen', '127.0.0.1:%i' % self.listenPort] )
|
||||
args.append( self.opts )
|
||||
|
||||
logfile = '/tmp/ivs.%s.log' % self.name
|
||||
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
|
||||
|
||||
def stop( self ):
|
||||
"Terminate IVS switch."
|
||||
self.cmd( 'kill %ivs' )
|
||||
self.cmd( 'wait' )
|
||||
self.deleteIntfs()
|
||||
|
||||
def attach( self, intf ):
|
||||
"Connect a data port"
|
||||
self.cmd( 'ivs-ctl', 'add-port', '--datapath', self.name, intf )
|
||||
|
||||
def detach( self, intf ):
|
||||
"Disconnect a data port"
|
||||
self.cmd( 'ivs-ctl', 'del-port', '--datapath', self.name, intf )
|
||||
|
||||
def dpctl( self, *args ):
|
||||
"Run dpctl command"
|
||||
if not self.listenPort:
|
||||
return "can't run dpctl without passive listening port"
|
||||
return self.cmd( 'ovs-ofctl ' + ' '.join( args ) +
|
||||
' tcp:127.0.0.1:%i' % self.listenPort )
|
||||
|
||||
|
||||
class Controller( Node ):
|
||||
"""A Controller is a Node that is running (or has execed?) an
|
||||
OpenFlow controller."""
|
||||
|
||||
def __init__( self, name, inNamespace=False, command='controller',
|
||||
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
|
||||
port=6633, protocol='tcp', **params ):
|
||||
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
|
||||
port=6633, **params ):
|
||||
self.command = command
|
||||
self.cargs = cargs
|
||||
self.cdir = cdir
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
self.protocol = protocol
|
||||
Node.__init__( self, name, inNamespace=inNamespace,
|
||||
ip=ip, **params )
|
||||
self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary
|
||||
self.checkListening()
|
||||
|
||||
def checkListening( self ):
|
||||
"Make sure no controllers are running on our port"
|
||||
# Verify that Telnet is installed first:
|
||||
out, _err, returnCode = errRun( "which telnet" )
|
||||
if 'telnet' not in out or returnCode != 0:
|
||||
raise Exception( "Error running telnet to check for listening "
|
||||
"controllers; please check that it is "
|
||||
"installed." )
|
||||
listening = self.cmd( "echo A | telnet -e A %s %d" %
|
||||
( self.ip, self.port ) )
|
||||
if 'Connected' in listening:
|
||||
servers = self.cmd( 'netstat -natp' ).split( '\n' )
|
||||
pstr = ':%d ' % self.port
|
||||
clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
|
||||
raise Exception( "Please shut down the controller which is"
|
||||
" running on port %d:\n" % self.port +
|
||||
'\n'.join( clist ) )
|
||||
ip=ip, **params )
|
||||
|
||||
def start( self ):
|
||||
"""Start <controller> <args> on controller.
|
||||
@@ -1269,7 +925,7 @@ class Controller( Node ):
|
||||
if self.cdir is not None:
|
||||
self.cmd( 'cd ' + self.cdir )
|
||||
self.cmd( self.command + ' ' + self.cargs % self.port +
|
||||
' 1>' + cout + ' 2>' + cout + '&' )
|
||||
' 1>' + cout + ' 2>' + cout + '&' )
|
||||
self.execed = False
|
||||
|
||||
def stop( self ):
|
||||
@@ -1288,21 +944,15 @@ class Controller( Node ):
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
return '<%s %s: %s:%s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name,
|
||||
self.IP(), self.port, self.pid )
|
||||
@classmethod
|
||||
def isAvailable( self ):
|
||||
return quietRun( 'which controller' )
|
||||
self.__class__.__name__, self.name,
|
||||
self.IP(), self.port, self.pid )
|
||||
|
||||
|
||||
class OVSController( Controller ):
|
||||
"Open vSwitch controller"
|
||||
def __init__( self, name, command='ovs-controller', **kwargs ):
|
||||
if quietRun( 'which test-controller' ):
|
||||
command = 'test-controller'
|
||||
Controller.__init__( self, name, command=command, **kwargs )
|
||||
@classmethod
|
||||
def isAvailable( self ):
|
||||
return quietRun( 'which ovs-controller' ) or quietRun( 'which test-controller' )
|
||||
|
||||
|
||||
class NOX( Controller ):
|
||||
"Controller to run a NOX application."
|
||||
@@ -1323,24 +973,24 @@ class NOX( Controller ):
|
||||
noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
|
||||
|
||||
Controller.__init__( self, name,
|
||||
command=noxCoreDir + '/nox_core',
|
||||
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
|
||||
' '.join( noxArgs ),
|
||||
cdir=noxCoreDir,
|
||||
**kwargs )
|
||||
command=noxCoreDir + '/nox_core',
|
||||
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
|
||||
' '.join( noxArgs ),
|
||||
cdir=noxCoreDir, **kwargs )
|
||||
|
||||
|
||||
class RemoteController( Controller ):
|
||||
"Controller running outside of Mininet's control."
|
||||
|
||||
def __init__( self, name, ip='127.0.0.1',
|
||||
port=6633, **kwargs):
|
||||
def __init__( self, name, defaultIP='127.0.0.1',
|
||||
port=6633, **kwargs):
|
||||
"""Init.
|
||||
name: name to give controller
|
||||
ip: the IP address where the remote controller is
|
||||
defaultIP: the IP address where the remote controller is
|
||||
listening
|
||||
port: the port where the remote controller is listening"""
|
||||
Controller.__init__( self, name, ip=ip, port=port, **kwargs )
|
||||
Controller.__init__( self, name, defaultIP=defaultIP, port=port,
|
||||
**kwargs )
|
||||
|
||||
def start( self ):
|
||||
"Overridden to do nothing."
|
||||
@@ -1349,17 +999,3 @@ class RemoteController( Controller ):
|
||||
def stop( self ):
|
||||
"Overridden to do nothing."
|
||||
return
|
||||
|
||||
def checkListening( self ):
|
||||
"Warn if remote controller is not accessible"
|
||||
listening = self.cmd( "echo A | telnet -e A %s %d" %
|
||||
( self.ip, self.port ) )
|
||||
if 'Connected' not in listening:
|
||||
warn( "Unable to contact the remote controller"
|
||||
" at %s:%d\n" % ( self.ip, self.port ) )
|
||||
|
||||
def DefaultController( name, order=[ Controller, OVSController ], **kwargs ):
|
||||
"find any controller that is available and run it"
|
||||
for controller in order:
|
||||
if controller.isAvailable():
|
||||
return controller( name, **kwargs )
|
||||
@@ -1,115 +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 setLogLevel, info
|
||||
|
||||
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 ):
|
||||
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 ):
|
||||
self.cmd( 'ifconfig', self, 'down' )
|
||||
self.cmd( 'brctl delbr', self )
|
||||
|
||||
class NAT( Node ):
|
||||
"""NAT: Provides connectivity to external network"""
|
||||
|
||||
def __init__( self, name, inetIntf='eth0', subnet='10.0/8', localIntf=None, **params):
|
||||
super( NAT, self ).__init__( name, **params )
|
||||
|
||||
"""Start NAT/forwarding between Mininet and external network
|
||||
inetIntf: interface for internet access
|
||||
subnet: Mininet subnet (default 10.0/8)="""
|
||||
self.inetIntf = inetIntf
|
||||
self.subnet = subnet
|
||||
self.localIntf = localIntf
|
||||
|
||||
def config( self, **params ):
|
||||
super( NAT, self).config( **params )
|
||||
"""Configure the NAT and iptables"""
|
||||
|
||||
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, '-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 )
|
||||
with open( cfile, 'a' ) as f:
|
||||
f.write( line )
|
||||
# Probably need to restart network-manager to be safe -
|
||||
# hopefully this won't disconnect you
|
||||
self.cmd( 'service network-manager restart' )
|
||||
|
||||
def terminate( self ):
|
||||
"""Stop NAT/forwarding between Mininet and external network"""
|
||||
# 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()
|
||||
|
||||
+32
-52
@@ -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"""
|
||||
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 ]
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
import unittest
|
||||
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 = unittest.defaultTestLoader.discover( testDir )
|
||||
# run tests
|
||||
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
# 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 )
|
||||
@@ -1,162 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Package: mininet
|
||||
Test creation and pings for topologies with link and/or CPU options."""
|
||||
|
||||
import unittest
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
|
||||
"Generic topology-with-options test runner."
|
||||
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
|
||||
lopts=lopts ),
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
switch=self.switchClass )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
|
||||
def assertWithinTolerance(self, measured, expected, tolerance_frac):
|
||||
"""Check that a given value is within a tolerance of expected
|
||||
tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
|
||||
"""
|
||||
self.assertGreaterEqual( float(measured),
|
||||
float(expected) * tolerance_frac )
|
||||
self.assertLessEqual( float( measured ),
|
||||
float(expected) + (1-tolerance_frac)
|
||||
* float( expected ) )
|
||||
|
||||
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 )
|
||||
mn.start()
|
||||
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
|
||||
mn.stop()
|
||||
for pct in results:
|
||||
#divide cpu by 100 to convert from percentage to fraction
|
||||
self.assertWithinTolerance( pct/100, CPU_FRACTION, CPU_TOLERANCE )
|
||||
|
||||
def testLinkBandwidth( self ):
|
||||
"Verify that link bandwidths are accurate within a bound."
|
||||
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 )
|
||||
bw_strs = mn.run( mn.iperf, format='m' )
|
||||
for bw_str in bw_strs:
|
||||
bw = float( bw_str.split(' ')[0] )
|
||||
self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
|
||||
|
||||
def testLinkDelay( self ):
|
||||
"Verify that link delays are accurate within a bound."
|
||||
DELAY_MS = 15
|
||||
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
|
||||
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
|
||||
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||
link=TCLink, switch=self.switchClass, autoStaticArp=True )
|
||||
ping_delays = mn.run( mn.pingFull )
|
||||
test_outputs = ping_delays[0]
|
||||
# Ignore unused variables below
|
||||
# pylint: disable-msg=W0612
|
||||
node, dest, ping_outputs = test_outputs
|
||||
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
|
||||
self.assertEqual( sent, received )
|
||||
# pylint: enable-msg=W0612
|
||||
for rttval in [rttmin, rttavg, rttmax]:
|
||||
# Multiply delay by 4 to cover there & back on two links
|
||||
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
|
||||
DELAY_TOLERANCE)
|
||||
|
||||
|
||||
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 )
|
||||
# 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()
|
||||
self.assertGreater( dropped_total, 0 )
|
||||
|
||||
def testMostOptions( self ):
|
||||
"Verify topology creation with most link options and CPU limits."
|
||||
lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
|
||||
hopts = { 'cpu': 0.5 / N }
|
||||
self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )
|
||||
|
||||
# pylint: enable=E1101
|
||||
|
||||
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
|
||||
"""Verify ability to create networks with host and link options
|
||||
(OVS kernel switch)."""
|
||||
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)."""
|
||||
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)."
|
||||
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)."
|
||||
switchClass = UserSwitch
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
+23
-71
@@ -4,93 +4,45 @@
|
||||
Test creation and all-pairs ping for each included mininet topo type."""
|
||||
|
||||
import unittest
|
||||
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
|
||||
|
||||
# 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
|
||||
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 )
|
||||
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 )
|
||||
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__':
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for the Mininet Walkthrough
|
||||
|
||||
TODO: missing xterm test
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import os
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
# PART 1
|
||||
def testHelp( self ):
|
||||
"Check the usage message"
|
||||
p = pexpect.spawn( 'mn -h' )
|
||||
index = p.expect( [ 'Usage: mn', pexpect.EOF ] )
|
||||
self.assertEqual( index, 0 )
|
||||
|
||||
def testWireshark( self ):
|
||||
"Use tshark to test the of dissector"
|
||||
tshark = pexpect.spawn( 'tshark -i lo -R of' )
|
||||
tshark.expect( 'Capturing on lo' )
|
||||
mn = pexpect.spawn( 'mn --test pingall' )
|
||||
mn.expect( '0% dropped' )
|
||||
tshark.expect( 'OFP 74 Hello' )
|
||||
tshark.sendintr()
|
||||
|
||||
def testBasic( self ):
|
||||
"Test basic CLI commands (help, nodes, net, dump)"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# help command
|
||||
p.sendline( 'help' )
|
||||
index = p.expect( [ 'commands', self.prompt ] )
|
||||
self.assertEqual( index, 0, 'No output for "help" command')
|
||||
# nodes command
|
||||
p.sendline( 'nodes' )
|
||||
p.expect( '([chs]\d ?){4}' )
|
||||
nodes = p.match.group( 0 ).split()
|
||||
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
|
||||
p.expect( self.prompt )
|
||||
# net command
|
||||
p.sendline( 'net' )
|
||||
expected = [ x for x in nodes ]
|
||||
while len( expected ) > 0:
|
||||
index = p.expect( expected )
|
||||
node = p.match.group( 0 )
|
||||
expected.remove( node )
|
||||
p.expect( '\n' )
|
||||
self.assertEqual( len( expected ), 0, '"nodes" and "net" differ')
|
||||
p.expect( self.prompt )
|
||||
# dump command
|
||||
p.sendline( 'dump' )
|
||||
expected = [ '<\w+ (%s)' % n for n in nodes ]
|
||||
actual = []
|
||||
for _ in nodes:
|
||||
index = p.expect( expected )
|
||||
node = p.match.group( 1 )
|
||||
actual.append( node )
|
||||
p.expect( '\n' )
|
||||
self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testHostCommands( self ):
|
||||
"Test ifconfig and ps on h1 and s1"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
# h1 ifconfig
|
||||
p.sendline( 'h1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index == 0 or index == 3:
|
||||
ifcount += 1
|
||||
elif index == 1:
|
||||
self.fail( 's1 interface displayed in "h1 ifconfig"' )
|
||||
elif index == 2:
|
||||
self.fail( 'eth0 displayed in "h1 ifconfig"' )
|
||||
else:
|
||||
break
|
||||
self.assertEqual( ifcount, 2, 'Missing interfaces on h1')
|
||||
# s1 ifconfig
|
||||
p.sendline( 's1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index == 0:
|
||||
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
||||
elif index == 1 or index == 2 or index == 3:
|
||||
ifcount += 1
|
||||
else:
|
||||
break
|
||||
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
|
||||
# h1 ps
|
||||
p.sendline( 'h1 ps -a' )
|
||||
p.expect( self.prompt )
|
||||
h1Output = p.before
|
||||
# s1 ps
|
||||
p.sendline( 's1 ps -a' )
|
||||
p.expect( self.prompt )
|
||||
s1Output = p.before
|
||||
# strip command from ps output
|
||||
h1Output = h1Output.split( '\n', 1 )[ 1 ]
|
||||
s1Output = s1Output.split( '\n', 1 )[ 1 ]
|
||||
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testConnectivity( self ):
|
||||
"Test ping and pingall"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( '1 packets transmitted, 1 received' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect( '0% dropped' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testSimpleHTTP( self ):
|
||||
"Start an HTTP server on h1 and wget from h2"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( ' h2 wget -O - h1' )
|
||||
p.expect( '200 OK' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 kill %python' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
# PART 2
|
||||
def testRegressionRun( self ):
|
||||
"Test pingpair (0% drop) and iperf (bw > 0) regression tests"
|
||||
# test pingpair
|
||||
p = pexpect.spawn( 'mn --test pingpair' )
|
||||
p.expect( '0% dropped' )
|
||||
p.expect( pexpect.EOF )
|
||||
# test iperf
|
||||
p = pexpect.spawn( 'mn --test iperf' )
|
||||
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw > 0 )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testTopoChange( self ):
|
||||
"Test pingall on single,3 and linear,4 topos"
|
||||
# testing single,3
|
||||
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
|
||||
p.expect( '(\d+)/(\d+) received')
|
||||
received = int( p.match.group( 1 ) )
|
||||
sent = int( p.match.group( 2 ) )
|
||||
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
|
||||
self.assertEqual( sent, received, 'Dropped packets in single,3')
|
||||
p.expect( pexpect.EOF )
|
||||
# testing linear,4
|
||||
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
|
||||
p.expect( '(\d+)/(\d+) received')
|
||||
received = int( p.match.group( 1 ) )
|
||||
sent = int( p.match.group( 2 ) )
|
||||
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
|
||||
self.assertEqual( sent, received, 'Dropped packets in linear,4')
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testLinkChange( self ):
|
||||
"Test TCLink bw and delay"
|
||||
p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
|
||||
# test bw
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'iperf' )
|
||||
p.expect( "Results: \['([\d\.]+) Mbits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
|
||||
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
|
||||
p.expect( self.prompt )
|
||||
# test delay
|
||||
p.sendline( 'h1 ping -c 4 h2' )
|
||||
p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
|
||||
delay = float( p.match.group( 2 ) )
|
||||
self.assertTrue( delay > 40, 'Delay < 40ms' )
|
||||
self.assertTrue( delay < 45, 'Delay > 40ms' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testVerbosity( self ):
|
||||
"Test debug and output verbosity"
|
||||
# test output
|
||||
p = pexpect.spawn( 'mn -v output' )
|
||||
p.expect( self.prompt )
|
||||
self.assertEqual( len( p.before ), 0, 'Too much output for "output"' )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
# test debug
|
||||
p = pexpect.spawn( 'mn -v debug --test none' )
|
||||
p.expect( pexpect.EOF )
|
||||
lines = p.before.split( '\n' )
|
||||
self.assertTrue( len( lines ) > 100, "Debug output is too short" )
|
||||
|
||||
def testCustomTopo( self ):
|
||||
"Start Mininet using a custom topo, then run pingall"
|
||||
custom = os.path.dirname( os.path.realpath( __file__ ) )
|
||||
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
|
||||
custom = os.path.normpath( custom )
|
||||
p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom )
|
||||
p.expect( '0% dropped' )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testStaticMAC( self ):
|
||||
"Verify that MACs are set to easy to read numbers"
|
||||
p = pexpect.spawn( 'mn --mac' )
|
||||
p.expect( self.prompt )
|
||||
for i in range( 1, 3 ):
|
||||
p.sendline( 'h%d ifconfig' % i )
|
||||
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
|
||||
p.expect( self.prompt )
|
||||
|
||||
def testSwitches( self ):
|
||||
"Run iperf test using user and ovsk switches"
|
||||
switches = [ 'user', 'ovsk' ]
|
||||
for sw in switches:
|
||||
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
|
||||
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw > 0 )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testBenchmark( self ):
|
||||
"Run benchmark and verify that it takes less than 2 seconds"
|
||||
p = pexpect.spawn( 'mn --test none' )
|
||||
p.expect( 'completed in ([\d\.]+) seconds' )
|
||||
time = float( p.match.group( 1 ) )
|
||||
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
|
||||
|
||||
def testOwnNamespace( self ):
|
||||
"Test running user switch in its own namespace"
|
||||
p = pexpect.spawn( 'mn --innamespace --switch user' )
|
||||
p.expect( self.prompt )
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
p.sendline( 's1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index == 1 or index == 3:
|
||||
ifcount += 1
|
||||
elif index == 0:
|
||||
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
||||
elif index == 2:
|
||||
self.fail( 'eth0 displayed in "s1 ifconfig"' )
|
||||
else:
|
||||
break
|
||||
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
|
||||
# verify that all hosts a reachable
|
||||
p.sendline( 'pingall' )
|
||||
p.expect( '(\d+)% dropped' )
|
||||
dropped = int( p.match.group( 1 ) )
|
||||
self.assertEqual( dropped, 0, 'pingall failed')
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
# PART 3
|
||||
def testPythonInterpreter( self ):
|
||||
"Test py and px by checking IP for h1 and adding h3"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# test host IP
|
||||
p.sendline( 'py h1.IP()' )
|
||||
p.expect( '10.0.0.1' )
|
||||
p.expect( self.prompt )
|
||||
# test adding host
|
||||
p.sendline( "px net.addHost('h3')" )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( "px net.addLink(s1, h3)" )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'net' )
|
||||
p.expect( 'h3' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'py h3.MAC()' )
|
||||
p.expect( '([a-f0-9]{2}:?){6}' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testLink( self ):
|
||||
"Test link CLI command using ping"
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'link s1 h1 down' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( 'unreachable' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'link s1 h1 up' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( '0% packet loss' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
@unittest.skipUnless( os.path.exists( '/tmp/pox' ) or
|
||||
'1 received' in quietRun( 'ping -c 1 github.com' ),
|
||||
'Github is not reachable; cannot download Pox' )
|
||||
def testRemoteController( self ):
|
||||
"Test Mininet using Pox controller"
|
||||
if not os.path.exists( '/tmp/pox' ):
|
||||
p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
|
||||
p.expect( pexpect.EOF )
|
||||
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
|
||||
net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
|
||||
net.expect( '0% dropped' )
|
||||
net.expect( pexpect.EOF )
|
||||
pox.sendintr()
|
||||
pox.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+75
-106
@@ -11,64 +11,30 @@ A Topo object can be a topology database for NOX, can represent a physical
|
||||
setup for testing, and can even be emulated with the Mininet package.
|
||||
'''
|
||||
|
||||
# BL: we may have to fix compatibility here.
|
||||
# networkx is also a fairly heavyweight dependency
|
||||
# from networkx.classes.graph import Graph
|
||||
|
||||
from networkx import Graph
|
||||
from mininet.util import irange, natural, naturalSeq
|
||||
|
||||
class MultiGraph( object ):
|
||||
"Utility class to track nodes and edges - replaces networkx.Graph"
|
||||
|
||||
def __init__( self ):
|
||||
self.data = {}
|
||||
|
||||
def add_node( self, node ):
|
||||
"Add node to graph"
|
||||
self.data.setdefault( node, [] )
|
||||
|
||||
def add_edge( self, src, dest ):
|
||||
"Add edge to graph"
|
||||
src, dest = sorted( ( src, dest ) )
|
||||
self.add_node( src )
|
||||
self.add_node( dest )
|
||||
self.data[ src ].append( dest )
|
||||
|
||||
def nodes( self ):
|
||||
"Return list of graph nodes"
|
||||
return self.data.keys()
|
||||
|
||||
def edges( self ):
|
||||
"Iterator: return graph edges"
|
||||
for src in self.data.keys():
|
||||
for dest in self.data[ src ]:
|
||||
yield ( src, dest )
|
||||
|
||||
def __getitem__( self, node ):
|
||||
"Return link dict for the given node"
|
||||
return self.data[node]
|
||||
|
||||
|
||||
class Topo(object):
|
||||
"Data center network representation for structured multi-trees."
|
||||
|
||||
def __init__(self, *args, **params):
|
||||
"""Topo object.
|
||||
Optional named parameters:
|
||||
def __init__(self, hopts=None, sopts=None, lopts=None):
|
||||
"""Topo object:
|
||||
hinfo: default host options
|
||||
sopts: default switch options
|
||||
lopts: default link options
|
||||
calls build()"""
|
||||
self.g = MultiGraph()
|
||||
lopts: default link options"""
|
||||
self.g = Graph()
|
||||
self.node_info = {}
|
||||
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
|
||||
self.hopts = params.pop( 'hopts', {} )
|
||||
self.sopts = params.pop( 'sopts', {} )
|
||||
self.lopts = params.pop( 'lopts', {} )
|
||||
self.hopts = {} if hopts is None else hopts
|
||||
self.sopts = {} if sopts is None else sopts
|
||||
self.lopts = {} if lopts is None else lopts
|
||||
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
||||
self.build( *args, **params )
|
||||
|
||||
def build( self, *args, **params ):
|
||||
"Override this method to build your topology."
|
||||
pass
|
||||
|
||||
def addNode(self, name, **opts):
|
||||
def add_node(self, name, **opts):
|
||||
"""Add Node to graph.
|
||||
name: name
|
||||
opts: node options
|
||||
@@ -77,40 +43,40 @@ class Topo(object):
|
||||
self.node_info[name] = opts
|
||||
return name
|
||||
|
||||
def addHost(self, name, **opts):
|
||||
def add_host(self, name, **opts):
|
||||
"""Convenience method: Add host to graph.
|
||||
name: host name
|
||||
opts: host options
|
||||
returns: host name"""
|
||||
if not opts and self.hopts:
|
||||
opts = self.hopts
|
||||
return self.addNode(name, **opts)
|
||||
return self.add_node(name, **opts)
|
||||
|
||||
def addSwitch(self, name, **opts):
|
||||
def add_switch(self, name, **opts):
|
||||
"""Convenience method: Add switch to graph.
|
||||
name: switch name
|
||||
opts: switch options
|
||||
returns: switch name"""
|
||||
if not opts and self.sopts:
|
||||
opts = self.sopts
|
||||
result = self.addNode(name, isSwitch=True, **opts)
|
||||
result = self.add_node(name, is_switch=True, **opts)
|
||||
return result
|
||||
|
||||
def addLink(self, node1, node2, port1=None, port2=None,
|
||||
**opts):
|
||||
def add_link(self, node1, node2, port1=None, port2=None,
|
||||
**opts):
|
||||
"""node1, node2: nodes to link together
|
||||
port1, port2: ports (optional)
|
||||
opts: link options (optional)
|
||||
returns: link info key"""
|
||||
if not opts and self.lopts:
|
||||
opts = self.lopts
|
||||
self.addPort(node1, node2, port1, port2)
|
||||
self.add_port(node1, node2, port1, port2)
|
||||
key = tuple(self.sorted([node1, node2]))
|
||||
self.link_info[key] = opts
|
||||
self.g.add_edge(*key)
|
||||
return key
|
||||
|
||||
def addPort(self, src, dst, sport=None, dport=None):
|
||||
def add_port(self, src, dst, sport=None, dport=None):
|
||||
'''Generate port mapping for new edge.
|
||||
@param src source switch name
|
||||
@param dst destination switch name
|
||||
@@ -118,8 +84,8 @@ class Topo(object):
|
||||
self.ports.setdefault(src, {})
|
||||
self.ports.setdefault(dst, {})
|
||||
# New port: number of outlinks + base
|
||||
src_base = 1 if self.isSwitch(src) else 0
|
||||
dst_base = 1 if self.isSwitch(dst) else 0
|
||||
src_base = 1 if self.is_switch(src) else 0
|
||||
dst_base = 1 if self.is_switch(dst) else 0
|
||||
if sport is None:
|
||||
sport = len(self.ports[src]) + src_base
|
||||
if dport is None:
|
||||
@@ -134,24 +100,24 @@ class Topo(object):
|
||||
else:
|
||||
return self.g.nodes()
|
||||
|
||||
def isSwitch(self, n):
|
||||
def is_switch(self, n):
|
||||
'''Returns true if node is a switch.'''
|
||||
info = self.node_info[n]
|
||||
return info and info.get('isSwitch', False)
|
||||
return info and info.get('is_switch', False)
|
||||
|
||||
def switches(self, sort=True):
|
||||
'''Return switches.
|
||||
sort: sort switches alphabetically
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
return [n for n in self.nodes(sort) if self.isSwitch(n)]
|
||||
return [n for n in self.nodes(sort) if self.is_switch(n)]
|
||||
|
||||
def hosts(self, sort=True):
|
||||
'''Return hosts.
|
||||
sort: sort hosts alphabetically
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
|
||||
return [n for n in self.nodes(sort) if not self.is_switch(n)]
|
||||
|
||||
def links(self, sort=True):
|
||||
'''Return links.
|
||||
@@ -175,18 +141,13 @@ class Topo(object):
|
||||
'''
|
||||
if src in self.ports and dst in self.ports[src]:
|
||||
assert dst in self.ports and src in self.ports[dst]
|
||||
return self.ports[src][dst], self.ports[dst][src]
|
||||
return (self.ports[src][dst], self.ports[dst][src])
|
||||
|
||||
def linkInfo( self, src, dst ):
|
||||
"Return link metadata"
|
||||
src, dst = self.sorted([src, dst])
|
||||
return self.link_info[(src, dst)]
|
||||
|
||||
def setlinkInfo( self, src, dst, info ):
|
||||
"Set link metadata"
|
||||
src, dst = self.sorted([src, dst])
|
||||
self.link_info[(src, dst)] = info
|
||||
|
||||
def nodeInfo( self, name ):
|
||||
"Return metadata (dict) for node"
|
||||
info = self.node_info[ name ]
|
||||
@@ -201,56 +162,64 @@ class Topo(object):
|
||||
"Items sorted in natural (i.e. alphabetical) order"
|
||||
return sorted(items, key=natural)
|
||||
|
||||
class SingleSwitchTopo(Topo):
|
||||
'''Single switch connected to k hosts.'''
|
||||
|
||||
class SingleSwitchTopo( Topo ):
|
||||
"Single switch connected to k hosts."
|
||||
def __init__(self, k=2, **opts):
|
||||
'''Init.
|
||||
|
||||
@param k number of hosts
|
||||
@param enable_all enables all nodes and switches?
|
||||
'''
|
||||
super(SingleSwitchTopo, self).__init__(**opts)
|
||||
|
||||
def build( self, k=2, **opts ):
|
||||
"k: number of hosts"
|
||||
self.k = k
|
||||
switch = self.addSwitch( 's1' )
|
||||
for h in irange( 1, k ):
|
||||
host = self.addHost( 'h%s' % h )
|
||||
self.addLink( host, switch )
|
||||
|
||||
switch = self.add_switch('s1')
|
||||
for h in irange(1, k):
|
||||
host = self.add_host('h%s' % h)
|
||||
self.add_link(host, switch)
|
||||
|
||||
|
||||
class SingleSwitchReversedTopo( Topo ):
|
||||
"""Single switch connected to k hosts, with reversed ports.
|
||||
The lowest-numbered host is connected to the highest-numbered port.
|
||||
Useful to verify that Mininet properly handles custom port numberings."""
|
||||
class SingleSwitchReversedTopo(Topo):
|
||||
'''Single switch connected to k hosts, with reversed ports.
|
||||
|
||||
def build( self, k=2 ):
|
||||
"k: number of hosts"
|
||||
The lowest-numbered host is connected to the highest-numbered port.
|
||||
|
||||
Useful to verify that Mininet properly handles custom port numberings.
|
||||
'''
|
||||
def __init__(self, k=2, **opts):
|
||||
'''Init.
|
||||
|
||||
@param k number of hosts
|
||||
@param enable_all enables all nodes and switches?
|
||||
'''
|
||||
super(SingleSwitchReversedTopo, self).__init__(**opts)
|
||||
self.k = k
|
||||
switch = self.addSwitch( 's1' )
|
||||
for h in irange( 1, k ):
|
||||
host = self.addHost( 'h%s' % h )
|
||||
self.addLink( host, switch,
|
||||
port1=0, port2=( k - h + 1 ) )
|
||||
switch = self.add_switch('s1')
|
||||
for h in irange(1, k):
|
||||
host = self.add_host('h%s' % h)
|
||||
self.add_link(host, switch,
|
||||
port1=0, port2=(k - h + 1))
|
||||
|
||||
class LinearTopo( Topo ):
|
||||
"Linear topology of k switches, with n hosts per switch."
|
||||
class LinearTopo(Topo):
|
||||
"Linear topology of k switches, with one host per switch."
|
||||
|
||||
def __init__(self, k=2, **opts):
|
||||
"""Init.
|
||||
k: number of switches (and hosts)
|
||||
hconf: host configuration options
|
||||
lconf: link configuration options"""
|
||||
|
||||
super(LinearTopo, self).__init__(**opts)
|
||||
|
||||
def build( self, k=2, n=1, **opts):
|
||||
"""k: number of switches
|
||||
n: number of hosts per switch"""
|
||||
self.k = k
|
||||
self.n = n
|
||||
|
||||
if n == 1:
|
||||
genHostName = lambda i, j: 'h%s' % i
|
||||
else:
|
||||
genHostName = lambda i, j: 'h%ss%d' % ( j, i )
|
||||
|
||||
lastSwitch = None
|
||||
for i in irange( 1, k ):
|
||||
# Add switch
|
||||
switch = self.addSwitch( 's%s' % i )
|
||||
# Add hosts to switch
|
||||
for j in irange( 1, n ):
|
||||
host = self.addHost( genHostName( i, j ) )
|
||||
self.addLink( host, switch )
|
||||
# Connect switch to previous
|
||||
for i in irange(1, k):
|
||||
host = self.add_host('h%s' % i)
|
||||
switch = self.add_switch('s%s' % i)
|
||||
self.add_link( host, switch)
|
||||
if lastSwitch:
|
||||
self.addLink( switch, lastSwitch )
|
||||
self.add_link( switch, lastSwitch)
|
||||
lastSwitch = switch
|
||||
+5
-38
@@ -6,7 +6,8 @@ from mininet.net import Mininet
|
||||
class TreeTopo( Topo ):
|
||||
"Topology for a tree network with a given depth and fanout."
|
||||
|
||||
def build( self, depth=1, fanout=2 ):
|
||||
def __init__( self, depth=1, fanout=2 ):
|
||||
super( TreeTopo, self ).__init__()
|
||||
# Numbering: h1..N, s1..M
|
||||
self.hostNum = 1
|
||||
self.switchNum = 1
|
||||
@@ -18,13 +19,13 @@ class TreeTopo( Topo ):
|
||||
returns: last node added"""
|
||||
isSwitch = depth > 0
|
||||
if isSwitch:
|
||||
node = self.addSwitch( 's%s' % self.switchNum )
|
||||
node = self.add_switch( 's%s' % self.switchNum )
|
||||
self.switchNum += 1
|
||||
for _ in range( fanout ):
|
||||
child = self.addTree( depth - 1, fanout )
|
||||
self.addLink( node, child )
|
||||
self.add_link( node, child )
|
||||
else:
|
||||
node = self.addHost( 'h%s' % self.hostNum )
|
||||
node = self.add_host( 'h%s' % self.hostNum )
|
||||
self.hostNum += 1
|
||||
return node
|
||||
|
||||
@@ -33,37 +34,3 @@ def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||
"Convenience function for creating tree networks."
|
||||
topo = TreeTopo( depth, fanout )
|
||||
return Mininet( topo, **kwargs )
|
||||
|
||||
|
||||
class TorusTopo( Topo ):
|
||||
"""2-D Torus topology
|
||||
WARNING: this topology has LOOPS and WILL NOT WORK
|
||||
with the default controller or any Ethernet bridge
|
||||
without STP turned on! It can be used with STP, e.g.:
|
||||
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
|
||||
|
||||
def build( self, x, y ):
|
||||
if x < 3 or y < 3:
|
||||
raise Exception( 'Please use 3x3 or greater for compatibility '
|
||||
'with 2.1' )
|
||||
hosts, switches, dpid = {}, {}, 0
|
||||
# Create and wire interior
|
||||
for i in range( 0, x ):
|
||||
for j in range( 0, y ):
|
||||
loc = '%dx%d' % ( i + 1, j + 1 )
|
||||
# dpid cannot be zero for OVS
|
||||
dpid = ( i + 1 ) * 256 + ( j + 1 )
|
||||
switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%016x' % dpid )
|
||||
host = hosts[ i, j ] = self.addHost( 'h' + loc )
|
||||
self.addLink( host, switch )
|
||||
# Connect switches
|
||||
for i in range( 0, x ):
|
||||
for j in range( 0, y ):
|
||||
sw1 = switches[ i, j ]
|
||||
sw2 = switches[ i, ( j + 1 ) % y ]
|
||||
sw3 = switches[ ( i + 1 ) % x, j ]
|
||||
self.addLink( sw1, sw2 )
|
||||
self.addLink( sw1, sw3 )
|
||||
|
||||
|
||||
|
||||
+41
-215
@@ -1,15 +1,11 @@
|
||||
"Utility functions for Mininet."
|
||||
|
||||
from mininet.log import output, info, error, warn, debug
|
||||
|
||||
from time import sleep
|
||||
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
from select import poll, POLLIN, POLLHUP
|
||||
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
from select import poll, POLLIN
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
from mininet.log import output, info, error
|
||||
import re
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import os
|
||||
|
||||
# Command execution support
|
||||
|
||||
@@ -47,13 +43,13 @@ def oldQuietRun( *cmd ):
|
||||
break
|
||||
out += data
|
||||
popen.poll()
|
||||
if popen.returncode is not None:
|
||||
if popen.returncode != None:
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
# This is a bit complicated, but it enables us to
|
||||
# monitor command output as it is happening
|
||||
# monitor commount output as it is happening
|
||||
|
||||
def errRun( *cmd, **kwargs ):
|
||||
"""Run a command and return stdout, stderr and return code
|
||||
@@ -75,33 +71,31 @@ def errRun( *cmd, **kwargs ):
|
||||
# cmd goes to stderr, output goes to stdout
|
||||
info( cmd, '\n' )
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
|
||||
# We use poll() because select() doesn't work with large fd numbers,
|
||||
# and thus communicate() doesn't work either
|
||||
# We use poll() because select() doesn't work with large fd numbers
|
||||
out, err = '', ''
|
||||
poller = poll()
|
||||
poller.register( popen.stdout, POLLIN )
|
||||
fdtofile = { popen.stdout.fileno(): popen.stdout }
|
||||
outDone, errDone = False, True
|
||||
if popen.stderr:
|
||||
fdtofile[ popen.stderr.fileno() ] = popen.stderr
|
||||
poller.register( popen.stderr, POLLIN )
|
||||
errDone = False
|
||||
while not outDone or not errDone:
|
||||
while True:
|
||||
readable = poller.poll()
|
||||
for fd, _event in readable:
|
||||
# Tell pylint to ignore unused variable event
|
||||
# pylint: disable-msg=W0612
|
||||
for fd, event in readable:
|
||||
# pylint: enable-msg=W0612
|
||||
f = fdtofile[ fd ]
|
||||
data = f.read( 1024 )
|
||||
if echo:
|
||||
output( data )
|
||||
if f == popen.stdout:
|
||||
out += data
|
||||
if data == '':
|
||||
outDone = True
|
||||
elif f == popen.stderr:
|
||||
err += data
|
||||
if data == '':
|
||||
errDone = True
|
||||
returncode = popen.wait()
|
||||
returncode = popen.poll()
|
||||
if returncode is not None:
|
||||
break
|
||||
return out, err, returncode
|
||||
|
||||
def errFail( *cmd, **kwargs ):
|
||||
@@ -117,7 +111,7 @@ def quietRun( cmd, **kwargs ):
|
||||
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
|
||||
|
||||
# pylint: enable-msg=E1103
|
||||
# pylint: disable-msg=E1101
|
||||
# pylint: disable-msg=E1101,W0612
|
||||
|
||||
def isShellBuiltin( cmd ):
|
||||
"Return True if cmd is a bash builtin."
|
||||
@@ -130,7 +124,7 @@ def isShellBuiltin( cmd ):
|
||||
|
||||
isShellBuiltin.builtIns = None
|
||||
|
||||
# pylint: enable-msg=E1101
|
||||
# pylint: enable-msg=E1101,W0612
|
||||
|
||||
# Interface management
|
||||
#
|
||||
@@ -155,12 +149,7 @@ def makeIntfPair( intf1, intf2 ):
|
||||
quietRun( 'ip link del ' + intf2 )
|
||||
# Create new pair
|
||||
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
|
||||
cmdOutput = quietRun( cmd )
|
||||
if cmdOutput == '':
|
||||
return True
|
||||
else:
|
||||
error( "Error creating interface pair: %s " % cmdOutput )
|
||||
return False
|
||||
return quietRun( cmd )
|
||||
|
||||
def retry( retries, delaySecs, fn, *args, **keywords ):
|
||||
"""Try something several times before giving up.
|
||||
@@ -176,34 +165,27 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
|
||||
error( "*** gave up after %i retries\n" % tries )
|
||||
exit( 1 )
|
||||
|
||||
def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
|
||||
def moveIntfNoRetry( intf, node, printError=False ):
|
||||
"""Move interface to node, without retrying.
|
||||
intf: string, interface
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns
|
||||
printError: if true, print error"""
|
||||
intf = str( intf )
|
||||
cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
|
||||
if srcNode:
|
||||
srcNode.cmd( cmd )
|
||||
else:
|
||||
quietRun( cmd )
|
||||
if ( ' %s:' % intf ) not in dstNode.cmd( 'ip link show', intf ):
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
|
||||
quietRun( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not ( ' %s:' % intf ) in links:
|
||||
if printError:
|
||||
error( '*** Error: moveIntf: ' + intf +
|
||||
' not successfully moved to ' + dstNode.name + '\n' )
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
return False
|
||||
return True
|
||||
|
||||
def moveIntf( intf, dstNode, srcNode=None, printError=False,
|
||||
retries=3, delaySecs=0.001 ):
|
||||
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
|
||||
"""Move interface to node, retrying on failure.
|
||||
intf: string, interface
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
|
||||
srcNode=srcNode, printError=printError )
|
||||
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
|
||||
|
||||
# Support for dumping network
|
||||
|
||||
@@ -236,7 +218,7 @@ def dumpNetConnections( net ):
|
||||
def _colonHex( val, bytecount ):
|
||||
"""Generate colon-hex string.
|
||||
val: input as unsigned int
|
||||
bytecount: number of bytes to convert
|
||||
bytescount: number of bytes to convert
|
||||
returns: chStr colon-hex string"""
|
||||
pieces = []
|
||||
for i in range( bytecount - 1, -1, -1 ):
|
||||
@@ -254,8 +236,9 @@ def macColonHex( mac ):
|
||||
def ipStr( ip ):
|
||||
"""Generate IP address string from an unsigned int.
|
||||
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
|
||||
returns: ip address string w.x.y.z"""
|
||||
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
|
||||
w = ( ip >> 24 ) & 0xff
|
||||
w = 10 if w == 0 else w
|
||||
x = ( ip >> 16 ) & 0xff
|
||||
y = ( ip >> 8 ) & 0xff
|
||||
z = ip & 0xff
|
||||
@@ -264,7 +247,7 @@ def ipStr( ip ):
|
||||
def ipNum( w, x, y, z ):
|
||||
"""Generate unsigned int from components of IP address
|
||||
returns: w << 24 | x << 16 | y << 8 | z"""
|
||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||
|
||||
def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||
"""Return IP address string from ints
|
||||
@@ -272,17 +255,15 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||
prefixLen: optional IP prefix length
|
||||
ipBaseNum: option base IP address as int
|
||||
returns IP address as string"""
|
||||
imax = 0xffffffff >> prefixLen
|
||||
assert i <= imax, 'Not enough IP addresses in the subnet'
|
||||
mask = 0xffffffff ^ imax
|
||||
ipnum = ( ipBaseNum & mask ) + i
|
||||
# Ugly but functional
|
||||
assert i < ( 1 << ( 32 - prefixLen ) )
|
||||
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
|
||||
ipnum = i + ( ipBaseNum & mask )
|
||||
return ipStr( ipnum )
|
||||
|
||||
def ipParse( ip ):
|
||||
"Parse an IP address and return an unsigned int."
|
||||
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
||||
while ( len(args) < 4 ):
|
||||
args.append( 0 )
|
||||
return ipNum( *args )
|
||||
|
||||
def netParse( ipstr ):
|
||||
@@ -292,10 +273,6 @@ def netParse( ipstr ):
|
||||
if '/' in ipstr:
|
||||
ip, pf = ipstr.split( '/' )
|
||||
prefixLen = int( pf )
|
||||
#if no prefix is specified, set the prefix to 24
|
||||
else:
|
||||
ip = ipstr
|
||||
prefixLen = 24
|
||||
return ipParse( ip ), prefixLen
|
||||
|
||||
def checkInt( s ):
|
||||
@@ -323,108 +300,22 @@ def makeNumeric( s ):
|
||||
else:
|
||||
return s
|
||||
|
||||
# Popen support
|
||||
|
||||
def pmonitor(popens, timeoutms=500, readline=True,
|
||||
readmax=1024 ):
|
||||
"""Monitor dict of hosts to popen objects
|
||||
a line at a time
|
||||
timeoutms: timeout for poll()
|
||||
readline: return single line of output
|
||||
yields: host, line/output (if any)
|
||||
terminates: when all EOFs received"""
|
||||
poller = poll()
|
||||
fdToHost = {}
|
||||
for host, popen in popens.iteritems():
|
||||
fd = popen.stdout.fileno()
|
||||
fdToHost[ fd ] = host
|
||||
poller.register( fd, POLLIN )
|
||||
if not readline:
|
||||
# Use non-blocking reads
|
||||
flags = fcntl( fd, F_GETFL )
|
||||
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
|
||||
while popens:
|
||||
fds = poller.poll( timeoutms )
|
||||
if fds:
|
||||
for fd, event in fds:
|
||||
host = fdToHost[ fd ]
|
||||
popen = popens[ host ]
|
||||
if event & POLLIN:
|
||||
if readline:
|
||||
# Attempt to read a line of output
|
||||
# This blocks until we receive a newline!
|
||||
line = popen.stdout.readline()
|
||||
else:
|
||||
line = popen.stdout.read( readmax )
|
||||
yield host, line
|
||||
# Check for EOF
|
||||
elif event & POLLHUP:
|
||||
poller.unregister( fd )
|
||||
del popens[ host ]
|
||||
else:
|
||||
yield None, ''
|
||||
|
||||
# Other stuff we use
|
||||
def sysctlTestAndSet( name, limit ):
|
||||
"Helper function to set sysctl limits"
|
||||
#convert non-directory names into directory names
|
||||
if '/' not in name:
|
||||
name = '/proc/sys/' + name.replace( '.', '/' )
|
||||
#read limit
|
||||
with open( name, 'r' ) as readFile:
|
||||
oldLimit = readFile.readline()
|
||||
if type( limit ) is int:
|
||||
#compare integer limits before overriding
|
||||
if int( oldLimit ) < limit:
|
||||
with open( name, 'w' ) as writeFile:
|
||||
writeFile.write( "%d" % limit )
|
||||
else:
|
||||
#overwrite non-integer limits
|
||||
with open( name, 'w' ) as writeFile:
|
||||
writeFile.write( limit )
|
||||
|
||||
def rlimitTestAndSet( name, limit ):
|
||||
"Helper function to set rlimits"
|
||||
soft, hard = getrlimit( name )
|
||||
if soft < limit:
|
||||
hardLimit = hard if limit < hard else limit
|
||||
setrlimit( name, ( limit, hardLimit ) )
|
||||
|
||||
def fixLimits():
|
||||
"Fix ridiculously small resource limits."
|
||||
debug( "*** Setting resource limits\n" )
|
||||
try:
|
||||
rlimitTestAndSet( RLIMIT_NPROC, 8192 )
|
||||
rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
|
||||
#Increase open file limit
|
||||
sysctlTestAndSet( 'fs.file-max', 10000 )
|
||||
#Increase network buffer space
|
||||
sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
|
||||
sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
|
||||
sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
|
||||
sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
|
||||
sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
|
||||
#Increase arp cache size
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
|
||||
#Increase routing table size
|
||||
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
|
||||
#Increase number of PTYs for nodes
|
||||
sysctlTestAndSet( 'kernel.pty.max', 20000 )
|
||||
except:
|
||||
warn( "*** Error setting resource limits. "
|
||||
"Mininet's performance may be affected.\n" )
|
||||
setrlimit( RLIMIT_NPROC, ( 8192, 8192 ) )
|
||||
setrlimit( RLIMIT_NOFILE, ( 16384, 16384 ) )
|
||||
|
||||
def mountCgroups():
|
||||
"Make sure cgroups file system is mounted"
|
||||
mounts = quietRun( 'cat /proc/mounts' )
|
||||
mounts = quietRun( 'mount' )
|
||||
cgdir = '/sys/fs/cgroup'
|
||||
csdir = cgdir + '/cpuset'
|
||||
if ('cgroup %s' % cgdir not in mounts and
|
||||
'cgroups %s' % cgdir not in mounts):
|
||||
if 'cgroups on %s' % cgdir not in mounts:
|
||||
raise Exception( "cgroups not mounted on " + cgdir )
|
||||
if 'cpuset %s' % csdir not in mounts:
|
||||
if 'cpuset on %s' % csdir not in mounts:
|
||||
errRun( 'mkdir -p ' + csdir )
|
||||
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
|
||||
|
||||
@@ -456,73 +347,8 @@ def irange(start, end):
|
||||
|
||||
def custom( cls, **params ):
|
||||
"Returns customized constructor for class cls."
|
||||
# Note: we may wish to see if we can use functools.partial() here
|
||||
# and in customConstructor
|
||||
def customized( *args, **kwargs):
|
||||
"Customized constructor"
|
||||
kwargs = kwargs.copy()
|
||||
kwargs.update( params )
|
||||
return cls( *args, **kwargs )
|
||||
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
|
||||
return customized
|
||||
|
||||
def splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
split = argstr.split( ',' )
|
||||
fn = split[ 0 ]
|
||||
params = split[ 1: ]
|
||||
# Convert int and float args; removes the need for function
|
||||
# to be flexible with input arg formats.
|
||||
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||
kwargs = {}
|
||||
for s in [ p for p in params if '=' in p ]:
|
||||
key, val = s.split( '=', 1 )
|
||||
kwargs[ key ] = makeNumeric( val )
|
||||
return fn, args, kwargs
|
||||
|
||||
def customConstructor( constructors, argStr ):
|
||||
"""Return custom constructor based on argStr
|
||||
The args and key/val pairs in argsStr will be automatically applied
|
||||
when the generated constructor is later used.
|
||||
"""
|
||||
cname, newargs, kwargs = splitArgs( argStr )
|
||||
constructor = constructors.get( cname, None )
|
||||
|
||||
if not constructor:
|
||||
raise Exception( "error: %s is unknown - please specify one of %s" %
|
||||
( cname, constructors.keys() ) )
|
||||
|
||||
def customized( name, *args, **params ):
|
||||
"Customized constructor, useful for Node, Link, and other classes"
|
||||
params = params.copy()
|
||||
params.update( kwargs )
|
||||
if not newargs:
|
||||
return constructor( name, *args, **params )
|
||||
if args:
|
||||
warn( 'warning: %s replacing %s with %s\n' % (
|
||||
constructor, args, newargs ) )
|
||||
return constructor( name, *newargs, **params )
|
||||
|
||||
customized.__name__ = 'customConstructor(%s)' % argStr
|
||||
return customized
|
||||
|
||||
def buildTopo( topos, topoStr ):
|
||||
"""Create topology from string with format (object, arg1, arg2,...).
|
||||
input topos is a dict of topo names to constructors, possibly w/args.
|
||||
"""
|
||||
topo, args, kwargs = splitArgs( topoStr )
|
||||
if topo not in topos:
|
||||
raise Exception( 'Invalid topo name %s' % topo )
|
||||
return topos[ topo ]( *args, **kwargs )
|
||||
|
||||
def ensureRoot():
|
||||
"""Ensure that we are running as root.
|
||||
|
||||
Probably we should only sudo when needed as per Big Switch's patch.
|
||||
"""
|
||||
if os.getuid() != 0:
|
||||
print "*** Mininet must run as root."
|
||||
exit( 1 )
|
||||
return
|
||||
@@ -5,102 +5,32 @@
|
||||
*
|
||||
* - closing all file descriptors except stdin/out/error
|
||||
* - detaching from a controlling tty using setsid
|
||||
* - running in network and mount namespaces
|
||||
* - running in a network namespace
|
||||
* - printing out the pid of a process so we can identify it later
|
||||
* - attaching to a namespace and cgroup
|
||||
* - setting RT scheduling
|
||||
*
|
||||
* Partially based on public domain setsid(1)
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <linux/sched.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#if !defined(VERSION)
|
||||
#define VERSION "(devel)"
|
||||
#endif
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
printf("Execution utility for Mininet\n\n"
|
||||
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
|
||||
"Options:\n"
|
||||
" -c: close all file descriptors except stdin/out/error\n"
|
||||
" -d: detach from tty by calling setsid()\n"
|
||||
" -n: run in new network and mount namespaces\n"
|
||||
" -p: print ^A + pid\n"
|
||||
" -a pid: attach to pid's network and mount namespaces\n"
|
||||
" -g group: add to cgroup\n"
|
||||
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
|
||||
" -v: print version\n",
|
||||
name);
|
||||
}
|
||||
|
||||
|
||||
int setns(int fd, int nstype)
|
||||
{
|
||||
return syscall(__NR_setns, fd, nstype);
|
||||
}
|
||||
|
||||
/* Validate alphanumeric path foo1/bar2/baz */
|
||||
void validate(char *path)
|
||||
{
|
||||
char *s;
|
||||
for (s=path; *s; s++) {
|
||||
if (!isalnum(*s) && *s != '/') {
|
||||
fprintf(stderr, "invalid path: %s\n", path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add our pid to cgroup */
|
||||
void cgroup(char *gname)
|
||||
{
|
||||
static char path[PATH_MAX];
|
||||
static char *groups[] = {
|
||||
"cpu", "cpuacct", "cpuset", NULL
|
||||
};
|
||||
char **gptr;
|
||||
pid_t pid = getpid();
|
||||
int count = 0;
|
||||
validate(gname);
|
||||
for (gptr = groups; *gptr; gptr++) {
|
||||
FILE *f;
|
||||
snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks",
|
||||
*gptr, gname);
|
||||
f = fopen(path, "w");
|
||||
if (f) {
|
||||
count++;
|
||||
fprintf(f, "%d\n", pid);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
if (!count) {
|
||||
fprintf(stderr, "cgroup: could not add to cgroup %s\n",
|
||||
gname);
|
||||
exit(1);
|
||||
}
|
||||
printf("Execution utility for Mininet.\n"
|
||||
"usage: %s [-cdnp]\n"
|
||||
"-c: close all file descriptors except stdin/out/error\n"
|
||||
"-d: detach from tty by calling setsid()\n"
|
||||
"-n: run in new network namespace\n"
|
||||
"-p: print ^A + pid\n", name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
char c;
|
||||
int fd;
|
||||
char path[PATH_MAX];
|
||||
int nsid;
|
||||
int pid;
|
||||
static struct sched_param sp;
|
||||
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
|
||||
|
||||
while ((c = getopt(argc, argv, "+cdnp")) != -1)
|
||||
switch(c) {
|
||||
case 'c':
|
||||
/* close file descriptors except stdin/out/error */
|
||||
@@ -114,86 +44,36 @@ int main(int argc, char *argv[])
|
||||
case -1:
|
||||
perror("fork");
|
||||
return 1;
|
||||
case 0: /* child */
|
||||
case 0: /* child */
|
||||
break;
|
||||
default: /* parent */
|
||||
default: /* parent */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
setsid();
|
||||
break;
|
||||
case 'n':
|
||||
/* run in network and mount namespaces */
|
||||
if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
|
||||
/* run in network namespace */
|
||||
if (unshare(CLONE_NEWNET) == -1) {
|
||||
perror("unshare");
|
||||
return 1;
|
||||
}
|
||||
/* mount sysfs to pick up the new network namespace */
|
||||
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
|
||||
perror("mount");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
/* print pid */
|
||||
printf("\001%d\n", getpid());
|
||||
fflush(stdout);
|
||||
break;
|
||||
case 'a':
|
||||
/* Attach to pid's network namespace and mount namespace */
|
||||
pid = atoi(optarg);
|
||||
sprintf(path, "/proc/%d/ns/net", pid);
|
||||
nsid = open(path, O_RDONLY);
|
||||
if (nsid < 0) {
|
||||
perror(path);
|
||||
return 1;
|
||||
}
|
||||
if (setns(nsid, 0) != 0) {
|
||||
perror("setns");
|
||||
return 1;
|
||||
}
|
||||
/* Plan A: call setns() to attach to mount namespace */
|
||||
sprintf(path, "/proc/%d/ns/mnt", pid);
|
||||
nsid = open(path, O_RDONLY);
|
||||
if (nsid < 0 || setns(nsid, 0) != 0) {
|
||||
/* Plan B: chroot into pid's root file system */
|
||||
sprintf(path, "/proc/%d/root", pid);
|
||||
if (chroot(path) < 0) {
|
||||
perror(path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
/* Attach to cgroup */
|
||||
cgroup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
/* Set RT scheduling priority */
|
||||
sp.sched_priority = atoi(optarg);
|
||||
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
|
||||
perror("sched_setscheduler");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
printf("%s\n", VERSION);
|
||||
exit(0);
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
exit(0);
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
perror(argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
perror(argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
usage(argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,39 +5,32 @@
|
||||
from setuptools import setup, find_packages
|
||||
from os.path import join
|
||||
|
||||
# Get version number from source tree
|
||||
import sys
|
||||
sys.path.append( '.' )
|
||||
from mininet.net import VERSION
|
||||
|
||||
scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ]
|
||||
|
||||
modname = distname = 'mininet'
|
||||
|
||||
setup(
|
||||
name=distname,
|
||||
version=VERSION,
|
||||
version='0.0.0',
|
||||
description='Process-based OpenFlow emulator',
|
||||
author='Bob Lantz',
|
||||
author_email='rlantz@cs.stanford.edu',
|
||||
packages=[ 'mininet', 'mininet.examples' ],
|
||||
packages=find_packages(exclude='test'),
|
||||
long_description="""
|
||||
Mininet is a network emulator which uses lightweight
|
||||
virtualization to create virtual networks for rapid
|
||||
prototyping of Software-Defined Network (SDN) designs
|
||||
using OpenFlow. http://mininet.org
|
||||
""",
|
||||
Insert longer description here.
|
||||
""",
|
||||
classifiers=[
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
||||
"Programming Language :: Python",
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: System :: Emulators",
|
||||
"Topic :: Internet",
|
||||
],
|
||||
keywords='networking emulator protocol Internet OpenFlow SDN',
|
||||
license='BSD',
|
||||
keywords='networking protocol Internet OpenFlow',
|
||||
license='unspecified',
|
||||
install_requires=[
|
||||
'setuptools'
|
||||
'setuptools',
|
||||
'networkx'
|
||||
],
|
||||
scripts=scripts,
|
||||
)
|
||||
+205
-403
@@ -5,22 +5,10 @@
|
||||
|
||||
# Fail on error
|
||||
set -e
|
||||
|
||||
|
||||
# Fail on unset var usage
|
||||
set -o nounset
|
||||
|
||||
# Get directory containing mininet folder
|
||||
MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )"
|
||||
|
||||
# Set up build directory, which by default is the working directory
|
||||
# unless the working directory is a subdirectory of mininet,
|
||||
# in which case we use the directory containing mininet
|
||||
BUILD_DIR="$(pwd -P)"
|
||||
case $BUILD_DIR in
|
||||
$MININET_DIR/*) BUILD_DIR=$MININET_DIR;; # currect directory is a subdirectory
|
||||
*) BUILD_DIR=$BUILD_DIR;;
|
||||
esac
|
||||
|
||||
# Location of CONFIG_NET_NS-enabled kernel(s)
|
||||
KERNEL_LOC=http://www.openflow.org/downloads/mininet
|
||||
|
||||
@@ -43,16 +31,9 @@ if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install lsb-release
|
||||
fi
|
||||
fi
|
||||
test -e /etc/fedora-release && DIST="Fedora"
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
install='sudo yum -y install'
|
||||
remove='sudo yum -y erase'
|
||||
pkginst='sudo rpm -ivh'
|
||||
# Prereqs for this script
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install redhat-lsb-core
|
||||
fi
|
||||
if ! which bc &> /dev/null; then
|
||||
$install bc
|
||||
fi
|
||||
fi
|
||||
if which lsb_release &> /dev/null; then
|
||||
DIST=`lsb_release -is`
|
||||
@@ -63,11 +44,19 @@ echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
|
||||
|
||||
# Kernel params
|
||||
|
||||
KERNEL_NAME=`uname -r`
|
||||
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
|
||||
|
||||
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora'; then
|
||||
echo "Install.sh currently only supports Ubuntu, Debian and Fedora."
|
||||
if [ "$DIST" = "Ubuntu" ]; then
|
||||
if [ "$RELEASE" = "10.04" ]; then
|
||||
KERNEL_NAME='3.0.0-15-generic'
|
||||
else
|
||||
KERNEL_NAME=`uname -r`
|
||||
fi
|
||||
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}
|
||||
elif [ "$DIST" = "Debian" ] && [ "$ARCH" = "i386" ] && [ "$CODENAME" = "lenny" ]; then
|
||||
KERNEL_NAME=2.6.33.1-mininet
|
||||
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
||||
KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
||||
else
|
||||
echo "Install.sh currently only supports Ubuntu and Debian Lenny i386."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -83,19 +72,39 @@ OVS_RELEASE=1.4.0
|
||||
OVS_PACKAGE_LOC=https://github.com/downloads/mininet/mininet
|
||||
OVS_BUILDSUFFIX=-ignore # was -2
|
||||
OVS_PACKAGE_NAME=ovs-$OVS_RELEASE-core-$DIST_LC-$RELEASE-$ARCH$OVS_BUILDSUFFIX.tar
|
||||
OVS_SRC=~/openvswitch
|
||||
OVS_TAG=v$OVS_RELEASE
|
||||
|
||||
# Command-line versions overrides that simplify custom VM creation
|
||||
# To use, pass in the vars on the cmd line before install.sh, e.g.
|
||||
# WS_DISSECTOR_REV=pre-ws-1.10.0 install.sh -w
|
||||
WS_DISSECTOR_REV=${WS_DISSECTOR_REV:-""}
|
||||
OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
|
||||
|
||||
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
|
||||
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
|
||||
|
||||
function kernel {
|
||||
echo "Install Mininet-compatible kernel if necessary"
|
||||
sudo apt-get update
|
||||
$install linux-image-$KERNEL_NAME
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||
$install linux-image-$KERNEL_NAME
|
||||
elif [ "$DIST" = "Debian" ]; then
|
||||
# The easy approach: download pre-built linux-image and linux-headers packages:
|
||||
wget -c $KERNEL_LOC/$KERNEL_HEADERS
|
||||
wget -c $KERNEL_LOC/$KERNEL_IMAGE
|
||||
|
||||
# Install custom linux headers and image:
|
||||
$pkginst $KERNEL_IMAGE $KERNEL_HEADERS
|
||||
|
||||
# The next two steps are to work around a bug in newer versions of
|
||||
# kernel-package, which fails to add initrd images with the latest kernels.
|
||||
# See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=525032
|
||||
# Generate initrd image if the .deb didn't install it:
|
||||
if ! test -e /boot/initrd.img-${KERNEL_NAME}; then
|
||||
sudo update-initramfs -c -k ${KERNEL_NAME}
|
||||
fi
|
||||
|
||||
# Ensure /boot/grub/menu.lst boots with initrd image:
|
||||
sudo update-grub
|
||||
|
||||
# The default should be the new kernel. Otherwise, you may need to modify
|
||||
# /boot/grub/menu.lst to set the default to the entry corresponding to the
|
||||
# kernel you just installed.
|
||||
fi
|
||||
}
|
||||
|
||||
function kernel_clean {
|
||||
@@ -107,250 +116,185 @@ function kernel_clean {
|
||||
fi
|
||||
|
||||
# Also remove downloaded packages:
|
||||
rm -f $HOME/linux-headers-* $HOME/linux-image-*
|
||||
rm -f ~/linux-headers-* ~/linux-image-*
|
||||
}
|
||||
|
||||
# Install Mininet deps
|
||||
function mn_deps {
|
||||
echo "Installing Mininet dependencies"
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install gcc make socat psmisc xterm openssh-clients iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man pyflakes pylint python-pep8
|
||||
else
|
||||
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
|
||||
python-setuptools cgroup-bin ethtool help2man \
|
||||
pyflakes pylint pep8
|
||||
$install gcc make screen psmisc xterm ssh iperf iproute \
|
||||
python-setuptools python-networkx cgroup-bin ethtool
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||
echo "Upgrading networkx to avoid deprecation warning"
|
||||
sudo easy_install --upgrade networkx
|
||||
fi
|
||||
|
||||
# Add sysctl parameters as noted in the INSTALL file to increase kernel
|
||||
# limits to support larger setups:
|
||||
sudo su -c "cat $HOME/mininet/util/sysctl_addon >> /etc/sysctl.conf"
|
||||
|
||||
# Load new sysctl settings:
|
||||
sudo sysctl -p
|
||||
|
||||
echo "Installing Mininet core"
|
||||
pushd $MININET_DIR/mininet
|
||||
pushd ~/mininet
|
||||
sudo make install
|
||||
popd
|
||||
}
|
||||
|
||||
# Install Mininet developer dependencies
|
||||
function mn_dev {
|
||||
echo "Installing Mininet developer dependencies"
|
||||
$install doxygen doxypy texlive-fonts-recommended
|
||||
}
|
||||
|
||||
# The following will cause a full OF install, covering:
|
||||
# -user switch
|
||||
# The instructions below are an abbreviated version from
|
||||
# http://www.openflowswitch.org/wk/index.php/Debian_Install
|
||||
# ... modified to use Debian Lenny rather than unstable.
|
||||
function of {
|
||||
echo "Installing OpenFlow reference implementation..."
|
||||
cd $BUILD_DIR
|
||||
$install autoconf automake libtool make gcc
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install git pkgconfig glibc-devel
|
||||
else
|
||||
$install git-core autotools-dev pkg-config libc6-dev
|
||||
fi
|
||||
cd ~/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc libtool libc6-dev
|
||||
git clone git://openflowswitch.org/openflow.git
|
||||
cd $BUILD_DIR/openflow
|
||||
cd ~/openflow
|
||||
|
||||
# Patch controller to handle more than 16 switches
|
||||
patch -p1 < $MININET_DIR/mininet/util/openflow-patches/controller.patch
|
||||
patch -p1 < ~/mininet/util/openflow-patches/controller.patch
|
||||
|
||||
# Resume the install:
|
||||
./boot.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd $BUILD_DIR
|
||||
}
|
||||
|
||||
function of13 {
|
||||
echo "Installing OpenFlow 1.3 soft switch implementation..."
|
||||
cd $BUILD_DIR/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
|
||||
unzip libpcre3-dev flex bison libboost-dev
|
||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||
# sent during tests, near link status changes:
|
||||
$remove avahi-daemon
|
||||
|
||||
if [ ! -d "ofsoftswitch13" ]; then
|
||||
git clone https://github.com/CPqD/ofsoftswitch13.git
|
||||
if [[ -n "$OF13_SWITCH_REV" ]]; then
|
||||
cd ofsoftswitch13
|
||||
git checkout ${OF13_SWITCH_REV}
|
||||
cd ..
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install netbee
|
||||
NBEESRC="nbeesrc-jan-10-2013"
|
||||
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
|
||||
wget -nc ${NBEEURL}${NBEESRC}.zip
|
||||
unzip ${NBEESRC}.zip
|
||||
cd ${NBEESRC}/src
|
||||
cmake .
|
||||
make
|
||||
cd $BUILD_DIR/
|
||||
sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib
|
||||
sudo ldconfig
|
||||
sudo cp -R ${NBEESRC}/include/ /usr/
|
||||
|
||||
# Resume the install:
|
||||
cd $BUILD_DIR/ofsoftswitch13
|
||||
./boot.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd $BUILD_DIR
|
||||
}
|
||||
|
||||
function wireshark_version_check {
|
||||
# Check Wireshark version
|
||||
WS=$(which wireshark)
|
||||
WS_VER_PATCH=(1 10) # targetting wireshark 1.10.0
|
||||
WS_VER=($($WS --version | sed 's/[a-z ]*\([0-9]*\).\([0-9]*\).\([0-9]*\).*/\1 \2 \3/'))
|
||||
if [ "${WS_VER[0]}" -lt "${WS_VER_PATCH[0]}" ] ||
|
||||
[[ "${WS_VER[0]}" -le "${WS_VER_PATCH[0]}" && "${WS_VER[1]}" -lt "${WS_VER_PATCH[1]}" ]]
|
||||
then
|
||||
# pre-1.10.0 wireshark
|
||||
echo "Setting revision: pre-ws-1.10.0"
|
||||
WS_DISSECTOR_REV="pre-ws-1.10.0"
|
||||
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||
if [ "$DIST" = "Ubuntu" ]; then
|
||||
BLACKLIST=/etc/modprobe.d/blacklist.conf
|
||||
else
|
||||
BLACKLIST=/etc/modprobe.d/blacklist
|
||||
fi
|
||||
sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||
cd ~
|
||||
}
|
||||
|
||||
function wireshark {
|
||||
echo "Installing Wireshark dissector..."
|
||||
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
# Just install Fedora's wireshark RPMS
|
||||
# Fedora's wirehark >= 1.10.2-2 includes an OF dissector
|
||||
# (it has been backported from the future Wireshark 1.12 code base)
|
||||
$install wireshark wireshark-gnome
|
||||
return
|
||||
fi
|
||||
|
||||
$install wireshark tshark libgtk2.0-dev
|
||||
sudo apt-get install -y wireshark libgtk2.0-dev
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" != "10.04" ]; then
|
||||
# Install newer version
|
||||
$install scons mercurial libglib2.0-dev
|
||||
$install libwiretap-dev libwireshark-dev
|
||||
cd $BUILD_DIR
|
||||
hg clone https://bitbucket.org/barnstorm/of-dissector
|
||||
if [[ -z "$WS_DISSECTOR_REV" ]]; then
|
||||
wireshark_version_check
|
||||
fi
|
||||
cd of-dissector
|
||||
if [[ -n "$WS_DISSECTOR_REV" ]]; then
|
||||
hg checkout ${WS_DISSECTOR_REV}
|
||||
fi
|
||||
# Build dissector
|
||||
cd src
|
||||
sudo apt-get install -y scons mercurial libglib2.0-dev
|
||||
sudo apt-get install -y libwiretap-dev libwireshark-dev
|
||||
cd ~
|
||||
hg clone https://bitbucket.org/onlab/of-dissector
|
||||
cd of-dissector/src
|
||||
export WIRESHARK=/usr/include/wireshark
|
||||
scons
|
||||
# libwireshark0/ on 11.04; libwireshark1/ on later
|
||||
WSDIR=`find /usr/lib -type d -name 'libwireshark*' | head -1`
|
||||
WSDIR=`ls -d /usr/lib/wireshark/libwireshark* | head -1`
|
||||
WSPLUGDIR=$WSDIR/plugins/
|
||||
sudo cp openflow.so $WSPLUGDIR
|
||||
echo "Copied openflow plugin to $WSPLUGDIR"
|
||||
else
|
||||
# Install older version from reference source
|
||||
cd $BUILD_DIR/openflow/utilities/wireshark_dissectors/openflow
|
||||
cd ~/openflow/utilities/wireshark_dissectors/openflow
|
||||
make
|
||||
sudo make install
|
||||
fi
|
||||
|
||||
# Copy coloring rules: OF is white-on-blue:
|
||||
mkdir -p $HOME/.wireshark
|
||||
cp $MININET_DIR/mininet/util/colorfilters $HOME/.wireshark
|
||||
}
|
||||
|
||||
|
||||
# Install Open vSwitch specific version Ubuntu package
|
||||
function ubuntuOvs {
|
||||
echo "Creating and Installing Open vSwitch packages..."
|
||||
|
||||
OVS_SRC=$BUILD_DIR/openvswitch
|
||||
OVS_TARBALL_LOC=http://openvswitch.org/releases
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
|
||||
rm -rf $OVS_SRC
|
||||
mkdir -p $OVS_SRC
|
||||
cd $OVS_SRC
|
||||
|
||||
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
|
||||
tar xzf openvswitch-$OVS_RELEASE.tar.gz
|
||||
else
|
||||
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
|
||||
# Remove any old packages
|
||||
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
|
||||
openvswitch-pki openvswitch-switch
|
||||
|
||||
# Get build deps
|
||||
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
|
||||
pkg-config bzip2 openssl python-all procps python-qt4 \
|
||||
python-zopeinterface python-twisted-conch dkms
|
||||
|
||||
# Build OVS
|
||||
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
|
||||
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
|
||||
cd ..
|
||||
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
|
||||
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
|
||||
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
|
||||
echo "Ignoring error installing openvswitch-controller"
|
||||
fi
|
||||
|
||||
modinfo openvswitch
|
||||
sudo ovs-vsctl show
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
else
|
||||
echo "Failed to install Open vSwitch. OS must be Ubuntu >= 12.04"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
mkdir -p ~/.wireshark
|
||||
cp ~/mininet/util/colorfilters ~/.wireshark
|
||||
}
|
||||
|
||||
|
||||
# Install Open vSwitch
|
||||
|
||||
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
|
||||
function ovs {
|
||||
echo "Installing Open vSwitch..."
|
||||
|
||||
if [ "$DIST" == "Fedora" ]; then
|
||||
$install openvswitch openvswitch-controller
|
||||
# Required for module build/dkms install
|
||||
$install $KERNEL_HEADERS
|
||||
|
||||
# First see if we have packages
|
||||
# XXX wget -c seems to fail from github/amazon s3
|
||||
cd /tmp
|
||||
if wget $OVS_PACKAGE_LOC/$OVS_PACKAGE_NAME; then
|
||||
$install patch dkms fakeroot python-argparse
|
||||
tar xf $OVS_PACKAGE_NAME
|
||||
orig=`tar tf $OVS_PACKAGE_NAME`
|
||||
# Now install packages in reasonable dependency order
|
||||
order='dkms common pki openvswitch-switch brcompat controller'
|
||||
pkgs=""
|
||||
for p in $order; do
|
||||
pkg=`echo "$orig" | grep $p`
|
||||
# Annoyingly, things seem to be missing without this flag
|
||||
$pkginst --force-confmiss $pkg
|
||||
done
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
echo "Done (hopefully) installing packages"
|
||||
cd ~
|
||||
return
|
||||
fi
|
||||
|
||||
# Manually installing openvswitch-datapath may be necessary
|
||||
# for manually built kernel .debs using Debian's defective kernel
|
||||
# packaging, which doesn't yield usable headers.
|
||||
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||
# If you've already installed a datapath, assume you
|
||||
# know what you're doing and don't need dkms datapath.
|
||||
# Otherwise, install it.
|
||||
$install openvswitch-datapath-dkms
|
||||
# Otherwise try distribution's OVS packages
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `echo "$RELEASE >= 11.10" | bc` = 1 ]; then
|
||||
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||
# If you've already installed a datapath, assume you
|
||||
# know what you're doing and don't need dkms datapath.
|
||||
# Otherwise, install it.
|
||||
$install openvswitch-datapath-dkms
|
||||
fi
|
||||
if $install openvswitch-switch openvswitch-controller; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
$install openvswitch-switch openvswitch-controller
|
||||
$install pkg-config gcc make python-dev libssl-dev libtool
|
||||
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
if [ "$CODENAME" = "lenny" ]; then
|
||||
$install git-core
|
||||
# Install Autoconf 2.63+ backport from Debian Backports repo:
|
||||
# Instructions from http://backports.org/dokuwiki/doku.php?id=instructions
|
||||
sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list"
|
||||
sudo apt-get update
|
||||
sudo apt-get -y --force-yes install debian-backports-keyring
|
||||
sudo apt-get -y --force-yes -t lenny-backports install autoconf
|
||||
fi
|
||||
else
|
||||
$install git
|
||||
fi
|
||||
|
||||
# Install OVS from release
|
||||
cd ~/
|
||||
git clone git://openvswitch.org/openvswitch $OVS_SRC
|
||||
cd $OVS_SRC
|
||||
git checkout $OVS_TAG
|
||||
./boot.sh
|
||||
BUILDDIR=/lib/modules/${KERNEL_NAME}/build
|
||||
if [ ! -e $BUILDDIR ]; then
|
||||
echo "Creating build sdirectory $BUILDDIR"
|
||||
sudo mkdir -p $BUILDDIR
|
||||
fi
|
||||
opts="--with-linux=$BUILDDIR"
|
||||
mkdir -p $OVS_BUILD
|
||||
cd $OVS_BUILD
|
||||
../configure $opts
|
||||
make
|
||||
sudo make install
|
||||
|
||||
modprobe
|
||||
}
|
||||
|
||||
function remove_ovs {
|
||||
@@ -374,22 +318,6 @@ function remove_ovs {
|
||||
echo "Done removing OVS"
|
||||
}
|
||||
|
||||
function ivs {
|
||||
echo "Installing Indigo Virtual Switch..."
|
||||
|
||||
IVS_SRC=$BUILD_DIR/ivs
|
||||
|
||||
# Install dependencies
|
||||
$install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
|
||||
|
||||
# Install IVS from source
|
||||
cd $BUILD_DIR
|
||||
git clone git://github.com/floodlight/ivs $IVS_SRC --recursive
|
||||
cd $IVS_SRC
|
||||
make
|
||||
sudo make install
|
||||
}
|
||||
|
||||
# Install NOX with tutorial files
|
||||
function nox {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
@@ -400,7 +328,7 @@ function nox {
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install python-dev libboost-dev
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
@@ -408,19 +336,14 @@ function nox {
|
||||
$install libsqlite3-dev python-simplejson
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd $BUILD_DIR/
|
||||
git clone https://github.com/noxrepo/nox-classic.git noxcore
|
||||
cd ~/
|
||||
git clone git://noxrepo.org/nox noxcore
|
||||
cd noxcore
|
||||
if ! git checkout -b destiny remotes/origin/destiny ; then
|
||||
echo "Did not check out a new destiny branch - assuming current branch is destiny"
|
||||
fi
|
||||
git checkout -b destiny remotes/origin/destiny
|
||||
|
||||
# Apply patches
|
||||
git checkout -b tutorial-destiny
|
||||
git am $MININET_DIR/mininet/util/nox-patches/*tutorial-port-nox-destiny*.patch
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
|
||||
git am $MININET_DIR/mininet/util/nox-patches/*nox-ubuntu12-hacks.patch
|
||||
fi
|
||||
git am ~/mininet/util/nox-patches/*.patch
|
||||
|
||||
# Build
|
||||
./boot.sh
|
||||
@@ -431,54 +354,13 @@ function nox {
|
||||
#make check
|
||||
|
||||
# Add NOX_CORE_DIR env var:
|
||||
sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=$BUILD_DIR/noxcore/build/src|' ~/.bashrc
|
||||
sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=~/noxcore/build/src|' ~/.bashrc
|
||||
|
||||
# To verify this install:
|
||||
#cd ~/noxcore/build/src
|
||||
#./nox_core -v -i ptcp:
|
||||
}
|
||||
|
||||
# Install NOX Classic/Zaku for OpenFlow 1.3
|
||||
function nox13 {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
|
||||
# Install NOX deps:
|
||||
$install autoconf automake g++ libtool python python-twisted \
|
||||
swig libssl-dev make
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd $BUILD_DIR/
|
||||
git clone https://github.com/CPqD/nox13oflib.git
|
||||
cd nox13oflib
|
||||
|
||||
# Build
|
||||
./boot.sh
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
make -j3
|
||||
#make check
|
||||
|
||||
# To verify this install:
|
||||
#cd ~/nox13oflib/build/src
|
||||
#./nox_core -v -i ptcp:
|
||||
}
|
||||
|
||||
|
||||
# "Install" POX
|
||||
function pox {
|
||||
echo "Installing POX into $BUILD_DIR/pox..."
|
||||
cd $BUILD_DIR
|
||||
git clone https://github.com/noxrepo/pox.git
|
||||
}
|
||||
|
||||
# Install OFtest
|
||||
function oftest {
|
||||
echo "Installing oftest..."
|
||||
@@ -487,69 +369,37 @@ function oftest {
|
||||
$install tcpdump python-scapy
|
||||
|
||||
# Install oftest:
|
||||
cd $BUILD_DIR/
|
||||
git clone git://github.com/floodlight/oftest
|
||||
cd ~/
|
||||
git clone git://openflow.org/oftest
|
||||
cd oftest
|
||||
cd tools/munger
|
||||
sudo make install
|
||||
}
|
||||
|
||||
# Install cbench
|
||||
function cbench {
|
||||
echo "Installing cbench..."
|
||||
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install net-snmp-devel libpcap-devel libconfig-devel
|
||||
else
|
||||
$install libsnmp-dev libpcap-dev libconfig-dev
|
||||
fi
|
||||
cd $BUILD_DIR/
|
||||
git clone git://gitosis.stanford.edu/oflops.git
|
||||
|
||||
$install libsnmp-dev libpcap-dev
|
||||
cd ~/
|
||||
git clone git://openflow.org/oflops.git
|
||||
cd oflops
|
||||
sh boot.sh || true # possible error in autoreconf, so run twice
|
||||
sh boot.sh
|
||||
./configure --with-openflow-src-dir=$BUILD_DIR/openflow
|
||||
./configure --with-openflow-src-dir=$HOME/openflow
|
||||
make
|
||||
sudo make install || true # make install fails; force past this
|
||||
}
|
||||
|
||||
function vm_other {
|
||||
echo "Doing other Mininet VM setup tasks..."
|
||||
|
||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||
# sent during tests, near link status changes:
|
||||
echo "Removing avahi-daemon"
|
||||
$remove avahi-daemon
|
||||
|
||||
# was: Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||
#echo "Attempting to disable IPv6"
|
||||
#if [ "$DIST" = "Ubuntu" ]; then
|
||||
# BLACKLIST=/etc/modprobe.d/blacklist.conf
|
||||
#else
|
||||
# BLACKLIST=/etc/modprobe.d/blacklist
|
||||
#fi
|
||||
#sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||
|
||||
# Disable IPv6
|
||||
if ! grep 'disable IPv6' /etc/sysctl.conf; then
|
||||
echo 'Disabling IPv6'
|
||||
echo '
|
||||
# Mininet: disable IPv6
|
||||
net.ipv6.conf.all.disable_ipv6 = 1
|
||||
net.ipv6.conf.default.disable_ipv6 = 1
|
||||
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
fi
|
||||
# Disabling IPv6 breaks X11 forwarding via ssh
|
||||
line='AddressFamily inet'
|
||||
file='/etc/ssh/sshd_config'
|
||||
echo "Adding $line to $file"
|
||||
if ! grep "$line" $file > /dev/null; then
|
||||
echo "$line" | sudo tee -a $file > /dev/null
|
||||
fi
|
||||
function other {
|
||||
echo "Doing other setup tasks..."
|
||||
|
||||
# Enable command auto completion using sudo; modify ~/.bashrc:
|
||||
sed -i -e 's|# for examples$|&\ncomplete -cf sudo|' ~/.bashrc
|
||||
|
||||
# Install tcpdump, cmd-line packet dump tool. Also install gitk,
|
||||
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
|
||||
# a graphical git history viewer.
|
||||
$install tcpdump gitk
|
||||
$install tcpdump tshark gitk
|
||||
|
||||
# Install common text editors
|
||||
$install vim nano emacs
|
||||
@@ -580,41 +430,23 @@ net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
# a good idea to use a symbolic link in place of the copy below.
|
||||
function modprobe {
|
||||
echo "Setting up modprobe for OVS kmod..."
|
||||
set +o nounset
|
||||
if [ -z "$OVS_KMODS" ]; then
|
||||
echo "OVS_KMODS not set. Aborting."
|
||||
else
|
||||
sudo cp $OVS_KMODS $DRIVERS_DIR
|
||||
sudo depmod -a ${KERNEL_NAME}
|
||||
fi
|
||||
set -o nounset
|
||||
|
||||
sudo cp $OVS_KMODS $DRIVERS_DIR
|
||||
sudo depmod -a ${KERNEL_NAME}
|
||||
}
|
||||
|
||||
function all {
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
printf "\nFedora 18+ support (still work in progress):\n"
|
||||
printf " * Fedora 18+ has kernel 3.10 RPMS in the updates repositories\n"
|
||||
printf " * Fedora 18+ has openvswitch 1.10 RPMS in the updates repositories\n"
|
||||
printf " * the install.sh script options [-bfnpvw] should work.\n"
|
||||
printf " * for a basic setup just try:\n"
|
||||
printf " install.sh -fnpv\n\n"
|
||||
exit 3
|
||||
fi
|
||||
echo "Installing all packages except for -eix (doxypy, ivs, nox-classic)..."
|
||||
echo "Running all commands..."
|
||||
kernel
|
||||
mn_deps
|
||||
# Skip mn_dev (doxypy/texlive/fonts/etc.) because it's huge
|
||||
# mn_dev
|
||||
of
|
||||
wireshark
|
||||
ovs
|
||||
# We may add ivs once it's more mature
|
||||
# ivs
|
||||
# NOX-classic is deprecated, but you can install it manually if desired.
|
||||
# nox
|
||||
pox
|
||||
nox
|
||||
oftest
|
||||
cbench
|
||||
other
|
||||
echo "Please reboot, then run ./mininet/util/install.sh -c to remove unneeded packages."
|
||||
echo "Enjoy Mininet!"
|
||||
}
|
||||
|
||||
@@ -622,7 +454,6 @@ function all {
|
||||
function vm_clean {
|
||||
echo "Cleaning VM..."
|
||||
sudo apt-get clean
|
||||
sudo apt-get autoremove
|
||||
sudo rm -rf /tmp/*
|
||||
sudo rm -rf openvswitch*.tar.gz
|
||||
|
||||
@@ -643,20 +474,16 @@ function vm_clean {
|
||||
git config --global user.name "None"
|
||||
git config --global user.email "None"
|
||||
|
||||
# Note: you can shrink the .vmdk in vmware using
|
||||
# vmware-vdiskmanager -k *.vmdk
|
||||
echo "Zeroing out disk blocks for efficient compaction..."
|
||||
time sudo dd if=/dev/zero of=/tmp/zero bs=1M
|
||||
sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero
|
||||
|
||||
# Remove mininet install script
|
||||
rm -f install-mininet.sh
|
||||
}
|
||||
|
||||
function usage {
|
||||
printf '\nUsage: %s [-abcdfhikmnprtvVwx03]\n\n' $(basename $0) >&2
|
||||
|
||||
printf 'Usage: %s [-acdfhkmntvxy]\n\n' $(basename $0) >&2
|
||||
|
||||
printf 'This install script attempts to install useful packages\n' >&2
|
||||
printf 'for Mininet. It should (hopefully) work on Ubuntu 11.10+\n' >&2
|
||||
printf 'If you run into trouble, try\n' >&2
|
||||
printf 'for Mininet. It should (hopefully) work on Ubuntu 10.04, 11.10\n' >&2
|
||||
printf 'and Debian 5.0 (Lenny). If you run into trouble, try\n' >&2
|
||||
printf 'installing one thing at a time, and looking at the \n' >&2
|
||||
printf 'specific installation function in this script.\n\n' >&2
|
||||
|
||||
@@ -664,68 +491,43 @@ function usage {
|
||||
printf -- ' -a: (default) install (A)ll packages - good luck!\n' >&2
|
||||
printf -- ' -b: install controller (B)enchmark (oflops)\n' >&2
|
||||
printf -- ' -c: (C)lean up after kernel install\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -e: install Mininet d(E)veloper dependencies\n' >&2
|
||||
printf -- ' -f: install Open(F)low\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -f: install open(F)low\n' >&2
|
||||
printf -- ' -h: print this (H)elp message\n' >&2
|
||||
printf -- ' -i: install (I)ndigo Virtual Switch\n' >&2
|
||||
printf -- ' -k: install new (K)ernel\n' >&2
|
||||
printf -- ' -m: install Open vSwitch kernel (M)odule from source dir\n' >&2
|
||||
printf -- ' -n: install Mini(N)et dependencies + core files\n' >&2
|
||||
printf -- ' -p: install (P)OX OpenFlow Controller\n' >&2
|
||||
printf -- ' -n: install mini(N)et dependencies + core files\n' >&2
|
||||
printf -- ' -r: remove existing Open vSwitch packages\n' >&2
|
||||
printf -- ' -s <dir>: place dependency (S)ource/build trees in <dir>\n' >&2
|
||||
printf -- ' -t: complete o(T)her Mininet VM setup tasks\n' >&2
|
||||
printf -- ' -v: install Open (V)switch\n' >&2
|
||||
printf -- ' -V <version>: install a particular version of Open (V)switch on Ubuntu\n' >&2
|
||||
printf -- ' -w: install OpenFlow (W)ireshark dissector\n' >&2
|
||||
printf -- ' -x: install NO(X) Classic OpenFlow controller\n' >&2
|
||||
printf -- ' -0: (default) -0[fx] installs OpenFlow 1.0 versions\n' >&2
|
||||
printf -- ' -3: -3[fx] installs OpenFlow 1.3 versions\n' >&2
|
||||
printf -- ' -t: install o(T)her stuff\n' >&2
|
||||
printf -- ' -v: install open (V)switch\n' >&2
|
||||
printf -- ' -w: install OpenFlow (w)ireshark dissector\n' >&2
|
||||
printf -- ' -x: install NO(X) OpenFlow controller\n' >&2
|
||||
printf -- ' -y: install (A)ll packages\n' >&2
|
||||
|
||||
exit 2
|
||||
}
|
||||
|
||||
OF_VERSION=1.0
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
all
|
||||
else
|
||||
while getopts 'abcdefhikmnprs:tvV:wx03' OPTION
|
||||
while getopts 'abcdfhkmnrtvwx' OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
a) all;;
|
||||
b) cbench;;
|
||||
c) kernel_clean;;
|
||||
d) vm_clean;;
|
||||
e) mn_dev;;
|
||||
f) case $OF_VERSION in
|
||||
1.0) of;;
|
||||
1.3) of13;;
|
||||
*) echo "Invalid OpenFlow version $OF_VERSION";;
|
||||
esac;;
|
||||
f) of;;
|
||||
h) usage;;
|
||||
i) ivs;;
|
||||
k) kernel;;
|
||||
m) modprobe;;
|
||||
n) mn_deps;;
|
||||
p) pox;;
|
||||
r) remove_ovs;;
|
||||
s) mkdir -p $OPTARG; # ensure the directory is created
|
||||
BUILD_DIR="$( cd -P "$OPTARG" && pwd )"; # get the full path
|
||||
echo "Dependency installation directory: $BUILD_DIR";;
|
||||
t) vm_other;;
|
||||
t) other;;
|
||||
v) ovs;;
|
||||
V) OVS_RELEASE=$OPTARG;
|
||||
ubuntuOvs;;
|
||||
w) wireshark;;
|
||||
x) case $OF_VERSION in
|
||||
1.0) nox;;
|
||||
1.3) nox13;;
|
||||
*) echo "Invalid OpenFlow version $OF_VERSION";;
|
||||
esac;;
|
||||
0) OF_VERSION=1.0;;
|
||||
3) OF_VERSION=1.3;;
|
||||
x) nox;;
|
||||
?) usage;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Attach to a Mininet host and run a command
|
||||
|
||||
if [ -z $1 ]; then
|
||||
echo "usage: $0 host cmd [args...]"
|
||||
exit 1
|
||||
else
|
||||
host=$1
|
||||
fi
|
||||
|
||||
pid=`ps ax | grep "mininet:$host$" | grep bash | awk '{print $1};'`
|
||||
|
||||
if echo $pid | grep -q ' '; then
|
||||
echo "Error: found multiple mininet:$host processes"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ "$pid" == "" ]; then
|
||||
echo "Could not find Mininet host $host"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [ -z $2 ]; then
|
||||
cmd='bash'
|
||||
else
|
||||
shift
|
||||
cmd=$*
|
||||
fi
|
||||
|
||||
cgroup=/sys/fs/cgroup/cpu/$host
|
||||
if [ -d "$cgroup" ]; then
|
||||
cg="-g $host"
|
||||
fi
|
||||
|
||||
# Check whether host should be running in a chroot dir
|
||||
rootdir="/var/run/mn/$host/root"
|
||||
if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
|
||||
cmd="'cd `pwd`; exec $cmd'"
|
||||
cmd="chroot $rootdir /bin/bash -c $cmd"
|
||||
fi
|
||||
|
||||
cmd="exec sudo mnexec -a $pid $cg $cmd"
|
||||
eval $cmd
|
||||
@@ -1,175 +0,0 @@
|
||||
From 166693d7cb640d4a41251b87e92c52d9c688196b Mon Sep 17 00:00:00 2001
|
||||
From: Bob Lantz <rlantz@cs.stanford.edu>
|
||||
Date: Mon, 14 May 2012 15:30:44 -0700
|
||||
Subject: [PATCH] Hacks to get NOX classic/destiny to compile under Ubuntu
|
||||
12.04
|
||||
|
||||
Thanks to Srinivasu R. Kanduru for the initial patch.
|
||||
|
||||
Apologies for the hacks - it is my hope that this will be fixed
|
||||
upstream eventually.
|
||||
|
||||
---
|
||||
config/ac_pkg_swig.m4 | 7 ++++---
|
||||
src/Make.vars | 2 +-
|
||||
src/nox/coreapps/pyrt/deferredcallback.cc | 2 +-
|
||||
src/nox/coreapps/pyrt/pyglue.cc | 2 +-
|
||||
src/nox/coreapps/pyrt/pyrt.cc | 2 +-
|
||||
src/nox/netapps/authenticator/auth.i | 2 ++
|
||||
src/nox/netapps/authenticator/flow_util.i | 1 +
|
||||
src/nox/netapps/routing/routing.i | 2 ++
|
||||
.../switch_management/pyswitch_management.i | 2 ++
|
||||
src/nox/netapps/tests/tests.cc | 2 +-
|
||||
src/nox/netapps/topology/pytopology.i | 2 ++
|
||||
11 files changed, 18 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/config/ac_pkg_swig.m4 b/config/ac_pkg_swig.m4
|
||||
index d12556e..9b608f2 100644
|
||||
--- a/config/ac_pkg_swig.m4
|
||||
+++ b/config/ac_pkg_swig.m4
|
||||
@@ -78,9 +78,10 @@ AC_DEFUN([AC_PROG_SWIG],[
|
||||
if test -z "$available_patch" ; then
|
||||
[available_patch=0]
|
||||
fi
|
||||
- if test $available_major -ne $required_major \
|
||||
- -o $available_minor -ne $required_minor \
|
||||
- -o $available_patch -lt $required_patch ; then
|
||||
+ major_done=`test $available_major -gt $required_major`
|
||||
+ minor_done=`test $available_minor -gt $required_minor`
|
||||
+ if test !$major_done -a !$minor_done \
|
||||
+ -a $available_patch -lt $required_patch ; then
|
||||
AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org])
|
||||
SWIG=''
|
||||
else
|
||||
diff --git a/src/Make.vars b/src/Make.vars
|
||||
index d70d6aa..93b2879 100644
|
||||
--- a/src/Make.vars
|
||||
+++ b/src/Make.vars
|
||||
@@ -53,7 +53,7 @@ AM_LDFLAGS += -export-dynamic
|
||||
endif
|
||||
|
||||
# set python runtimefiles to be installed in the same directory as pkg
|
||||
-pkglib_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||
+pkgdata_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||
BUILT_SOURCES = $(NOX_PYBUILDFILES)
|
||||
|
||||
# Runtime-files build and clean rules
|
||||
diff --git a/src/nox/coreapps/pyrt/deferredcallback.cc b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
index 3a40fa7..111a586 100644
|
||||
--- a/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
+++ b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
@@ -69,7 +69,7 @@ DeferredCallback::get_instance(const Callback& c)
|
||||
DeferredCallback* cb = new DeferredCallback(c);
|
||||
|
||||
// flag as used in *_wrap.cc....correct?
|
||||
- return SWIG_Python_NewPointerObj(cb, s, SWIG_POINTER_OWN | 0);
|
||||
+ return SWIG_Python_NewPointerObj(m, cb, s, SWIG_POINTER_OWN | 0);
|
||||
}
|
||||
|
||||
bool
|
||||
diff --git a/src/nox/coreapps/pyrt/pyglue.cc b/src/nox/coreapps/pyrt/pyglue.cc
|
||||
index 48b9716..317fd04 100644
|
||||
--- a/src/nox/coreapps/pyrt/pyglue.cc
|
||||
+++ b/src/nox/coreapps/pyrt/pyglue.cc
|
||||
@@ -874,7 +874,7 @@ to_python(const Flow& flow)
|
||||
if (!s) {
|
||||
throw std::runtime_error("Could not find Flow SWIG type_info");
|
||||
}
|
||||
- return SWIG_Python_NewPointerObj(f, s, SWIG_POINTER_OWN | 0);
|
||||
+ return SWIG_Python_NewPointerObj(m, f, s, SWIG_POINTER_OWN | 0);
|
||||
|
||||
// PyObject* dict = PyDict_New();
|
||||
// if (!dict) {
|
||||
diff --git a/src/nox/coreapps/pyrt/pyrt.cc b/src/nox/coreapps/pyrt/pyrt.cc
|
||||
index fbda461..8ec05d6 100644
|
||||
--- a/src/nox/coreapps/pyrt/pyrt.cc
|
||||
+++ b/src/nox/coreapps/pyrt/pyrt.cc
|
||||
@@ -776,7 +776,7 @@ Python_event_manager::create_python_context(const Context* ctxt,
|
||||
pretty_print_python_exception());
|
||||
}
|
||||
|
||||
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||
Py_INCREF(pyctxt); // XXX needed?
|
||||
|
||||
//Py_DECREF(m);
|
||||
diff --git a/src/nox/netapps/authenticator/auth.i b/src/nox/netapps/authenticator/auth.i
|
||||
index 1de1a17..bfa04e2 100644
|
||||
--- a/src/nox/netapps/authenticator/auth.i
|
||||
+++ b/src/nox/netapps/authenticator/auth.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.authenticator.pyauth"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "core_events.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
diff --git a/src/nox/netapps/authenticator/flow_util.i b/src/nox/netapps/authenticator/flow_util.i
|
||||
index f67c3ef..2a314e2 100644
|
||||
--- a/src/nox/netapps/authenticator/flow_util.i
|
||||
+++ b/src/nox/netapps/authenticator/flow_util.i
|
||||
@@ -32,6 +32,7 @@ using namespace vigil::applications;
|
||||
%}
|
||||
|
||||
%include "common-defs.i"
|
||||
+%include "std_list.i"
|
||||
|
||||
%import "netinet/netinet.i"
|
||||
%import "pyrt/event.i"
|
||||
diff --git a/src/nox/netapps/routing/routing.i b/src/nox/netapps/routing/routing.i
|
||||
index 44ccb3d..f9221a2 100644
|
||||
--- a/src/nox/netapps/routing/routing.i
|
||||
+++ b/src/nox/netapps/routing/routing.i
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
%module "nox.netapps.routing.pyrouting"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "pyrouting.hh"
|
||||
#include "routing.hh"
|
||||
diff --git a/src/nox/netapps/switch_management/pyswitch_management.i b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
index 72bfed4..ad2c90d 100644
|
||||
--- a/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
+++ b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.pyswitch_management"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "switch_management_proxy.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
diff --git a/src/nox/netapps/tests/tests.cc b/src/nox/netapps/tests/tests.cc
|
||||
index 20e900d..f027028 100644
|
||||
--- a/src/nox/netapps/tests/tests.cc
|
||||
+++ b/src/nox/netapps/tests/tests.cc
|
||||
@@ -306,7 +306,7 @@ private:
|
||||
throw runtime_error("Could not find PyContext SWIG type_info.");
|
||||
}
|
||||
|
||||
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||
assert(pyctxt);
|
||||
|
||||
Py_DECREF(m);
|
||||
diff --git a/src/nox/netapps/topology/pytopology.i b/src/nox/netapps/topology/pytopology.i
|
||||
index 94a9f4b..7a8cd94 100644
|
||||
--- a/src/nox/netapps/topology/pytopology.i
|
||||
+++ b/src/nox/netapps/topology/pytopology.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.topology"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "pytopology.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
--
|
||||
1.7.5.4
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
0001: This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||
0002: This patch hacks nox-destiny to compile on Ubuntu 12.04.
|
||||
This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||
@@ -1,11 +0,0 @@
|
||||
obj-m = sch_htb.o
|
||||
KVERSION = $(shell uname -r)
|
||||
all:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
||||
install:
|
||||
test -e /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak || mv /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak
|
||||
cp sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko
|
||||
rmmod sch_htb
|
||||
modprobe sch_htb
|
||||
clean:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
||||
@@ -1,10 +0,0 @@
|
||||
Modified sch_htb implementation with ofbuf support.
|
||||
|
||||
To compile, just type make. To use this module instead
|
||||
of regular sch_htb, do:
|
||||
|
||||
0. make
|
||||
1. rmmod sch_htb
|
||||
2. insmod ./sch_htb.ko
|
||||
|
||||
To revert, just rmmod sch_htb.
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user