Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d06ab3591 | |||
| 9977ca16bb | |||
| 1631044f7b |
@@ -1 +0,0 @@
|
||||
*.py diff=python
|
||||
@@ -1,19 +0,0 @@
|
||||
Mininet uses GitHub issues for bug reports and feature requests only.
|
||||
These issues can be viewed at bugs.mininet.org
|
||||
|
||||
If you have a question that is not a bug report or a feature request,
|
||||
please use the documentation at docs.mininet.org, the FAQ
|
||||
at faq.mininet.org, and the mininet-discuss mailing list.
|
||||
|
||||
For bug reports, please fill in the following information in detail,
|
||||
and also feel free to include additional information such as debug
|
||||
output from mn -v debug, etc.
|
||||
--- Cut Here ---
|
||||
### Expected/Desired Behavior:
|
||||
|
||||
### Actual Behavior:
|
||||
|
||||
### Detailed Steps to Reproduce the Behavior:
|
||||
|
||||
### Additional Information:
|
||||
|
||||
+2
-8
@@ -1,14 +1,8 @@
|
||||
mnexec
|
||||
*.pyc
|
||||
*~
|
||||
*.1
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
\#*\#
|
||||
mininet.egg-info
|
||||
build
|
||||
dist
|
||||
doc/html
|
||||
doc/latex
|
||||
trunk
|
||||
build/*
|
||||
dist/*
|
||||
|
||||
|
||||
@@ -41,21 +41,16 @@ load-plugins=
|
||||
# can either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once).
|
||||
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
|
||||
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
|
||||
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
|
||||
locally-disabled, locally-enabled
|
||||
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
|
||||
|
||||
# bad-continuation, wrong-import-order
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html
|
||||
output-format=colorized
|
||||
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
|
||||
|
||||
# Include message's id in output
|
||||
# Include message's id in outpu
|
||||
include-ids=yes
|
||||
|
||||
# Put messages in a separate file for each module / package specified on the
|
||||
@@ -63,7 +58,7 @@ include-ids=yes
|
||||
# written in a file name "pylint_global.[txt|html]".
|
||||
files-output=no
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
# Tells wether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highes
|
||||
@@ -113,10 +108,10 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Regular expression which should only match correct function names
|
||||
function-rgx=[a-z_][a-z0-9]{2,30}$
|
||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct method names
|
||||
method-rgx=[a-z_][a-z0-9]{2,30}$
|
||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct instance attribute names
|
||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
@@ -125,7 +120,7 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9]{2,30}$
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct list comprehension /
|
||||
# generator expression variable names
|
||||
@@ -196,7 +191,7 @@ additional-builtins=
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp,build
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
||||
|
||||
# checks for sign of poor/misdesign:
|
||||
@@ -269,8 +264,7 @@ int-import-graph=
|
||||
max-line-length=80
|
||||
|
||||
# Maximum number of lines in a module
|
||||
# XXX 1500 -> 4000 for miniedit.py
|
||||
max-module-lines=4000
|
||||
max-module-lines=1500
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
language: python
|
||||
sudo: required
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- dist: trusty
|
||||
python: 2.7
|
||||
env: dist="14.04 LTS trusty"
|
||||
- dist: trusty
|
||||
python: 3.6
|
||||
env: dist="14.04 LTS trusty"
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq vlan
|
||||
- PYTHON=`which python` util/install.sh -n
|
||||
|
||||
install:
|
||||
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
|
||||
- pip install pexpect || pip3 install pexpect
|
||||
- util/install.sh -nfvw
|
||||
|
||||
script:
|
||||
- alias sudo="sudo env PATH=$PATH"
|
||||
- export PYTHON=`which python`
|
||||
- echo 'px import sys; print(sys.version_info)' | sudo $PYTHON bin/mn -v output
|
||||
- sudo $PYTHON bin/mn --test pingall
|
||||
- sudo $PYTHON mininet/test/runner.py -v -quick
|
||||
- sudo $PYTHON examples/test/runner.py -v -quick
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
|
||||
# More details: https://docs.travis-ci.com/user/notifications
|
||||
@@ -1,52 +0,0 @@
|
||||
Mininet Contributors
|
||||
|
||||
Mininet is an open source project and we gratefully acknowledge
|
||||
the many contributions to the project! If you have contributed
|
||||
code into the project and are not on this list, please let us know
|
||||
or send a pull request.
|
||||
|
||||
Contributors include:
|
||||
|
||||
Mininet Core Team (and alumni)
|
||||
|
||||
Bob Lantz
|
||||
Brandon Heller
|
||||
Nikhil Handigol
|
||||
Vimal Jeyakumar
|
||||
Brian O'Connor
|
||||
Cody Burkard
|
||||
|
||||
Additional Mininet Contributors
|
||||
|
||||
Tomasz Buchert
|
||||
Gustavo Pantuza Coelho Pinto
|
||||
Fernando Cappi
|
||||
Ryan Cox
|
||||
Shaun Crampton
|
||||
David Erickson
|
||||
Glen Gibb
|
||||
Andrew Ferguson
|
||||
Eder Leao Fernandes
|
||||
Gregory Gee
|
||||
Jon Hall
|
||||
Roan Huang
|
||||
Vitaly Ivanov
|
||||
Babis Kaidos
|
||||
Rich Lane
|
||||
Rémy Léone
|
||||
Zi Shen Lim
|
||||
David Mahler
|
||||
Murphy McCauley
|
||||
José Pedro Oliveira
|
||||
James Page
|
||||
Angad Singh
|
||||
Piyush Srivastava
|
||||
Ed Swierk
|
||||
Darshan Thaker
|
||||
Andreas Wundsam
|
||||
Isaku Yamahata
|
||||
Baohua Yang
|
||||
|
||||
Thanks also to everyone who has submitted issues and pull
|
||||
requests on github, and to our friendly mininet-discuss
|
||||
mailing list!
|
||||
@@ -1,179 +1,273 @@
|
||||
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.3.0d4
|
||||
Mininet 1.0.0
|
||||
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
pre-built VM image, and 2) native installation on Ubuntu. You can also
|
||||
easily create your own Mininet VM image (4).
|
||||
|
||||
The supported installation methods for Mininet are 1) using
|
||||
a pre-built VM image, and 2) native installation on Ubuntu or Debian.
|
||||
(Other distributions may be supported in the future - if you would
|
||||
like to contribute an installation script, we would welcome it!)
|
||||
|
||||
1. Easiest "installation" - use our pre-built VM image!
|
||||
1. Easiest "install" - use our pre-built VM image!
|
||||
|
||||
The easiest way to get Mininet running is to start with one of our
|
||||
pre-built virtual machine images from <http://mininet.org/>
|
||||
The easiest way to get Mininet running is to start with one of our pre-built
|
||||
virtual machine images from http://openflow.org/mininet
|
||||
|
||||
Boot up the VM image, log in, and follow the instructions on the
|
||||
Mininet web site.
|
||||
Boot up the VM image, log in, and follow the instructions on the wiki page.
|
||||
|
||||
One advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
An additional advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
|
||||
Although a single Mininet instance can simulate multiple networks
|
||||
with multiple controllers, only one Mininet instance may currently
|
||||
be run at a time, and Mininet requires root access in the machine
|
||||
it's running on. Therefore, if you have a multiuser system, you
|
||||
may wish to consider running Mininet in a VM.
|
||||
2. Native installation (experimental!) for Ubuntu 10.04 LTS
|
||||
|
||||
2. Next-easiest option: use our Ubuntu package!
|
||||
If you are running Ubuntu 10.04 LTS (or possibly Debian 5), you may be
|
||||
able to use our handy install.sh script, which is in mininet/util.
|
||||
|
||||
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
|
||||
12.10+:
|
||||
WARNING: USE AT YOUR OWN RISK!
|
||||
|
||||
sudo apt-get install mininet
|
||||
install.sh is a bit intrusive and may possibly damage your OS and/or
|
||||
home directory, by creating/modifying several directories such as
|
||||
mininet, openflow, openvswitch and noxcore. Although we hope it won't
|
||||
do anything completely terrible, you may want to look at the script
|
||||
before you run it, and you should make sure your system and home
|
||||
directory are backed up just in case!
|
||||
|
||||
Note: if you are upgrading from an older version of Mininet, make
|
||||
sure you remove the old OVS from `/usr/local`:
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
you may use
|
||||
|
||||
sudo rm /usr/local/bin/ovs*
|
||||
sudo rm /usr/local/sbin/ovs*
|
||||
$ mininet/util/install.sh
|
||||
|
||||
3. Native installation from source
|
||||
This takes about 20-30 minutes.
|
||||
|
||||
3.1. Native installation from source on Ubuntu 12.04+
|
||||
Alternately, you can install just the pieces you need.
|
||||
|
||||
If you're reading this, you've probably already done so, but the
|
||||
command to download the Mininet source code is:
|
||||
We recommend the following steps, in order:
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
[a) On Debian 5, first install a Mininet-compatible kernel:
|
||||
$ mininet/util/install.sh -k
|
||||
Reboot and run 'uname -r' to make sure you're running the new kernel.]
|
||||
|
||||
Note that the above git command will check out the latest and greatest
|
||||
Mininet (which we recommend!) If you want to run the last tagged/released
|
||||
version of Mininet, you can look at the release tags using
|
||||
b) Install mininet and its dependencies:
|
||||
$ mininet/util/install.sh -n
|
||||
|
||||
c) Install OpenFlow 1.0 and associated useful software
|
||||
$ mininet/util/install.sh -f
|
||||
|
||||
cd mininet
|
||||
git tag
|
||||
d) Install Open vSwitch and its kernel module
|
||||
$ mininet/util/install.sh -vm
|
||||
|
||||
and then
|
||||
e) If you wish to install the version of NOX we use in the tutorial:
|
||||
$ mininet/util/install.sh -x
|
||||
|
||||
git checkout <release tag>
|
||||
Note: NOX development is progressing over time, so after you complete
|
||||
the tutorial you may wish to install the latest and greatest NOX from
|
||||
noxrepo.org.
|
||||
|
||||
where <release tag> is the release you want to check out.
|
||||
Good luck! Some additional installation notes are provided below, for
|
||||
the brave and/or Linux-savvy, or those who are trying to understand what
|
||||
is installed and why.
|
||||
|
||||
If you are running Ubuntu, Debian, or Fedora, you may be able to use
|
||||
our handy `install.sh` script, which is in `util/`.
|
||||
p.s. Note that only one instance of Mininet is currently supported on a single
|
||||
machine - that's one reason we recommend using a VM to run it.
|
||||
|
||||
*WARNING: USE AT YOUR OWN RISK!*
|
||||
---
|
||||
|
||||
`install.sh` is a bit intrusive and may possibly damage your OS
|
||||
and/or home directory, by creating/modifying several directories
|
||||
such as `mininet`, `openflow`, `oftest`, `pox`, etc.. We recommend
|
||||
trying it in a VM before trying it on a system you use from day to day.
|
||||
Mininet Manual Installation Notes
|
||||
|
||||
Although we hope it won't do anything completely terrible, you may
|
||||
want to look at the script before you run it, and you should make
|
||||
sure your system and home directory are backed up just in case!
|
||||
These installation notes assume you understand how to do things like
|
||||
compile kernels, apply patches, configure networks, write code, etc.. If
|
||||
this is unfamiliar territory, or if you run into trouble, we recommend
|
||||
using one of our pre-built virtual machine images (see above.)
|
||||
|
||||
To install Mininet itself, the OpenFlow reference implementation, and
|
||||
Open vSwitch, you may use:
|
||||
If you wish to try to create a VM to run Mininet, you may also wish
|
||||
to look at the Wiki page:
|
||||
|
||||
util/install.sh -fnv
|
||||
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
|
||||
|
||||
This should be reasonably quick, and the following command should
|
||||
work after the installation:
|
||||
0. Obtaining Mininet
|
||||
|
||||
sudo mn --test pingall
|
||||
If you're reading this, you've already done it, but the command to
|
||||
download mininet is:
|
||||
|
||||
git clone git://openflow.org/mininet.git
|
||||
|
||||
1. Core Mininet installation
|
||||
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
including POX, the OpenFlow WireShark dissector, the `oftest`
|
||||
framework, and other potentially useful software, you may use:
|
||||
The core Mininet installation requires gcc, make, python,
|
||||
and setuptools. On Ubuntu and Debian you may install them with:
|
||||
|
||||
# aptitude install gcc make python setuptools
|
||||
|
||||
util/install.sh -a
|
||||
To install Mininet itself, with root privileges:
|
||||
|
||||
# cd mininet
|
||||
# make install
|
||||
|
||||
This takes about 4 minutes on our test system.
|
||||
This places the mininet package in /usr/lib/python-*/site-packages/,
|
||||
so that 'import mininet' will work, and installs the primary mn
|
||||
script (mn) as well as its helper utility (mnexec.)
|
||||
|
||||
On Ubuntu and Debian, Mininet's dependencies and core files may also be
|
||||
installed using mininet/util/install.sh -n
|
||||
|
||||
You can change the directory where the dependencies are installed using
|
||||
the -s <directory> flag.
|
||||
2. Installation script for Ubuntu/Debian Lenny
|
||||
|
||||
util/install.sh -s <directory> -a
|
||||
If you are running Ubuntu 10.04 or Debian Lenny, you may be able to use the
|
||||
util/install.sh script to install a compatible Linux kernel as well as
|
||||
other software including the OpenFlow reference implementation, the Open
|
||||
vSwitch switch implementation, and the NOX OpenFlow controller.
|
||||
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
Many different installation options are possible by passing different
|
||||
options to install.sh; install.sh -h lists them all.
|
||||
|
||||
Assuming the mininet source tree is installed in ~/mininet, the steps to run
|
||||
install.sh to install EVERYTHING we use for OpenFlow tutorials are:
|
||||
|
||||
% cd
|
||||
% time ~/mininet/util/install.sh # installs tons of stuff
|
||||
% sudo reboot # to load new kernel
|
||||
% ~/mininet/util/install.sh -c # to clean out unneeded kernel stuff
|
||||
|
||||
As root execute the following operations:
|
||||
This installs a lot of useful software, but it will take a while (30
|
||||
minutes or more, depending on your network connection, computer, etc..)
|
||||
|
||||
Probably the minimal semi-useful configuration would be to install
|
||||
Mininet itself, kernel support if necessary, and either the
|
||||
reference OpenFlow switch or Open vSwitch. This could be installed
|
||||
as follows:
|
||||
|
||||
% sudo ~/mininet/util/install.sh -knvm
|
||||
|
||||
Respectively, this installs kernel support, core mininet dependencies,
|
||||
Open vSwitch, and the Open vSwitch kernel module. If a new kernel was
|
||||
installed, then a reboot may be required.
|
||||
|
||||
* install git
|
||||
If install.sh cannot be used for some reason (e.g. you're on Fedora
|
||||
or some other Linux - please don't say CentOS) or if you don't want to
|
||||
install all of these components (they're useful!), the kernel and
|
||||
OpenFlow software requirements are described in steps [3] and [4],
|
||||
which follow.
|
||||
|
||||
yum install git
|
||||
If you successfully used install.sh, congratulations! You're basically
|
||||
done. Proceed to step [6] for additional advice.
|
||||
|
||||
3. Linux Kernel requirements
|
||||
|
||||
* create an user account (e.g. mininet) and add it to the wheel group
|
||||
Mininet requires a kernel built with network namespace support enabled,
|
||||
i.e. with CONFIG_NET_NS=Y, such as the kernel shipped with
|
||||
Ubuntu 10.04 LTS, currently 2.6.32. On Ubuntu 10.04, you should not need
|
||||
to install or build a custom kernel, although 2.6.33+ is faster at
|
||||
tearing down virtual ethernet pairs.
|
||||
|
||||
useradd [...] mininet
|
||||
usermod -a -G wheel mininet
|
||||
For Ubuntu and Debian, we provide a 2.6.33 kernel package which you may be
|
||||
able to install using "util/install.sh -k". Note our kernel package
|
||||
requires an ext2 or ext3 root file system, so it won't work if you have
|
||||
a default Ubuntu install, which uses ext4.
|
||||
|
||||
If your kernel wasn't compiled with CONFIG_NET_NS=Y, you will need to
|
||||
build and install a kernel that does! >= 2.6.33 works better, but may
|
||||
be harder to get working, depending on your Linux distribution.
|
||||
|
||||
A script for building Debian packages for 2.6.33.1 is provided in
|
||||
mininet/util/kbuild. You may wish to read it, as it applies patches
|
||||
to enable 2.6.33.1 to build under debian-stable, and to enable the
|
||||
tun driver to work correctly with Mininet.
|
||||
|
||||
Earlier kernels (e.g. 2.6.29) work with CONFIG_NET_NS enabled and no
|
||||
additional patches, but are much slower at removing veth interfaces,
|
||||
resulting in much slower switch shutdown.
|
||||
|
||||
* change the SElinux setting to permissive. It can be done
|
||||
temporarily with:
|
||||
For scalable configurations, you might need to increase some of your
|
||||
kernel limits. Sample params are in util/sysctl_addon, which can be
|
||||
appended to /etc/sysctl.conf (and modified as necessary for your
|
||||
desired configuration):
|
||||
|
||||
setenforce 0
|
||||
sudo su -c "cat sysctl_addon >> /etc/sysctl.conf"
|
||||
|
||||
then login with the new account (e.g. mininet) and do the following:
|
||||
To save the config change, run:
|
||||
|
||||
* clone the Mininet repository
|
||||
sudo sysctl -p
|
||||
|
||||
4. OpenFlow software and configuration requirements
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
Mininet requires either the reference OpenFlow switch implementation
|
||||
(from openflowswitch.org) or Open vSwitch (openvswitch.org) to be
|
||||
installed. "make test" requires the reference user space
|
||||
implementations as well as Open vSwitch. Note the reference kernel
|
||||
implementation is not currently included in OpenFlow 1.0.
|
||||
|
||||
On Ubuntu and Debian, the install.sh script may be used with the '-f'
|
||||
option to install the OpenFlow reference implementation, the '-v' option
|
||||
to build Open vSwitch, and the '-m' option to install the Open vSwitch
|
||||
kernel module into /lib/modules (note: you must build Open vSwitch first!)
|
||||
|
||||
Mininet will automatically load and remove kernel module dependencies
|
||||
for supported switch types, using modprobe and rmmod - but these
|
||||
modules must be in a location where modprobe can find them (e.g.
|
||||
something like /lib/modules/`uname -r`/kernel/drivers/net/)
|
||||
|
||||
* install Mininet, the OpenFlow reference implementation, and
|
||||
Open vSwitch
|
||||
The reference OpenFlow controller (controller(8)) only supports 16
|
||||
switches by default! If you wish to run a network with more than 16
|
||||
switches, please recompile controller(8) with larger limits, or use a
|
||||
different controller such as nox. A patch to controller(8) is included
|
||||
as util/openflow-patches/controller.patch.
|
||||
|
||||
5. Other software dependencies
|
||||
|
||||
util/install.sh -fnv
|
||||
On Ubuntu and Debian, other Mininet dependencies may be installed using
|
||||
the '-n' option of the install.sh script.
|
||||
|
||||
* enable and start openvswitch
|
||||
To run the iperf test, you need to install iperf:
|
||||
|
||||
sudo systemctl enable openvswitch
|
||||
sudo systemctl start openvswitch
|
||||
sudo aptitude/yum install iperf
|
||||
|
||||
* test the mininet installation
|
||||
We assume you already have ping installed. ;-)
|
||||
|
||||
To use xterm or sshd with Mininet, you need the following:
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||
|
||||
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
|
||||
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
|
||||
|
||||
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
|
||||
time install-mininet-vm.sh
|
||||
|
||||
Finally, verify that Mininet is installed and working in the VM:
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
5. Installation on other Linux distributions
|
||||
|
||||
Although we don't support other Linux distributions directly, it
|
||||
should be possible to install and run Mininet with some degree of
|
||||
manual effort.
|
||||
|
||||
In general, you must have:
|
||||
|
||||
* A Linux kernel compiled with network namespace support enabled
|
||||
|
||||
* An compatible software switch such as Open vSwitch or
|
||||
the Linux bridge.
|
||||
|
||||
* Python, `bash`, `ping`, `iperf`, etc.
|
||||
|
||||
* Root privileges (required for network device access)
|
||||
|
||||
We encourage contribution of patches to the `install.sh` script to
|
||||
support other Linux distributions.
|
||||
sudo aptitude/yum install sshd xterm screen
|
||||
|
||||
Some examples may have additional requirements - consult the specific
|
||||
example file for details.
|
||||
|
||||
The install.sh script has an '-x' option to install the version of
|
||||
NOX from the OpenFlow tutorial.
|
||||
|
||||
6. Other notes and recommendations
|
||||
|
||||
If you did not install certain useful packages and you wish to later,
|
||||
it may be possible to install them using install.sh.
|
||||
|
||||
Mininet should be run either on a machine with
|
||||
no other important processes, or on a virtual machine (recommended!)
|
||||
|
||||
Multiple concurrent Mininet instances are not supported!
|
||||
|
||||
Good luck!
|
||||
|
||||
Mininet Team
|
||||
|
||||
---
|
||||
|
||||
Historical information on OpenFlow 0.8.9 and the reference kernel module:
|
||||
|
||||
The kernel reference implementation has been deprecated, but it may
|
||||
be possible to get it work with Mininet.
|
||||
|
||||
To switch to the most recent OpenFlow 0.8.9 release branch (the most
|
||||
recent one with full NOX support and kernel datapath support) in your
|
||||
OpenFlow git tree:
|
||||
|
||||
git checkout -b release/0.8.9 remotes/origin/release/0.8.9
|
||||
|
||||
A patch to enable datapath.c to compile with recent kernels
|
||||
is included in util/openflow-patches/datapath.patch.
|
||||
|
||||
In OpenFlow 1.0, switch port numbering starts at 1 (for better or for worse.)
|
||||
To run with previous versions of OpenFlow, it may be necessary
|
||||
to change SWITCH_PORT_BASE from 1 to 0 in node.py.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
Mininet 2.3.0d4 License
|
||||
Mininet 1.0.0 License
|
||||
|
||||
Copyright (c) 2013-2018 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,34 +1,21 @@
|
||||
MININET = mininet/*.py
|
||||
TEST = mininet/test/*.py
|
||||
EXAMPLES = mininet/examples/*.py
|
||||
MN = bin/mn
|
||||
PYTHON ?= python
|
||||
PYMN = $(PYTHON) -B bin/mn
|
||||
BIN = $(MN)
|
||||
EXAMPLES = examples/*.py
|
||||
BIN = bin/mn
|
||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||
MNEXEC = mnexec
|
||||
MANPAGES = mn.1 mnexec.1
|
||||
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
|
||||
PREFIX ?= /usr
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
MANDIR ?= $(PREFIX)/share/man/man1
|
||||
DOCDIRS = doc/html doc/latex
|
||||
PDF = doc/latex/refman.pdf
|
||||
|
||||
CFLAGS += -Wall -Wextra
|
||||
P8IGN = E251,E201,E302,E202
|
||||
|
||||
all: codecheck test
|
||||
|
||||
clean:
|
||||
rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS)
|
||||
rm -rf build dist *.egg-info *.pyc $(MNEXEC)
|
||||
|
||||
codecheck: $(PYSRC)
|
||||
-echo "Running code check"
|
||||
util/versioncheck.py
|
||||
pyflakes $(PYSRC)
|
||||
pylint --rcfile=.pylint $(PYSRC)
|
||||
# Exclude miniedit from pep8 checking for now
|
||||
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
|
||||
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
|
||||
|
||||
errcheck: $(PYSRC)
|
||||
-echo "Running check for errors only"
|
||||
@@ -38,43 +25,15 @@ errcheck: $(PYSRC)
|
||||
test: $(MININET) $(TEST)
|
||||
-echo "Running tests"
|
||||
mininet/test/test_nets.py
|
||||
mininet/test/test_hifi.py
|
||||
|
||||
slowtest: $(MININET)
|
||||
-echo "Running slower tests (walkthrough, examples)"
|
||||
mininet/test/test_walkthrough.py -v
|
||||
mininet/examples/test/runner.py -v
|
||||
install: $(MNEXEC)
|
||||
install $(MNEXEC) /usr/local/bin/
|
||||
python setup.py install
|
||||
|
||||
mnexec: mnexec.c $(MN) mininet/net.py
|
||||
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
|
||||
develop: $(MNEXEC)
|
||||
install $(MNEXEC) /usr/local/bin/
|
||||
python setup.py develop
|
||||
|
||||
install-mnexec: $(MNEXEC)
|
||||
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
|
||||
doc:
|
||||
doxygen doxygen.cfg
|
||||
|
||||
install-manpages: $(MANPAGES)
|
||||
install -D -t $(MANDIR) $(MANPAGES)
|
||||
|
||||
install: install-mnexec install-manpages
|
||||
$(PYTHON) setup.py install
|
||||
|
||||
develop: $(MNEXEC) $(MANPAGES)
|
||||
# Perhaps we should link these as well
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
$(PYTHON) setup.py develop
|
||||
|
||||
man: $(MANPAGES)
|
||||
|
||||
mn.1: $(MN)
|
||||
PYTHONPATH=. help2man -N -n "create a Mininet network." \
|
||||
--no-discard-stderr "$(PYMN)" -o $@
|
||||
|
||||
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,132 +0,0 @@
|
||||
Mininet: Rapid Prototyping for Software Defined Networks
|
||||
========================================================
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
Mininet 2.3.0d4
|
||||
|
||||
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
|
||||
|
||||
### What is Mininet?
|
||||
|
||||
Mininet emulates a complete network of hosts, links, and switches
|
||||
on a single machine. To create a sample two-host, one-switch network,
|
||||
just run:
|
||||
|
||||
`sudo mn`
|
||||
|
||||
Mininet is useful for interactive development, testing, and demos,
|
||||
especially those using OpenFlow and SDN. OpenFlow-based network
|
||||
controllers prototyped in Mininet can usually be transferred to
|
||||
hardware with minimal changes for full line-rate execution.
|
||||
|
||||
### How does it work?
|
||||
|
||||
Mininet creates virtual networks using process-based virtualization
|
||||
and network namespaces - features that are available in recent Linux
|
||||
kernels. In Mininet, hosts are emulated as `bash` processes running in
|
||||
a network namespace, so any code that would normally run on a Linux
|
||||
server (like a web server or client program) should run just fine
|
||||
within a Mininet "Host". The Mininet "Host" will have its own private
|
||||
network interface and can only see its own processes. Switches in
|
||||
Mininet are software-based switches like Open vSwitch or the OpenFlow
|
||||
reference switch. Links are virtual ethernet pairs, which live in the
|
||||
Linux kernel and connect our emulated switches to emulated hosts
|
||||
(processes).
|
||||
|
||||
### Features
|
||||
|
||||
Mininet includes:
|
||||
|
||||
* A command-line launcher (`mn`) to instantiate networks.
|
||||
|
||||
* A handy Python API for creating networks of varying sizes and
|
||||
topologies.
|
||||
|
||||
* Examples (in the `examples/` directory) to help you get started.
|
||||
|
||||
* Full API documentation via Python `help()` docstrings, as well as
|
||||
the ability to generate PDF/HTML documentation with `make doc`.
|
||||
|
||||
* Parametrized topologies (`Topo` subclasses) using the Mininet
|
||||
object. For example, a tree network may be created with the
|
||||
command:
|
||||
|
||||
`mn --topo tree,depth=2,fanout=3`
|
||||
|
||||
* A command-line interface (`CLI` class) which provides useful
|
||||
diagnostic commands (like `iperf` and `ping`), as well as the
|
||||
ability to run a command to a node. For example,
|
||||
|
||||
`mininet> h11 ifconfig -a`
|
||||
|
||||
tells host h11 to run the command `ifconfig -a`
|
||||
|
||||
* A "cleanup" command to get rid of junk (interfaces, processes, files
|
||||
in /tmp, etc.) which might be left around by Mininet or Linux. Try
|
||||
this if things stop working!
|
||||
|
||||
`mn -c`
|
||||
|
||||
### New features in this release
|
||||
|
||||
This is primarily a performance improvement and bug fix release.
|
||||
|
||||
- Batch startup has been implemented for Open vSwitch, improving
|
||||
startup performance.
|
||||
|
||||
- OVS patch links have been implemented via OVSLink and --link ovs
|
||||
|
||||
Warning! These links have *serious limitations* compared to
|
||||
virtual Ethernet pairs: they are not attached to real Linux
|
||||
interfaces so you cannot use tcpdump or wireshark with them;
|
||||
they also cannot be used in long chains - we don't recommend more
|
||||
than 64 OVSLinks, for example --linear,64. However, they can offer
|
||||
significantly better performance than veth pairs, for certain
|
||||
configurations.
|
||||
|
||||
- You can now easily install Mininet on a Raspberry Pi ;-)
|
||||
|
||||
- Additional information for this release and previous releases
|
||||
may be found in the release notes on docs.mininet.org
|
||||
|
||||
### Installation
|
||||
|
||||
See `INSTALL` for installation instructions and details.
|
||||
|
||||
### Documentation
|
||||
|
||||
In addition to the API documentation (`make doc`), much useful
|
||||
information, including a Mininet walkthrough and an introduction
|
||||
to the Python API, is available on the
|
||||
[Mininet Web Site](http://mininet.org).
|
||||
There is also a wiki which you are encouraged to read and to
|
||||
contribute to, particularly the Frequently Asked Questions (FAQ.)
|
||||
|
||||
### Support
|
||||
|
||||
Mininet is community-supported. We encourage you to join the
|
||||
Mininet mailing list, `mininet-discuss` at:
|
||||
|
||||
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
|
||||
|
||||
### Join Us
|
||||
|
||||
Thanks again to all of the Mininet contributors!
|
||||
|
||||
Mininet is an open source project and is currently hosted
|
||||
at <https://github.com/mininet>. You are encouraged to download
|
||||
the code, examine it, modify it, and submit bug reports, bug fixes,
|
||||
feature requests, new features and other issues and pull requests.
|
||||
Thanks to everyone who has contributed code to the Mininet project
|
||||
(see CONTRIBUTORS for more info!) It is because of everyone's
|
||||
hard work that Mininet continues to grow and improve.
|
||||
|
||||
### Enjoy Mininet
|
||||
|
||||
Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
|
||||
Bob Lantz
|
||||
Mininet Core Team
|
||||
|
||||
[1]: https://travis-ci.org/mininet/mininet.svg?branch=master
|
||||
@@ -12,148 +12,79 @@ 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
|
||||
import mininet.cli
|
||||
from mininet.log import lg, LEVELS, info, debug, warn, error, output
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, LEVELS, info, warn
|
||||
from mininet.net import Mininet, MininetWithControlNet
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
Ryu, NOX, RemoteController, findController,
|
||||
DefaultController, NullController,
|
||||
UserSwitch, OVSSwitch, OVSBridge,
|
||||
IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink, TCULink, OVSLink
|
||||
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
|
||||
SingleSwitchReversedTopo, MinimalTopo )
|
||||
from mininet.topolib import TreeTopo, TorusTopo
|
||||
from mininet.util import customClass, specialClass, splitArgs
|
||||
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
|
||||
OVSLegacyKernelSwitch )
|
||||
from mininet.link import Link, TCLink
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import makeNumeric, custom, customConstructor, splitArgs
|
||||
from mininet.util import buildTopo
|
||||
|
||||
from functools import partial
|
||||
|
||||
# Experimental! cluster edition prototype
|
||||
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
|
||||
RemoteOVSSwitch, RemoteLink,
|
||||
SwitchBinPlacer, RandomPlacer,
|
||||
ClusterCleanup )
|
||||
from mininet.examples.clustercli import ClusterCLI
|
||||
|
||||
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
|
||||
|
||||
# built in topologies, created only when run
|
||||
TOPODEF = 'minimal'
|
||||
TOPOS = { 'minimal': MinimalTopo,
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo,
|
||||
'torus': TorusTopo }
|
||||
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo }
|
||||
|
||||
SWITCHDEF = 'default'
|
||||
SWITCHDEF = 'ovsk'
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
'ovs': OVSSwitch,
|
||||
'ovsbr' : OVSBridge,
|
||||
# Keep ovsk for compatibility with 2.0
|
||||
'ovsk': OVSSwitch,
|
||||
'ivs': IVSSwitch,
|
||||
'lxbr': LinuxBridge,
|
||||
'default': OVSSwitch }
|
||||
'ovsk': OVSKernelSwitch,
|
||||
'ovsl': OVSLegacyKernelSwitch }
|
||||
|
||||
HOSTDEF = 'proc'
|
||||
HOSTS = { 'proc': Host,
|
||||
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
|
||||
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
|
||||
'rt': custom( CPULimitedHost, sched='rt' ),
|
||||
'cfs': custom( CPULimitedHost, sched='cfs' ) }
|
||||
|
||||
CONTROLLERDEF = 'default'
|
||||
CONTROLLERDEF = 'ref'
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'ryu': Ryu,
|
||||
'default': DefaultController, # Note: overridden below
|
||||
'none': NullController }
|
||||
'none': lambda name: None }
|
||||
|
||||
LINKDEF = 'default'
|
||||
LINKS = { 'default': Link, # Note: overridden below
|
||||
'tc': TCLink,
|
||||
'tcu': TCULink,
|
||||
'ovs': OVSLink }
|
||||
LINKS = { 'default': Link,
|
||||
'tc': TCLink }
|
||||
|
||||
# TESTS dict can contain functions and/or Mininet() method names
|
||||
# XXX: it would be nice if we could specify a default test, but
|
||||
# this may be tricky
|
||||
TESTS = { name: True
|
||||
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
|
||||
|
||||
CLI = None # Set below if needed
|
||||
# optional tests to run
|
||||
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||
'none' ]
|
||||
|
||||
# Locally defined tests
|
||||
def allTest( net ):
|
||||
"Run ping and iperf tests"
|
||||
net.waitConnected()
|
||||
net.start()
|
||||
net.ping()
|
||||
net.iperf()
|
||||
|
||||
def nullTest( _net ):
|
||||
"Null 'test' (does nothing)"
|
||||
pass
|
||||
|
||||
TESTS.update( all=allTest, none=nullTest, build=nullTest )
|
||||
|
||||
# Map to alternate spellings of Mininet() methods
|
||||
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp' }
|
||||
|
||||
def runTests( mn, options ):
|
||||
"""Run tests
|
||||
mn: Mininet object
|
||||
option: list of test optinos """
|
||||
# Split option into test name and parameters
|
||||
for option in options:
|
||||
# Multiple tests may be separated by '+' for now
|
||||
for test in option.split( '+' ):
|
||||
test, args, kwargs = splitArgs( test )
|
||||
test = ALTSPELLING.get( test.lower(), test )
|
||||
testfn = TESTS.get( test, test )
|
||||
if callable( testfn ):
|
||||
testfn( mn, *args, **kwargs )
|
||||
elif hasattr( mn, test ):
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )( *args, **kwargs )
|
||||
else:
|
||||
raise Exception( 'Test %s is unknown - please specify one of '
|
||||
'%s ' % ( test, TESTS.keys() ) )
|
||||
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
"""Convenience function to add choices dicts to OptionParser.
|
||||
opts: OptionParser instance
|
||||
choicesDict: dictionary of valid choices, must include default
|
||||
default: default choice key
|
||||
name: long option name
|
||||
kwargs: additional arguments to add_option"""
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
helpList = [ '%s=%s' % ( k, v.__name__ )
|
||||
for k, v in choicesDict.items() ]
|
||||
helpStr += ' ' + ( ' '.join( helpList ) )
|
||||
params = dict( type='string', default=default, help=helpStr )
|
||||
params.update( **kwargs )
|
||||
opts.add_option( '--' + name, **params )
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
output( "%s\n" % VERSION )
|
||||
exit()
|
||||
help: string"""
|
||||
if default not in choicesDict:
|
||||
raise Exception( 'Invalid default %s for choices dict: %s' %
|
||||
( default, name ) )
|
||||
if not helpStr:
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
opts.add_option( '--' + name,
|
||||
type='string',
|
||||
default = default,
|
||||
help = helpStr )
|
||||
|
||||
|
||||
class MininetRunner( object ):
|
||||
@@ -169,35 +100,9 @@ class MininetRunner( object ):
|
||||
self.setup()
|
||||
self.begin()
|
||||
|
||||
def custom( self, _option, _opt_str, value, _parser ):
|
||||
"""Parse custom file and add params.
|
||||
option: option e.g. --custom
|
||||
opt_str: option string e.g. --custom
|
||||
value: the value the follows the option
|
||||
parser: option parser instance"""
|
||||
files = []
|
||||
if os.path.isfile( value ):
|
||||
# Accept any single file (including those with commas)
|
||||
files.append( value )
|
||||
else:
|
||||
# Accept a comma-separated list of filenames
|
||||
files += value.split(',')
|
||||
|
||||
for fileName in files:
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
# pylint: disable=exec-used
|
||||
exec( compile( open( fileName ).read(), fileName, 'exec' ),
|
||||
customs, customs )
|
||||
for name, val in customs.items():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
def setCustom( self, name, value ):
|
||||
"Set custom parameters for MininetRunner."
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links'
|
||||
'testnames', 'tests' ):
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
|
||||
# Update dictionaries
|
||||
param = name.upper()
|
||||
globals()[ param ].update( value )
|
||||
@@ -208,203 +113,140 @@ class MininetRunner( object ):
|
||||
# Add or modify global variable or class
|
||||
globals()[ name ] = value
|
||||
|
||||
def setNat( self, _option, opt_str, value, parser ):
|
||||
"Set NAT option(s)"
|
||||
assert self # satisfy pylint
|
||||
parser.values.nat = True
|
||||
# first arg, first char != '-'
|
||||
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-':
|
||||
value = parser.rargs.pop( 0 )
|
||||
_, args, kwargs = splitArgs( opt_str + ',' + value )
|
||||
parser.values.nat_args = args
|
||||
parser.values.nat_kwargs = kwargs
|
||||
def parseCustomFile( self, fileName ):
|
||||
"Parse custom file and add params before parsing cmd-line options."
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
parser.values.nat_args = []
|
||||
parser.values.nat_kwargs = {}
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
def parseArgs( self ):
|
||||
"""Parse command-line args and return options object.
|
||||
returns: opts parse options dict"""
|
||||
if '--custom' in sys.argv:
|
||||
index = sys.argv.index( '--custom' )
|
||||
if len( sys.argv ) > index + 1:
|
||||
filename = sys.argv[ index + 1 ]
|
||||
self.parseCustomFile( filename )
|
||||
else:
|
||||
raise Exception( 'Custom file name not found' )
|
||||
|
||||
desc = ( "The %prog utility creates Mininet network from the\n"
|
||||
"command line. It can create parametrized topologies,\n"
|
||||
"invoke the Mininet CLI, and run tests." )
|
||||
|
||||
usage = ( '%prog [options]\n'
|
||||
'(type %prog -h for details)' )
|
||||
|
||||
opts = OptionParser( description=desc, usage=usage )
|
||||
opts = OptionParser()
|
||||
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
||||
addDictOption( opts, CONTROLLERS, [], 'controller', action='append' )
|
||||
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
||||
addDictOption( opts, LINKS, LINKDEF, 'link' )
|
||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||
|
||||
opts.add_option( '--clean', '-c', action='store_true',
|
||||
default=False, help='clean and exit' )
|
||||
opts.add_option( '--custom', action='callback',
|
||||
callback=self.custom,
|
||||
type='string',
|
||||
help='read custom classes or params from .py file(s)'
|
||||
)
|
||||
opts.add_option( '--test', default=[], action='append',
|
||||
dest='test', help='|'.join( TESTS.keys() ) )
|
||||
default=False, help='clean and exit' )
|
||||
opts.add_option( '--custom', type='string', default=None,
|
||||
help='read custom topo and node params from .py file' )
|
||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||
default=TESTS[ 0 ],
|
||||
help='|'.join( TESTS ) )
|
||||
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=list( LEVELS.keys() ), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6654,
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6635,
|
||||
help='base port for passive switch listening' )
|
||||
opts.add_option( '--nolistenport', action='store_true',
|
||||
default=False, help="don't use passive listening " +
|
||||
"port")
|
||||
default=False, help="don't use passive listening port")
|
||||
opts.add_option( '--pre', type='string', default=None,
|
||||
help='CLI script to run before tests' )
|
||||
help='CLI script to run before tests' )
|
||||
opts.add_option( '--post', type='string', default=None,
|
||||
help='CLI script to run after tests' )
|
||||
help='CLI script to run after tests' )
|
||||
opts.add_option( '--prefixlen', type='int', default=8,
|
||||
help='prefix length (e.g. /8) for automatic '
|
||||
'network configuration' )
|
||||
opts.add_option( '--pin', action='store_true',
|
||||
default=False, help="pin hosts to CPU cores "
|
||||
"(requires --host cfs or --host rt)" )
|
||||
opts.add_option( '--nat', action='callback', callback=self.setNat,
|
||||
help="[option=val...] adds a NAT to the topology that"
|
||||
" connects Mininet hosts to the physical network."
|
||||
" Warning: This may route any traffic on the machine"
|
||||
" that uses Mininet's"
|
||||
" IP subnet into the Mininet network."
|
||||
" If you need to change"
|
||||
" Mininet's IP subnet, see the --ipbase option." )
|
||||
opts.add_option( '--version', action='callback', callback=version,
|
||||
help='prints the version and exits' )
|
||||
opts.add_option( '--cluster', type='string', default=None,
|
||||
metavar='server1,server2...',
|
||||
help=( 'run on multiple servers (experimental!)' ) )
|
||||
opts.add_option( '--placement', type='choice',
|
||||
choices=list( PLACEMENT.keys() ), default='block',
|
||||
metavar='block|random',
|
||||
help=( 'node placement for --cluster '
|
||||
'(experimental!) ' ) )
|
||||
|
||||
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."
|
||||
|
||||
# set logging verbosity
|
||||
if LEVELS[self.options.verbosity] > LEVELS['output']:
|
||||
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
'output!\n'
|
||||
'Please restart Mininet with -v [debug, info, output].\n'
|
||||
'Please restart Mininet with -v [debug, info, output].'
|
||||
% self.options.verbosity )
|
||||
lg.setLogLevel( self.options.verbosity )
|
||||
|
||||
# Maybe we'll reorganize this someday...
|
||||
# pylint: disable=too-many-branches,too-many-statements,global-statement
|
||||
|
||||
def begin( self ):
|
||||
"Create and run mininet."
|
||||
|
||||
global CLI
|
||||
|
||||
opts = self.options
|
||||
|
||||
if opts.cluster:
|
||||
servers = opts.cluster.split( ',' )
|
||||
for server in servers:
|
||||
ClusterCleanup.add( server )
|
||||
|
||||
if opts.clean:
|
||||
if self.options.clean:
|
||||
cleanup()
|
||||
exit()
|
||||
|
||||
start = time.time()
|
||||
|
||||
if not opts.controller:
|
||||
# Update default based on available controllers
|
||||
CONTROLLERS[ 'default' ] = findController()
|
||||
opts.controller = [ 'default' ]
|
||||
if not CONTROLLERS[ 'default' ]:
|
||||
opts.controller = [ 'none' ]
|
||||
if opts.switch == 'default':
|
||||
info( '*** No default OpenFlow controller found '
|
||||
'for default switch!\n' )
|
||||
info( '*** Falling back to OVS Bridge\n' )
|
||||
opts.switch = 'ovsbr'
|
||||
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
raise Exception( "Could not find a default controller "
|
||||
"for switch %s" %
|
||||
opts.switch )
|
||||
|
||||
topo = buildTopo( TOPOS, opts.topo )
|
||||
switch = customClass( SWITCHES, opts.switch )
|
||||
host = customClass( HOSTS, opts.host )
|
||||
controller = [ customClass( CONTROLLERS, c )
|
||||
for c in opts.controller ]
|
||||
|
||||
if opts.switch == 'user' and opts.link == 'default':
|
||||
debug( '*** Using TCULink with UserSwitch\n' )
|
||||
# Use link configured correctly for UserSwitch
|
||||
opts.link = 'tcu'
|
||||
|
||||
link = customClass( LINKS, opts.link )
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customConstructor( SWITCHES, self.options.switch )
|
||||
host = customConstructor( HOSTS, self.options.host )
|
||||
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||
link = customConstructor( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( opts )
|
||||
|
||||
if opts.nolistenport:
|
||||
opts.listenport = None
|
||||
|
||||
# Handle innamespace, cluster options
|
||||
if opts.innamespace and opts.cluster:
|
||||
error( "Please specify --innamespace OR --cluster\n" )
|
||||
exit()
|
||||
Net = MininetWithControlNet if opts.innamespace else Mininet
|
||||
if opts.cluster:
|
||||
warn( '*** WARNING: Experimental cluster mode!\n'
|
||||
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
|
||||
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
|
||||
Net = partial( MininetCluster, servers=servers,
|
||||
placement=PLACEMENT[ opts.placement ] )
|
||||
mininet.cli.CLI = ClusterCLI
|
||||
self.validate( self.options )
|
||||
|
||||
inNamespace = self.options.innamespace
|
||||
Net = MininetWithControlNet if inNamespace else Mininet
|
||||
ipBase = self.options.ipbase
|
||||
xterms = self.options.xterms
|
||||
mac = self.options.mac
|
||||
arp = self.options.arp
|
||||
pin = self.options.pin
|
||||
listenPort = None
|
||||
if not self.options.nolistenport:
|
||||
listenPort = self.options.listenport
|
||||
mn = Net( topo=topo,
|
||||
switch=switch, host=host, controller=controller, link=link,
|
||||
ipBase=opts.ipbase, inNamespace=opts.innamespace,
|
||||
xterms=opts.xterms, autoSetMacs=opts.mac,
|
||||
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
|
||||
listenPort=opts.listenport )
|
||||
switch=switch, host=host, controller=controller,
|
||||
link=link,
|
||||
ipBase=ipBase,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, autoPinCpus=pin,
|
||||
listenPort=listenPort )
|
||||
|
||||
if opts.ensure_value( 'nat', False ):
|
||||
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
|
||||
if self.options.pre:
|
||||
CLI( mn, script=self.options.pre )
|
||||
|
||||
# --custom files can set CLI or change mininet.cli.CLI
|
||||
CLI = mininet.cli.CLI if CLI is None else CLI
|
||||
|
||||
if opts.pre:
|
||||
CLI( mn, script=opts.pre )
|
||||
test = self.options.test
|
||||
test = ALTSPELLING.get( test, test )
|
||||
|
||||
mn.start()
|
||||
|
||||
if opts.test:
|
||||
runTests( mn, opts.test )
|
||||
else:
|
||||
if test == 'none':
|
||||
pass
|
||||
elif test == 'all':
|
||||
mn.start()
|
||||
mn.ping()
|
||||
mn.iperf()
|
||||
elif test == 'cli':
|
||||
CLI( mn )
|
||||
elif test != 'build':
|
||||
getattr( mn, test )()
|
||||
|
||||
if opts.post:
|
||||
CLI( mn, script=opts.post )
|
||||
if self.options.post:
|
||||
CLI( mn, script=self.options.post )
|
||||
|
||||
mn.stop()
|
||||
|
||||
@@ -413,21 +255,4 @@ class MininetRunner( object ):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
MininetRunner()
|
||||
except KeyboardInterrupt:
|
||||
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
|
||||
cleanup()
|
||||
except Exception:
|
||||
# Print exception
|
||||
type_, val_, trace_ = sys.exc_info()
|
||||
errorMsg = ( "-"*80 + "\n" +
|
||||
"Caught exception. Cleaning up...\n\n" +
|
||||
"%s: %s\n" % ( type_.__name__, val_ ) +
|
||||
"-"*80 + "\n" )
|
||||
error( errorMsg )
|
||||
# Print stack trace to debug log
|
||||
import traceback
|
||||
stackTrace = traceback.format_exc()
|
||||
debug( stackTrace + "\n" )
|
||||
cleanup()
|
||||
MininetRunner()
|
||||
|
||||
+24
-13
@@ -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,84 @@
|
||||
|
||||
Mininet Examples
|
||||
|
||||
These examples are intended to help you get started using
|
||||
Mininet's Python API.
|
||||
|
||||
---
|
||||
|
||||
baresshd.py:
|
||||
|
||||
This example uses Mininet's medium-level API to create an sshd
|
||||
process running in a namespace. Doesn't use OpenFlow.
|
||||
|
||||
consoles.py:
|
||||
|
||||
This example creates a grid of console windows, one for each node,
|
||||
and allows interaction with and monitoring of each console, including
|
||||
graphical monitoring.
|
||||
|
||||
controllers.py:
|
||||
|
||||
This example creates a network and adds multiple controllers to it.
|
||||
|
||||
emptynet.py:
|
||||
|
||||
This example demonstrates creating an empty network (i.e. with no
|
||||
topology object) and adding nodes to it.
|
||||
|
||||
linearbandwidth.py:
|
||||
|
||||
This example shows how to create a custom topology programatically
|
||||
by subclassing Topo, and how to run a series of tests on it.
|
||||
|
||||
miniedit.py:
|
||||
|
||||
This example demonstrates creating a network via a graphical editor.
|
||||
|
||||
multiping.py:
|
||||
|
||||
This example demonstrates one method for
|
||||
monitoring output from multiple hosts, using node.monitor().
|
||||
|
||||
multipoll.py:
|
||||
|
||||
This example demonstrates monitoring output files from multiple hosts.
|
||||
|
||||
multitest.py:
|
||||
|
||||
This example creates a network and runs multiple tests on it.
|
||||
|
||||
popenpoll.py:
|
||||
|
||||
This example demonstrates monitoring output from multiple hosts using
|
||||
the node.popen() interface (which returns Popen objects) and pmonitor().
|
||||
|
||||
scratchnet.py, scratchnetuser.py:
|
||||
|
||||
These two examples demonstrate how to create a network by using the lowest-
|
||||
level Mininet functions. Generally the higher-level API is easier to use,
|
||||
but scratchnet shows what is going on behind the scenes.
|
||||
|
||||
simpleperf.py:
|
||||
|
||||
A simple example of configuring network and CPU bandwidth limits.
|
||||
|
||||
sshd.py:
|
||||
|
||||
This example shows how to run an sshd process in each host, allowing
|
||||
you to log in via ssh. This requires connecting the Mininet data network
|
||||
to an interface in the root namespace (generaly the control network
|
||||
already lives in the root namespace, so it does not need to be explicitly
|
||||
connected.)
|
||||
|
||||
treeping64.py:
|
||||
|
||||
This example creates a 64-host tree network, and attempts to check full
|
||||
connectivity using ping, for different switch/datapath types.
|
||||
|
||||
tree1024.py:
|
||||
|
||||
This example attempts to create a 1024-host network, and then runs the
|
||||
CLI on it. It may run into scalability limits, depending on available
|
||||
memory and sysctl configuration (see INSTALL.)
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
Mininet Examples
|
||||
========================================================
|
||||
|
||||
These examples are intended to help you get started using
|
||||
Mininet's Python API.
|
||||
|
||||
========================================================
|
||||
|
||||
#### baresshd.py:
|
||||
|
||||
This example uses Mininet's medium-level API to create an sshd
|
||||
process running in a namespace. Doesn't use OpenFlow.
|
||||
|
||||
#### bind.py:
|
||||
|
||||
This example shows how you can create private directories for each
|
||||
node in a Mininet topology.
|
||||
|
||||
#### cluster.py:
|
||||
|
||||
This example contains all of the code for experimental cluster
|
||||
edition. Remote classes and MininetCluster can be imported from
|
||||
here to create a topology with nodes on remote machines.
|
||||
|
||||
#### clusterSanity.py:
|
||||
|
||||
This example runs cluster edition locally as a sanity check to test
|
||||
basic functionality.
|
||||
|
||||
#### clustercli.py:
|
||||
|
||||
This example contains a CLI for experimental cluster edition.
|
||||
|
||||
#### clusterdemo.py:
|
||||
|
||||
This example is a basic demo of cluster edition on 3 servers with
|
||||
a tree topology of depth 3 and fanout 3.
|
||||
|
||||
#### consoles.py:
|
||||
|
||||
This example creates a grid of console windows, one for each node,
|
||||
and allows interaction with and monitoring of each console, including
|
||||
graphical monitoring.
|
||||
|
||||
#### controllers.py:
|
||||
|
||||
This example creates a network with multiple controllers, by
|
||||
using a custom `Switch()` subclass.
|
||||
|
||||
#### controllers2.py:
|
||||
|
||||
This example creates a network with multiple controllers by
|
||||
creating an empty network, adding nodes to it, and manually
|
||||
starting the switches.
|
||||
|
||||
#### controlnet.py:
|
||||
|
||||
This examples shows how you can model the control network as well
|
||||
as the data network, by actually creating two Mininet objects.
|
||||
|
||||
#### cpu.py:
|
||||
|
||||
This example tests iperf bandwidth for varying CPU limits.
|
||||
|
||||
#### emptynet.py:
|
||||
|
||||
This example demonstrates creating an empty network (i.e. with no
|
||||
topology object) and adding nodes to it.
|
||||
|
||||
#### hwintf.py:
|
||||
|
||||
This example shows how to add an interface (for example a real
|
||||
hardware interface) to a network after the network is created.
|
||||
|
||||
#### intfoptions.py:
|
||||
|
||||
This example reconfigures a TCIntf during runtime with different
|
||||
traffic control commands to test bandwidth, loss, and delay.
|
||||
|
||||
#### limit.py:
|
||||
|
||||
This example shows how to use link and CPU limits.
|
||||
|
||||
#### linearbandwidth.py:
|
||||
|
||||
This example shows how to create a custom topology programatically
|
||||
by subclassing Topo, and how to run a series of tests on it.
|
||||
|
||||
#### linuxrouter.py:
|
||||
|
||||
This example shows how to create and configure a router in Mininet
|
||||
that uses Linux IP forwarding.
|
||||
|
||||
#### miniedit.py:
|
||||
|
||||
This example demonstrates creating a network via a graphical editor.
|
||||
|
||||
#### mobility.py:
|
||||
|
||||
This example demonstrates detaching an interface from one switch and
|
||||
attaching it another as a basic way to move a host around a network.
|
||||
|
||||
#### multiLink.py:
|
||||
|
||||
This example demonstrates the creation of multiple links between
|
||||
nodes using a custom Topology class.
|
||||
|
||||
#### multiping.py:
|
||||
|
||||
This example demonstrates one method for
|
||||
monitoring output from multiple hosts, using `node.monitor()`.
|
||||
|
||||
#### multipoll.py:
|
||||
|
||||
This example demonstrates monitoring output files from multiple hosts.
|
||||
|
||||
#### multitest.py:
|
||||
|
||||
This example creates a network and runs multiple tests on it.
|
||||
|
||||
#### nat.py:
|
||||
|
||||
This example shows how to connect a Mininet network to the Internet
|
||||
using NAT. It also answers the eternal question "why can't I ping
|
||||
`google.com`?"
|
||||
|
||||
#### natnet.py:
|
||||
|
||||
This example demonstrates how to create a network using a NAT node
|
||||
to connect hosts to the internet.
|
||||
|
||||
#### numberedports.py:
|
||||
|
||||
This example verifies the mininet ofport numbers match up to the ovs port numbers.
|
||||
It also verifies that the port numbers match up to the interface numbers
|
||||
|
||||
#### popen.py:
|
||||
|
||||
This example monitors a number of hosts using `host.popen()` and
|
||||
`pmonitor()`.
|
||||
|
||||
#### popenpoll.py:
|
||||
|
||||
This example demonstrates monitoring output from multiple hosts using
|
||||
the `node.popen()` interface (which returns `Popen` objects) and `pmonitor()`.
|
||||
|
||||
#### scratchnet.py, scratchnetuser.py:
|
||||
|
||||
These two examples demonstrate how to create a network by using the lowest-
|
||||
level Mininet functions. Generally the higher-level API is easier to use,
|
||||
but scratchnet shows what is going on behind the scenes.
|
||||
|
||||
#### simpleperf.py:
|
||||
|
||||
A simple example of configuring network and CPU bandwidth limits.
|
||||
|
||||
#### sshd.py:
|
||||
|
||||
This example shows how to run an `sshd` process in each host, allowing
|
||||
you to log in via `ssh`. This requires connecting the Mininet data network
|
||||
to an interface in the root namespace (generaly the control network
|
||||
already lives in the root namespace, so it does not need to be explicitly
|
||||
connected.)
|
||||
|
||||
#### tree1024.py:
|
||||
|
||||
This example attempts to create a 1024-host network, and then runs the
|
||||
CLI on it. It may run into scalability limits, depending on available
|
||||
memory and `sysctl` configuration (see `INSTALL`.)
|
||||
|
||||
#### treeping64.py:
|
||||
|
||||
This example creates a 64-host tree network, and attempts to check full
|
||||
connectivity using `ping`, for different switch/datapath types.
|
||||
|
||||
#### vlanhost.py:
|
||||
|
||||
An example of how to subclass Host to use a VLAN on its primary interface.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
"""
|
||||
Mininet Examples
|
||||
See README for details
|
||||
"""
|
||||
+8
-25
@@ -2,45 +2,28 @@
|
||||
|
||||
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
||||
|
||||
import sys
|
||||
|
||||
from mininet.node import Host
|
||||
from mininet.util import ensureRoot, waitListening
|
||||
from mininet.log import info, warn, output
|
||||
|
||||
|
||||
ensureRoot()
|
||||
timeout = 5
|
||||
|
||||
info( "*** Creating nodes\n" )
|
||||
print "*** Creating nodes"
|
||||
h1 = Host( 'h1' )
|
||||
|
||||
root = Host( 'root', inNamespace=False )
|
||||
|
||||
info( "*** Creating link\n" )
|
||||
print "*** Creating links"
|
||||
h1.linkTo( root )
|
||||
|
||||
info( h1 )
|
||||
print h1
|
||||
|
||||
info( "*** Configuring nodes\n" )
|
||||
print "*** Configuring nodes"
|
||||
h1.setIP( '10.0.0.1', 8 )
|
||||
root.setIP( '10.0.0.2', 8 )
|
||||
|
||||
info( "*** Creating banner file\n" )
|
||||
print "*** Creating banner file"
|
||||
f = open( '/tmp/%s.banner' % h1.name, 'w' )
|
||||
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
|
||||
f.close()
|
||||
|
||||
info( "*** Running sshd\n" )
|
||||
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
|
||||
# add arguments from the command line
|
||||
if len( sys.argv ) > 1:
|
||||
cmd += ' ' + ' '.join( sys.argv[ 1: ] )
|
||||
h1.cmd( cmd )
|
||||
listening = waitListening( server=h1, port=22, timeout=timeout )
|
||||
print "*** Running sshd"
|
||||
h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
|
||||
|
||||
if listening:
|
||||
output( "*** You may now ssh into", h1.name, "at", h1.IP(), '\n' )
|
||||
else:
|
||||
warn( "*** Warning: after %s seconds, %s is not listening on port 22"
|
||||
% ( timeout, h1.name ), '\n' )
|
||||
print "*** You may now ssh into", h1.name, "at", h1.IP()
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
bind.py: Bind mount example
|
||||
|
||||
This creates hosts with private directories that the user specifies.
|
||||
These hosts may have persistent directories that will be available
|
||||
across multiple mininet session, or temporary directories that will
|
||||
only last for one mininet session. To specify a persistent
|
||||
directory, add a tuple to a list of private directories:
|
||||
|
||||
[ ( 'directory to be mounted on', 'directory to be mounted' ) ]
|
||||
|
||||
String expansion may be used to create a directory template for
|
||||
each host. To do this, add a %(name)s in place of the host name
|
||||
when creating your list of directories:
|
||||
|
||||
[ ( '/var/run', '/tmp/%(name)s/var/run' ) ]
|
||||
|
||||
If no persistent directory is specified, the directories will default
|
||||
to temporary private directories. To do this, simply create a list of
|
||||
directories to be made private. A tmpfs will then be mounted on them.
|
||||
|
||||
You may use both temporary and persistent directories at the same
|
||||
time. In the following privateDirs string, each host will have a
|
||||
persistent directory in the root filesystem at
|
||||
"/tmp/(hostname)/var/run" mounted on "/var/run". Each host will also
|
||||
have a temporary private directory mounted on "/var/log".
|
||||
|
||||
[ ( '/var/run', '/tmp/%(name)s/var/run' ), '/var/log' ]
|
||||
|
||||
This example has both persistent directories mounted on '/var/log'
|
||||
and '/var/run'. It also has a temporary private directory mounted
|
||||
on '/var/mn'
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Host
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from functools import partial
|
||||
|
||||
|
||||
# Sample usage
|
||||
|
||||
def testHostWithPrivateDirs():
|
||||
"Test bind mounts"
|
||||
topo = SingleSwitchTopo( 10 )
|
||||
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
|
||||
( '/var/run', '/tmp/%(name)s/var/run' ),
|
||||
'/var/mn' ]
|
||||
host = partial( Host,
|
||||
privateDirs=privateDirs )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
net.start()
|
||||
directories = [ directory[ 0 ] if isinstance( directory, tuple )
|
||||
else directory for directory in privateDirs ]
|
||||
info( 'Private Directories:', directories, '\n' )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testHostWithPrivateDirs()
|
||||
info( 'Done.\n')
|
||||
-1001
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
A sanity check for cluster edition
|
||||
'''
|
||||
|
||||
from mininet.examples.cluster import MininetCluster
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.examples.clustercli import ClusterCLI as CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
|
||||
def clusterSanity():
|
||||
"Sanity check for cluster mode"
|
||||
topo = SingleSwitchTopo()
|
||||
net = MininetCluster( topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
clusterSanity()
|
||||
@@ -1,106 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"CLI for Mininet Cluster Edition prototype demo"
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import output, error
|
||||
|
||||
# pylint: disable=global-statement
|
||||
nx, graphviz_layout, plt = None, None, None # Will be imported on demand
|
||||
|
||||
|
||||
class ClusterCLI( CLI ):
|
||||
"CLI with additional commands for Cluster Edition demo"
|
||||
|
||||
@staticmethod
|
||||
def colorsFor( seq ):
|
||||
"Return a list of background colors for a sequence"
|
||||
colors = [ 'red', 'lightgreen', 'cyan', 'yellow', 'orange',
|
||||
'magenta', 'pink', 'grey', 'brown',
|
||||
'white' ]
|
||||
slen, clen = len( seq ), len( colors )
|
||||
reps = max( 1, slen / clen )
|
||||
colors = colors * reps
|
||||
colors = colors[ 0 : slen ]
|
||||
return colors
|
||||
|
||||
def do_plot( self, _line ):
|
||||
"Plot topology colored by node placement"
|
||||
# Import networkx if needed
|
||||
global nx, plt, graphviz_layout
|
||||
if not nx:
|
||||
try:
|
||||
# pylint: disable=import-error
|
||||
import networkx
|
||||
nx = networkx # satisfy pylint
|
||||
from matplotlib import pyplot
|
||||
plt = pyplot # satisfy pylint
|
||||
import pygraphviz
|
||||
assert pygraphviz # silence pyflakes
|
||||
# Networkx moved this around
|
||||
if hasattr( nx, 'graphviz_layout' ):
|
||||
graphviz_layout = nx.graphviz_layout
|
||||
else:
|
||||
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
|
||||
# pylint: enable=import-error
|
||||
except ImportError:
|
||||
error( 'plot requires networkx, matplotlib and pygraphviz - '
|
||||
'please install them and try again\n' )
|
||||
return
|
||||
# Make a networkx Graph
|
||||
g = nx.Graph()
|
||||
mn = self.mn
|
||||
servers = getattr( mn, 'servers', [ 'localhost' ] )
|
||||
hosts, switches = mn.hosts, mn.switches
|
||||
nodes = hosts + switches
|
||||
g.add_nodes_from( nodes )
|
||||
links = [ ( link.intf1.node, link.intf2.node )
|
||||
for link in self.mn.links ]
|
||||
g.add_edges_from( links )
|
||||
# Pick some shapes and colors
|
||||
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
|
||||
color = dict( zip( servers, self.colorsFor( servers ) ) )
|
||||
# Plot it!
|
||||
pos = graphviz_layout( g )
|
||||
opts = { 'ax': None, 'font_weight': 'bold',
|
||||
'width': 2, 'edge_color': 'darkblue' }
|
||||
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
|
||||
for h in hosts ]
|
||||
scolors = [ color[ getattr( s, 'server', 'localhost' ) ]
|
||||
for s in switches ]
|
||||
nx.draw_networkx( g, pos=pos, nodelist=hosts, node_size=800,
|
||||
label='host', node_color=hcolors, node_shape='s',
|
||||
**opts )
|
||||
nx.draw_networkx( g, pos=pos, nodelist=switches, node_size=1000,
|
||||
node_color=scolors, node_shape='o', **opts )
|
||||
# Get rid of axes, add title, and show
|
||||
fig = plt.gcf()
|
||||
ax = plt.gca()
|
||||
ax.get_xaxis().set_visible( False )
|
||||
ax.get_yaxis().set_visible( False )
|
||||
fig.canvas.set_window_title( 'Mininet')
|
||||
plt.title( 'Node Placement', fontweight='bold' )
|
||||
plt.show()
|
||||
|
||||
def do_status( self, _line ):
|
||||
"Report on node shell status"
|
||||
nodes = self.mn.hosts + self.mn.switches
|
||||
for node in nodes:
|
||||
node.shell.poll()
|
||||
exited = [ node for node in nodes
|
||||
if node.shell.returncode is not None ]
|
||||
if exited:
|
||||
for node in exited:
|
||||
output( '%s has exited with code %d\n'
|
||||
% ( node, node.shell.returncode ) )
|
||||
else:
|
||||
output( 'All nodes are still running.\n' )
|
||||
|
||||
def do_placement( self, _line ):
|
||||
"Describe node placement"
|
||||
mn = self.mn
|
||||
nodes = mn.hosts + mn.switches + mn.controllers
|
||||
for server in mn.servers:
|
||||
names = [ n.name for n in nodes if hasattr( n, 'server' )
|
||||
and n.server == server ]
|
||||
output( '%s: %s\n' % ( server, ' '.join( names ) ) )
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
|
||||
|
||||
from mininet.examples.cluster import ( MininetCluster, SwitchBinPlacer,
|
||||
RemoteLink )
|
||||
# ^ Could also use: RemoteSSHLink, RemoteGRELink
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.examples.clustercli import ClusterCLI as CLI
|
||||
|
||||
def demo():
|
||||
"Simple Demo of Cluster Mode"
|
||||
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers, Link=RemoteLink,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
demo()
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
|
||||
|
||||
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
def perf(Link):
|
||||
"Test connectivity nand performance over Link"
|
||||
net = Mininet( host=RemoteHost, link=Link )
|
||||
h1 = net.addHost( 'h1')
|
||||
h2 = net.addHost( 'h2', server='ubuntu2' )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.iperf()
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
perf( RemoteSSHLink )
|
||||
perf( RemoteGRELink )
|
||||
+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', port=6633 )
|
||||
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,62 +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, info
|
||||
|
||||
def multiControllerNet():
|
||||
"Create a network from semi-scratch with multiple controllers."
|
||||
|
||||
net = Mininet( controller=Controller, switch=OVSSwitch )
|
||||
|
||||
info( "*** Creating (reference) controllers\n" )
|
||||
c1 = net.addController( 'c1', port=6633 )
|
||||
c2 = net.addController( 'c2', port=6634 )
|
||||
|
||||
info( "*** Creating switches\n" )
|
||||
s1 = net.addSwitch( 's1' )
|
||||
s2 = net.addSwitch( 's2' )
|
||||
|
||||
info( "*** Creating hosts\n" )
|
||||
hosts1 = [ net.addHost( 'h%d' % n ) for n in ( 3, 4 ) ]
|
||||
hosts2 = [ net.addHost( 'h%d' % n ) for n in ( 5, 6 ) ]
|
||||
|
||||
info( "*** Creating links\n" )
|
||||
for h in hosts1:
|
||||
net.addLink( s1, h )
|
||||
for h in hosts2:
|
||||
net.addLink( s2, h )
|
||||
net.addLink( s1, s2 )
|
||||
|
||||
info( "*** Starting network\n" )
|
||||
net.build()
|
||||
c1.start()
|
||||
c2.start()
|
||||
s1.start( [ c1 ] )
|
||||
s2.start( [ c2 ] )
|
||||
|
||||
info( "*** Testing network\n" )
|
||||
net.pingAll()
|
||||
|
||||
info( "*** Running CLI\n" )
|
||||
CLI( net )
|
||||
|
||||
info( "*** Stopping network\n" )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' ) # for CLI output
|
||||
multiControllerNet()
|
||||
@@ -1,158 +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 and to delete intfs"""
|
||||
|
||||
def checkListening( self ):
|
||||
"Ignore spurious error"
|
||||
pass
|
||||
|
||||
def stop( self, *args, **kwargs ):
|
||||
"Make sure intfs are deleted"
|
||||
kwargs.update( deleteIntfs=True )
|
||||
super( DataController, self ).stop( *args, **kwargs )
|
||||
|
||||
|
||||
class MininetFacade( object ):
|
||||
"""Mininet object facade that allows a single CLI to
|
||||
talk to one or more networks"""
|
||||
|
||||
def __init__( self, net, *args, **kwargs ):
|
||||
"""Create MininetFacade object.
|
||||
net: Primary Mininet object
|
||||
args: unnamed networks passed as arguments
|
||||
kwargs: named networks passed as arguments"""
|
||||
self.net = net
|
||||
self.nets = [ net ] + list( args ) + list( kwargs.values() )
|
||||
self.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()
|
||||
+33
-59
@@ -1,41 +1,30 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
cpu.py: test iperf bandwidth for varying cpu limits
|
||||
|
||||
Since we are limiting the hosts (only), we should expect the iperf
|
||||
processes to be affected, as well as any system processing which is
|
||||
billed to the hosts.
|
||||
|
||||
We reserve >50% of cycles for system processing; we assume that
|
||||
this is enough for it not to affect results. Hosts are limited to
|
||||
40% of total cycles, which we assume is enough to make them CPU
|
||||
bound.
|
||||
|
||||
As CPU performance increases over time, we may have to reduce the
|
||||
overall CPU allocation so that the host processing is still CPU bound.
|
||||
This is perhaps an argument for specifying performance in a more
|
||||
system-independent manner.
|
||||
|
||||
It would also be nice to have a better handle on limiting packet
|
||||
processing cycles. It's not entirely clear to me how those are
|
||||
billed to user or system processes if we are using OVS with a kernel
|
||||
datapath. With a user datapath, they are easier to account for, but
|
||||
overall performance is usually lower.
|
||||
|
||||
Although the iperf client uses more CPU and should be CPU bound (?),
|
||||
we measure the received data at the server since the client transmit
|
||||
rate includes buffering.
|
||||
cpu.py: test iperf bandwidth for varying cpu limtis
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, waitListening, decode
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.clean import cleanup
|
||||
from mininet.util import custom
|
||||
from mininet.log import setLogLevel, output
|
||||
|
||||
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
from time import sleep
|
||||
|
||||
def waitListening(client, server, port):
|
||||
"Wait until server is listening on port"
|
||||
if not client.cmd('which telnet'):
|
||||
raise Exception('Could not find telnet')
|
||||
cmd = ('sh -c "echo A | telnet -e A %s %s"' %
|
||||
(server.IP(), port))
|
||||
while 'Connected' not in client.cmd(cmd):
|
||||
output('waiting for', server,
|
||||
'to listen on port', port, '\n')
|
||||
sleep(.5)
|
||||
|
||||
|
||||
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
cpu: cpu limit as fraction of overall CPU time"""
|
||||
|
||||
@@ -44,36 +33,22 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
results = {}
|
||||
|
||||
for sched in 'rt', 'cfs':
|
||||
info( '*** Testing with', sched, 'bandwidth limiting\n' )
|
||||
print '*** Testing with', sched, 'bandwidth limiting'
|
||||
for cpu in cpuLimits:
|
||||
# cpu is the cpu fraction for all hosts, so we divide
|
||||
# it across two hosts
|
||||
host = custom( CPULimitedHost, sched=sched,
|
||||
period_us=period_us,
|
||||
cpu=.5*cpu )
|
||||
try:
|
||||
net = Mininet( topo=topo, host=host )
|
||||
# pylint: disable=bare-except
|
||||
except:
|
||||
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
|
||||
cleanup()
|
||||
break
|
||||
cpu=cpu )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
|
||||
client, server = hosts[ 0 ], hosts[ -1 ]
|
||||
info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' %
|
||||
( 100.0 * cpu ) )
|
||||
# We measure at the server because it doesn't include
|
||||
# the client's buffer fill rate
|
||||
popen = server.popen( 'iperf -yc -s -p 5001' )
|
||||
server.cmd( 'iperf -s -p 5001 &' )
|
||||
waitListening( client, server, 5001 )
|
||||
# ignore empty result from waitListening/telnet
|
||||
popen.stdout.readline()
|
||||
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
|
||||
result = decode( popen.stdout.readline() ).split( ',' )
|
||||
result = client.cmd( 'iperf -yc -t %s -c %s' % (
|
||||
seconds, server.IP() ) ).split( ',' )
|
||||
bps = float( result[ -1 ] )
|
||||
popen.terminate()
|
||||
server.cmdPrint( 'kill %iperf' )
|
||||
net.stop()
|
||||
updated = results.get( sched, [] )
|
||||
updated += [ ( cpu, bps ) ]
|
||||
@@ -85,23 +60,22 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
def dump( results ):
|
||||
"Dump results"
|
||||
|
||||
fmt = '%s\t%s\t%s\n'
|
||||
fmt = '%s\t%s\t%s'
|
||||
|
||||
info( '\n' )
|
||||
info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) )
|
||||
print
|
||||
print fmt % ( 'sched', 'cpu', 'client MB/s' )
|
||||
print
|
||||
|
||||
for sched in sorted( results.keys() ):
|
||||
entries = results[ sched ]
|
||||
for cpu, bps in entries:
|
||||
pct = '%d%%' % ( cpu * 100 )
|
||||
mbps = '%.2e' % bps
|
||||
info( fmt % ( sched, pct, mbps ) )
|
||||
pct = '%.2f%%' % ( cpu * 100 )
|
||||
mbps = bps / 1e6
|
||||
print fmt % ( sched, pct, mbps )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# These are the limits for the hosts/iperfs - the
|
||||
# rest is for system processes
|
||||
limits = [ .5, .4, .3, .2, .1 ]
|
||||
limits = [ .45, .4, .3, .2, .1 ]
|
||||
out = bwtest( limits )
|
||||
dump( out )
|
||||
|
||||
@@ -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()
|
||||
|
||||
+8
-18
@@ -6,47 +6,37 @@ hardware interface) to a network after the network is created.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel, info, error
|
||||
from mininet.net import Mininet
|
||||
from mininet.link import Intf
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import quietRun
|
||||
|
||||
def checkIntf( intf ):
|
||||
"Make sure intf exists and is not configured."
|
||||
config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True )
|
||||
if not config:
|
||||
if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
|
||||
error( 'Error:', intf, 'does not exist!\n' )
|
||||
exit( 1 )
|
||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', config )
|
||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
|
||||
if ips:
|
||||
error( 'Error:', intf, 'has an IP address,'
|
||||
'and is probably in use!\n' )
|
||||
'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 )
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
'''
|
||||
example of using various TCIntf options.
|
||||
reconfigures a single interface using intf.config()
|
||||
to use different traffic control commands to test
|
||||
bandwidth, loss, and delay
|
||||
'''
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.link import TCLink
|
||||
|
||||
def intfOptions():
|
||||
"run various traffic control commands on a single interface"
|
||||
net = Mininet( autoStaticArp=True )
|
||||
net.addController( 'c0' )
|
||||
h1 = net.addHost( 'h1' )
|
||||
h2 = net.addHost( 'h2' )
|
||||
s1 = net.addSwitch( 's1' )
|
||||
link1 = net.addLink( h1, s1, cls=TCLink )
|
||||
net.addLink( h2, s1 )
|
||||
net.start()
|
||||
|
||||
# flush out latency from reactive forwarding delay
|
||||
net.pingAll()
|
||||
|
||||
info( '\n*** Configuring one intf with bandwidth of 5 Mb\n' )
|
||||
link1.intf1.config( bw=5 )
|
||||
info( '\n*** Running iperf to test\n' )
|
||||
net.iperf()
|
||||
|
||||
info( '\n*** Configuring one intf with loss of 50%\n' )
|
||||
link1.intf1.config( loss=50 )
|
||||
info( '\n' )
|
||||
net.iperf( ( h1, h2 ), l4Type='UDP' )
|
||||
|
||||
info( '\n*** Configuring one intf with delay of 15ms\n' )
|
||||
link1.intf1.config( delay='15ms' )
|
||||
info( '\n*** Run a ping to confirm delay\n' )
|
||||
net.pingPairFull()
|
||||
|
||||
info( '\n*** Done testing\n' )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
intfOptions()
|
||||
+24
-15
@@ -9,35 +9,44 @@ from mininet.link import TCIntf
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, quietRun
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
from time import sleep
|
||||
|
||||
def testLinkLimit( net, bw ):
|
||||
"Run bandwidth limit test"
|
||||
info( '*** Testing network %.2f Mbps bandwidth limit\n' % bw )
|
||||
net.iperf()
|
||||
print '*** Testing network %.2f Mbps bandwidth limit' % bw
|
||||
net.iperf( )
|
||||
|
||||
def limit( bw=10, cpu=.1 ):
|
||||
def testCpuLimit( net, cpu ):
|
||||
"run CPU limit test"
|
||||
pct = cpu * 100
|
||||
print '*** Testing CPU %.0f%% bandwidth limit' % pct
|
||||
h1, h2 = net.hosts
|
||||
h1.cmd( 'while true; do a=1; done &' )
|
||||
h2.cmd( 'while true; do a=1; done &' )
|
||||
pid1 = h1.cmd( 'echo $!' ).strip()
|
||||
pid2 = h2.cmd( 'echo $!' ).strip()
|
||||
cmd = 'ps -p %s,%s -o pid,%%cpu,args' % ( pid1, pid2 )
|
||||
# It's a shame that this is what pylint prefers
|
||||
for _ in range( 5 ):
|
||||
sleep( 1 )
|
||||
print quietRun( cmd ).strip()
|
||||
h1.cmd( 'kill %1')
|
||||
h2.cmd( 'kill %1')
|
||||
|
||||
def limit( bw=10, cpu=.4 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
bw: interface bandwidth limit in Mbps
|
||||
cpu: cpu limit as fraction of overall CPU time"""
|
||||
intf = custom( TCIntf, bw=bw )
|
||||
myTopo = TreeTopo( depth=1, fanout=2 )
|
||||
for sched in 'rt', 'cfs':
|
||||
info( '*** Testing with', sched, 'bandwidth limiting\n' )
|
||||
if sched == 'rt':
|
||||
release = quietRun( 'uname -r' ).strip('\r\n')
|
||||
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s'
|
||||
% release )
|
||||
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
|
||||
info( '*** RT Scheduler is not enabled in your kernel. '
|
||||
'Skipping this test\n' )
|
||||
continue
|
||||
print '*** Testing with', sched, 'bandwidth limiting'
|
||||
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
|
||||
net = Mininet( topo=myTopo, intf=intf, host=host )
|
||||
net.start()
|
||||
testLinkLimit( net, bw=bw )
|
||||
net.runCpuLimitTest( cpu=cpu )
|
||||
testCpuLimit( net, cpu=cpu )
|
||||
net.stop()
|
||||
|
||||
def verySimpleLimit( bw=150 ):
|
||||
|
||||
+28
-44
@@ -23,14 +23,11 @@ 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, info
|
||||
from mininet.util import irange, quietRun
|
||||
from mininet.link import TCLink
|
||||
from functools import partial
|
||||
from mininet.log import lg
|
||||
from mininet.util import irange
|
||||
|
||||
import sys
|
||||
flush = sys.stdout.flush
|
||||
@@ -44,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 ):
|
||||
@@ -71,56 +68,43 @@ def linearBandwidthTest( lengths ):
|
||||
hostCount = switchCount + 1
|
||||
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
# UserSwitch is horribly slow with recent kernels.
|
||||
# We can reinstate it once its performance is fixed
|
||||
del switches[ 'reference user' ]
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
topo = LinearTestTopo( hostCount )
|
||||
|
||||
# Select TCP Reno
|
||||
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
|
||||
assert 'reno' in output
|
||||
|
||||
for datapath in switches.keys():
|
||||
info( "*** testing", datapath, "datapath\n" )
|
||||
print "*** testing", datapath, "datapath"
|
||||
Switch = switches[ datapath ]
|
||||
results[ datapath ] = []
|
||||
link = partial( TCLink, delay='2ms', bw=10 )
|
||||
net = Mininet( topo=topo, switch=Switch,
|
||||
controller=Controller, waitConnected=True,
|
||||
link=link )
|
||||
net = Mininet( topo=topo, switch=Switch )
|
||||
net.start()
|
||||
info( "*** testing basic connectivity\n" )
|
||||
print "*** testing basic connectivity"
|
||||
for n in lengths:
|
||||
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
|
||||
info( "*** testing bandwidth\n" )
|
||||
print "*** testing bandwidth"
|
||||
for n in lengths:
|
||||
src, dst = net.hosts[ 0 ], net.hosts[ n ]
|
||||
# Try to prime the pump to reduce PACKET_INs during test
|
||||
# since the reference controller is reactive
|
||||
src.cmd( 'telnet', dst.IP(), '5001' )
|
||||
info( "testing", src.name, "<->", dst.name, '\n' )
|
||||
# serverbw = received; _clientbw = buffered
|
||||
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=10 )
|
||||
info( serverbw, '\n' )
|
||||
print "testing", src.name, "<->", dst.name,
|
||||
bandwidth = net.iperf( [ src, dst ] )
|
||||
print bandwidth
|
||||
flush()
|
||||
results[ datapath ] += [ ( n, serverbw ) ]
|
||||
results[ datapath ] += [ ( n, bandwidth ) ]
|
||||
net.stop()
|
||||
|
||||
for datapath in switches.keys():
|
||||
info( "\n*** Linear network results for", datapath, "datapath:\n" )
|
||||
print
|
||||
print "*** Linear network results for", datapath, "datapath:"
|
||||
print
|
||||
result = results[ datapath ]
|
||||
info( "SwitchCount\tiperf Results\n" )
|
||||
for switchCount, serverbw in result:
|
||||
info( switchCount, '\t\t' )
|
||||
info( serverbw, '\n' )
|
||||
info( '\n')
|
||||
info( '\n' )
|
||||
print "SwitchCount\tiperf Results"
|
||||
for switchCount, bandwidth in result:
|
||||
print switchCount, '\t\t',
|
||||
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
|
||||
print
|
||||
print
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info' )
|
||||
sizes = [ 1, 10, 20, 40, 60, 80 ]
|
||||
info( "*** Running linearBandwidthTest", sizes, '\n' )
|
||||
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
|
||||
print "*** Running linearBandwidthTest", sizes
|
||||
linearBandwidthTest( sizes )
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
linuxrouter.py: Example network with Linux IP router
|
||||
|
||||
This example converts a Node into a router using IP forwarding
|
||||
already built into Linux.
|
||||
|
||||
The example topology creates a router and three IP subnets:
|
||||
|
||||
- 192.168.1.0/24 (r0-eth1, IP: 192.168.1.1)
|
||||
- 172.16.0.0/12 (r0-eth2, IP: 172.16.0.1)
|
||||
- 10.0.0.0/8 (r0-eth3, IP: 10.0.0.1)
|
||||
|
||||
Each subnet consists of a single host connected to
|
||||
a single switch:
|
||||
|
||||
r0-eth1 - s1-eth1 - h1-eth0 (IP: 192.168.1.100)
|
||||
r0-eth2 - s2-eth1 - h2-eth0 (IP: 172.16.0.100)
|
||||
r0-eth3 - s3-eth1 - h3-eth0 (IP: 10.0.0.100)
|
||||
|
||||
The example relies on default routing entries that are
|
||||
automatically created for each router interface, as well
|
||||
as 'defaultRoute' parameters for the host interfaces.
|
||||
|
||||
Additional routes may be added to the router or hosts by
|
||||
executing 'ip route' or 'route' commands on the router or hosts.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
|
||||
|
||||
class LinuxRouter( Node ):
|
||||
"A Node with IP forwarding enabled."
|
||||
|
||||
def config( self, **params ):
|
||||
super( LinuxRouter, self).config( **params )
|
||||
# Enable forwarding on the router
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
||||
|
||||
def terminate( self ):
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
super( LinuxRouter, self ).terminate()
|
||||
|
||||
|
||||
class NetworkTopo( Topo ):
|
||||
"A LinuxRouter connecting three IP subnets"
|
||||
|
||||
def build( self, **_opts ):
|
||||
|
||||
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
|
||||
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
|
||||
|
||||
s1, s2, s3 = [ self.addSwitch( s ) for s in ( 's1', 's2', 's3' ) ]
|
||||
|
||||
self.addLink( s1, router, intfName2='r0-eth1',
|
||||
params2={ 'ip' : defaultIP } ) # for clarity
|
||||
self.addLink( s2, router, intfName2='r0-eth2',
|
||||
params2={ 'ip' : '172.16.0.1/12' } )
|
||||
self.addLink( s3, router, intfName2='r0-eth3',
|
||||
params2={ 'ip' : '10.0.0.1/8' } )
|
||||
|
||||
h1 = self.addHost( 'h1', ip='192.168.1.100/24',
|
||||
defaultRoute='via 192.168.1.1' )
|
||||
h2 = self.addHost( 'h2', ip='172.16.0.100/12',
|
||||
defaultRoute='via 172.16.0.1' )
|
||||
h3 = self.addHost( 'h3', ip='10.0.0.100/8',
|
||||
defaultRoute='via 10.0.0.1' )
|
||||
|
||||
for h, s in [ (h1, s1), (h2, s2), (h3, s3) ]:
|
||||
self.addLink( h, s )
|
||||
|
||||
|
||||
def run():
|
||||
"Test linux router"
|
||||
topo = NetworkTopo()
|
||||
net = Mininet( topo=topo ) # controller is used by s1-s3
|
||||
net.start()
|
||||
info( '*** Routing Table on Router:\n' )
|
||||
info( net[ 'r0' ].cmd( 'route' ) )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
run()
|
||||
+106
-3002
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.log import info, output, warn, setLogLevel
|
||||
|
||||
from random import randint
|
||||
|
||||
|
||||
class MobilitySwitch( OVSSwitch ):
|
||||
"Switch that can reattach and rename interfaces"
|
||||
|
||||
def delIntf( self, intf ):
|
||||
"Remove (and detach) an interface"
|
||||
port = self.ports[ intf ]
|
||||
del self.ports[ intf ]
|
||||
del self.intfs[ port ]
|
||||
del self.nameToIntf[ intf.name ]
|
||||
|
||||
def addIntf( self, intf, rename=False, **kwargs ):
|
||||
"Add (and reparent) an interface"
|
||||
OVSSwitch.addIntf( self, intf, **kwargs )
|
||||
intf.node = self
|
||||
if rename:
|
||||
self.renameIntf( intf )
|
||||
|
||||
def attach( self, intf ):
|
||||
"Attach an interface and set its port"
|
||||
port = self.ports[ intf ]
|
||||
if port:
|
||||
if self.isOldOVS():
|
||||
self.cmd( 'ovs-vsctl add-port', self, intf )
|
||||
else:
|
||||
self.cmd( 'ovs-vsctl add-port', self, intf,
|
||||
'-- set Interface', intf,
|
||||
'ofport_request=%s' % port )
|
||||
self.validatePort( intf )
|
||||
|
||||
def validatePort( self, intf ):
|
||||
"Validate intf's OF port number"
|
||||
ofport = int( self.cmd( 'ovs-vsctl get Interface', intf,
|
||||
'ofport' ) )
|
||||
if ofport != self.ports[ intf ]:
|
||||
warn( 'WARNING: ofport for', intf, 'is actually', ofport,
|
||||
'\n' )
|
||||
|
||||
def renameIntf( self, intf, newname='' ):
|
||||
"Rename an interface (to its canonical name)"
|
||||
intf.ifconfig( 'down' )
|
||||
if not newname:
|
||||
newname = '%s-eth%d' % ( self.name, self.ports[ intf ] )
|
||||
intf.cmd( 'ip link set', intf, 'name', newname )
|
||||
del self.nameToIntf[ intf.name ]
|
||||
intf.name = newname
|
||||
self.nameToIntf[ intf.name ] = intf
|
||||
intf.ifconfig( 'up' )
|
||||
|
||||
def moveIntf( self, intf, switch, port=None, rename=True ):
|
||||
"Move one of our interfaces to another switch"
|
||||
self.detach( intf )
|
||||
self.delIntf( intf )
|
||||
switch.addIntf( intf, port=port, rename=rename )
|
||||
switch.attach( intf )
|
||||
|
||||
|
||||
def printConnections( switches ):
|
||||
"Compactly print connected nodes to each switch"
|
||||
for sw in switches:
|
||||
output( '%s: ' % sw )
|
||||
for intf in sw.intfList():
|
||||
link = intf.link
|
||||
if link:
|
||||
intf1, intf2 = link.intf1, link.intf2
|
||||
remote = intf1 if intf1.node != sw else intf2
|
||||
output( '%s(%s) ' % ( remote.node, sw.ports[ intf ] ) )
|
||||
output( '\n' )
|
||||
|
||||
|
||||
def moveHost( host, oldSwitch, newSwitch, newPort=None ):
|
||||
"Move a host from old switch to new switch"
|
||||
hintf, sintf = host.connectionsTo( oldSwitch )[ 0 ]
|
||||
oldSwitch.moveIntf( sintf, newSwitch, port=newPort )
|
||||
return hintf, sintf
|
||||
|
||||
|
||||
def mobilityTest():
|
||||
"A simple test of mobility"
|
||||
info( '* Simple mobility test\n' )
|
||||
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
|
||||
info( '* Starting network:\n' )
|
||||
net.start()
|
||||
printConnections( net.switches )
|
||||
info( '* Testing network\n' )
|
||||
net.pingAll()
|
||||
info( '* Identifying switch interface for h1\n' )
|
||||
h1, old = net.get( 'h1', 's1' )
|
||||
for s in 2, 3, 1:
|
||||
new = net[ 's%d' % s ]
|
||||
port = randint( 10, 20 )
|
||||
info( '* Moving', h1, 'from', old, 'to', new, 'port', port, '\n' )
|
||||
hintf, sintf = moveHost( h1, old, new, newPort=port )
|
||||
info( '*', hintf, 'is now connected to', sintf, '\n' )
|
||||
info( '* Clearing out old flows\n' )
|
||||
for sw in net.switches:
|
||||
sw.dpctl( 'del-flows' )
|
||||
info( '* New network:\n' )
|
||||
printConnections( net.switches )
|
||||
info( '* Testing connectivity:\n' )
|
||||
net.pingAll()
|
||||
old = new
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
mobilityTest()
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This is a simple example that demonstrates multiple links
|
||||
between nodes.
|
||||
"""
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import Topo
|
||||
|
||||
def runMultiLink():
|
||||
"Create and run multiple link network"
|
||||
topo = simpleMultiLinkTopo( n=2 )
|
||||
net = Mininet( topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
class simpleMultiLinkTopo( Topo ):
|
||||
"Simple topology with multiple links"
|
||||
|
||||
def __init__( self, n, **kwargs ):
|
||||
Topo.__init__( self, **kwargs )
|
||||
|
||||
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
|
||||
s1 = self.addSwitch( 's1' )
|
||||
|
||||
for _ in range( n ):
|
||||
self.addLink( s1, h1 )
|
||||
self.addLink( s1, h2 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
runMultiLink()
|
||||
+11
-9
@@ -8,24 +8,28 @@ multiple hosts and monitor their output interactively for a period=
|
||||
of time.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import info, setLogLevel
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from select import poll, POLLIN
|
||||
from time import time
|
||||
|
||||
def chunks( 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,8 +39,8 @@ def startpings( host, targetips ):
|
||||
' done; '
|
||||
'done &' )
|
||||
|
||||
info( '*** Host %s (%s) will be pinging ips: %s\n' %
|
||||
( host.name, host.IP(), targetips ) )
|
||||
print ( '*** Host %s (%s) will be pinging ips: %s' %
|
||||
( host.name, host.IP(), targetips ) )
|
||||
|
||||
host.cmd( cmd )
|
||||
|
||||
@@ -45,7 +49,7 @@ def multiping( netsize, chunksize, seconds):
|
||||
|
||||
# Create network and identify subnets
|
||||
topo = SingleSwitchTopo( netsize )
|
||||
net = Mininet( topo=topo, waitConnected=True )
|
||||
net = Mininet( topo=topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
subnets = chunks( hosts, chunksize )
|
||||
@@ -59,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 )
|
||||
|
||||
@@ -70,7 +72,7 @@ def multiping( netsize, chunksize, seconds):
|
||||
readable = poller.poll(1000)
|
||||
for fd, _mask in readable:
|
||||
node = Node.outToNode[ fd ]
|
||||
info( '%s:' % node.name, node.monitor().strip(), '\n' )
|
||||
print '%s:' % node.name, node.monitor().strip()
|
||||
|
||||
# Stop pings
|
||||
for host in hosts:
|
||||
|
||||
+7
-10
@@ -5,24 +5,21 @@ Simple example of sending output to multiple files and
|
||||
monitoring them
|
||||
"""
|
||||
|
||||
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import info, setLogLevel
|
||||
from mininet.util import decode
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from time import time
|
||||
from select import poll, POLLIN
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
|
||||
def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
"Monitor set of files and return [(host, line)...]"
|
||||
devnull = open( '/dev/null', 'w' )
|
||||
tails, fdToFile, fdToHost = {}, {}, {}
|
||||
for h, outfile in outfiles.items():
|
||||
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
|
||||
@@ -41,7 +38,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
host = fdToHost[ fd ]
|
||||
# Wait for a line of output
|
||||
line = f.readline().strip()
|
||||
yield host, decode( line )
|
||||
yield host, line
|
||||
else:
|
||||
# If we timed out, return nothing
|
||||
yield None, ''
|
||||
@@ -56,7 +53,7 @@ def monitorTest( N=3, seconds=3 ):
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
info( "Starting test...\n" )
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
outfiles, errfiles = {}, {}
|
||||
for h in hosts:
|
||||
@@ -70,10 +67,10 @@ def monitorTest( N=3, seconds=3 ):
|
||||
'>', outfiles[ h ],
|
||||
'2>', errfiles[ h ],
|
||||
'&' )
|
||||
info( "Monitoring output for", seconds, "seconds\n" )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
|
||||
if h:
|
||||
info( '%s: %s\n' % ( h.name, line ) )
|
||||
print '%s: %s' % ( h.name, line )
|
||||
for h in hosts:
|
||||
h.cmd('kill %ping')
|
||||
net.stop()
|
||||
|
||||
@@ -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" )
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Example to create a Mininet topology and connect it to the internet via NAT
|
||||
"""
|
||||
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, info
|
||||
from mininet.topolib import TreeNet
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4 )
|
||||
# Add NAT connectivity
|
||||
net.addNAT().configDefault()
|
||||
net.start()
|
||||
info( "*** Hosts are running and should have internet connectivity\n" )
|
||||
info( "*** Type 'exit' or control-D to shut down network\n" )
|
||||
CLI( net )
|
||||
# Shut down NAT
|
||||
net.stop()
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
natnet.py: Example network with NATs
|
||||
|
||||
|
||||
h0
|
||||
|
|
||||
s0
|
||||
|
|
||||
----------------
|
||||
| |
|
||||
nat1 nat2
|
||||
| |
|
||||
s1 s2
|
||||
| |
|
||||
h1 h2
|
||||
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.nodelib import NAT
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.cli import CLI
|
||||
from mininet.util import irange
|
||||
|
||||
class InternetTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def __init__(self, n=2, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
|
||||
# set up inet switch
|
||||
inetSwitch = self.addSwitch('s0')
|
||||
# add inet host
|
||||
inetHost = self.addHost('h0')
|
||||
self.addLink(inetSwitch, inetHost)
|
||||
|
||||
# add local nets
|
||||
for i in irange(1, n):
|
||||
inetIntf = 'nat%d-eth0' % i
|
||||
localIntf = 'nat%d-eth1' % i
|
||||
localIP = '192.168.%d.1' % i
|
||||
localSubnet = '192.168.%d.0/24' % i
|
||||
natParams = { 'ip' : '%s/24' % localIP }
|
||||
# add NAT to topology
|
||||
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
|
||||
inetIntf=inetIntf, localIntf=localIntf)
|
||||
switch = self.addSwitch('s%d' % i)
|
||||
# connect NAT to inet and local switches
|
||||
self.addLink(nat, inetSwitch, intfName1=inetIntf)
|
||||
self.addLink(nat, switch, intfName1=localIntf, params1=natParams)
|
||||
# add host and connect to local switch
|
||||
host = self.addHost('h%d' % i,
|
||||
ip='192.168.%d.100/24' % i,
|
||||
defaultRoute='via %s' % localIP)
|
||||
self.addLink(host, switch)
|
||||
|
||||
def run():
|
||||
"Create network and run the CLI"
|
||||
topo = InternetTopo()
|
||||
net = Mininet(topo=topo)
|
||||
net.start()
|
||||
CLI(net)
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
run()
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Create a network with 5 hosts, numbered 1-4 and 9.
|
||||
Validate that the port numbers match to the interface name,
|
||||
and that the ovs ports match the mininet ports.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller
|
||||
from mininet.log import setLogLevel, info, warn
|
||||
|
||||
def validatePort( switch, intf ):
|
||||
"Validate intf's OF port number"
|
||||
ofport = int( switch.cmd( 'ovs-vsctl get Interface', intf,
|
||||
'ofport' ) )
|
||||
if ofport != switch.ports[ intf ]:
|
||||
warn( 'WARNING: ofport for', intf, 'is actually', ofport, '\n' )
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def testPortNumbering():
|
||||
|
||||
"""Test port numbering:
|
||||
Create a network with 5 hosts (using Mininet's
|
||||
mid-level API) and check that implicit and
|
||||
explicit port numbering works as expected."""
|
||||
|
||||
net = Mininet( controller=Controller )
|
||||
|
||||
info( '*** Adding controller\n' )
|
||||
net.addController( 'c0' )
|
||||
|
||||
info( '*** Adding hosts\n' )
|
||||
h1 = net.addHost( 'h1', ip='10.0.0.1' )
|
||||
h2 = net.addHost( 'h2', ip='10.0.0.2' )
|
||||
h3 = net.addHost( 'h3', ip='10.0.0.3' )
|
||||
h4 = net.addHost( 'h4', ip='10.0.0.4' )
|
||||
h5 = net.addHost( 'h5', ip='10.0.0.5' )
|
||||
|
||||
info( '*** Adding switch\n' )
|
||||
s1 = net.addSwitch( 's1' )
|
||||
|
||||
info( '*** Creating links\n' )
|
||||
# host 1-4 connect to ports 1-4 on the switch
|
||||
net.addLink( h1, s1 )
|
||||
net.addLink( h2, s1 )
|
||||
net.addLink( h3, s1 )
|
||||
net.addLink( h4, s1 )
|
||||
# specify a different port to connect host 5 to on the switch.
|
||||
net.addLink( h5, s1, port1=1, port2= 9)
|
||||
|
||||
info( '*** Starting network\n' )
|
||||
net.start()
|
||||
|
||||
# print the interfaces and their port numbers
|
||||
info( '\n*** printing and validating the ports '
|
||||
'running on each interface\n' )
|
||||
for intfs in s1.intfList():
|
||||
if not intfs.name == "lo":
|
||||
info( intfs, ': ', s1.ports[intfs],
|
||||
'\n' )
|
||||
info( 'Validating that', intfs,
|
||||
'is actually on port', s1.ports[intfs], '... ' )
|
||||
if validatePort( s1, intfs ):
|
||||
info( 'Validated.\n' )
|
||||
info( '\n' )
|
||||
|
||||
# test the network with pingall
|
||||
net.pingAll()
|
||||
info( '\n' )
|
||||
|
||||
info( '*** Stopping network\n' )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testPortNumbering()
|
||||
+2
-3
@@ -5,11 +5,10 @@ This example monitors a number of hosts using host.popen() and
|
||||
pmonitor()
|
||||
"""
|
||||
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import custom, pmonitor
|
||||
|
||||
def monitorhosts( hosts=5, sched='cfs' ):
|
||||
@@ -28,7 +27,7 @@ def monitorhosts( hosts=5, sched='cfs' ):
|
||||
# Monitor them and print output
|
||||
for host, line in pmonitor( popens ):
|
||||
if host:
|
||||
info( "<%s>: %s" % ( host.name, line ) )
|
||||
print "<%s>: %s" % ( host.name, line.strip() )
|
||||
# Done
|
||||
net.stop()
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.util import pmonitor
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from time import time
|
||||
from signal import SIGINT
|
||||
|
||||
@@ -16,21 +14,20 @@ def pmonitorTest( N=3, seconds=10 ):
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
info( "Starting test...\n" )
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
popens = {}
|
||||
for h in hosts:
|
||||
popens[ h ] = h.popen('ping', server.IP() )
|
||||
info( "Monitoring output for", seconds, "seconds\n" )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
endTime = time() + seconds
|
||||
for h, line in pmonitor( popens, timeoutms=500 ):
|
||||
if h:
|
||||
info( '<%s>: %s' % ( h.name, line ) )
|
||||
print '%s: %s' % ( h.name, line ),
|
||||
if time() >= endTime:
|
||||
for p in popens.values():
|
||||
p.send_signal( SIGINT )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
pmonitorTest()
|
||||
|
||||
@@ -8,7 +8,6 @@ but it exposes the configuration details and allows customization.
|
||||
For most tasks, the higher-level API will be preferable.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.link import Link
|
||||
@@ -41,7 +40,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
|
||||
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||
switch.cmd( 'ovs-vsctl add-br dp0' )
|
||||
for intf in switch.intfs.values():
|
||||
switch.cmd( 'ovs-vsctl add-port dp0 %s\n' % intf )
|
||||
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
|
||||
|
||||
# Note: controller and switch are in root namespace, and we
|
||||
# can connect via loopback interface
|
||||
|
||||
@@ -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:
|
||||
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
info( '*** Starting controller and user datapath\n' )
|
||||
controller.cmd( cname + ' ' + cargs + '&' )
|
||||
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
||||
intfs = str( sintf1 ), str( sintf2 )
|
||||
intfs = [ str( i ) for i in sintf1, sintf2 ]
|
||||
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
|
||||
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
|
||||
|
||||
|
||||
Executable → Regular
+21
-37
@@ -2,59 +2,43 @@
|
||||
|
||||
"""
|
||||
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
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.link import TCLink
|
||||
from mininet.util import dumpNodeConnections
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from sys import argv
|
||||
|
||||
# It would be nice if we didn't have to do this:
|
||||
# pylint: disable=arguments-differ
|
||||
|
||||
class SingleSwitchTopo( Topo ):
|
||||
class SingleSwitchTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def build( self, n=2, lossy=True ):
|
||||
switch = self.addSwitch('s1')
|
||||
def __init__(self, n=2, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
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)
|
||||
if lossy:
|
||||
# 10 Mbps, 5ms delay, 10% packet loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
else:
|
||||
# 10 Mbps, 5ms delay, no packet loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=0, use_htb=True)
|
||||
host = self.add_host('h%s' % (h + 1),
|
||||
cpu=.5 / n)
|
||||
# 10 Mbps, 5ms delay, 10% loss
|
||||
self.add_link(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
|
||||
|
||||
def perfTest( lossy=True ):
|
||||
def perfTest():
|
||||
"Create network and run simple performance test"
|
||||
topo = SingleSwitchTopo( n=4, lossy=lossy )
|
||||
net = Mininet( topo=topo,
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
autoStaticArp=True )
|
||||
topo = SingleSwitchTopo(n=4)
|
||||
net = Mininet(topo=topo,
|
||||
host=CPULimitedHost, link=TCLink)
|
||||
net.start()
|
||||
info( "Dumping host connections\n" )
|
||||
print "Dumping host connections"
|
||||
dumpNodeConnections(net.hosts)
|
||||
info( "Testing bandwidth between h1 and h4\n" )
|
||||
print "Testing network connectivity"
|
||||
net.pingAll()
|
||||
print "Testing bandwidth between h1 and h4"
|
||||
h1, h4 = net.getNodeByName('h1', 'h4')
|
||||
net.iperf( ( h1, h4 ), l4Type='UDP' )
|
||||
net.iperf((h1, h4))
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# Prevent test_simpleperf from failing due to packet loss
|
||||
perfTest( lossy=( 'testmode' not in argv ) )
|
||||
setLogLevel('info')
|
||||
perfTest()
|
||||
|
||||
+21
-33
@@ -16,58 +16,50 @@ 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, info
|
||||
from mininet.node import Node
|
||||
from mininet.log import lg
|
||||
from mininet.node import Node, OVSKernelSwitch
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import waitListening
|
||||
|
||||
from mininet.link import Link
|
||||
|
||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||
"Convenience function for creating tree networks."
|
||||
topo = TreeTopo( depth, fanout )
|
||||
return Mininet( topo, **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 = network.addLink( root, switch ).intf1
|
||||
root.setIP( ip, intf=intf )
|
||||
intf = Link( root, switch ).intf1
|
||||
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 + '&' )
|
||||
info( "*** Waiting for ssh daemons to start\n" )
|
||||
for server in network.hosts:
|
||||
waitListening( server=server, port=22, timeout=5 )
|
||||
|
||||
info( "\n*** Hosts are running sshd at the following addresses:\n" )
|
||||
print
|
||||
print "*** Hosts are running sshd at the following addresses:"
|
||||
print
|
||||
for host in network.hosts:
|
||||
info( host.name, host.IP(), '\n' )
|
||||
info( "\n*** Type 'exit' or control-D to shut down network\n" )
|
||||
print host.name, host.IP()
|
||||
print
|
||||
print "*** Type 'exit' or control-D to shut down network"
|
||||
CLI( network )
|
||||
for host in network.hosts:
|
||||
host.cmd( 'kill %' + cmd )
|
||||
@@ -75,9 +67,5 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4 )
|
||||
# get sshd args from the command line or use default args
|
||||
# useDNS=no -u0 to avoid reverse DNS lookup timeout
|
||||
argvopts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
|
||||
'-D -o UseDNS=no -u0' )
|
||||
sshd( net, opts=argvopts )
|
||||
net = TreeNet( depth=1, fanout=4, switch=OVSKernelSwitch )
|
||||
sshd( net )
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Run all mininet.examples tests
|
||||
-v : verbose output
|
||||
-quick : skip tests that take more than ~30 seconds
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
from mininet.util import ensureRoot
|
||||
from mininet.clean import cleanup
|
||||
|
||||
class MininetTestResult( unittest.TextTestResult ):
|
||||
def addFailure( self, test, err ):
|
||||
super( MininetTestResult, self ).addFailure( test, err )
|
||||
cleanup()
|
||||
def addError( self,test, err ):
|
||||
super( MininetTestResult, self ).addError( test, err )
|
||||
cleanup()
|
||||
|
||||
class MininetTestRunner( unittest.TextTestRunner ):
|
||||
def _makeResult( self ):
|
||||
return MininetTestResult( self.stream, self.descriptions, self.verbosity )
|
||||
|
||||
def runTests( testDir, verbosity=1 ):
|
||||
"discover and run all tests in testDir"
|
||||
# ensure root and cleanup before starting tests
|
||||
ensureRoot()
|
||||
cleanup()
|
||||
# discover all tests in testDir
|
||||
testSuite = unittest.defaultTestLoader.discover( testDir )
|
||||
# run tests
|
||||
success = MininetTestRunner( verbosity=verbosity ).run( testSuite ).wasSuccessful()
|
||||
sys.exit( 0 if success else 1 )
|
||||
|
||||
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,68 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for baresshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
from mininet.clean import cleanup, sh
|
||||
from sys import stdout
|
||||
|
||||
class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
opts = [ 'Welcome to h1', pexpect.EOF, pexpect.TIMEOUT ]
|
||||
|
||||
def connected( self ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
|
||||
'-o StrictHostKeyChecking=no '
|
||||
'-i /tmp/ssh/test_rsa exit' )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def setUp( self ):
|
||||
# verify that sshd is not running
|
||||
self.assertFalse( self.connected() )
|
||||
# create public key pair for testing
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
sh( 'mkdir /tmp/ssh' )
|
||||
sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" )
|
||||
sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' )
|
||||
# run example with custom sshd args
|
||||
cmd = ( 'python -m mininet.examples.baresshd '
|
||||
'-o AuthorizedKeysFile=/tmp/ssh/authorized_keys '
|
||||
'-o StrictModes=no' )
|
||||
p = pexpect.spawn( cmd )
|
||||
runOpts = [ 'You may now ssh into h1 at 10.0.0.1',
|
||||
'after 5 seconds, h1 is not listening on port 22',
|
||||
pexpect.EOF, pexpect.TIMEOUT ]
|
||||
while True:
|
||||
index = p.expect( runOpts )
|
||||
if index == 0:
|
||||
break
|
||||
else:
|
||||
self.tearDown()
|
||||
self.fail( 'sshd failed to start in host h1' )
|
||||
|
||||
def testSSH( self ):
|
||||
"Simple test to verify that we can ssh into h1"
|
||||
result = False
|
||||
# try to connect up to 3 times; sshd can take a while to start
|
||||
result = self.connected()
|
||||
self.assertTrue( result )
|
||||
|
||||
def tearDown( self ):
|
||||
# kill the ssh process
|
||||
sh( "ps aux | grep ssh |grep 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
|
||||
from mininet.util 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,27 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
A simple sanity check test for cluster edition
|
||||
'''
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
|
||||
class clusterSanityCheck( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testClusterPingAll( self ):
|
||||
p = pexpect.spawn( 'python -m mininet.examples.clusterSanity' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect ( '(\d+)% dropped' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Tests for controllers.py and controllers2.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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
|
||||
from mininet.util 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,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for cpu.py
|
||||
|
||||
results format:
|
||||
|
||||
sched cpu received bits/sec
|
||||
cfs 50% 8.14e+09
|
||||
cfs 40% 6.48e+09
|
||||
cfs 30% 4.56e+09
|
||||
cfs 20% 2.84e+09
|
||||
cfs 10% 1.29e+09
|
||||
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import 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', timeout=300 )
|
||||
# matches each line from results( shown above )
|
||||
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)',
|
||||
pexpect.EOF ]
|
||||
scheds = []
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
sched = p.match.group( 1 )
|
||||
cpu = float( p.match.group( 2 ) )
|
||||
bw = float( p.match.group( 3 ) )
|
||||
if sched not in scheds:
|
||||
scheds.append( sched )
|
||||
else:
|
||||
self.assertTrue( bw < previous_bw,
|
||||
"%e should be less than %e\n" %
|
||||
( 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
|
||||
from mininet.util 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
|
||||
|
||||
from mininet.util 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.stop( deleteIntfs=True )
|
||||
self.n0.stop( deleteIntfs=True )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for intfOptions.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testIntfOptions( unittest.TestCase ):
|
||||
|
||||
def testIntfOptions( self ):
|
||||
"verify that intf.config is correctly limiting traffic"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
|
||||
tolerance = .2 # plus or minus 20%
|
||||
opts = [ "Results: \['([\d\.]+) .bits/sec",
|
||||
"Results: \['10M', '([\d\.]+) .bits/sec",
|
||||
"h(\d+)->h(\d+): (\d)/(\d),"
|
||||
"rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
|
||||
pexpect.EOF ]
|
||||
while True:
|
||||
index = p.expect( opts, timeout=600 )
|
||||
if index == 0:
|
||||
BW = 5
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertGreaterEqual( bw, BW * ( 1 - tolerance ) )
|
||||
self.assertLessEqual( bw, BW * ( 1 + tolerance ) )
|
||||
elif index == 1:
|
||||
BW = 10
|
||||
measuredBw = float( p.match.group( 1 ) )
|
||||
loss = ( measuredBw / BW ) * 100
|
||||
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ) )
|
||||
self.assertLessEqual( loss, 50 * ( 1 + tolerance ) )
|
||||
elif index == 2:
|
||||
delay = float( p.match.group( 6 ) )
|
||||
self.assertGreaterEqual( delay, 15 * ( 1 - tolerance ) )
|
||||
self.assertLessEqual( delay, 15 * ( 1 + tolerance ) )
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for limit.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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,49 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for linearbandwidth.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testLinearBandwidth( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testLinearBandwidth( self ):
|
||||
"Verify that bandwidth is monotonically decreasing as # of hops increases"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
|
||||
count = 0
|
||||
opts = [ '\*\*\* Linear network results',
|
||||
'(\d+)\s+([\d\.]+) (.bits)',
|
||||
pexpect.EOF ]
|
||||
while True:
|
||||
index = p.expect( opts, timeout=600 )
|
||||
if index == 0:
|
||||
count += 1
|
||||
elif index == 1:
|
||||
n = int( p.match.group( 1 ) )
|
||||
bw = float( p.match.group( 2 ) )
|
||||
unit = p.match.group( 3 )
|
||||
if unit[ 0 ] == 'K':
|
||||
bw *= 10 ** 3
|
||||
elif unit[ 0 ] == 'M':
|
||||
bw *= 10 ** 6
|
||||
elif unit[ 0 ] == 'G':
|
||||
bw *= 10 ** 9
|
||||
# check that we have a previous result to compare to
|
||||
if n != 1:
|
||||
info = ( 'bw: %.2e bits/s across %d switches, '
|
||||
'previous: %.2e bits/s across %d switches' %
|
||||
( bw, n, previous_bw, previous_n ) )
|
||||
self.assertTrue( bw < previous_bw, info )
|
||||
previous_bw, previous_n = bw, n
|
||||
else:
|
||||
break
|
||||
|
||||
# verify that we received results from at least one switch
|
||||
self.assertTrue( count > 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for linuxrouter.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testLinuxRouter( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testPingall( self ):
|
||||
"Test connectivity between hosts"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.linuxrouter' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect ( '(\d+)% dropped' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( percent, 0 )
|
||||
|
||||
def testRouterPing( self ):
|
||||
"Test connectivity from h1 to router"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.linuxrouter' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 r0' )
|
||||
p.expect ( '(\d+)% packet loss' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( percent, 0 )
|
||||
|
||||
def testTTL( self ):
|
||||
"Verify that the TTL is decremented"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.linuxrouter' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect ( 'ttl=(\d+)' )
|
||||
ttl = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( ttl, 63 ) # 64 - 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -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,55 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
Test for multiple links between nodes
|
||||
validates mininet interfaces against systems interfaces
|
||||
'''
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testMultiLink( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def testMultiLink(self):
|
||||
p = pexpect.spawn( 'python -m mininet.examples.multilink' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'intfs' )
|
||||
p.expect( 's(\d): lo' )
|
||||
intfsOutput = p.before
|
||||
# parse interfaces from mininet intfs, and store them in a list
|
||||
hostToIntfs = intfsOutput.split( '\r\n' )[ 1:3 ]
|
||||
intfList = []
|
||||
for hostToIntf in hostToIntfs:
|
||||
intfList += [ intf for intf in
|
||||
hostToIntf.split()[1].split(',') ]
|
||||
|
||||
# get interfaces from system by running ifconfig on every host
|
||||
sysIntfList = []
|
||||
opts = [ 'h(\d)-eth(\d)', self.prompt ]
|
||||
p.expect( self.prompt )
|
||||
|
||||
p.sendline( 'h1 ifconfig' )
|
||||
while True:
|
||||
p.expect( opts )
|
||||
if p.after == self.prompt:
|
||||
break
|
||||
sysIntfList.append( p.after )
|
||||
|
||||
p.sendline( 'h2 ifconfig' )
|
||||
while True:
|
||||
p.expect( opts )
|
||||
if p.after == self.prompt:
|
||||
break
|
||||
sysIntfList.append( p.after )
|
||||
|
||||
failMsg = ( 'The systems interfaces and mininet interfaces\n'
|
||||
'are not the same' )
|
||||
|
||||
self.assertEqual( sysIntfList, intfList, msg=failMsg )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for multiping.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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, p.match.group(0) + '\n' +
|
||||
target + ' received %d != 0 packets' % received )
|
||||
else:
|
||||
self.assertEqual( received, 1, p.match.group(0) + '\n' +
|
||||
target + ' received %d != 1 packets' % received )
|
||||
try:
|
||||
pings[ name ].remove( target )
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
self.assertTrue( len( pings ) > 0, 'too few pings' )
|
||||
for t in pings.values():
|
||||
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for multipoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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, seconds = {}, -1
|
||||
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,
|
||||
'%d pings < %d seconds' % ( count, seconds ) )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for multitest.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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
|
||||
from mininet.util 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
|
||||
from mininet.util import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testNATNet( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
def setUp( self ):
|
||||
self.net = pexpect.spawn( 'python -m mininet.examples.natnet' )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testPublicPing( self ):
|
||||
"Attempt to ping the public server (h0) from h1 and h2"
|
||||
self.net.sendline( 'h1 ping -c 1 h0' )
|
||||
self.net.expect ( '(\d+)% packet loss' )
|
||||
percent = int( self.net.match.group( 1 ) ) if self.net.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
self.net.sendline( 'h2 ping -c 1 h0' )
|
||||
self.net.expect ( '(\d+)% packet loss' )
|
||||
percent = int( self.net.match.group( 1 ) ) if self.net.match else -1
|
||||
self.assertEqual( percent, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testPrivatePing( self ):
|
||||
"Attempt to ping h1 and h2 from public server"
|
||||
self.net.sendline( 'h0 ping -c 1 -t 1 h1' )
|
||||
result = self.net.expect ( [ 'unreachable', 'loss' ] )
|
||||
self.assertEqual( result, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
self.net.sendline( 'h0 ping -c 1 -t 1 h2' )
|
||||
result = self.net.expect ( [ 'unreachable', 'loss' ] )
|
||||
self.assertEqual( result, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def testPrivateToPrivatePing( self ):
|
||||
"Attempt to ping from NAT'ed host h1 to NAT'ed host h2"
|
||||
self.net.sendline( 'h1 ping -c 1 -t 1 h2' )
|
||||
result = self.net.expect ( [ '[Uu]nreachable', 'loss' ] )
|
||||
self.assertEqual( result, 0 )
|
||||
self.net.expect( self.prompt )
|
||||
|
||||
def tearDown( self ):
|
||||
self.net.sendline( 'exit' )
|
||||
self.net.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for numberedports.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
from collections import defaultdict
|
||||
from mininet.node import OVSSwitch
|
||||
|
||||
class testNumberedports( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( OVSSwitch.setup() or OVSSwitch.isOldOVS(), "old version of OVS" )
|
||||
def testConsistency( self ):
|
||||
"""verify consistency between mininet and ovs ports"""
|
||||
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
|
||||
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
|
||||
'Validating that s1-eth\d is actually on port \d ... WARNING',
|
||||
pexpect.EOF ]
|
||||
correct_ports = True
|
||||
count = 0
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
count += 1
|
||||
elif index == 1:
|
||||
correct_ports = False
|
||||
elif index == 2:
|
||||
self.assertNotEqual( 0, count )
|
||||
break
|
||||
self.assertTrue( correct_ports )
|
||||
|
||||
def testNumbering( self ):
|
||||
"""verify that all of the port numbers are printed correctly and consistent with their interface"""
|
||||
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
|
||||
opts = [ 's1-eth(\d+) : (\d+)',
|
||||
pexpect.EOF ]
|
||||
count_intfs = 0
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
count_intfs += 1
|
||||
intfport = p.match.group( 1 )
|
||||
ofport = p.match.group( 2 )
|
||||
self.assertEqual( intfport, ofport )
|
||||
elif index == 1:
|
||||
break
|
||||
self.assertNotEqual( 0, count_intfs )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for popen.py and popenpoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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
|
||||
from mininet.util 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, timeout=120 )
|
||||
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,34 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for simpleperf.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from mininet.examples.simpleperf import SingleSwitchTopo
|
||||
|
||||
class testSimplePerf( unittest.TestCase ):
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testE2E( self ):
|
||||
"Run the example and verify iperf results"
|
||||
# 10 Mb/s, plus or minus 20% tolerance
|
||||
BW = 10
|
||||
TOLERANCE = .2
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
|
||||
# check iperf results
|
||||
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
|
||||
measuredBw = float( p.match.group( 1 ) )
|
||||
lowerBound = BW * ( 1 - TOLERANCE )
|
||||
upperBound = BW + ( 1 + TOLERANCE )
|
||||
self.assertGreaterEqual( measuredBw, lowerBound )
|
||||
self.assertLessEqual( measuredBw, upperBound )
|
||||
p.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for sshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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,31 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for tree1024.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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 20 h1024' )
|
||||
p.expect ( '(\d+)% packet loss' )
|
||||
packetLossPercent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
# Tolerate slow startup on some systems - we should revisit this
|
||||
# and determine the root cause.
|
||||
self.assertLess( packetLossPercent, 60 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for treeping64.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util 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()
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test for vlanhost.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testVLANHost( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testVLANTopo( self ):
|
||||
"Test connectivity (or lack thereof) between hosts in VLANTopo"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.vlanhost' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall 1' ) #ping timeout=1
|
||||
p.expect( '(\d+)% dropped', timeout=30 ) # there should be 24 failed pings
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( percent, 80 )
|
||||
|
||||
def testSpecificVLAN( self ):
|
||||
"Test connectivity between hosts on a specific VLAN"
|
||||
vlan = 1001
|
||||
p = pexpect.spawn( 'python -m mininet.examples.vlanhost %d' % vlan )
|
||||
p.expect( self.prompt )
|
||||
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect ( '(\d+)% packet loss' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
|
||||
p.sendline( 'h1 ifconfig' )
|
||||
i = p.expect( ['h1-eth0.%d' % vlan, pexpect.TIMEOUT ], timeout=2 )
|
||||
p.expect( self.prompt )
|
||||
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
self.assertEqual( percent, 0 ) # no packet loss on ping
|
||||
self.assertEqual( i, 0 ) # check vlan intf is present
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -9,10 +9,10 @@ and running sysctl -p. Check util/sysctl_addon.
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import OVSSwitch
|
||||
from mininet.node import OVSKernelSwitch
|
||||
from mininet.topolib import TreeNet
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
network = TreeNet( depth=2, fanout=32, switch=OVSSwitch )
|
||||
network = TreeNet( depth=2, fanout=32, switch=OVSKernelSwitch )
|
||||
network.run( CLI, network )
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
"Create a 64-node tree network, and test connectivity using ping."
|
||||
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
|
||||
from mininet.topolib import TreeNet
|
||||
|
||||
@@ -12,20 +11,21 @@ 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:
|
||||
info( "*** Testing", name, "datapath\n" )
|
||||
print "*** Testing", name, "datapath"
|
||||
switch = switches[ name ]
|
||||
network = TreeNet( depth=2, fanout=8, switch=switch )
|
||||
result = network.run( network.pingAll )
|
||||
results[ name ] = result
|
||||
|
||||
info( "\n*** Tree network ping results:\n" )
|
||||
print
|
||||
print "*** Tree network ping results:"
|
||||
for name in switches:
|
||||
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
|
||||
info( '\n' )
|
||||
print "%s: %d%% packet loss" % ( name, results[ name ] )
|
||||
print
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
vlanhost.py: Host subclass that uses a VLAN tag for the default interface.
|
||||
|
||||
Dependencies:
|
||||
This class depends on the "vlan" package
|
||||
$ sudo apt-get install vlan
|
||||
|
||||
Usage (example uses VLAN ID=1000):
|
||||
From the command line:
|
||||
sudo mn --custom vlanhost.py --host vlan,vlan=1000
|
||||
|
||||
From a script (see exampleUsage function below):
|
||||
from functools import partial
|
||||
from vlanhost import VLANHost
|
||||
|
||||
....
|
||||
|
||||
host = partial( VLANHost, vlan=1000 )
|
||||
net = Mininet( host=host, ... )
|
||||
|
||||
Directly running this script:
|
||||
sudo python vlanhost.py 1000
|
||||
|
||||
"""
|
||||
|
||||
from mininet.node import Host
|
||||
from mininet.topo import Topo
|
||||
from mininet.util import quietRun
|
||||
from mininet.log import error
|
||||
|
||||
class VLANHost( Host ):
|
||||
"Host connected to VLAN interface"
|
||||
|
||||
def config( self, vlan=100, **params ):
|
||||
"""Configure VLANHost according to (optional) parameters:
|
||||
vlan: VLAN ID for default interface"""
|
||||
|
||||
r = super( VLANHost, self ).config( **params )
|
||||
|
||||
intf = self.defaultIntf()
|
||||
# remove IP from default, "physical" interface
|
||||
self.cmd( 'ifconfig %s inet 0' % intf )
|
||||
# create VLAN interface
|
||||
self.cmd( 'vconfig add %s %d' % ( intf, vlan ) )
|
||||
# assign the host's IP to the VLAN interface
|
||||
self.cmd( 'ifconfig %s.%d inet %s' % ( intf, vlan, params['ip'] ) )
|
||||
# update the intf name and host's intf map
|
||||
newName = '%s.%d' % ( intf, vlan )
|
||||
# update the (Mininet) interface to refer to VLAN interface name
|
||||
intf.name = newName
|
||||
# add VLAN interface to host's name to intf map
|
||||
self.nameToIntf[ newName ] = intf
|
||||
|
||||
return r
|
||||
|
||||
hosts = { 'vlan': VLANHost }
|
||||
|
||||
|
||||
def exampleAllHosts( vlan ):
|
||||
"""Simple example of how VLANHost can be used in a script"""
|
||||
# This is where the magic happens...
|
||||
host = partial( VLANHost, vlan=vlan )
|
||||
# vlan (type: int): VLAN ID to be used by all hosts
|
||||
|
||||
# Start a basic network using our VLANHost
|
||||
topo = SingleSwitchTopo( k=2 )
|
||||
net = Mininet( host=host, topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
|
||||
class VLANStarTopo( Topo ):
|
||||
"""Example topology that uses host in multiple VLANs
|
||||
|
||||
The topology has a single switch. There are k VLANs with
|
||||
n hosts in each, all connected to the single switch. There
|
||||
are also n hosts that are not in any VLAN, also connected to
|
||||
the switch."""
|
||||
|
||||
def build( self, k=2, n=2, vlanBase=100 ):
|
||||
s1 = self.addSwitch( 's1' )
|
||||
for i in range( k ):
|
||||
vlan = vlanBase + i
|
||||
for j in range(n):
|
||||
name = 'h%d-%d' % ( j+1, vlan )
|
||||
h = self.addHost( name, cls=VLANHost, vlan=vlan )
|
||||
self.addLink( h, s1 )
|
||||
for j in range( n ):
|
||||
h = self.addHost( 'h%d' % (j+1) )
|
||||
self.addLink( h, s1 )
|
||||
|
||||
|
||||
def exampleCustomTags():
|
||||
"""Simple example that exercises VLANStarTopo"""
|
||||
|
||||
net = Mininet( topo=VLANStarTopo() )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
setLogLevel( 'info' )
|
||||
|
||||
if not quietRun( 'which vconfig' ):
|
||||
error( "Cannot find command 'vconfig'\nThe package",
|
||||
"'vlan' is required in Ubuntu or Debian,",
|
||||
"or 'vconfig' in Fedora\n" )
|
||||
exit()
|
||||
|
||||
if len( sys.argv ) >= 2:
|
||||
exampleAllHosts( vlan=int( sys.argv[ 1 ] ) )
|
||||
else:
|
||||
exampleCustomTags()
|
||||
+32
-99
@@ -10,118 +10,51 @@ It may also get rid of 'false positives', but hopefully
|
||||
nothing irreplaceable!
|
||||
"""
|
||||
|
||||
from subprocess import ( Popen, PIPE, check_output as co,
|
||||
CalledProcessError )
|
||||
import time
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from mininet.log import info
|
||||
from mininet.term import cleanUpScreens
|
||||
from mininet.util import decode
|
||||
|
||||
def sh( cmd ):
|
||||
"Print a command and send it to the shell"
|
||||
info( cmd + '\n' )
|
||||
result = Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
return decode( result )
|
||||
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
|
||||
def killprocs( pattern ):
|
||||
"Reliably terminate processes matching a pattern (including args)"
|
||||
sh( 'pkill -9 -f %s' % pattern )
|
||||
# Make sure they are gone
|
||||
while True:
|
||||
try:
|
||||
pids = co( [ 'pgrep', '-f', pattern ] )
|
||||
except CalledProcessError:
|
||||
pids = ''
|
||||
if pids:
|
||||
sh( 'pkill -9 -f %s' % pattern )
|
||||
time.sleep( .5 )
|
||||
else:
|
||||
break
|
||||
def cleanup():
|
||||
"""Clean up junk which might be left over from old runs;
|
||||
do fast stuff before slow dp and link removal!"""
|
||||
|
||||
class Cleanup( object ):
|
||||
"Wrapper for cleanup()"
|
||||
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
|
||||
"\n")
|
||||
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
||||
zombies += 'ovs-openflowd 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.
|
||||
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
||||
|
||||
callbacks = []
|
||||
info( "*** Removing junk from /tmp\n" )
|
||||
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
|
||||
|
||||
@classmethod
|
||||
def cleanup( cls):
|
||||
"""Clean up junk which might be left over from old runs;
|
||||
do fast stuff before slow dp and link removal!"""
|
||||
info( "*** Removing old screen sessions\n" )
|
||||
cleanUpScreens()
|
||||
|
||||
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
|
||||
"pings/noxes\n" )
|
||||
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
|
||||
'lt-nox_core ovs-openflowd ovs-controller'
|
||||
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
|
||||
# Note: real zombie processes can't actually be killed, since they
|
||||
# are already (un)dead. Then again,
|
||||
# you can't connect to them either, so they're mostly harmless.
|
||||
# Send SIGTERM first to give processes a chance to shutdown cleanly.
|
||||
sh( 'killall ' + zombies + ' 2> /dev/null' )
|
||||
time.sleep( 1 )
|
||||
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
||||
info( "*** Removing excess kernel datapaths\n" )
|
||||
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' )
|
||||
for dp in dps:
|
||||
if dp != '':
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
|
||||
# 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" )
|
||||
cleanUpScreens()
|
||||
|
||||
info( "*** Removing excess kernel datapaths\n" )
|
||||
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'"
|
||||
).splitlines()
|
||||
for dp in dps:
|
||||
if dp:
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
info( "*** Removing OVS datapaths\n" )
|
||||
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
|
||||
if dps:
|
||||
sh( "ovs-vsctl " + " -- ".join( "--if-exists del-br " + dp
|
||||
for dp in dps if dp ) )
|
||||
# And in case the above didn't work...
|
||||
dps = sh( "ovs-vsctl --timeout=1 list-br" ).strip().splitlines()
|
||||
for dp in dps:
|
||||
info( "*** Removing OVS datapaths" )
|
||||
dps = sh("ovs-vsctl list-br").split( '\n' )
|
||||
for dp in dps:
|
||||
if dp:
|
||||
sh( 'ovs-vsctl del-br ' + dp )
|
||||
|
||||
info( "*** Removing all links of the pattern foo-ethX\n" )
|
||||
links = sh( "ip link show | "
|
||||
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'"
|
||||
).splitlines()
|
||||
# Delete blocks of links
|
||||
n = 1000 # chunk size
|
||||
for i in range( 0, len( links ), n ):
|
||||
cmd = ';'.join( 'ip link del %s' % link
|
||||
for link in links[ i : i + n ] )
|
||||
sh( '( %s ) 2> /dev/null' % cmd )
|
||||
info( "*** Removing all links of the pattern foo-ethX\n" )
|
||||
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
|
||||
for link in links:
|
||||
if link != '':
|
||||
sh( "ip link del " + link )
|
||||
|
||||
if 'tap9' in sh( 'ip link show' ):
|
||||
info( "*** Removing tap9 - assuming it's from cluster edition\n" )
|
||||
sh( 'ip link del tap9' )
|
||||
|
||||
info( "*** Killing stale mininet node processes\n" )
|
||||
killprocs( 'mininet:' )
|
||||
|
||||
info( "*** Shutting down stale tunnels\n" )
|
||||
killprocs( 'Tunnel=Ethernet' )
|
||||
killprocs( '.ssh/mn')
|
||||
sh( 'rm -f ~/.ssh/mn/*' )
|
||||
|
||||
# Call any additional cleanup code if necessary
|
||||
for callback in cls.callbacks:
|
||||
callback()
|
||||
|
||||
info( "*** Cleanup complete.\n" )
|
||||
|
||||
@classmethod
|
||||
def addCleanupCallback( cls, callback ):
|
||||
"Add cleanup callback"
|
||||
if callback not in cls.callbacks:
|
||||
cls.callbacks.append( callback )
|
||||
|
||||
|
||||
cleanup = Cleanup.cleanup
|
||||
addCleanupCallback = Cleanup.addCleanupCallback
|
||||
info( "*** Cleanup complete.\n" )
|
||||
|
||||
+63
-182
@@ -31,13 +31,10 @@ from os import isatty
|
||||
from select import poll, POLLIN
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import atexit
|
||||
|
||||
from mininet.log import info, output, error
|
||||
from mininet.term import makeTerms, runX11
|
||||
from mininet.util import ( quietRun, dumpNodeConnections,
|
||||
dumpPorts )
|
||||
from mininet.term import makeTerms
|
||||
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
|
||||
|
||||
class CLI( Cmd ):
|
||||
"Simple command-line interface to talk to nodes."
|
||||
@@ -45,13 +42,14 @@ class CLI( Cmd ):
|
||||
prompt = 'mininet> '
|
||||
|
||||
def __init__( self, mininet, stdin=sys.stdin, script=None ):
|
||||
"""Start and run interactive or batch mode CLI
|
||||
mininet: Mininet network object
|
||||
stdin: standard input for CLI
|
||||
script: script to run in batch mode"""
|
||||
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()
|
||||
@@ -59,66 +57,31 @@ class CLI( Cmd ):
|
||||
self.inputFile = script
|
||||
Cmd.__init__( self )
|
||||
info( '*** Starting CLI:\n' )
|
||||
|
||||
if self.inputFile:
|
||||
self.do_source( self.inputFile )
|
||||
return
|
||||
|
||||
self.initReadline()
|
||||
self.run()
|
||||
|
||||
readlineInited = False
|
||||
|
||||
@classmethod
|
||||
def initReadline( cls ):
|
||||
"Set up history if readline is available"
|
||||
# Only set up readline once to prevent multiplying the history file
|
||||
if cls.readlineInited:
|
||||
return
|
||||
cls.readlineInited = True
|
||||
try:
|
||||
from readline import ( read_history_file, write_history_file,
|
||||
set_history_length )
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
history_path = os.path.expanduser( '~/.mininet_history' )
|
||||
if os.path.isfile( history_path ):
|
||||
read_history_file( history_path )
|
||||
set_history_length( 1000 )
|
||||
atexit.register( lambda: write_history_file( history_path ) )
|
||||
|
||||
def run( self ):
|
||||
"Run our cmdloop(), catching KeyboardInterrupt"
|
||||
while True:
|
||||
try:
|
||||
# Make sure no nodes are still waiting
|
||||
for node in self.mn.values():
|
||||
for node in self.nodelist:
|
||||
while node.waiting:
|
||||
info( 'stopping', node, '\n' )
|
||||
node.sendInt()
|
||||
node.waitOutput()
|
||||
node.monitor()
|
||||
if self.isatty():
|
||||
quietRun( 'stty echo sane intr ^C' )
|
||||
quietRun( 'stty sane' )
|
||||
self.cmdloop()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
# Output a message - unless it's also interrupted
|
||||
# pylint: disable=broad-except
|
||||
try:
|
||||
output( '\nInterrupt\n' )
|
||||
except Exception:
|
||||
pass
|
||||
# pylint: enable=broad-except
|
||||
output( '\nInterrupt\n' )
|
||||
|
||||
def emptyline( self ):
|
||||
"Don't repeat last command when you hit return."
|
||||
pass
|
||||
|
||||
def getLocals( self ):
|
||||
"Local variable bindings for py command"
|
||||
self.locals.update( self.mn )
|
||||
return self.locals
|
||||
# Disable pylint "Unused argument: 'arg's'" messages, as well as
|
||||
# "method could be a function" warning, since each CLI function
|
||||
# must have the same interface
|
||||
# pylint: disable-msg=R0201
|
||||
|
||||
helpStr = (
|
||||
'You may also send a command to a node using:\n'
|
||||
@@ -147,72 +110,46 @@ class CLI( Cmd ):
|
||||
|
||||
def do_nodes( self, _line ):
|
||||
"List all nodes."
|
||||
nodes = ' '.join( sorted( self.mn ) )
|
||||
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
|
||||
output( 'available nodes are: \n%s\n' % nodes )
|
||||
|
||||
def do_ports( self, _line ):
|
||||
"display ports and interfaces for each switch"
|
||||
dumpPorts( self.mn.switches )
|
||||
|
||||
def do_net( self, _line ):
|
||||
"List network connections."
|
||||
dumpNodeConnections( self.mn.values() )
|
||||
dumpNodeConnections( self.nodelist )
|
||||
|
||||
def do_sh( self, line ):
|
||||
"""Run an external shell command
|
||||
Usage: sh [cmd args]"""
|
||||
assert self # satisfy pylint and allow override
|
||||
"Run an external shell command"
|
||||
call( line, shell=True )
|
||||
|
||||
# do_py() and do_px() need to catch any exception during eval()/exec()
|
||||
# pylint: disable=broad-except
|
||||
# do_py() needs to catch any exception during eval()
|
||||
# pylint: disable-msg=W0703
|
||||
|
||||
def do_py( self, line ):
|
||||
"""Evaluate a Python expression.
|
||||
Node names may be used, e.g.: py h1.cmd('ls')"""
|
||||
Node names may be used, e.g.: h1.cmd('ls')"""
|
||||
try:
|
||||
result = eval( line, globals(), self.getLocals() )
|
||||
result = eval( line, globals(), self.locals )
|
||||
if not result:
|
||||
return
|
||||
elif isinstance( result, str ):
|
||||
output( result + '\n' )
|
||||
else:
|
||||
output( repr( result ) + '\n' )
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# We are in fact using the exec() pseudo-function
|
||||
# pylint: disable=exec-used
|
||||
# pylint: 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 as e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# pylint: enable=broad-except,exec-used
|
||||
|
||||
def do_pingall( self, line ):
|
||||
def do_pingall( self, _line ):
|
||||
"Ping between all hosts."
|
||||
self.mn.pingAll( line )
|
||||
self.mn.pingAll()
|
||||
|
||||
def do_pingpair( self, _line ):
|
||||
"Ping between first two hosts, useful for testing."
|
||||
self.mn.pingPair()
|
||||
|
||||
def do_pingallfull( self, _line ):
|
||||
"Ping between all hosts, returns all ping results."
|
||||
self.mn.pingAllFull()
|
||||
|
||||
def do_pingpairfull( self, _line ):
|
||||
"Ping between first two hosts, returns all ping results."
|
||||
self.mn.pingPairFull()
|
||||
|
||||
def do_iperf( self, line ):
|
||||
"""Simple iperf TCP test between two (optionally specified) hosts.
|
||||
Usage: iperf node1 node2"""
|
||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||
args = line.split()
|
||||
if not args:
|
||||
self.mn.iperf()
|
||||
@@ -220,19 +157,18 @@ class CLI( Cmd ):
|
||||
hosts = []
|
||||
err = False
|
||||
for arg in args:
|
||||
if arg not in self.mn:
|
||||
if arg not in self.nodemap:
|
||||
err = True
|
||||
error( "node '%s' not in network\n" % arg )
|
||||
else:
|
||||
hosts.append( self.mn[ arg ] )
|
||||
hosts.append( self.nodemap[ arg ] )
|
||||
if not err:
|
||||
self.mn.iperf( hosts )
|
||||
else:
|
||||
error( 'invalid number of args: iperf src dst\n' )
|
||||
|
||||
def do_iperfudp( self, line ):
|
||||
"""Simple iperf UDP test between two (optionally specified) hosts.
|
||||
Usage: iperfudp bw node1 node2"""
|
||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||
args = line.split()
|
||||
if not args:
|
||||
self.mn.iperf( l4Type='UDP' )
|
||||
@@ -241,11 +177,11 @@ class CLI( Cmd ):
|
||||
hosts = []
|
||||
err = False
|
||||
for arg in args[ 1:3 ]:
|
||||
if arg not in self.mn:
|
||||
if arg not in self.nodemap:
|
||||
err = True
|
||||
error( "node '%s' not in network\n" % arg )
|
||||
else:
|
||||
hosts.append( self.mn[ arg ] )
|
||||
hosts.append( self.nodemap[ arg ] )
|
||||
if not err:
|
||||
self.mn.iperf( hosts, l4Type='UDP', udpBw=udpBw )
|
||||
else:
|
||||
@@ -254,18 +190,17 @@ class CLI( Cmd ):
|
||||
|
||||
def do_intfs( self, _line ):
|
||||
"List interfaces."
|
||||
for node in self.mn.values():
|
||||
for node in self.nodelist:
|
||||
output( '%s: %s\n' %
|
||||
( node.name, ','.join( node.intfNames() ) ) )
|
||||
( node.name, ','.join( node.intfNames() ) ) )
|
||||
|
||||
def do_dump( self, _line ):
|
||||
"Dump node info."
|
||||
for node in self.mn.values():
|
||||
for node in self.nodelist:
|
||||
output( '%s\n' % repr( node ) )
|
||||
|
||||
def do_link( self, line ):
|
||||
"""Bring link(s) between two nodes up or down.
|
||||
Usage: link node1 node2 [up/down]"""
|
||||
"Bring link(s) between two nodes up or down."
|
||||
args = line.split()
|
||||
if len(args) != 3:
|
||||
error( 'invalid number of args: link end1 end2 [up down]\n' )
|
||||
@@ -275,39 +210,24 @@ class CLI( Cmd ):
|
||||
self.mn.configLinkStatus( *args )
|
||||
|
||||
def do_xterm( self, line, term='xterm' ):
|
||||
"""Spawn xterm(s) for the given node(s).
|
||||
Usage: xterm node1 node2 ..."""
|
||||
"Spawn xterm(s) for the given node(s)."
|
||||
args = line.split()
|
||||
if not args:
|
||||
error( 'usage: %s node1 node2 ...\n' % term )
|
||||
else:
|
||||
for arg in args:
|
||||
if arg not in self.mn:
|
||||
if arg not in self.nodemap:
|
||||
error( "node '%s' not in network\n" % arg )
|
||||
else:
|
||||
node = self.mn[ arg ]
|
||||
node = self.nodemap[ arg ]
|
||||
self.mn.terms += makeTerms( [ node ], term = term )
|
||||
|
||||
def do_x( self, line ):
|
||||
"""Create an X11 tunnel to the given node,
|
||||
optionally starting a client.
|
||||
Usage: x node [cmd args]"""
|
||||
args = line.split()
|
||||
if not args:
|
||||
error( 'usage: x node [cmd args]...\n' )
|
||||
else:
|
||||
node = self.mn[ args[ 0 ] ]
|
||||
cmd = args[ 1: ]
|
||||
self.mn.terms += runX11( node, cmd )
|
||||
|
||||
def do_gterm( self, line ):
|
||||
"""Spawn gnome-terminal(s) for the given node(s).
|
||||
Usage: gterm node1 node2 ..."""
|
||||
"Spawn gnome-terminal(s) for the given node(s)."
|
||||
self.do_xterm( line, term='gterm' )
|
||||
|
||||
def do_exit( self, _line ):
|
||||
"Exit"
|
||||
assert self # satisfy pylint and allow override
|
||||
return 'exited by user command'
|
||||
|
||||
def do_quit( self, line ):
|
||||
@@ -324,8 +244,7 @@ class CLI( Cmd ):
|
||||
return isatty( self.stdin.fileno() )
|
||||
|
||||
def do_noecho( self, line ):
|
||||
"""Run an interactive command with echoing turned off.
|
||||
Usage: noecho [cmd args]"""
|
||||
"Run an interactive command with echoing turned off."
|
||||
if self.isatty():
|
||||
quietRun( 'stty -echo' )
|
||||
self.default( line )
|
||||
@@ -333,8 +252,7 @@ class CLI( Cmd ):
|
||||
quietRun( 'stty echo' )
|
||||
|
||||
def do_source( self, line ):
|
||||
"""Read commands from an input file.
|
||||
Usage: source <file>"""
|
||||
"Read commands from an input file."
|
||||
args = line.split()
|
||||
if len(args) != 1:
|
||||
error( 'usage: source <file>\n' )
|
||||
@@ -349,12 +267,10 @@ class CLI( Cmd ):
|
||||
break
|
||||
except IOError:
|
||||
error( 'error reading file %s\n' % args[ 0 ] )
|
||||
self.inputFile.close()
|
||||
self.inputFile = None
|
||||
|
||||
def do_dpctl( self, line ):
|
||||
"""Run dpctl (or ovs-ofctl) command on all switches.
|
||||
Usage: dpctl command [arg1] [arg2] ..."""
|
||||
"Run dpctl command on all switches."
|
||||
args = line.split()
|
||||
if len(args) < 1:
|
||||
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
|
||||
@@ -370,62 +286,37 @@ class CLI( Cmd ):
|
||||
elapsed = time.time() - start
|
||||
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
|
||||
|
||||
def do_links( self, _line ):
|
||||
"Report on links"
|
||||
for link in self.mn.links:
|
||||
output( link, link.status(), '\n' )
|
||||
|
||||
def do_switch( self, line ):
|
||||
"Starts or stops a switch"
|
||||
args = line.split()
|
||||
if len(args) != 2:
|
||||
error( 'invalid number of args: switch <switch name>'
|
||||
'{start, stop}\n' )
|
||||
return
|
||||
sw = args[ 0 ]
|
||||
command = args[ 1 ]
|
||||
if sw not in self.mn or self.mn.get( sw ) not in self.mn.switches:
|
||||
error( 'invalid switch: %s\n' % args[ 1 ] )
|
||||
else:
|
||||
sw = args[ 0 ]
|
||||
command = args[ 1 ]
|
||||
if command == 'start':
|
||||
self.mn.get( sw ).start( self.mn.controllers )
|
||||
elif command == 'stop':
|
||||
self.mn.get( sw ).stop( deleteIntfs=False )
|
||||
else:
|
||||
error( 'invalid command: '
|
||||
'switch <switch name> {start, stop}\n' )
|
||||
|
||||
def default( self, line ):
|
||||
"""Called on an input line when the command prefix is not recognized.
|
||||
Overridden to run shell commands when a node is the first
|
||||
CLI argument. Past the first CLI argument, node names are
|
||||
automatically replaced with corresponding IP addrs."""
|
||||
Overridden to run shell commands when a node is the first CLI argument.
|
||||
Past the first CLI argument, node names are automatically replaced with
|
||||
corresponding IP addrs."""
|
||||
|
||||
first, args, line = self.parseline( line )
|
||||
if 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:
|
||||
error( '*** Please enter a command for node: %s <cmd>\n'
|
||||
% 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 )
|
||||
@@ -443,7 +334,7 @@ class CLI( Cmd ):
|
||||
if False and self.inputFile:
|
||||
key = self.inputFile.read( 1 )
|
||||
if key is not '':
|
||||
node.write( key )
|
||||
node.write(key)
|
||||
else:
|
||||
self.inputFile = None
|
||||
if isReadable( self.inPoller ):
|
||||
@@ -455,18 +346,8 @@ class CLI( Cmd ):
|
||||
if not node.waiting:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
# There is an at least one race condition here, since
|
||||
# it's possible to interrupt ourselves after we've
|
||||
# read data but before it has been printed.
|
||||
node.sendInt()
|
||||
|
||||
def precmd( self, line ):
|
||||
"allow for comments in the cli"
|
||||
if '#' in line:
|
||||
line = line.split( '#' )[ 0 ]
|
||||
return line
|
||||
|
||||
|
||||
# Helper functions
|
||||
|
||||
def isReadable( poller ):
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../examples
|
||||
+90
-273
@@ -26,14 +26,14 @@ Link: basic link class for creating veth pairs
|
||||
|
||||
from mininet.log import info, error, debug
|
||||
from mininet.util import makeIntfPair
|
||||
from time import sleep
|
||||
import re
|
||||
|
||||
class Intf( object ):
|
||||
|
||||
"Basic interface object that can configure itself."
|
||||
|
||||
def __init__( self, name, node=None, port=None, link=None,
|
||||
mac=None, **params ):
|
||||
def __init__( self, name, node=None, port=None, link=None, **params ):
|
||||
"""name: interface name (e.g. h1-eth0)
|
||||
node: owning node (where this intf most likely lives)
|
||||
link: parent link if we're part of a link
|
||||
@@ -41,21 +41,9 @@ class Intf( object ):
|
||||
self.node = node
|
||||
self.name = name
|
||||
self.link = link
|
||||
self.mac = mac
|
||||
self.ip, self.prefixLen = None, None
|
||||
|
||||
# if interface is lo, we know the ip is 127.0.0.1.
|
||||
# This saves an ifconfig command per node
|
||||
if self.name == 'lo':
|
||||
self.ip = '127.0.0.1'
|
||||
self.prefixLen = 8
|
||||
self.mac, self.ip, self.prefixLen = None, None, None
|
||||
# Add to node (and move ourselves if necessary )
|
||||
if node:
|
||||
moveIntfFn = params.pop( 'moveIntfFn', None )
|
||||
if moveIntfFn:
|
||||
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
|
||||
else:
|
||||
node.addIntf( self, port=port )
|
||||
node.addIntf( self, port=port )
|
||||
# Save params for future reference
|
||||
self.params = params
|
||||
self.config( **params )
|
||||
@@ -76,9 +64,6 @@ class Intf( object ):
|
||||
self.ip, self.prefixLen = ipstr.split( '/' )
|
||||
return self.ifconfig( ipstr, 'up' )
|
||||
else:
|
||||
if prefixLen is None:
|
||||
raise Exception( 'No prefix length set for IP address %s'
|
||||
% ( ipstr, ) )
|
||||
self.ip, self.prefixLen = ipstr, prefixLen
|
||||
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
|
||||
|
||||
@@ -95,10 +80,7 @@ class Intf( object ):
|
||||
|
||||
def updateIP( self ):
|
||||
"Return updated IP address based on ifconfig"
|
||||
# use pexec instead of node.cmd so that we dont read
|
||||
# backgrounded output from the cli.
|
||||
ifconfig, _err, _exitCode = self.node.pexec(
|
||||
'ifconfig %s' % self.name )
|
||||
ifconfig = self.ifconfig()
|
||||
ips = self._ipMatchRegex.findall( ifconfig )
|
||||
self.ip = ips[ 0 ] if ips else None
|
||||
return self.ip
|
||||
@@ -110,19 +92,6 @@ class Intf( object ):
|
||||
self.mac = macs[ 0 ] if macs else None
|
||||
return self.mac
|
||||
|
||||
# Instead of updating ip and mac separately,
|
||||
# use one ifconfig call to do it simultaneously.
|
||||
# This saves an ifconfig command, which improves performance.
|
||||
|
||||
def updateAddr( self ):
|
||||
"Return IP address and MAC address based on ifconfig."
|
||||
ifconfig = self.ifconfig()
|
||||
ips = self._ipMatchRegex.findall( ifconfig )
|
||||
macs = self._macMatchRegex.findall( ifconfig )
|
||||
self.ip = ips[ 0 ] if ips else None
|
||||
self.mac = macs[ 0 ] if macs else None
|
||||
return self.ip, self.mac
|
||||
|
||||
def IP( self ):
|
||||
"Return IP address"
|
||||
return self.ip
|
||||
@@ -134,15 +103,8 @@ class Intf( object ):
|
||||
def isUp( self, setUp=False ):
|
||||
"Return whether interface is up"
|
||||
if setUp:
|
||||
cmdOutput = self.ifconfig( 'up' )
|
||||
# no output indicates success
|
||||
if cmdOutput:
|
||||
error( "Error setting %s up: %s " % ( self.name, cmdOutput ) )
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return "UP" in self.ifconfig()
|
||||
self.ifconfig( 'up' )
|
||||
return "UP" in self.ifconfig()
|
||||
|
||||
def rename( self, newname ):
|
||||
"Rename interface"
|
||||
@@ -164,13 +126,13 @@ class Intf( object ):
|
||||
method: config method name
|
||||
param: arg=value (ignore if value=None)
|
||||
value may also be list or dict"""
|
||||
name, value = list( param.items() )[ 0 ]
|
||||
name, value = param.items()[ 0 ]
|
||||
f = getattr( self, method, None )
|
||||
if not f or value is None:
|
||||
return
|
||||
if isinstance( value, list ):
|
||||
if type( value ) is list:
|
||||
result = f( *value )
|
||||
elif isinstance( value, dict ):
|
||||
elif type( value ) is dict:
|
||||
result = f( **value )
|
||||
else:
|
||||
result = f( value )
|
||||
@@ -193,25 +155,15 @@ class Intf( object ):
|
||||
self.setParam( r, 'setIP', ip=ip )
|
||||
self.setParam( r, 'isUp', up=up )
|
||||
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
|
||||
self.updateIP()
|
||||
self.updateMAC()
|
||||
return r
|
||||
|
||||
def delete( self ):
|
||||
"Delete interface"
|
||||
self.cmd( 'ip link del ' + self.name )
|
||||
# We used to do this, but it slows us down:
|
||||
# if self.node.inNamespace:
|
||||
# Link may have been dumped into root NS
|
||||
# quietRun( 'ip link del ' + self.name )
|
||||
self.node.delIntf( self )
|
||||
self.link = None
|
||||
|
||||
def status( self ):
|
||||
"Return intf status as a string"
|
||||
links, _err, _result = self.node.pexec( 'ip link show' )
|
||||
if self.name in links:
|
||||
return "OK"
|
||||
else:
|
||||
return "MISSING"
|
||||
# Does it help to sleep to let things run?
|
||||
sleep( 0.001 )
|
||||
|
||||
def __repr__( self ):
|
||||
return '<%s %s>' % ( self.__class__.__name__, self.name )
|
||||
@@ -225,19 +177,15 @@ class TCIntf( Intf ):
|
||||
Allows specification of bandwidth limits (various methods)
|
||||
as well as delay, loss and max queue length"""
|
||||
|
||||
# The parameters we use seem to work reasonably up to 1 Gb/sec
|
||||
# For higher data rates, we will probably need to change them.
|
||||
bwParamMax = 1000
|
||||
|
||||
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False ):
|
||||
enable_ecn=False, enable_red=False ):
|
||||
"Return tc commands to set bandwidth"
|
||||
|
||||
cmds, parent = [], ' root '
|
||||
|
||||
if bw and ( bw < 0 or bw > self.bwParamMax ):
|
||||
error( 'Bandwidth limit', bw, 'is outside supported range 0..%d'
|
||||
% self.bwParamMax, '- ignoring\n' )
|
||||
if bw and ( bw < 0 or bw > 1000 ):
|
||||
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
|
||||
|
||||
elif bw is not None:
|
||||
# BL: this seems a bit brittle...
|
||||
if ( speedup > 0 and
|
||||
@@ -248,59 +196,58 @@ class TCIntf( Intf ):
|
||||
# are specifying the correct sizes. For now I have used
|
||||
# the same settings we had in the mininet-hifi code.
|
||||
if use_hfsc:
|
||||
cmds += [ '%s qdisc add dev %s root handle 5:0 hfsc default 1',
|
||||
'%s class add dev %s parent 5:0 classid 5:1 hfsc sc '
|
||||
cmds += [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
|
||||
'%s class add dev %s parent 1:0 classid 1:1 hfsc sc '
|
||||
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
|
||||
elif use_tbf:
|
||||
if latency_ms is None:
|
||||
latency_ms = 15.0 * 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 ' +
|
||||
'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 ' +
|
||||
'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 loss and ( loss < 0 or loss > 100 ):
|
||||
if delay and delay < 0:
|
||||
error( 'Negative delay', delay, '\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 %.5f ' % loss if loss 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"
|
||||
@@ -308,41 +255,16 @@ class TCIntf( Intf ):
|
||||
debug(" *** executing command: %s\n" % c)
|
||||
return self.cmd( c )
|
||||
|
||||
def config( self, bw=None, delay=None, jitter=None, loss=None,
|
||||
gro=False, txo=True, rxo=True,
|
||||
speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False,
|
||||
max_queue_size=None, **params ):
|
||||
"""Configure the port and set its properties.
|
||||
bw: bandwidth in b/s (e.g. '10m')
|
||||
delay: transmit delay (e.g. '1ms' )
|
||||
jitter: jitter (e.g. '1ms')
|
||||
loss: loss (e.g. '1%' )
|
||||
gro: enable GRO (False)
|
||||
txo: enable transmit checksum offload (True)
|
||||
rxo: enable receive checksum offload (True)
|
||||
speedup: experimental switch-side bw option
|
||||
use_hfsc: use HFSC scheduling
|
||||
use_tbf: use TBF scheduling
|
||||
latency_ms: TBF latency parameter
|
||||
enable_ecn: enable ECN (False)
|
||||
enable_red: enable RED (False)
|
||||
max_queue_size: queue limit parameter for netem"""
|
||||
|
||||
# Support old names for parameters
|
||||
gro = not params.pop( 'disable_gro', not gro )
|
||||
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)
|
||||
|
||||
def on( isOn ):
|
||||
"Helper method: bool -> 'on'/'off'"
|
||||
return 'on' if isOn else 'off'
|
||||
|
||||
# Set offload parameters with ethool
|
||||
self.cmd( 'ethtool -K', self,
|
||||
'gro', on( gro ),
|
||||
'tx', on( txo ),
|
||||
'rx', on( rxo ) )
|
||||
# Disable GRO
|
||||
if disable_gro:
|
||||
self.cmd( 'ethtool -K %s gro off' % self )
|
||||
|
||||
# Optimization: return if nothing else to configure
|
||||
# Question: what happens if we want to reset things?
|
||||
@@ -351,46 +273,34 @@ class TCIntf( Intf ):
|
||||
return
|
||||
|
||||
# Clear existing configuration
|
||||
tcoutput = self.tc( '%s qdisc show dev %s' )
|
||||
if "priomap" not in tcoutput and "noqueue" not in tcoutput:
|
||||
cmds = [ '%s qdisc del dev %s root' ]
|
||||
else:
|
||||
cmds = []
|
||||
cmds = [ '%s qdisc del dev %s root' ]
|
||||
|
||||
# Bandwidth limits via various methods
|
||||
bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
|
||||
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 [] ) +
|
||||
( ['%.5f%% loss' % loss ] if loss is not None else [] ) +
|
||||
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
|
||||
( ['%d%% loss' % loss ] if loss is not None else [] ) +
|
||||
( [ '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
|
||||
|
||||
@@ -400,11 +310,10 @@ class Link( object ):
|
||||
"""A basic link is just a veth pair.
|
||||
Other types of links could be tunnels, link emulators, etc.."""
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def __init__( self, node1, node2, port1=None, port2=None,
|
||||
intfName1=None, intfName2=None, addr1=None, addr2=None,
|
||||
intfName1=None, intfName2=None,
|
||||
intf=Intf, cls1=None, cls2=None, params1=None,
|
||||
params2=None, fast=True ):
|
||||
params2=None ):
|
||||
"""Create veth link to another node, making two new interfaces.
|
||||
node1: first node
|
||||
node2: second node
|
||||
@@ -417,157 +326,65 @@ class Link( object ):
|
||||
params1: parameters for interface 1
|
||||
params2: parameters for interface 2"""
|
||||
# This is a bit awkward; it seems that having everything in
|
||||
# params is more orthogonal, but being able to specify
|
||||
# in-line arguments is more convenient! So we support both.
|
||||
if params1 is None:
|
||||
params1 = {}
|
||||
if params2 is None:
|
||||
params2 = {}
|
||||
# Allow passing in params1=params2
|
||||
if params2 is params1:
|
||||
params2 = dict( params1 )
|
||||
if port1 is not None:
|
||||
params1[ 'port' ] = port1
|
||||
if port2 is not None:
|
||||
params2[ 'port' ] = port2
|
||||
if 'port' not in params1:
|
||||
params1[ 'port' ] = node1.newPort()
|
||||
if 'port' not in params2:
|
||||
params2[ 'port' ] = node2.newPort()
|
||||
# params would be more orthogonal, but being able to specify
|
||||
# in-line arguments is more convenient!
|
||||
if port1 is None:
|
||||
port1 = node1.newPort()
|
||||
if port2 is None:
|
||||
port2 = node2.newPort()
|
||||
if not intfName1:
|
||||
intfName1 = self.intfName( node1, params1[ 'port' ] )
|
||||
intfName1 = self.intfName( node1, port1 )
|
||||
if not intfName2:
|
||||
intfName2 = self.intfName( node2, params2[ 'port' ] )
|
||||
intfName2 = self.intfName( node2, port2 )
|
||||
|
||||
self.fast = fast
|
||||
if fast:
|
||||
params1.setdefault( 'moveIntfFn', self._ignore )
|
||||
params2.setdefault( 'moveIntfFn', self._ignore )
|
||||
self.makeIntfPair( intfName1, intfName2, addr1, addr2,
|
||||
node1, node2, deleteIntfs=False )
|
||||
else:
|
||||
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
|
||||
self.makeIntfPair( intfName1, intfName2 )
|
||||
|
||||
if not cls1:
|
||||
cls1 = intf
|
||||
if not cls2:
|
||||
cls2 = intf
|
||||
if not params1:
|
||||
params1 = {}
|
||||
if not params2:
|
||||
params2 = {}
|
||||
|
||||
intf1 = cls1( name=intfName1, node=node1,
|
||||
link=self, mac=addr1, **params1 )
|
||||
intf2 = cls2( name=intfName2, node=node2,
|
||||
link=self, mac=addr2, **params2 )
|
||||
intf1 = cls1( name=intfName1, node=node1, port=port1,
|
||||
link=self, **params1 )
|
||||
intf2 = cls2( name=intfName2, node=node2, port=port2,
|
||||
link=self, **params2 )
|
||||
|
||||
# All we are is dust in the wind, and our two interfaces
|
||||
self.intf1, self.intf2 = intf1, intf2
|
||||
# pylint: enable=too-many-branches
|
||||
|
||||
@staticmethod
|
||||
def _ignore( *args, **kwargs ):
|
||||
"Ignore any arguments"
|
||||
pass
|
||||
|
||||
def intfName( self, node, n ):
|
||||
@classmethod
|
||||
def intfName( cls, node, n ):
|
||||
"Construct a canonical interface name node-ethN for interface n."
|
||||
# Leave this as an instance method for now
|
||||
assert self
|
||||
return node.name + '-eth' + repr( n )
|
||||
|
||||
@classmethod
|
||||
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
|
||||
node1=None, node2=None, deleteIntfs=True ):
|
||||
def makeIntfPair( cls, intf1, intf2 ):
|
||||
"""Create pair of interfaces
|
||||
intfname1: name for interface 1
|
||||
intfname2: name for interface 2
|
||||
addr1: MAC address for interface 1 (optional)
|
||||
addr2: MAC address for interface 2 (optional)
|
||||
node1: home node for interface 1 (optional)
|
||||
node2: home node for interface 2 (optional)
|
||||
(override this method [and possibly delete()]
|
||||
intf1: name of interface 1
|
||||
intf2: name of interface 2
|
||||
(override this class method [and possibly delete()]
|
||||
to change link type)"""
|
||||
# Leave this as a class method for now
|
||||
assert cls
|
||||
return makeIntfPair( intfname1, intfname2, addr1, addr2, node1, node2,
|
||||
deleteIntfs=deleteIntfs )
|
||||
makeIntfPair( intf1, intf2 )
|
||||
|
||||
def delete( self ):
|
||||
"Delete this link"
|
||||
self.intf1.delete()
|
||||
self.intf1 = None
|
||||
self.intf2.delete()
|
||||
self.intf2 = None
|
||||
|
||||
def stop( self ):
|
||||
"Override to stop and clean up link as needed"
|
||||
self.delete()
|
||||
|
||||
def status( self ):
|
||||
"Return link status as a string"
|
||||
return "(%s %s)" % ( self.intf1.status(), self.intf2.status() )
|
||||
|
||||
def __str__( self ):
|
||||
return '%s<->%s' % ( self.intf1, self.intf2 )
|
||||
|
||||
|
||||
class OVSIntf( Intf ):
|
||||
"Patch interface on an OVSSwitch"
|
||||
|
||||
def ifconfig( self, *args ):
|
||||
cmd = ' '.join( args )
|
||||
if cmd == 'up':
|
||||
# OVSIntf is always up
|
||||
return
|
||||
else:
|
||||
raise Exception( 'OVSIntf cannot do ifconfig ' + cmd )
|
||||
|
||||
|
||||
class OVSLink( Link ):
|
||||
"""Link that makes patch links between OVSSwitches
|
||||
Warning: in testing we have found that no more
|
||||
than ~64 OVS patch links should be used in row."""
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"See Link.__init__() for options"
|
||||
from mininet.node import OVSSwitch
|
||||
self.isPatchLink = False
|
||||
if ( isinstance( node1, OVSSwitch ) and
|
||||
isinstance( node2, OVSSwitch ) ):
|
||||
self.isPatchLink = True
|
||||
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
def makeIntfPair( self, *args, **kwargs ):
|
||||
"Usually delegated to OVSSwitch"
|
||||
if self.isPatchLink:
|
||||
return None, None
|
||||
else:
|
||||
return Link.makeIntfPair( *args, **kwargs )
|
||||
|
||||
|
||||
class TCLink( Link ):
|
||||
"Link with symmetric TC interfaces configured via opts"
|
||||
def __init__( self, node1, node2, port1=None, port2=None,
|
||||
intfName1=None, intfName2=None,
|
||||
addr1=None, addr2=None, **params ):
|
||||
intfName1=None, intfName2=None, **params ):
|
||||
Link.__init__( self, node1, node2, port1=port1, port2=port2,
|
||||
intfName1=intfName1, intfName2=intfName2,
|
||||
cls1=TCIntf,
|
||||
cls2=TCIntf,
|
||||
addr1=addr1, addr2=addr2,
|
||||
params1=params,
|
||||
params2=params )
|
||||
|
||||
|
||||
class TCULink( TCLink ):
|
||||
"""TCLink with default settings optimized for UserSwitch
|
||||
(txo=rxo=0/False). Unfortunately with recent Linux kernels,
|
||||
enabling TX and RX checksum offload on veth pairs doesn't work
|
||||
well with UserSwitch: either it gets terrible performance or
|
||||
TCP packets with bad checksums are generated, forwarded, and
|
||||
*dropped* due to having bad checksums! OVS and LinuxBridge seem
|
||||
to cope with this somehow, but it is likely to be an issue with
|
||||
many software Ethernet bridges."""
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
kwargs.update( txo=False, rxo=False )
|
||||
TCLink.__init__( self, *args, **kwargs )
|
||||
params2=params)
|
||||
|
||||
+25
-24
@@ -11,11 +11,11 @@ import types
|
||||
OUTPUT = 25
|
||||
|
||||
LEVELS = { 'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
|
||||
# change this to logging.INFO to get printouts when running unit tests
|
||||
LOGLEVELDEFAULT = OUTPUT
|
||||
@@ -57,19 +57,21 @@ class StreamHandlerNoNewline( logging.StreamHandler ):
|
||||
|
||||
class Singleton( type ):
|
||||
"""Singleton pattern from Wikipedia
|
||||
See http://en.wikipedia.org/wiki/Singleton_Pattern
|
||||
See http://en.wikipedia.org/wiki/SingletonPattern#Python
|
||||
|
||||
Intended to be used as a __metaclass_ param, as shown for the class
|
||||
below."""
|
||||
below.
|
||||
|
||||
def __init__( cls, name, bases, dict_ ):
|
||||
super( Singleton, cls ).__init__( name, bases, dict_ )
|
||||
cls.instance = None
|
||||
Changed cls first args to mcs to satisfy pylint."""
|
||||
|
||||
def __call__( cls, *args, **kw ):
|
||||
if cls.instance is None:
|
||||
cls.instance = super( Singleton, cls ).__call__( *args, **kw )
|
||||
return cls.instance
|
||||
def __init__( mcs, name, bases, dict_ ):
|
||||
super( Singleton, mcs ).__init__( name, bases, dict_ )
|
||||
mcs.instance = None
|
||||
|
||||
def __call__( mcs, *args, **kw ):
|
||||
if mcs.instance is None:
|
||||
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
|
||||
return mcs.instance
|
||||
|
||||
|
||||
class MininetLogger( Logger, object ):
|
||||
@@ -115,7 +117,7 @@ class MininetLogger( Logger, object ):
|
||||
Convenience function to support lowercase names.
|
||||
levelName: level name from LEVELS"""
|
||||
level = LOGLEVELDEFAULT
|
||||
if levelname is not None:
|
||||
if levelname != None:
|
||||
if levelname not in LEVELS:
|
||||
raise Exception( 'unknown levelname seen in setLogLevel' )
|
||||
else:
|
||||
@@ -124,8 +126,8 @@ class MininetLogger( Logger, object ):
|
||||
self.setLevel( level )
|
||||
self.handlers[ 0 ].setLevel( level )
|
||||
|
||||
# pylint: disable=method-hidden
|
||||
# "An attribute inherited from mininet.log hide this method" (sic)
|
||||
# pylint: disable-msg=E0202
|
||||
# "An attribute inherited from mininet.log hide this method"
|
||||
# Not sure why this is occurring - this function definitely gets called.
|
||||
|
||||
# See /usr/lib/python2.5/logging/__init__.py; modified from warning()
|
||||
@@ -142,7 +144,7 @@ class MininetLogger( Logger, object ):
|
||||
if self.isEnabledFor( OUTPUT ):
|
||||
self._log( OUTPUT, msg, args, kwargs )
|
||||
|
||||
# pylint: enable=method-hidden
|
||||
# pylint: enable-msg=E0202
|
||||
|
||||
lg = MininetLogger()
|
||||
|
||||
@@ -160,7 +162,7 @@ def makeListCompatible( fn ):
|
||||
"Generated function. Closure-ish."
|
||||
if len( args ) == 1:
|
||||
return fn( *args )
|
||||
args = ' '.join( str( arg ) for arg in args )
|
||||
args = ' '.join( [ str( arg ) for arg in args ] )
|
||||
return fn( args )
|
||||
|
||||
# Fix newfn's name and docstring
|
||||
@@ -168,10 +170,9 @@ def makeListCompatible( fn ):
|
||||
setattr( newfn, '__doc__', fn.__doc__ )
|
||||
return newfn
|
||||
|
||||
_loggers = lg.info, lg.output, lg.warn, lg.error, lg.debug
|
||||
_loggers = tuple( makeListCompatible( logger )
|
||||
for logger in _loggers )
|
||||
lg.info, lg.output, lg.warn, lg.error, lg.debug = _loggers
|
||||
info, output, warn, error, debug = _loggers
|
||||
info, output, warn, error, debug = (
|
||||
lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
|
||||
makeListCompatible( f ) for f in
|
||||
lg.info, lg.output, lg.warn, lg.error, lg.debug ]
|
||||
|
||||
setLogLevel = lg.setLogLevel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"Module dependency utility functions for Mininet."
|
||||
|
||||
from mininet.util import quietRun, BaseString
|
||||
from mininet.util import quietRun
|
||||
from mininet.log import info, error, debug
|
||||
from os import environ
|
||||
|
||||
@@ -19,7 +19,7 @@ def modprobe( mod ):
|
||||
return quietRun( [ 'modprobe', mod ] )
|
||||
|
||||
OF_KMOD = 'ofdatapath'
|
||||
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
|
||||
OVS_KMOD = 'openvswitch_mod'
|
||||
TUN = 'tun'
|
||||
|
||||
def moduleDeps( subtract=None, add=None ):
|
||||
@@ -28,9 +28,9 @@ def moduleDeps( subtract=None, add=None ):
|
||||
add: string or list of module names to add, if not already loaded"""
|
||||
subtract = subtract if subtract is not None else []
|
||||
add = add if add is not None else []
|
||||
if isinstance( subtract, BaseString ):
|
||||
if type( subtract ) is str:
|
||||
subtract = [ subtract ]
|
||||
if isinstance( add, BaseString ):
|
||||
if type( add ) is str:
|
||||
add = [ add ]
|
||||
for mod in subtract:
|
||||
if mod in lsmod():
|
||||
@@ -48,8 +48,8 @@ def moduleDeps( subtract=None, add=None ):
|
||||
modprobeOutput = modprobe( mod )
|
||||
if modprobeOutput:
|
||||
error( 'Error inserting ' + mod +
|
||||
' - is it installed and available via modprobe?\n' +
|
||||
'Error was: "%s"\n' % modprobeOutput )
|
||||
' - is it installed and available via modprobe?\n' +
|
||||
'Error was: "%s"\n' % modprobeOutput )
|
||||
if mod not in lsmod():
|
||||
error( 'Failed to insert ' + mod + ' - quitting.\n' )
|
||||
exit( 1 )
|
||||
@@ -63,6 +63,6 @@ def pathCheck( *args, **kwargs ):
|
||||
for arg in args:
|
||||
if not quietRun( 'which ' + arg ):
|
||||
error( 'Cannot find required executable %s.\n' % arg +
|
||||
'Please make sure that %s is installed ' % moduleName +
|
||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||
'Please make sure that %s is installed ' % moduleName +
|
||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||
exit( 1 )
|
||||
|
||||
+99
-457
@@ -90,35 +90,25 @@ import os
|
||||
import re
|
||||
import select
|
||||
import signal
|
||||
import random
|
||||
|
||||
from time import sleep
|
||||
from itertools import chain, groupby
|
||||
from math import ceil
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import info, error, debug, output, warn
|
||||
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
|
||||
Controller )
|
||||
from mininet.nodelib import NAT
|
||||
from mininet.log import info, error, debug, output
|
||||
from mininet.node import Host, OVSKernelSwitch, Controller
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
|
||||
macColonHex, ipStr, ipParse, netParse, ipAdd,
|
||||
waitListening, BaseString )
|
||||
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.3.0d4"
|
||||
|
||||
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
|
||||
@@ -144,9 +134,7 @@ class Mininet( object ):
|
||||
self.intf = intf
|
||||
self.ipBase = ipBase
|
||||
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
|
||||
hostIP = ( 0xffffffff >> self.prefixLen ) & self.ipBaseNum
|
||||
# Start for address allocation
|
||||
self.nextIP = hostIP if hostIP > 0 else 1
|
||||
self.nextIP = 1 # start for address allocation
|
||||
self.inNamespace = inNamespace
|
||||
self.xterms = xterms
|
||||
self.cleanup = cleanup
|
||||
@@ -156,12 +144,10 @@ class Mininet( object ):
|
||||
self.numCores = numCores()
|
||||
self.nextCore = 0 # next core for pinning hosts to CPUs
|
||||
self.listenPort = listenPort
|
||||
self.waitConn = waitConnected
|
||||
|
||||
self.hosts = []
|
||||
self.switches = []
|
||||
self.controllers = []
|
||||
self.links = []
|
||||
|
||||
self.nameToNode = {} # name to Node (Host/Switch) objects
|
||||
|
||||
@@ -173,36 +159,6 @@ class Mininet( object ):
|
||||
if topo and build:
|
||||
self.build()
|
||||
|
||||
def waitConnected( self, timeout=None, delay=.5 ):
|
||||
"""wait for each switch to connect to a controller,
|
||||
up to 5 seconds
|
||||
timeout: time to wait, or None to wait indefinitely
|
||||
delay: seconds to sleep per iteration
|
||||
returns: True if all switches are connected"""
|
||||
info( '*** Waiting for switches to connect\n' )
|
||||
time = 0
|
||||
remaining = list( self.switches )
|
||||
while True:
|
||||
for switch in tuple( remaining ):
|
||||
if switch.connected():
|
||||
info( '%s ' % switch )
|
||||
remaining.remove( switch )
|
||||
if not remaining:
|
||||
info( '\n' )
|
||||
return True
|
||||
if timeout is not None and time > timeout:
|
||||
break
|
||||
sleep( delay )
|
||||
time += delay
|
||||
warn( 'Timed out after %d seconds\n' % time )
|
||||
for switch in remaining:
|
||||
if not switch.connected():
|
||||
warn( 'Warning: %s is not connected to a controller\n'
|
||||
% switch.name )
|
||||
else:
|
||||
remaining.remove( switch )
|
||||
return not remaining
|
||||
|
||||
def addHost( self, name, cls=None, **params ):
|
||||
"""Add host.
|
||||
name: name of host to add
|
||||
@@ -212,10 +168,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
|
||||
@@ -228,24 +183,6 @@ class Mininet( object ):
|
||||
self.nameToNode[ name ] = h
|
||||
return h
|
||||
|
||||
def delNode( self, node, nodes=None):
|
||||
"""Delete node
|
||||
node: node to delete
|
||||
nodes: optional list to delete from (e.g. self.hosts)"""
|
||||
if nodes is None:
|
||||
nodes = ( self.hosts if node in self.hosts else
|
||||
( self.switches if node in self.switches else
|
||||
( self.controllers if node in self.controllers else
|
||||
[] ) ) )
|
||||
node.stop( deleteIntfs=True )
|
||||
node.terminate()
|
||||
nodes.remove( node )
|
||||
del self.nameToNode[ node.name ]
|
||||
|
||||
def delHost( self, host ):
|
||||
"Delete a host"
|
||||
self.delNode( host, nodes=self.hosts )
|
||||
|
||||
def addSwitch( self, name, cls=None, **params ):
|
||||
"""Add switch.
|
||||
name: name of switch to add
|
||||
@@ -264,63 +201,19 @@ class Mininet( object ):
|
||||
self.nameToNode[ name ] = sw
|
||||
return sw
|
||||
|
||||
def delSwitch( self, switch ):
|
||||
"Delete a switch"
|
||||
self.delNode( switch, nodes=self.switches )
|
||||
|
||||
def addController( self, name='c0', controller=None, **params ):
|
||||
"""Add controller.
|
||||
controller: Controller class"""
|
||||
# Get controller class
|
||||
if not controller:
|
||||
controller = self.controller
|
||||
# Construct new controller if one is not given
|
||||
if isinstance( name, Controller ):
|
||||
controller_new = name
|
||||
# Pylint thinks controller is a str()
|
||||
# pylint: disable=maybe-no-member
|
||||
name = controller_new.name
|
||||
# pylint: enable=maybe-no-member
|
||||
else:
|
||||
controller_new = controller( name, **params )
|
||||
# Add new controller to net
|
||||
controller_new = controller( name, **params )
|
||||
if controller_new: # allow controller-less setups
|
||||
self.controllers.append( controller_new )
|
||||
self.nameToNode[ name ] = controller_new
|
||||
return controller_new
|
||||
|
||||
def delController( self, controller ):
|
||||
"""Delete a controller
|
||||
Warning - does not reconfigure switches, so they
|
||||
may still attempt to connect to it!"""
|
||||
self.delNode( controller )
|
||||
|
||||
def addNAT( self, name='nat0', connect=True, inNamespace=False,
|
||||
**params):
|
||||
"""Add a NAT to the Mininet network
|
||||
name: name of NAT node
|
||||
connect: switch to connect to | True (s1) | None
|
||||
inNamespace: create in a network namespace
|
||||
params: other NAT node params, notably:
|
||||
ip: used as default gateway address"""
|
||||
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
|
||||
subnet=self.ipBase, **params )
|
||||
# find first switch and create link
|
||||
if connect:
|
||||
if not isinstance( connect, Node ):
|
||||
# Use first switch if not specified
|
||||
connect = self.switches[ 0 ]
|
||||
# Connect the nat to the switch
|
||||
self.addLink( nat, connect )
|
||||
# Set the default route on hosts
|
||||
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
|
||||
for host in self.hosts:
|
||||
if host.inNamespace:
|
||||
host.setDefaultRoute( 'via %s' % natIP )
|
||||
return nat
|
||||
|
||||
# BL: We now have four ways to look up nodes
|
||||
# This may (should?) be cleaned up in the future.
|
||||
# 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:
|
||||
@@ -331,99 +224,21 @@ class Mininet( object ):
|
||||
"Convenience alias for getNodeByName"
|
||||
return self.getNodeByName( *args )
|
||||
|
||||
# Even more convenient syntax for node lookup and iteration
|
||||
def __getitem__( self, key ):
|
||||
"net[ name ] operator: Return node with given name"
|
||||
return self.nameToNode[ key ]
|
||||
|
||||
def __delitem__( self, key ):
|
||||
"del net[ name ] operator - delete node with given name"
|
||||
self.delNode( self.nameToNode[ key ] )
|
||||
|
||||
def __iter__( self ):
|
||||
"return iterator over node names"
|
||||
for node in chain( self.hosts, self.switches, self.controllers ):
|
||||
yield node.name
|
||||
|
||||
def __len__( self ):
|
||||
"returns number of nodes in net"
|
||||
return ( len( self.hosts ) + len( self.switches ) +
|
||||
len( self.controllers ) )
|
||||
|
||||
def __contains__( self, item ):
|
||||
"returns True if net contains named node"
|
||||
return item in self.nameToNode
|
||||
|
||||
def keys( self ):
|
||||
"return a list of all node names or net's keys"
|
||||
return list( self )
|
||||
|
||||
def values( self ):
|
||||
"return a list of all nodes or net's values"
|
||||
return [ self[name] for name in self ]
|
||||
|
||||
def items( self ):
|
||||
"return (key,value) tuple list for every node in net"
|
||||
return zip( self.keys(), self.values() )
|
||||
|
||||
@staticmethod
|
||||
def randMac():
|
||||
"Return a random, non-multicast MAC address"
|
||||
return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
|
||||
0x020000000000 )
|
||||
|
||||
def addLink( self, node1, node2, port1=None, port2=None,
|
||||
cls=None, **params ):
|
||||
""""Add a link from node1 to node2
|
||||
node1: source node (or name)
|
||||
node2: dest node (or name)
|
||||
port1: source port (optional)
|
||||
port2: dest port (optional)
|
||||
cls: link class (optional)
|
||||
params: additional link params (optional)
|
||||
node1: source node
|
||||
node2: dest node
|
||||
port1: source port
|
||||
port2: dest port
|
||||
returns: link object"""
|
||||
# Accept node objects or names
|
||||
node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
|
||||
node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
|
||||
options = dict( params )
|
||||
# Port is optional
|
||||
if port1 is not None:
|
||||
options.setdefault( 'port1', port1 )
|
||||
if port2 is not None:
|
||||
options.setdefault( 'port2', port2 )
|
||||
if self.intf is not None:
|
||||
options.setdefault( 'intf', self.intf )
|
||||
# Set default MAC - this should probably be in Link
|
||||
options.setdefault( 'addr1', self.randMac() )
|
||||
options.setdefault( 'addr2', self.randMac() )
|
||||
cls = self.link if cls is None else cls
|
||||
link = cls( node1, node2, **options )
|
||||
self.links.append( link )
|
||||
return link
|
||||
|
||||
def delLink( self, link ):
|
||||
"Remove a link from this network"
|
||||
link.delete()
|
||||
self.links.remove( link )
|
||||
|
||||
def linksBetween( self, node1, node2 ):
|
||||
"Return Links between node1 and node2"
|
||||
return [ link for link in self.links
|
||||
if ( node1, node2 ) in (
|
||||
( link.intf1.node, link.intf2.node ),
|
||||
( link.intf2.node, link.intf1.node ) ) ]
|
||||
|
||||
def delLinkBetween( self, node1, node2, index=0, allLinks=False ):
|
||||
"""Delete link(s) between node1 and node2
|
||||
index: index of link to delete if multiple links (0)
|
||||
allLinks: ignore index and delete all such links (False)
|
||||
returns: deleted link(s)"""
|
||||
links = self.linksBetween( node1, node2 )
|
||||
if not allLinks:
|
||||
links = [ links[ index ] ]
|
||||
for link in links:
|
||||
self.delLink( link )
|
||||
return links
|
||||
defaults = { 'port1': port1,
|
||||
'port2': port2,
|
||||
'intf': self.intf }
|
||||
defaults.update( params )
|
||||
if not cls:
|
||||
cls = self.link
|
||||
return cls( node1, node2, **defaults )
|
||||
|
||||
def configHosts( self ):
|
||||
"Configure a set of hosts."
|
||||
@@ -431,7 +246,7 @@ class Mininet( object ):
|
||||
info( host.name + ' ' )
|
||||
intf = host.defaultIntf()
|
||||
if intf:
|
||||
host.configDefault()
|
||||
host.configDefault( defaultRoute=intf )
|
||||
else:
|
||||
# Don't configure nonexistent intf
|
||||
host.configDefault( ip=None, mac=None )
|
||||
@@ -441,6 +256,7 @@ class Mininet( object ):
|
||||
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
|
||||
# This may not be the right place to do this, but
|
||||
# it needs to be done somewhere.
|
||||
host.cmd( 'ifconfig lo up' )
|
||||
info( '\n' )
|
||||
|
||||
def buildFromTopo( self, topo=None ):
|
||||
@@ -455,18 +271,10 @@ class Mininet( object ):
|
||||
|
||||
info( '*** Creating network\n' )
|
||||
|
||||
if not self.controllers and self.controller:
|
||||
if not self.controllers:
|
||||
# Add a default controller
|
||||
info( '*** Adding controller\n' )
|
||||
classes = self.controller
|
||||
if not isinstance( classes, list ):
|
||||
classes = [ classes ]
|
||||
for i, cls in enumerate( classes ):
|
||||
# Allow Controller objects because nobody understands partial()
|
||||
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():
|
||||
@@ -475,32 +283,29 @@ class Mininet( object ):
|
||||
|
||||
info( '\n*** Adding switches:\n' )
|
||||
for switchName in topo.switches():
|
||||
# A bit ugly: add batch parameter if appropriate
|
||||
params = topo.nodeInfo( switchName)
|
||||
cls = params.get( 'cls', self.switch )
|
||||
if hasattr( cls, 'batchStartup' ):
|
||||
params.setdefault( 'batch', True )
|
||||
self.addSwitch( switchName, **params )
|
||||
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
|
||||
info( switchName + ' ' )
|
||||
|
||||
info( '\n*** Adding links:\n' )
|
||||
for srcName, dstName, params in topo.links(
|
||||
sort=True, withInfo=True ):
|
||||
self.addLink( **params )
|
||||
info( '(%s, %s) ' % ( srcName, dstName ) )
|
||||
for srcName, dstName in topo.links(sort=True):
|
||||
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
|
||||
params = topo.linkInfo( srcName, dstName )
|
||||
srcPort, dstPort = topo.port( srcName, dstName )
|
||||
self.addLink( src, dst, srcPort, dstPort, **params )
|
||||
info( '(%s, %s) ' % ( src.name, dst.name ) )
|
||||
|
||||
info( '\n' )
|
||||
|
||||
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()
|
||||
@@ -512,9 +317,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' )
|
||||
@@ -540,59 +342,32 @@ class Mininet( object ):
|
||||
self.build()
|
||||
info( '*** Starting controller\n' )
|
||||
for controller in self.controllers:
|
||||
info( controller.name + ' ')
|
||||
controller.start()
|
||||
info( '\n' )
|
||||
info( '*** Starting %s switches\n' % len( self.switches ) )
|
||||
for switch in self.switches:
|
||||
info( switch.name + ' ')
|
||||
switch.start( self.controllers )
|
||||
started = {}
|
||||
for swclass, switches in groupby(
|
||||
sorted( self.switches,
|
||||
key=lambda s: str( type( s ) ) ), type ):
|
||||
switches = tuple( switches )
|
||||
if hasattr( swclass, 'batchStartup' ):
|
||||
success = swclass.batchStartup( switches )
|
||||
started.update( { s: s for s in success } )
|
||||
info( '\n' )
|
||||
if self.waitConn:
|
||||
self.waitConnected()
|
||||
|
||||
def stop( self ):
|
||||
"Stop the controller(s), switches and hosts"
|
||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||
for controller in self.controllers:
|
||||
info( controller.name + ' ' )
|
||||
controller.stop()
|
||||
info( '\n' )
|
||||
if self.terms:
|
||||
info( '*** Stopping %i terms\n' % len( self.terms ) )
|
||||
self.stopXterms()
|
||||
info( '*** Stopping %i links\n' % len( self.links ) )
|
||||
for link in self.links:
|
||||
info( '.' )
|
||||
link.stop()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
stopped = {}
|
||||
for swclass, switches in groupby(
|
||||
sorted( self.switches,
|
||||
key=lambda s: str( type( s ) ) ), type ):
|
||||
switches = tuple( switches )
|
||||
if hasattr( swclass, 'batchShutdown' ):
|
||||
success = swclass.batchShutdown( switches )
|
||||
stopped.update( { s: s for s in success } )
|
||||
for switch in self.switches:
|
||||
info( switch.name + ' ' )
|
||||
if switch not in stopped:
|
||||
switch.stop()
|
||||
switch.terminate()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
||||
for host in self.hosts:
|
||||
info( host.name + ' ' )
|
||||
host.terminate()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
for switch in self.switches:
|
||||
info( switch.name + ' ' )
|
||||
switch.stop()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||
for controller in self.controllers:
|
||||
info( controller.name + ' ' )
|
||||
controller.stop()
|
||||
info( '\n*** Done\n' )
|
||||
|
||||
def run( self, test, *args, **kwargs ):
|
||||
@@ -612,13 +387,13 @@ class Mininet( object ):
|
||||
if hosts is None:
|
||||
hosts = self.hosts
|
||||
poller = select.poll()
|
||||
h1 = hosts[ 0 ] # so we can call class method fdToNode
|
||||
Node = hosts[ 0 ] # so we can call class method fdToNode
|
||||
for host in hosts:
|
||||
poller.register( host.stdout )
|
||||
while True:
|
||||
ready = poller.poll( timeoutms )
|
||||
for fd, event in ready:
|
||||
host = h1.fdToNode( fd )
|
||||
host = Node.fdToNode( fd )
|
||||
if event & select.POLLIN:
|
||||
line = host.readline()
|
||||
if line is not None:
|
||||
@@ -635,20 +410,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
|
||||
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
|
||||
return (1, 0)
|
||||
r = r'(\d+) packets transmitted, (\d+) received'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is None:
|
||||
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
|
||||
@@ -661,15 +435,8 @@ class Mininet( object ):
|
||||
output( '%s -> ' % node.name )
|
||||
for dest in hosts:
|
||||
if node != dest:
|
||||
opts = ''
|
||||
if timeout:
|
||||
opts = '-W %s' % timeout
|
||||
if dest.intfs:
|
||||
result = node.cmd( 'ping -c1 %s %s' %
|
||||
(opts, dest.IP()) )
|
||||
sent, received = self._parsePing( result )
|
||||
else:
|
||||
sent, received = 0, 0
|
||||
result = node.cmd( 'ping -c1 ' + dest.IP() )
|
||||
sent, received = self._parsePing( result )
|
||||
packets += sent
|
||||
if received > sent:
|
||||
error( '*** Error: received too many packets' )
|
||||
@@ -679,84 +446,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+)( packets)? received'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is None:
|
||||
error( '*** Error: could not parse ping output: %s\n' %
|
||||
pingOutput )
|
||||
return errorTuple
|
||||
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
|
||||
r = r'rtt min/avg/max/mdev = '
|
||||
r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is None:
|
||||
if received == 0:
|
||||
return errorTuple
|
||||
error( '*** Error: could not parse ping output: %s\n' %
|
||||
pingOutput )
|
||||
return errorTuple
|
||||
rttmin = float( m.group( 1 ) )
|
||||
rttavg = float( m.group( 2 ) )
|
||||
rttmax = float( m.group( 3 ) )
|
||||
rttdev = float( m.group( 4 ) )
|
||||
return sent, received, rttmin, rttavg, rttmax, rttdev
|
||||
|
||||
def pingFull( self, hosts=None, timeout=None ):
|
||||
"""Ping between all specified hosts and return all data.
|
||||
hosts: list of hosts
|
||||
timeout: time to wait for a response, as string
|
||||
returns: all ping data; see function body."""
|
||||
# should we check if running?
|
||||
# Each value is a tuple: (src, dsd, [all ping outputs])
|
||||
all_outputs = []
|
||||
if not hosts:
|
||||
hosts = self.hosts
|
||||
output( '*** Ping: testing ping reachability\n' )
|
||||
for node in hosts:
|
||||
output( '%s -> ' % node.name )
|
||||
for dest in hosts:
|
||||
if node != dest:
|
||||
opts = ''
|
||||
if timeout:
|
||||
opts = '-W %s' % timeout
|
||||
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
|
||||
outputs = self._parsePingFull( result )
|
||||
sent, received, rttmin, rttavg, rttmax, rttdev = outputs
|
||||
all_outputs.append( (node, dest, outputs) )
|
||||
output( ( '%s ' % dest.name ) if received else 'X ' )
|
||||
output( '\n' )
|
||||
output( "*** Results: \n" )
|
||||
for outputs in all_outputs:
|
||||
src, dest, ping_outputs = outputs
|
||||
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
|
||||
output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
|
||||
output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
|
||||
(rttmin, rttavg, rttmax, rttdev) )
|
||||
return all_outputs
|
||||
|
||||
def pingAll( self, timeout=None ):
|
||||
def pingAll( self ):
|
||||
"""Ping between all hosts.
|
||||
returns: ploss packet loss percentage"""
|
||||
return self.ping( timeout=timeout )
|
||||
return self.ping()
|
||||
|
||||
def pingPair( self ):
|
||||
"""Ping between first two hosts, useful for testing.
|
||||
@@ -764,17 +462,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.
|
||||
@@ -791,48 +478,41 @@ class Mininet( object ):
|
||||
|
||||
# XXX This should be cleaned up
|
||||
|
||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
|
||||
seconds=5, port=5001):
|
||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
|
||||
"""Run iperf between two hosts.
|
||||
hosts: list of hosts; if None, uses first and last hosts
|
||||
hosts: list of hosts; if None, uses opposite hosts
|
||||
l4Type: string, one of [ TCP, UDP ]
|
||||
udpBw: bandwidth target for UDP test
|
||||
fmt: iperf format argument if any
|
||||
seconds: iperf time to transmit
|
||||
port: iperf port
|
||||
returns: two-element array of [ server, client ] speeds
|
||||
note: send() is buffered, so client rate can be much higher than
|
||||
the actual transmission rate; on an unloaded system, server
|
||||
rate should be much closer to the actual receive rate"""
|
||||
hosts = hosts or [ self.hosts[ 0 ], self.hosts[ -1 ] ]
|
||||
assert len( hosts ) == 2
|
||||
returns: results two-element array of server and client speeds"""
|
||||
if not quietRun( 'which telnet' ):
|
||||
error( 'Cannot find telnet in $PATH - required for iperf test' )
|
||||
return
|
||||
if not hosts:
|
||||
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
|
||||
else:
|
||||
assert len( hosts ) == 2
|
||||
client, server = hosts
|
||||
output( '*** Iperf: testing', l4Type, 'bandwidth between',
|
||||
client, 'and', server, '\n' )
|
||||
output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
|
||||
output( "%s and %s\n" % ( client.name, server.name ) )
|
||||
server.cmd( 'killall -9 iperf' )
|
||||
iperfArgs = 'iperf -p %d ' % port
|
||||
iperfArgs = 'iperf '
|
||||
bwArgs = ''
|
||||
if l4Type == 'UDP':
|
||||
iperfArgs += '-u '
|
||||
bwArgs = '-b ' + udpBw + ' '
|
||||
elif l4Type != 'TCP':
|
||||
raise Exception( 'Unexpected l4 type: %s' % l4Type )
|
||||
if fmt:
|
||||
iperfArgs += '-f %s ' % fmt
|
||||
server.sendCmd( iperfArgs + '-s' )
|
||||
if l4Type == 'TCP':
|
||||
if not waitListening( client, server.IP(), port ):
|
||||
raise Exception( 'Could not connect to iperf on port %d'
|
||||
% port )
|
||||
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
|
||||
server.IP() + ' ' + bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
server.sendCmd( iperfArgs + '-s', printPid=True )
|
||||
servout = ''
|
||||
# We want the last *b/sec from the iperf server output
|
||||
# for TCP, there are two of them because of waitListening
|
||||
count = 2 if l4Type == 'TCP' else 1
|
||||
while len( re.findall( '/sec', servout ) ) < count:
|
||||
servout += server.monitor( timeoutms=5000 )
|
||||
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()):
|
||||
output('waiting for iperf to start up...')
|
||||
sleep(.5)
|
||||
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
|
||||
bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
server.sendInt()
|
||||
servout += server.waitOutput()
|
||||
debug( 'Server output: %s\n' % servout )
|
||||
@@ -842,51 +522,6 @@ class Mininet( object ):
|
||||
output( '*** Results: %s\n' % result )
|
||||
return result
|
||||
|
||||
def runCpuLimitTest( self, cpu, duration=5 ):
|
||||
"""run CPU limit test with 'while true' processes.
|
||||
cpu: desired CPU fraction of each host
|
||||
duration: test duration in seconds (integer)
|
||||
returns a single list of measured CPU fractions as floats.
|
||||
"""
|
||||
pct = cpu * 100
|
||||
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
|
||||
hosts = self.hosts
|
||||
cores = int( quietRun( 'nproc' ) )
|
||||
# number of processes to run a while loop on per host
|
||||
num_procs = int( ceil( cores * cpu ) )
|
||||
pids = {}
|
||||
for h in hosts:
|
||||
pids[ h ] = []
|
||||
for _core in range( num_procs ):
|
||||
h.cmd( 'while true; do a=1; done &' )
|
||||
pids[ h ].append( h.cmd( 'echo $!' ).strip() )
|
||||
outputs = {}
|
||||
time = {}
|
||||
# get the initial cpu time for each host
|
||||
for host in hosts:
|
||||
outputs[ host ] = []
|
||||
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
|
||||
host, 'r' ) as f:
|
||||
time[ host ] = float( f.read() )
|
||||
for _ in range( duration ):
|
||||
sleep( 1 )
|
||||
for host in hosts:
|
||||
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
|
||||
host, 'r' ) as f:
|
||||
readTime = float( f.read() )
|
||||
outputs[ host ].append( ( ( readTime - time[ host ] )
|
||||
/ 1000000000 ) / cores * 100 )
|
||||
time[ host ] = readTime
|
||||
for h, pids in pids.items():
|
||||
for pid in pids:
|
||||
h.cmd( 'kill -9 %s' % pid )
|
||||
cpu_fractions = []
|
||||
for _host, outputs in outputs.items():
|
||||
for pct in outputs:
|
||||
cpu_fractions.append( pct )
|
||||
output( '*** Results: %s\n' % cpu_fractions )
|
||||
return cpu_fractions
|
||||
|
||||
# BL: I think this can be rewritten now that we have
|
||||
# a real link class.
|
||||
def configLinkStatus( self, src, dst, status ):
|
||||
@@ -899,8 +534,10 @@ class Mininet( object ):
|
||||
elif dst not in self.nameToNode:
|
||||
error( 'dst not in network: %s\n' % dst )
|
||||
else:
|
||||
src = self.nameToNode[ src ]
|
||||
dst = self.nameToNode[ dst ]
|
||||
if type( src ) is str:
|
||||
src = self.nameToNode[ src ]
|
||||
if type( dst ) is str:
|
||||
dst = self.nameToNode[ dst ]
|
||||
connections = src.connectionsTo( dst )
|
||||
if len( connections ) == 0:
|
||||
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
|
||||
@@ -926,7 +563,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
|
||||
|
||||
@@ -967,7 +609,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 ]
|
||||
|
||||
+270
-788
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user