Merge branch '2.0dev' into 2.0merge
Conflicts: .gitignore .pylint examples/miniedit.py mininet/node.py mininet/util.py util/install.sh util/vm/install-mininet-vm.sh
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
*.py diff=python
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
mnexec
|
mnexec
|
||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
|
*.1
|
||||||
|
*.xcodeproj
|
||||||
|
*.xcworkspace
|
||||||
|
\#*\#
|
||||||
mininet.egg-info
|
mininet.egg-info
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
|
doc/html
|
||||||
|
doc/latex
|
||||||
|
trunk
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ ignore=CVS
|
|||||||
# Pickle collected data for later comparisons.
|
# Pickle collected data for later comparisons.
|
||||||
persistent=yes
|
persistent=yes
|
||||||
|
|
||||||
# Set the cache size for astng objects.
|
|
||||||
cache-size=500
|
|
||||||
|
|
||||||
# List of plugins (as comma separated values of python modules names) to load,
|
# List of plugins (as comma separated values of python modules names) to load,
|
||||||
# usually to register additional checkers.
|
# usually to register additional checkers.
|
||||||
load-plugins=
|
load-plugins=
|
||||||
@@ -35,24 +32,15 @@ load-plugins=
|
|||||||
|
|
||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
# Enable only checker(s) with the given id(s). This option conflicts with the
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
# disable-checker option
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
#enable-checker=
|
# multiple time.
|
||||||
|
#enable=
|
||||||
|
|
||||||
# Enable all checker(s) except those with the given id(s). This option
|
# Disable the message, report, category or checker with the given id(s). You
|
||||||
# conflicts with the enable-checker option
|
# can either give multiple identifier separated by comma (,) or put this option
|
||||||
#disable-checker=
|
# multiple time (only on the command line, not in the configuration file where
|
||||||
|
# it should appear only once).
|
||||||
# Enable all messages in the listed categories (IRCWEF).
|
|
||||||
#enable-msg-cat=
|
|
||||||
|
|
||||||
# Disable all messages in the listed categories (IRCWEF).
|
|
||||||
disable-msg-cat=IR
|
|
||||||
|
|
||||||
# Enable the message(s) with the given id(s).
|
|
||||||
#enable-msg=
|
|
||||||
|
|
||||||
# Disable the message(s) with the given id(s).
|
|
||||||
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
|
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +48,7 @@ disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,
|
|||||||
|
|
||||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||||
# (visual studio) and html
|
# (visual studio) and html
|
||||||
output-format=text
|
output-format=colorized
|
||||||
|
|
||||||
# Include message's id in outpu
|
# Include message's id in outpu
|
||||||
include-ids=yes
|
include-ids=yes
|
||||||
@@ -276,7 +264,7 @@ int-import-graph=
|
|||||||
max-line-length=80
|
max-line-length=80
|
||||||
|
|
||||||
# Maximum number of lines in a module
|
# Maximum number of lines in a module
|
||||||
max-module-lines=1000
|
max-module-lines=1500
|
||||||
|
|
||||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
# tab).
|
# tab).
|
||||||
|
|||||||
@@ -1,29 +1,54 @@
|
|||||||
|
|
||||||
Mininet Installation/Configuration Notes
|
Mininet Installation/Configuration Notes
|
||||||
|
|
||||||
Mininet 1.0.0
|
Mininet 2.0.0rc1
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
The supported installation methods for Mininet are 1) using
|
The supported installation methods for Mininet are 1) using
|
||||||
a pre-built VM image, and 2) native installation on Ubuntu or Debian.
|
a pre-built VM image, and 2) native installation on Ubuntu. You
|
||||||
|
can also easily create your own Mininet VM image (3).
|
||||||
|
|
||||||
(Other distributions may be supported in the future - if you would
|
(Other distributions may be supported in the future - if you would
|
||||||
like to contribute an installation script, we would welcome it!)
|
like to contribute an installation script, we would welcome it!)
|
||||||
|
|
||||||
1. Easiest "install" - use our pre-built VM image!
|
1. Easiest "installation" - use our pre-built VM image!
|
||||||
|
|
||||||
The easiest way to get Mininet running is to start with one of our pre-built
|
The easiest way to get Mininet running is to start with one of our pre-built
|
||||||
virtual machine images from http://openflow.org/mininet
|
virtual machine images from http://openflow.org/mininet
|
||||||
|
|
||||||
Boot up the VM image, log in, and follow the instructions on the wiki page.
|
Boot up the VM image, log in, and follow the instructions on the wiki page.
|
||||||
|
|
||||||
An additional advantage of using the VM image is that it doesn't mess with
|
One advantage of using the VM image is that it doesn't mess with
|
||||||
your native OS installation or damage it in any way.
|
your native OS installation or damage it in any way.
|
||||||
|
|
||||||
2. Native installation (experimental!) for Ubuntu 10.04 LTS
|
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.
|
||||||
|
|
||||||
If you are running Ubuntu 10.04 LTS (or possibly Debian 5), you may be
|
2. Next-easiest option: use our Ubuntu package!
|
||||||
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+
|
||||||
|
|
||||||
|
sudo apt-get install mininet
|
||||||
|
|
||||||
|
Note: if you are upgrading from an older version of Mininet, make sure you
|
||||||
|
remove the old OVS from /usr/local:
|
||||||
|
|
||||||
|
sudo rm /usr/local/bin/ovs*
|
||||||
|
sudo rm /usr/local/sbin/ovs*
|
||||||
|
|
||||||
|
3. Native installation from source on Ubuntu 11.10+
|
||||||
|
|
||||||
|
If you're reading this, you've probably already done so, but the command to
|
||||||
|
download the Mininet source code is;
|
||||||
|
|
||||||
|
git clone git://openflow.org/mininet.git
|
||||||
|
|
||||||
|
If you are running Ubuntu, you may be able to use our handy install.sh script,
|
||||||
|
which is in mininet/util.
|
||||||
|
|
||||||
WARNING: USE AT YOUR OWN RISK!
|
WARNING: USE AT YOUR OWN RISK!
|
||||||
|
|
||||||
@@ -34,240 +59,59 @@ 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
|
before you run it, and you should make sure your system and home
|
||||||
directory are backed up just in case!
|
directory are backed up just in case!
|
||||||
|
|
||||||
|
To install Mininet itself, the OpenFlow reference implementation, and
|
||||||
|
Open vSwitch, you may use:
|
||||||
|
|
||||||
|
$ mininet/util/install.sh -fnv
|
||||||
|
|
||||||
|
This should be reasonably quick and the following command should work
|
||||||
|
after the installation:
|
||||||
|
|
||||||
|
$ sudo mn --test pingall
|
||||||
|
|
||||||
To install ALL of the software which we use for OpenFlow tutorials,
|
To install ALL of the software which we use for OpenFlow tutorials,
|
||||||
you may use
|
including NOX classic, the OpenFlow WireShark dissector, the oftest
|
||||||
|
framework, and other potentially useful software (and to add some stuff
|
||||||
|
to /etc/sysctl.conf which may or may not be useful) you may use
|
||||||
|
|
||||||
$ mininet/util/install.sh
|
$ mininet/util/install.sh -a
|
||||||
|
|
||||||
This takes about 20-30 minutes.
|
This takes about 20 minutes on our test system.
|
||||||
|
|
||||||
Alternately, you can install just the pieces you need.
|
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||||
|
|
||||||
We recommend the following steps, in order:
|
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
|
||||||
|
is easy! First, create a new Ubuntu VM. Then, run
|
||||||
|
|
||||||
[a) On Debian 5, first install a Mininet-compatible kernel:
|
$ wget https://raw.github.com/mininet/mininet/util/vm/install-mininet-vm.sh
|
||||||
$ mininet/util/install.sh -k
|
$ time install-mininet-vm.sh
|
||||||
Reboot and run 'uname -r' to make sure you're running the new kernel.]
|
|
||||||
|
|
||||||
b) Install mininet and its dependencies:
|
5. Installation on other Linux distributions
|
||||||
$ mininet/util/install.sh -n
|
|
||||||
|
|
||||||
c) Install OpenFlow 1.0 and associated useful software
|
|
||||||
$ mininet/util/install.sh -f
|
|
||||||
|
|
||||||
d) Install Open vSwitch and its kernel module
|
Although we don't support other Linux distributions directly, it should be
|
||||||
$ mininet/util/install.sh -vm
|
possible to install and run Mininet with some degree of manual effort.
|
||||||
|
|
||||||
e) If you wish to install the version of NOX we use in the tutorial:
|
In general, you must have:
|
||||||
$ mininet/util/install.sh -x
|
|
||||||
|
|
||||||
Note: NOX development is progressing over time, so after you complete
|
* A Linux kernel compiled with network namespace support enabled
|
||||||
the tutorial you may wish to install the latest and greatest NOX from
|
|
||||||
noxrepo.org.
|
|
||||||
|
|
||||||
Good luck! Some additional installation notes are provided below, for
|
* An OpenFlow implementation (either the reference user or kernel
|
||||||
the brave and/or Linux-savvy, or those who are trying to understand what
|
space implementations, or Open vSwitch.) Appropriate kernel modules
|
||||||
is installed and why.
|
(e.g. tun and ofdatapath for the reference kernel implementation) must
|
||||||
|
be loaded.
|
||||||
|
|
||||||
p.s. Note that only one instance of Mininet is currently supported on a single
|
* Python, `bash`, `ping`, `iperf`, etc.
|
||||||
machine - that's one reason we recommend using a VM to run it.
|
|
||||||
|
|
||||||
---
|
* Root privileges (required for network device access)
|
||||||
|
|
||||||
Mininet Manual Installation Notes
|
We encourage contribution of patches to the `install.sh` script to support
|
||||||
|
other Linux distributions.
|
||||||
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.)
|
|
||||||
|
|
||||||
If you wish to try to create a VM to run Mininet, you may also wish
|
|
||||||
to look at the Wiki page:
|
|
||||||
|
|
||||||
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
|
|
||||||
|
|
||||||
0. Obtaining Mininet
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
To install Mininet itself, with root privileges:
|
|
||||||
|
|
||||||
# cd mininet
|
|
||||||
# make install
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
2. Installation script for Ubuntu/Debian Lenny
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
If you successfully used install.sh, congratulations! You're basically
|
|
||||||
done. Proceed to step [6] for additional advice.
|
|
||||||
|
|
||||||
3. Linux Kernel requirements
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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):
|
|
||||||
|
|
||||||
sudo su -c "cat sysctl_addon >> /etc/sysctl.conf"
|
|
||||||
|
|
||||||
To save the config change, run:
|
|
||||||
|
|
||||||
sudo sysctl -p
|
|
||||||
|
|
||||||
4. OpenFlow software and configuration requirements
|
|
||||||
|
|
||||||
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/)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
On Ubuntu and Debian, other Mininet dependencies may be installed using
|
|
||||||
the '-n' option of the install.sh script.
|
|
||||||
|
|
||||||
To run the iperf test, you need to install iperf:
|
|
||||||
|
|
||||||
sudo aptitude/yum install iperf
|
|
||||||
|
|
||||||
We assume you already have ping installed. ;-)
|
|
||||||
|
|
||||||
To use xterm or sshd with Mininet, you need the following:
|
|
||||||
|
|
||||||
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!
|
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,6 +1,10 @@
|
|||||||
Mininet 1.0.0 License
|
Mininet 2.0.0rc1 License
|
||||||
|
|
||||||
Copyright (c) 2009-2011 Bob Lantz and Brandon Heller
|
Copyright (c) 2012 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
|
||||||
|
|
||||||
We are making Mininet available for public use and benefit with the
|
We are making Mininet available for public use and benefit with the
|
||||||
expectation that others will use, modify and enhance the Software and
|
expectation that others will use, modify and enhance the Software and
|
||||||
|
|||||||
@@ -1,30 +1,65 @@
|
|||||||
all: codecheck test
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf build dist *.egg-info *.pyc mnexec bin/mnexec
|
|
||||||
|
|
||||||
MININET = mininet/*.py
|
MININET = mininet/*.py
|
||||||
TEST = mininet/test/*.py
|
TEST = mininet/test/*.py
|
||||||
EXAMPLES = examples/*.py
|
EXAMPLES = examples/*.py
|
||||||
BIN = bin/mn
|
MN = bin/mn
|
||||||
|
BIN = $(MN)
|
||||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||||
|
MNEXEC = mnexec
|
||||||
|
MANPAGES = mn.1 mnexec.1
|
||||||
P8IGN = E251,E201,E302,E202
|
P8IGN = E251,E201,E302,E202
|
||||||
|
BINDIR = /usr/bin
|
||||||
|
MANDIR = /usr/share/man/man1
|
||||||
|
DOCDIRS = doc/html doc/latex
|
||||||
|
PDF = doc/latex/refman.pdf
|
||||||
|
|
||||||
|
all: codecheck test
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS)
|
||||||
|
|
||||||
codecheck: $(PYSRC)
|
codecheck: $(PYSRC)
|
||||||
-echo "Running code check"
|
-echo "Running code check"
|
||||||
|
util/versioncheck.py
|
||||||
pyflakes $(PYSRC)
|
pyflakes $(PYSRC)
|
||||||
pylint --rcfile=.pylint $(PYSRC)
|
pylint --rcfile=.pylint $(PYSRC)
|
||||||
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
|
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
|
||||||
|
|
||||||
|
errcheck: $(PYSRC)
|
||||||
|
-echo "Running check for errors only"
|
||||||
|
pyflakes $(PYSRC)
|
||||||
|
pylint -E --rcfile=.pylint $(PYSRC)
|
||||||
|
|
||||||
test: $(MININET) $(TEST)
|
test: $(MININET) $(TEST)
|
||||||
-echo "Running tests"
|
-echo "Running tests"
|
||||||
mininet/test/test_nets.py
|
mininet/test/test_nets.py
|
||||||
|
mininet/test/test_hifi.py
|
||||||
|
|
||||||
install: mnexec
|
mnexec: mnexec.c $(MN) mininet/net.py
|
||||||
cp mnexec bin/
|
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
|
||||||
|
|
||||||
|
install: $(MNEXEC) $(MANPAGES)
|
||||||
|
install $(MNEXEC) $(BINDIR)
|
||||||
|
install $(MANPAGES) $(MANDIR)
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
|
||||||
doc:
|
develop: $(MNEXEC) $(MANPAGES)
|
||||||
doxygen doxygen.cfg
|
# 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 $< -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
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
|
|
||||||
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
|
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
|
||||||
|
Mininet: Rapid Prototyping for Software Defined Networks
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
*The best way to emulate almost any network on your laptop!*
|
||||||
|
|
||||||
|
Version 2.0.0rc1
|
||||||
|
|
||||||
|
### What is Mininet?
|
||||||
|
|
||||||
|
Mininet emulates a complete network of hosts, links, and switches
|
||||||
|
on a single machine. To create a sample two-host, one-switch network,
|
||||||
|
just run:
|
||||||
|
|
||||||
|
`sudo mn`
|
||||||
|
|
||||||
|
Mininet is useful for interactive development, testing, and demos,
|
||||||
|
especially those using OpenFlow and SDN. OpenFlow-based network
|
||||||
|
controllers prototyped in Mininet can usually be transferred to
|
||||||
|
hardware with minimal changes for full line-rate execution.
|
||||||
|
|
||||||
|
### How does it work?
|
||||||
|
|
||||||
|
Mininet creates virtual networks using process-based virtualization
|
||||||
|
and network namespaces - features that are available in recent Linux
|
||||||
|
kernels. In Mininet, hosts are emulated as `bash` processes running in
|
||||||
|
a network namespace, so any code that would normally run on a Linux
|
||||||
|
server (like a web server or client program) should run just fine
|
||||||
|
within a Mininet "Host". The Mininet "Host" will have its own private
|
||||||
|
network interface and can only see its own processes. Switches in
|
||||||
|
Mininet are software-based switches like Open vSwitch or the OpenFlow
|
||||||
|
reference switch. Links are virtual ethernet pairs, which live in the
|
||||||
|
Linux kernel and connect our emulated switches to emulated hosts
|
||||||
|
(processes).
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
Mininet includes:
|
||||||
|
|
||||||
|
* A command-line launcher (`mn`) to instantiate networks.
|
||||||
|
|
||||||
|
* A handy Python API for creating networks of varying sizes and
|
||||||
|
topologies.
|
||||||
|
|
||||||
|
* Examples (in the `examples/` directory) to help you get started.
|
||||||
|
|
||||||
|
* Full API documentation via Python `help()` docstrings, as well as
|
||||||
|
the ability to generate PDF/HTML documentation with `make doc`.
|
||||||
|
|
||||||
|
* Parametrized topologies (`Topo` subclasses) using the Mininet
|
||||||
|
object. For example, a tree network may be created with the
|
||||||
|
command:
|
||||||
|
|
||||||
|
`mn --topo tree,depth=2,fanout=3`
|
||||||
|
|
||||||
|
* A command-line interface (`CLI` class) which provides useful
|
||||||
|
diagnostic commands (like `iperf` and `ping`), as well as the
|
||||||
|
ability to run a command to a node. For example,
|
||||||
|
|
||||||
|
`mininet> h11 ifconfig -a`
|
||||||
|
|
||||||
|
tells host h11 to run the command `ifconfig -a`
|
||||||
|
|
||||||
|
* A "cleanup" command to get rid of junk (interfaces, processes, files
|
||||||
|
in /tmp, etc.) which might be left around by Mininet or Linux. Try
|
||||||
|
this if things stop working!
|
||||||
|
|
||||||
|
`mn -c`
|
||||||
|
|
||||||
|
### New features in 2.0.0rc1
|
||||||
|
|
||||||
|
Mininet 2.0.0rc1 is a major upgrade and provides
|
||||||
|
a number of enhancements and new features, including:
|
||||||
|
|
||||||
|
* "Mininet-HiFi" functionality:
|
||||||
|
|
||||||
|
* Link bandwidth limits using `tc` (`TCIntf` and `TCLink` classes)
|
||||||
|
|
||||||
|
* CPU isolation and bandwidth limits (`CPULimitedHost` class)
|
||||||
|
|
||||||
|
* Support for Open vSwitch 1.4+ (including Ubuntu OVS packages)
|
||||||
|
|
||||||
|
* Debian packaging (and `apt-get install mininet` in Ubuntu 12.10)
|
||||||
|
|
||||||
|
* First-class Interface (`Intf`) and Link (`Link`) classes for easier
|
||||||
|
extensibility
|
||||||
|
|
||||||
|
* An upgraded Topology (`Topo`) class which supports node and link
|
||||||
|
customization
|
||||||
|
|
||||||
|
* Man pages for the `mn` and `mnexec` utilities.
|
||||||
|
|
||||||
|
[Since the API (most notably the topology) has changed, existing code
|
||||||
|
that runs in Mininet 1.0 will need to be changed to run with Mininet
|
||||||
|
2.0. This is the primary reason for the major version number change.]
|
||||||
|
|
||||||
|
### 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://openflow.org/mininet).
|
||||||
|
There is also a wiki which you are encouraged to read and to
|
||||||
|
contribute to, particularly the Frequently Asked Questions (FAQ.)
|
||||||
|
|
||||||
|
### Support
|
||||||
|
|
||||||
|
Mininet is community-supported. We encourage you to join the
|
||||||
|
Mininet mailing list, `mininet-discuss` at:
|
||||||
|
|
||||||
|
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
|
||||||
|
Mininet is an open-source project and is currently hosted at
|
||||||
|
<https://github.com/mininet>. You are encouraged to download the code,
|
||||||
|
examine it, modify it, and submit bug reports, bug fixes, feature
|
||||||
|
requests, and enhancements!
|
||||||
|
|
||||||
|
Best wishes, and we look forward to seeing what you can do with
|
||||||
|
Mininet to change the networking world!
|
||||||
|
|
||||||
|
### Credits
|
||||||
|
|
||||||
|
The Mininet Team:
|
||||||
|
|
||||||
|
* Bob Lantz
|
||||||
|
* Brandon Heller
|
||||||
|
* Nikhil Handigol
|
||||||
|
* Vimal Jeyakumar
|
||||||
@@ -12,69 +12,67 @@ Example to pull custom params (topo, switch, etc.) from a file:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import os.path
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
# Fix setuptools' evil madness, and open up (more?) security holes
|
||||||
|
if 'PYTHONPATH' in os.environ:
|
||||||
|
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||||
|
|
||||||
from mininet.clean import cleanup
|
from mininet.clean import cleanup
|
||||||
from mininet.cli import CLI
|
from mininet.cli import CLI
|
||||||
from mininet.log import lg, LEVELS, info
|
from mininet.log import lg, LEVELS, info
|
||||||
from mininet.net import Mininet, init
|
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||||
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
|
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||||
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
|
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
|
||||||
|
OVSLegacyKernelSwitch )
|
||||||
|
from mininet.link import Link, TCLink
|
||||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||||
from mininet.topolib import TreeTopo
|
from mininet.topolib import TreeTopo
|
||||||
from mininet.util import makeNumeric
|
from mininet.util import custom, customConstructor
|
||||||
|
from mininet.util import buildTopo
|
||||||
|
|
||||||
|
|
||||||
# built in topologies, created only when run
|
# built in topologies, created only when run
|
||||||
TOPODEF = 'minimal'
|
TOPODEF = 'minimal'
|
||||||
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
||||||
'linear': LinearTopo,
|
'linear': LinearTopo,
|
||||||
'reversed': SingleSwitchReversedTopo,
|
'reversed': SingleSwitchReversedTopo,
|
||||||
'single': SingleSwitchTopo,
|
'single': SingleSwitchTopo,
|
||||||
'tree': TreeTopo }
|
'tree': TreeTopo }
|
||||||
|
|
||||||
SWITCHDEF = 'ovsk'
|
SWITCHDEF = 'ovsk'
|
||||||
SWITCHES = { 'kernel': KernelSwitch,
|
SWITCHES = { 'user': UserSwitch,
|
||||||
'user': UserSwitch,
|
'ovsk': OVSKernelSwitch,
|
||||||
'ovsk': OVSKernelSwitch }
|
'ovsl': OVSLegacyKernelSwitch }
|
||||||
|
|
||||||
HOSTDEF = 'process'
|
HOSTDEF = 'proc'
|
||||||
HOSTS = { 'process': Host }
|
HOSTS = { 'proc': Host,
|
||||||
|
'rt': custom( CPULimitedHost, sched='rt' ),
|
||||||
|
'cfs': custom( CPULimitedHost, sched='cfs' ) }
|
||||||
|
|
||||||
CONTROLLERDEF = 'ref'
|
CONTROLLERDEF = 'ovsc'
|
||||||
# a and b are the name and inNamespace params.
|
|
||||||
CONTROLLERS = { 'ref': Controller,
|
CONTROLLERS = { 'ref': Controller,
|
||||||
'nox_dump': lambda name: NOX( name, 'packetdump' ),
|
'ovsc': OVSController,
|
||||||
'nox_pysw': lambda name: NOX( name, 'pyswitch' ),
|
'nox': NOX,
|
||||||
'remote': lambda name: None,
|
'remote': RemoteController,
|
||||||
'none': lambda name: None }
|
'none': lambda name: None }
|
||||||
|
|
||||||
|
LINKDEF = 'default'
|
||||||
|
LINKS = { 'default': Link,
|
||||||
|
'tc': TCLink }
|
||||||
|
|
||||||
|
|
||||||
# optional tests to run
|
# optional tests to run
|
||||||
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||||
'none' ]
|
'none' ]
|
||||||
|
|
||||||
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
|
ALTSPELLING = { 'pingall': 'pingAll',
|
||||||
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
|
'pingpair': 'pingPair',
|
||||||
|
'iperfudp': 'iperfUdp',
|
||||||
def buildTopo( topo ):
|
'iperfUDP': 'iperfUdp',
|
||||||
"Create topology from string with format (object, arg1, arg2,...)."
|
'prefixlen': 'prefixLen' }
|
||||||
topo_split = topo.split( ',' )
|
|
||||||
topo_name = topo_split[ 0 ]
|
|
||||||
topo_params = topo_split[ 1: ]
|
|
||||||
|
|
||||||
# Convert int and float args; removes the need for every topology to
|
|
||||||
# be flexible with input arg formats.
|
|
||||||
topo_seq_params = [ s for s in topo_params if '=' not in s ]
|
|
||||||
topo_seq_params = [ makeNumeric( s ) for s in topo_seq_params ]
|
|
||||||
topo_kw_params = {}
|
|
||||||
for s in [ p for p in topo_params if '=' in p ]:
|
|
||||||
key, val = s.split( '=' )
|
|
||||||
topo_kw_params[ key ] = makeNumeric( val )
|
|
||||||
|
|
||||||
if topo_name not in TOPOS.keys():
|
|
||||||
raise Exception( 'Invalid topo_name %s' % topo_name )
|
|
||||||
return TOPOS[ topo_name ]( *topo_seq_params, **topo_kw_params )
|
|
||||||
|
|
||||||
|
|
||||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||||
@@ -86,16 +84,21 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
|||||||
help: string"""
|
help: string"""
|
||||||
if default not in choicesDict:
|
if default not in choicesDict:
|
||||||
raise Exception( 'Invalid default %s for choices dict: %s' %
|
raise Exception( 'Invalid default %s for choices dict: %s' %
|
||||||
( default, name ) )
|
( default, name ) )
|
||||||
if not helpStr:
|
if not helpStr:
|
||||||
helpStr = '[' + ' '.join( choicesDict.keys() ) + ']'
|
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||||
|
'[,param=value...]' )
|
||||||
opts.add_option( '--' + name,
|
opts.add_option( '--' + name,
|
||||||
type='choice',
|
type='string',
|
||||||
choices=choicesDict.keys(),
|
default = default,
|
||||||
default = default,
|
help = helpStr )
|
||||||
help = helpStr )
|
|
||||||
|
|
||||||
|
|
||||||
|
def version( *_args ):
|
||||||
|
"Print Mininet version and exit"
|
||||||
|
print "%s" % VERSION
|
||||||
|
exit()
|
||||||
|
|
||||||
class MininetRunner( object ):
|
class MininetRunner( object ):
|
||||||
"Build, setup, and run Mininet."
|
"Build, setup, and run Mininet."
|
||||||
|
|
||||||
@@ -124,11 +127,11 @@ class MininetRunner( object ):
|
|||||||
|
|
||||||
def parseCustomFile( self, fileName ):
|
def parseCustomFile( self, fileName ):
|
||||||
"Parse custom file and add params before parsing cmd-line options."
|
"Parse custom file and add params before parsing cmd-line options."
|
||||||
custom = {}
|
customs = {}
|
||||||
if os.path.isfile( fileName ):
|
if os.path.isfile( fileName ):
|
||||||
execfile( fileName, custom, custom )
|
execfile( fileName, customs, customs )
|
||||||
for name in custom:
|
for name, val in customs.iteritems():
|
||||||
self.setCustom( name, custom[ name ] )
|
self.setCustom( name, val )
|
||||||
else:
|
else:
|
||||||
raise Exception( 'could not find custom file: %s' % fileName )
|
raise Exception( 'could not find custom file: %s' % fileName )
|
||||||
|
|
||||||
@@ -136,58 +139,64 @@ class MininetRunner( object ):
|
|||||||
"""Parse command-line args and return options object.
|
"""Parse command-line args and return options object.
|
||||||
returns: opts parse options dict"""
|
returns: opts parse options dict"""
|
||||||
if '--custom' in sys.argv:
|
if '--custom' in sys.argv:
|
||||||
print "custom in sys.argv"
|
|
||||||
index = sys.argv.index( '--custom' )
|
index = sys.argv.index( '--custom' )
|
||||||
if len( sys.argv ) > index + 1:
|
if len( sys.argv ) > index + 1:
|
||||||
custom = sys.argv[ index + 1 ]
|
filename = sys.argv[ index + 1 ]
|
||||||
self.parseCustomFile( custom )
|
self.parseCustomFile( filename )
|
||||||
else:
|
else:
|
||||||
raise Exception( 'Custom file name not found' )
|
raise Exception( 'Custom file name not found' )
|
||||||
|
|
||||||
opts = OptionParser()
|
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 )
|
||||||
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||||
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
||||||
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
||||||
|
addDictOption( opts, LINKS, LINKDEF, 'link' )
|
||||||
|
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||||
|
|
||||||
opts.add_option( '--topo', type='string', default=TOPODEF,
|
|
||||||
help='[' + ' '.join( TOPOS.keys() ) + '],arg1,arg2,'
|
|
||||||
'...argN')
|
|
||||||
opts.add_option( '--clean', '-c', action='store_true',
|
opts.add_option( '--clean', '-c', action='store_true',
|
||||||
default=False, help='clean and exit' )
|
default=False, help='clean and exit' )
|
||||||
opts.add_option( '--custom', type='string', default=None,
|
opts.add_option( '--custom', type='string', default=None,
|
||||||
help='read custom topo and node params from .py file' )
|
help='read custom topo and node params from .py' +
|
||||||
|
'file' )
|
||||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||||
default=TESTS[ 0 ],
|
default=TESTS[ 0 ],
|
||||||
help='[' + ' '.join( TESTS ) + ']' )
|
help='|'.join( TESTS ) )
|
||||||
opts.add_option( '--xterms', '-x', action='store_true',
|
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',
|
opts.add_option( '--mac', action='store_true',
|
||||||
default=False, help='set MACs equal to DPIDs' )
|
default=False, help='automatically set host MACs' )
|
||||||
opts.add_option( '--arp', action='store_true',
|
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',
|
opts.add_option( '--verbosity', '-v', type='choice',
|
||||||
choices=LEVELS.keys(), default = 'info',
|
choices=LEVELS.keys(), default = 'info',
|
||||||
help = '[' + ' '.join( LEVELS.keys() ) + ']' )
|
help = '|'.join( LEVELS.keys() ) )
|
||||||
opts.add_option( '--ip', type='string', default='127.0.0.1',
|
|
||||||
help='[ip address as a dotted decimal string for a'
|
|
||||||
'remote controller]' )
|
|
||||||
opts.add_option( '--port', type='int', default=6633,
|
|
||||||
help='[port integer for a listening remote'
|
|
||||||
' controller]' )
|
|
||||||
opts.add_option( '--innamespace', action='store_true',
|
opts.add_option( '--innamespace', action='store_true',
|
||||||
default=False, help='sw and ctrl in namespace?' )
|
default=False, help='sw and ctrl in namespace?' )
|
||||||
opts.add_option( '--listenport', type='int', default=6634,
|
opts.add_option( '--listenport', type='int', default=6635,
|
||||||
help='[base port for passive switch listening'
|
help='base port for passive switch listening' )
|
||||||
' controller]' )
|
|
||||||
opts.add_option( '--nolistenport', action='store_true',
|
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,
|
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,
|
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,
|
opts.add_option( '--prefixlen', type='int', default=8,
|
||||||
help='[prefix length (e.g. /8) for automatic '
|
help='prefix length (e.g. /8) for automatic '
|
||||||
'network configuration]' )
|
'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( '--version', action='callback', callback=version )
|
||||||
|
|
||||||
self.options, self.args = opts.parse_args()
|
self.options, self.args = opts.parse_args()
|
||||||
|
|
||||||
@@ -202,9 +211,6 @@ class MininetRunner( object ):
|
|||||||
% self.options.verbosity )
|
% self.options.verbosity )
|
||||||
lg.setLogLevel( self.options.verbosity )
|
lg.setLogLevel( self.options.verbosity )
|
||||||
|
|
||||||
# validate environment setup
|
|
||||||
init()
|
|
||||||
|
|
||||||
def begin( self ):
|
def begin( self ):
|
||||||
"Create and run mininet."
|
"Create and run mininet."
|
||||||
|
|
||||||
@@ -214,35 +220,33 @@ class MininetRunner( object ):
|
|||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
topo = buildTopo( self.options.topo )
|
topo = buildTopo( TOPOS, self.options.topo )
|
||||||
switch = SWITCHES[ self.options.switch ]
|
switch = customConstructor( SWITCHES, self.options.switch )
|
||||||
host = HOSTS[ self.options.host ]
|
host = customConstructor( HOSTS, self.options.host )
|
||||||
controller = CONTROLLERS[ self.options.controller ]
|
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||||
if self.options.controller == 'remote':
|
link = customConstructor( LINKS, self.options.link )
|
||||||
controller = lambda a: RemoteController( a,
|
|
||||||
defaultIP=self.options.ip,
|
|
||||||
port=self.options.port )
|
|
||||||
|
|
||||||
if self.validate:
|
if self.validate:
|
||||||
self.validate( self.options )
|
self.validate( self.options )
|
||||||
|
|
||||||
# We should clarify what this is actually for...
|
|
||||||
# It seems like it should be default values for the
|
|
||||||
# *data* network, so it may be misnamed.
|
|
||||||
controllerParams = ControllerParams( '10.0.0.0',
|
|
||||||
self.options.prefixlen)
|
|
||||||
|
|
||||||
inNamespace = self.options.innamespace
|
inNamespace = self.options.innamespace
|
||||||
|
Net = MininetWithControlNet if inNamespace else Mininet
|
||||||
|
ipBase = self.options.ipbase
|
||||||
xterms = self.options.xterms
|
xterms = self.options.xterms
|
||||||
mac = self.options.mac
|
mac = self.options.mac
|
||||||
arp = self.options.arp
|
arp = self.options.arp
|
||||||
|
pin = self.options.pin
|
||||||
listenPort = None
|
listenPort = None
|
||||||
if not self.options.nolistenport:
|
if not self.options.nolistenport:
|
||||||
listenPort = self.options.listenport
|
listenPort = self.options.listenport
|
||||||
mn = Mininet( topo, switch, host, controller, controllerParams,
|
mn = Net( topo=topo,
|
||||||
inNamespace=inNamespace,
|
switch=switch, host=host, controller=controller,
|
||||||
xterms=xterms, autoSetMacs=mac,
|
link=link,
|
||||||
autoStaticArp=arp, listenPort=listenPort )
|
ipBase=ipBase,
|
||||||
|
inNamespace=inNamespace,
|
||||||
|
xterms=xterms, autoSetMacs=mac,
|
||||||
|
autoStaticArp=arp, autoPinCpus=pin,
|
||||||
|
listenPort=listenPort )
|
||||||
|
|
||||||
if self.options.pre:
|
if self.options.pre:
|
||||||
CLI( mn, script=self.options.pre )
|
CLI( mn, script=self.options.pre )
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ class MyTopo( Topo ):
|
|||||||
rightHost = 4
|
rightHost = 4
|
||||||
|
|
||||||
# Add nodes
|
# Add nodes
|
||||||
self.add_node( leftSwitch, Node( is_switch=True ) )
|
self.addNode( leftSwitch, Node( isSwitch=True ) )
|
||||||
self.add_node( rightSwitch, Node( is_switch=True ) )
|
self.addNode( rightSwitch, Node( isSwitch=True ) )
|
||||||
self.add_node( leftHost, Node( is_switch=False ) )
|
self.addNode( leftHost, Node( isSwitch=False ) )
|
||||||
self.add_node( rightHost, Node( is_switch=False ) )
|
self.addNode( rightHost, Node( isSwitch=False ) )
|
||||||
|
|
||||||
# Add edges
|
# Add edges
|
||||||
self.add_edge( leftHost, leftSwitch )
|
self.add_edge( leftHost, leftSwitch )
|
||||||
|
|||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
mininet (2.0.0d2-0ubuntu1) quantal; urgency=low
|
||||||
|
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
-- Bob Lantz <rlantz@cs.stanford.edu> Sun, 01 Jul 2012 23:19:54 +0000
|
||||||
|
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
7
|
||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
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 (>= 7.0.50~),
|
||||||
|
help2man,
|
||||||
|
python-dev,
|
||||||
|
python-pkg-resources,
|
||||||
|
python-setuptools
|
||||||
|
Homepage: http://openflow.org/mininet
|
||||||
|
|
||||||
|
Package: mininet
|
||||||
|
Architecture: any
|
||||||
|
Depends:
|
||||||
|
openvswitch-controller,
|
||||||
|
openvswitch-switch,
|
||||||
|
python-networkx,
|
||||||
|
telnet,
|
||||||
|
${misc:Depends},
|
||||||
|
${python:Depends},
|
||||||
|
${shlibs:Depends}
|
||||||
|
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
+33
@@ -0,0 +1,33 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
|
||||||
|
Upstream-Name: mininet
|
||||||
|
Source: https://github.com/mininet/mininet
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2009-2012 Bob Lantz,
|
||||||
|
2009-2012 Brandon Heller
|
||||||
|
License:
|
||||||
|
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
@@ -0,0 +1 @@
|
|||||||
|
README
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
examples/*
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
mnexec /usr/bin
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
*.1
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@ --buildsystem=python_distutils --with=python2
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
make man
|
||||||
|
make mnexec
|
||||||
|
dh_auto_build
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
3.0 (quilt)
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version=3
|
||||||
|
opts=filenamemangle=s/.*tarball\/(.*)/$1\.tar\.gz/ \
|
||||||
|
https://github.com/mininet/mininet/tags .*/tarball/(\d[\d\.abd]+)
|
||||||
@@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
|
|||||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||||
# by quotes) that should identify the project.
|
# by quotes) that should identify the project.
|
||||||
|
|
||||||
PROJECT_NAME = Mininet
|
PROJECT_NAME = "Mininet Python API Reference Manual"
|
||||||
|
|
||||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
||||||
# This could be handy for archiving the generated documentation or
|
# 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
|
# If left blank the directory from which doxygen is run is used as the
|
||||||
# path to strip.
|
# 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 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
|
# 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
|
# by the printer. Possible values are: a4, a4wide, letter, legal and
|
||||||
# executive. If left blank a4wide will be used.
|
# executive. If left blank a4wide will be used.
|
||||||
|
|
||||||
PAPER_TYPE = a4wide
|
PAPER_TYPE = letter
|
||||||
|
|
||||||
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
|
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
|
||||||
# packages that should be included in the LaTeX output.
|
# packages that should be included in the LaTeX output.
|
||||||
+47
-6
@@ -6,17 +6,39 @@ 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:
|
consoles.py:
|
||||||
|
|
||||||
This example creates a grid of console windows, one for each node,
|
This example creates a grid of console windows, one for each node,
|
||||||
and allows interaction with and monitoring of each console, including
|
and allows interaction with and monitoring of each console, including
|
||||||
graphical monitoring.
|
graphical monitoring.
|
||||||
|
|
||||||
|
controllers.py:
|
||||||
|
|
||||||
|
This example creates a network and adds multiple controllers to it.
|
||||||
|
|
||||||
|
cpu.py:
|
||||||
|
|
||||||
|
This example tests iperf bandwidth for varying CPU limits.
|
||||||
|
|
||||||
emptynet.py:
|
emptynet.py:
|
||||||
|
|
||||||
This example demonstrates creating an empty network (i.e. with no
|
This example demonstrates creating an empty network (i.e. with no
|
||||||
topology object) and adding nodes to it.
|
topology object) and adding nodes to it.
|
||||||
|
|
||||||
|
hwintf.py:
|
||||||
|
|
||||||
|
This example shows how to add an interface (for example a real
|
||||||
|
hardware interface) to a network after the network is created.
|
||||||
|
|
||||||
|
limit.py:
|
||||||
|
|
||||||
|
This example shows how to use link and CPU limits.
|
||||||
|
|
||||||
linearbandwidth.py:
|
linearbandwidth.py:
|
||||||
|
|
||||||
This example shows how to create a custom topology programatically
|
This example shows how to create a custom topology programatically
|
||||||
@@ -26,16 +48,39 @@ miniedit.py:
|
|||||||
|
|
||||||
This example demonstrates creating a network via a graphical editor.
|
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:
|
multitest.py:
|
||||||
|
|
||||||
This example creates a network and runs multiple tests on it.
|
This example creates a network and runs multiple tests on it.
|
||||||
|
|
||||||
|
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:
|
scratchnet.py, scratchnetuser.py:
|
||||||
|
|
||||||
These two examples demonstrate how to create a network by using the lowest-
|
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,
|
level Mininet functions. Generally the higher-level API is easier to use,
|
||||||
but scratchnet shows what is going on behind the scenes.
|
but scratchnet shows what is going on behind the scenes.
|
||||||
|
|
||||||
|
simpleperf.py:
|
||||||
|
|
||||||
|
A simple example of configuring network and CPU bandwidth limits.
|
||||||
|
|
||||||
sshd.py:
|
sshd.py:
|
||||||
|
|
||||||
This example shows how to run an sshd process in each host, allowing
|
This example shows how to run an sshd process in each host, allowing
|
||||||
@@ -44,10 +89,10 @@ 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
|
already lives in the root namespace, so it does not need to be explicitly
|
||||||
connected.)
|
connected.)
|
||||||
|
|
||||||
treeping64:
|
treeping64.py:
|
||||||
|
|
||||||
This example creates a 64-host tree network, and attempts to check full
|
This example creates a 64-host tree network, and attempts to check full
|
||||||
connectivity using ping, for three different switch/datapath types.
|
connectivity using ping, for different switch/datapath types.
|
||||||
|
|
||||||
tree1024.py:
|
tree1024.py:
|
||||||
|
|
||||||
@@ -55,7 +100,3 @@ 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
|
CLI on it. It may run into scalability limits, depending on available
|
||||||
memory and sysctl configuration (see INSTALL.)
|
memory and sysctl configuration (see INSTALL.)
|
||||||
|
|
||||||
udpbwtest.py:
|
|
||||||
|
|
||||||
This example shows how to run a test across an entire network, and monitor
|
|
||||||
the output of a set of hosts in real time.
|
|
||||||
|
|||||||
@@ -3,17 +3,23 @@
|
|||||||
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
||||||
|
|
||||||
from mininet.node import Host
|
from mininet.node import Host
|
||||||
|
from mininet.util import ensureRoot
|
||||||
|
|
||||||
|
ensureRoot()
|
||||||
|
|
||||||
print "*** Creating nodes"
|
print "*** Creating nodes"
|
||||||
h1 = Host( 'h1' )
|
h1 = Host( 'h1' )
|
||||||
|
|
||||||
root = Host( 'root', inNamespace=False )
|
root = Host( 'root', inNamespace=False )
|
||||||
|
|
||||||
print "*** Creating links"
|
print "*** Creating links"
|
||||||
h1.linkTo( root )
|
h1.linkTo( root )
|
||||||
|
|
||||||
|
print h1
|
||||||
|
|
||||||
print "*** Configuring nodes"
|
print "*** Configuring nodes"
|
||||||
h1.setIP( h1.intfs[ 0 ], '10.0.0.1', 8 )
|
h1.setIP( '10.0.0.1', 8 )
|
||||||
root.setIP( root.intfs[ 0 ], '10.0.0.2', 8 )
|
root.setIP( '10.0.0.2', 8 )
|
||||||
|
|
||||||
print "*** Creating banner file"
|
print "*** Creating banner file"
|
||||||
f = open( '/tmp/%s.banner' % h1.name, 'w' )
|
f = open( '/tmp/%s.banner' % h1.name, 'w' )
|
||||||
|
|||||||
+15
-24
@@ -74,11 +74,11 @@ class Console( Frame ):
|
|||||||
"Pop up a new terminal window for a node."
|
"Pop up a new terminal window for a node."
|
||||||
net.terms += makeTerms( [ node ], title )
|
net.terms += makeTerms( [ node ], title )
|
||||||
label = Button( self, text=self.node.name, command=newTerm,
|
label = Button( self, text=self.node.name, command=newTerm,
|
||||||
**self.buttonStyle )
|
**self.buttonStyle )
|
||||||
label.pack( side='top', fill='x' )
|
label.pack( side='top', fill='x' )
|
||||||
text = Text( self, wrap='word', **self.textStyle )
|
text = Text( self, wrap='word', **self.textStyle )
|
||||||
ybar = Scrollbar( self, orient='vertical', width=7,
|
ybar = Scrollbar( self, orient='vertical', width=7,
|
||||||
command=text.yview )
|
command=text.yview )
|
||||||
text.configure( yscrollcommand=ybar.set )
|
text.configure( yscrollcommand=ybar.set )
|
||||||
text.pack( side='left', expand=True, fill='both' )
|
text.pack( side='left', expand=True, fill='both' )
|
||||||
ybar.pack( side='right', fill='y' )
|
ybar.pack( side='right', fill='y' )
|
||||||
@@ -95,7 +95,7 @@ class Console( Frame ):
|
|||||||
# way to trigger a file event handler from Tk's
|
# way to trigger a file event handler from Tk's
|
||||||
# event loop!
|
# event loop!
|
||||||
self.tk.createfilehandler( self.node.stdout, READABLE,
|
self.tk.createfilehandler( self.node.stdout, READABLE,
|
||||||
self.handleReadable )
|
self.handleReadable )
|
||||||
|
|
||||||
# We're not a terminal (yet?), so we ignore the following
|
# We're not a terminal (yet?), so we ignore the following
|
||||||
# control characters other than [\b\n\r]
|
# control characters other than [\b\n\r]
|
||||||
@@ -107,8 +107,10 @@ class Console( Frame ):
|
|||||||
self.text.insert( 'end', text )
|
self.text.insert( 'end', text )
|
||||||
self.text.mark_set( 'insert', 'end' )
|
self.text.mark_set( 'insert', 'end' )
|
||||||
self.text.see( 'insert' )
|
self.text.see( 'insert' )
|
||||||
|
outputHook = lambda x, y: True # make pylint happier
|
||||||
if self.outputHook:
|
if self.outputHook:
|
||||||
self.outputHook( self, text )
|
outputHook = self.outputHook
|
||||||
|
outputHook( self, text )
|
||||||
|
|
||||||
def handleKey( self, event ):
|
def handleKey( self, event ):
|
||||||
"If it's an interactive command, send it to the node."
|
"If it's an interactive command, send it to the node."
|
||||||
@@ -130,27 +132,22 @@ class Console( Frame ):
|
|||||||
self.sendCmd( cmd )
|
self.sendCmd( cmd )
|
||||||
|
|
||||||
# Callback ignores event
|
# Callback ignores event
|
||||||
# pylint: disable-msg=W0613
|
def handleInt( self, _event=None ):
|
||||||
def handleInt( self, event=None ):
|
|
||||||
"Handle control-c."
|
"Handle control-c."
|
||||||
self.node.sendInt()
|
self.node.sendInt()
|
||||||
# pylint: enable-msg=W0613
|
|
||||||
|
|
||||||
def sendCmd( self, cmd ):
|
def sendCmd( self, cmd ):
|
||||||
"Send a command to our node."
|
"Send a command to our node."
|
||||||
if not self.node.waiting:
|
if not self.node.waiting:
|
||||||
self.node.sendCmd( cmd )
|
self.node.sendCmd( cmd )
|
||||||
|
|
||||||
# Callback ignores fds
|
def handleReadable( self, _fds, timeoutms=None ):
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
def handleReadable( self, fds, timeoutms=None ):
|
|
||||||
"Handle file readable event."
|
"Handle file readable event."
|
||||||
data = self.node.monitor( timeoutms )
|
data = self.node.monitor( timeoutms )
|
||||||
self.append( data )
|
self.append( data )
|
||||||
if not self.node.waiting:
|
if not self.node.waiting:
|
||||||
# Print prompt
|
# Print prompt
|
||||||
self.append( self.prompt )
|
self.append( self.prompt )
|
||||||
# pylint: enable-msg=W0613
|
|
||||||
|
|
||||||
def waiting( self ):
|
def waiting( self ):
|
||||||
"Are we waiting for output?"
|
"Are we waiting for output?"
|
||||||
@@ -172,11 +169,8 @@ class Graph( Frame ):
|
|||||||
|
|
||||||
"Graph that we can add bars to over time."
|
"Graph that we can add bars to over time."
|
||||||
|
|
||||||
def __init__( self, parent=None,
|
def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
|
||||||
bg = 'white',
|
barwidth=10, ymax=3.5,):
|
||||||
gheight=200, gwidth=500,
|
|
||||||
barwidth=10,
|
|
||||||
ymax=3.5,):
|
|
||||||
|
|
||||||
Frame.__init__( self, parent )
|
Frame.__init__( self, parent )
|
||||||
|
|
||||||
@@ -198,7 +192,7 @@ class Graph( Frame ):
|
|||||||
width = 25
|
width = 25
|
||||||
ymax = self.ymax
|
ymax = self.ymax
|
||||||
scale = Canvas( self, width=width, height=height,
|
scale = Canvas( self, width=width, height=height,
|
||||||
background=self.bg )
|
background=self.bg )
|
||||||
opts = { 'fill': 'red' }
|
opts = { 'fill': 'red' }
|
||||||
# Draw scale line
|
# Draw scale line
|
||||||
scale.create_line( width - 1, height, width - 1, 0, **opts )
|
scale.create_line( width - 1, height, width - 1, 0, **opts )
|
||||||
@@ -214,7 +208,7 @@ class Graph( Frame ):
|
|||||||
ofs = 20
|
ofs = 20
|
||||||
height = self.gheight + ofs
|
height = self.gheight + ofs
|
||||||
self.graph.configure( scrollregion=( 0, -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 ) )
|
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
|
||||||
|
|
||||||
def yview( self, *args ):
|
def yview( self, *args ):
|
||||||
@@ -234,7 +228,7 @@ class Graph( Frame ):
|
|||||||
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
|
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
|
||||||
ybar = Scrollbar( self, orient='vertical', command=self.yview )
|
ybar = Scrollbar( self, orient='vertical', command=self.yview )
|
||||||
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
|
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
|
||||||
scrollregion=(0, 0, width, height ) )
|
scrollregion=(0, 0, width, height ) )
|
||||||
scale.configure( yscrollcommand=ybar.set )
|
scale.configure( yscrollcommand=ybar.set )
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
@@ -255,7 +249,7 @@ class Graph( Frame ):
|
|||||||
x1 = x0 + self.barwidth
|
x1 = x0 + self.barwidth
|
||||||
y0 = self.gheight
|
y0 = self.gheight
|
||||||
y1 = ( 1 - percent ) * 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.xpos += 1
|
||||||
self.updateScrollRegions()
|
self.updateScrollRegions()
|
||||||
self.graph.xview( 'moveto', '1.0' )
|
self.graph.xview( 'moveto', '1.0' )
|
||||||
@@ -319,9 +313,7 @@ class ConsoleApp( Frame ):
|
|||||||
|
|
||||||
self.pack( expand=True, fill='both' )
|
self.pack( expand=True, fill='both' )
|
||||||
|
|
||||||
# Update callback doesn't use console arg
|
def updateGraph( self, _console, output ):
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
def updateGraph( self, console, output ):
|
|
||||||
"Update our graph."
|
"Update our graph."
|
||||||
m = re.search( r'(\d+) Mbits/sec', output )
|
m = re.search( r'(\d+) Mbits/sec', output )
|
||||||
if not m:
|
if not m:
|
||||||
@@ -332,7 +324,6 @@ class ConsoleApp( Frame ):
|
|||||||
self.graph.addBar( self.bw )
|
self.graph.addBar( self.bw )
|
||||||
self.bw = 0
|
self.bw = 0
|
||||||
self.updates = 0
|
self.updates = 0
|
||||||
# pylint: enable-msg=W0613
|
|
||||||
|
|
||||||
def setOutputHook( self, fn=None, consoles=None ):
|
def setOutputHook( self, fn=None, consoles=None ):
|
||||||
"Register fn as output hook [on specific consoles.]"
|
"Register fn as output hook [on specific consoles.]"
|
||||||
|
|||||||
Executable
+81
@@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
cpu.py: test iperf bandwidth for varying cpu limits
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.node import CPULimitedHost
|
||||||
|
from mininet.topolib import TreeTopo
|
||||||
|
from mininet.util import custom
|
||||||
|
from mininet.log import setLogLevel, output
|
||||||
|
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
def waitListening(client, server, port):
|
||||||
|
"Wait until server is listening on port"
|
||||||
|
if not client.cmd('which telnet'):
|
||||||
|
raise Exception('Could not find telnet')
|
||||||
|
cmd = ('sh -c "echo A | telnet -e A %s %s"' %
|
||||||
|
(server.IP(), port))
|
||||||
|
while 'Connected' not in client.cmd(cmd):
|
||||||
|
output('waiting for', server,
|
||||||
|
'to listen on port', port, '\n')
|
||||||
|
sleep(.5)
|
||||||
|
|
||||||
|
|
||||||
|
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
|
||||||
|
"""Example/test of link and CPU bandwidth limits
|
||||||
|
cpu: cpu limit as fraction of overall CPU time"""
|
||||||
|
|
||||||
|
topo = TreeTopo( depth=1, fanout=2 )
|
||||||
|
|
||||||
|
results = {}
|
||||||
|
|
||||||
|
for sched in 'rt', 'cfs':
|
||||||
|
print '*** Testing with', sched, 'bandwidth limiting'
|
||||||
|
for cpu in cpuLimits:
|
||||||
|
host = custom( CPULimitedHost, sched=sched,
|
||||||
|
period_us=period_us,
|
||||||
|
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 ]
|
||||||
|
server.cmd( 'iperf -s -p 5001 &' )
|
||||||
|
waitListening( client, server, 5001 )
|
||||||
|
result = client.cmd( 'iperf -yc -t %s -c %s' % (
|
||||||
|
seconds, server.IP() ) ).split( ',' )
|
||||||
|
bps = float( result[ -1 ] )
|
||||||
|
server.cmdPrint( 'kill %iperf' )
|
||||||
|
net.stop()
|
||||||
|
updated = results.get( sched, [] )
|
||||||
|
updated += [ ( cpu, bps ) ]
|
||||||
|
results[ sched ] = updated
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def dump( results ):
|
||||||
|
"Dump results"
|
||||||
|
|
||||||
|
fmt = '%s\t%s\t%s'
|
||||||
|
|
||||||
|
print
|
||||||
|
print fmt % ( 'sched', 'cpu', 'client MB/s' )
|
||||||
|
print
|
||||||
|
|
||||||
|
for sched in sorted( results.keys() ):
|
||||||
|
entries = results[ sched ]
|
||||||
|
for cpu, bps in entries:
|
||||||
|
pct = '%.2f%%' % ( cpu * 100 )
|
||||||
|
mbps = bps / 1e6
|
||||||
|
print fmt % ( sched, pct, mbps )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel( 'info' )
|
||||||
|
limits = [ .45, .4, .3, .2, .1 ]
|
||||||
|
out = bwtest( limits )
|
||||||
|
dump( out )
|
||||||
+1
-1
@@ -21,7 +21,7 @@ def checkIntf( intf ):
|
|||||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
|
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
|
||||||
if ips:
|
if ips:
|
||||||
error( 'Error:', intf, 'has an IP address,'
|
error( 'Error:', intf, 'has an IP address,'
|
||||||
'and is probably in use!\n' )
|
'and is probably in use!\n' )
|
||||||
exit( 1 )
|
exit( 1 )
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
limit.py: example of using link and CPU limits
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.link import TCIntf
|
||||||
|
from mininet.node import CPULimitedHost
|
||||||
|
from mininet.topolib import TreeTopo
|
||||||
|
from mininet.util import custom
|
||||||
|
from mininet.log import setLogLevel
|
||||||
|
|
||||||
|
|
||||||
|
def testLinkLimit( net, bw ):
|
||||||
|
"Run bandwidth limit test"
|
||||||
|
print '*** Testing network %.2f Mbps bandwidth limit' % bw
|
||||||
|
net.iperf( )
|
||||||
|
|
||||||
|
|
||||||
|
def limit( bw=10, cpu=.1 ):
|
||||||
|
"""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':
|
||||||
|
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 )
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
def verySimpleLimit( bw=150 ):
|
||||||
|
"Absurdly simple limiting test"
|
||||||
|
intf = custom( TCIntf, bw=bw )
|
||||||
|
net = Mininet( intf=intf )
|
||||||
|
h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' )
|
||||||
|
net.addLink( h1, h2 )
|
||||||
|
net.start()
|
||||||
|
net.pingAll()
|
||||||
|
net.iperf()
|
||||||
|
h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() )
|
||||||
|
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
|
||||||
|
h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() )
|
||||||
|
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel( 'info' )
|
||||||
|
limit()
|
||||||
+30
-32
@@ -6,9 +6,9 @@ using both kernel and user datapaths.
|
|||||||
|
|
||||||
We construct a network of N hosts and N-1 switches, connected as follows:
|
We construct a network of N hosts and N-1 switches, connected as follows:
|
||||||
|
|
||||||
h1 <-> sN+1 <-> sN+2 .. sN+N-1
|
h1 <-> s1 <-> s2 .. sN-1
|
||||||
| | |
|
| | |
|
||||||
h2 h3 hN
|
h2 h3 hN
|
||||||
|
|
||||||
WARNING: by default, the reference controller only supports 16
|
WARNING: by default, the reference controller only supports 16
|
||||||
switches, so this test WILL NOT WORK unless you have recompiled
|
switches, so this test WILL NOT WORK unless you have recompiled
|
||||||
@@ -23,42 +23,40 @@ of switches, this example demonstrates:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.node import UserSwitch, OVSKernelSwitch
|
||||||
|
from mininet.topo import Topo
|
||||||
|
from mininet.log import lg
|
||||||
|
from mininet.util import irange
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
flush = sys.stdout.flush
|
flush = sys.stdout.flush
|
||||||
|
|
||||||
from mininet.net import init, Mininet
|
|
||||||
# from mininet.node import KernelSwitch
|
|
||||||
from mininet.node import UserSwitch, OVSKernelSwitch
|
|
||||||
from mininet.topo import Topo, Node
|
|
||||||
from mininet.log import lg
|
|
||||||
|
|
||||||
class LinearTestTopo( Topo ):
|
class LinearTestTopo( Topo ):
|
||||||
"Topology for a string of N hosts and N-1 switches."
|
"Topology for a string of N hosts and N-1 switches."
|
||||||
|
|
||||||
def __init__( self, N ):
|
def __init__( self, N, **params ):
|
||||||
|
|
||||||
# Add default members to class.
|
# Initialize topology
|
||||||
super( LinearTestTopo, self ).__init__()
|
Topo.__init__( self, **params )
|
||||||
|
|
||||||
# Create switch and host nodes
|
# Create switches and hosts
|
||||||
hosts = range( 1, N + 1 )
|
hosts = [ self.addHost( 'h%s' % h )
|
||||||
switches = range( N + 1 , N + N )
|
for h in irange( 1, N ) ]
|
||||||
for h in hosts:
|
switches = [ self.addSwitch( 's%s' % s )
|
||||||
self.add_node( h, Node( is_switch=False ) )
|
for s in irange( 1, N - 1 ) ]
|
||||||
for s in switches:
|
|
||||||
self.add_node( s, Node( is_switch=True ) )
|
|
||||||
|
|
||||||
# Wire up switches
|
# Wire up switches
|
||||||
for s in switches[ :-1 ]:
|
last = None
|
||||||
self.add_edge( s, s + 1 )
|
for switch in switches:
|
||||||
|
if last:
|
||||||
|
self.addLink( last, switch )
|
||||||
|
last = switch
|
||||||
|
|
||||||
# Wire up hosts
|
# Wire up hosts
|
||||||
self.add_edge( hosts[ 0 ], switches[ 0 ] )
|
self.addLink( hosts[ 0 ], switches[ 0 ] )
|
||||||
for h in hosts[ 1: ]:
|
for host, switch in zip( hosts[ 1: ], switches ):
|
||||||
self.add_edge( h, h + N - 1 )
|
self.addLink( host, switch )
|
||||||
|
|
||||||
# Consider all switches and hosts 'on'
|
|
||||||
self.enable_all()
|
|
||||||
|
|
||||||
|
|
||||||
def linearBandwidthTest( lengths ):
|
def linearBandwidthTest( lengths ):
|
||||||
@@ -69,15 +67,16 @@ def linearBandwidthTest( lengths ):
|
|||||||
switchCount = max( lengths )
|
switchCount = max( lengths )
|
||||||
hostCount = switchCount + 1
|
hostCount = switchCount + 1
|
||||||
|
|
||||||
switches = { # 'reference kernel': KernelSwitch,
|
switches = { 'reference user': UserSwitch,
|
||||||
'reference user': UserSwitch,
|
'Open vSwitch kernel': OVSKernelSwitch }
|
||||||
'Open vSwitch kernel': OVSKernelSwitch }
|
|
||||||
|
topo = LinearTestTopo( hostCount )
|
||||||
|
|
||||||
for datapath in switches.keys():
|
for datapath in switches.keys():
|
||||||
print "*** testing", datapath, "datapath"
|
print "*** testing", datapath, "datapath"
|
||||||
Switch = switches[ datapath ]
|
Switch = switches[ datapath ]
|
||||||
results[ datapath ] = []
|
results[ datapath ] = []
|
||||||
net = Mininet( topo=LinearTestTopo( hostCount ), switch=Switch )
|
net = Mininet( topo=topo, switch=Switch )
|
||||||
net.start()
|
net.start()
|
||||||
print "*** testing basic connectivity"
|
print "*** testing basic connectivity"
|
||||||
for n in lengths:
|
for n in lengths:
|
||||||
@@ -106,7 +105,6 @@ def linearBandwidthTest( lengths ):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
lg.setLogLevel( 'info' )
|
lg.setLogLevel( 'info' )
|
||||||
init()
|
|
||||||
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
|
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
|
||||||
print "*** Running linearBandwidthTest", sizes
|
print "*** Running linearBandwidthTest", sizes
|
||||||
linearBandwidthTest( sizes )
|
linearBandwidthTest( sizes )
|
||||||
|
|||||||
+17
-28
@@ -112,7 +112,7 @@ class MiniEdit( Frame ):
|
|||||||
appMenu = Menu( mbar, tearoff=False )
|
appMenu = Menu( mbar, tearoff=False )
|
||||||
mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
|
mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
|
||||||
appMenu.add_command( label='About MiniEdit', command=self.about,
|
appMenu.add_command( label='About MiniEdit', command=self.about,
|
||||||
font=font)
|
font=font)
|
||||||
appMenu.add_separator()
|
appMenu.add_separator()
|
||||||
appMenu.add_command( label='Quit', command=self.quit, font=font )
|
appMenu.add_command( label='Quit', command=self.quit, font=font )
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ class MiniEdit( Frame ):
|
|||||||
editMenu = Menu( mbar, tearoff=False )
|
editMenu = Menu( mbar, tearoff=False )
|
||||||
mbar.add_cascade( label="Edit", font=font, menu=editMenu )
|
mbar.add_cascade( label="Edit", font=font, menu=editMenu )
|
||||||
editMenu.add_command( label="Cut", font=font,
|
editMenu.add_command( label="Cut", font=font,
|
||||||
command=lambda: self.deleteSelection( None ) )
|
command=lambda: self.deleteSelection( None ) )
|
||||||
|
|
||||||
runMenu = Menu( mbar, tearoff=False )
|
runMenu = Menu( mbar, tearoff=False )
|
||||||
mbar.add_cascade( label="Run", font=font, menu=runMenu )
|
mbar.add_cascade( label="Run", font=font, menu=runMenu )
|
||||||
@@ -143,7 +143,7 @@ class MiniEdit( Frame ):
|
|||||||
f = Frame( self )
|
f = Frame( self )
|
||||||
|
|
||||||
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
|
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
|
||||||
bg=self.bg )
|
bg=self.bg )
|
||||||
|
|
||||||
# Scroll bars
|
# Scroll bars
|
||||||
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
|
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
|
||||||
@@ -177,7 +177,7 @@ class MiniEdit( Frame ):
|
|||||||
bbox = self.canvas.bbox( 'all' )
|
bbox = self.canvas.bbox( 'all' )
|
||||||
if bbox is not None:
|
if bbox is not None:
|
||||||
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
|
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
|
||||||
bbox[ 3 ] ) )
|
bbox[ 3 ] ) )
|
||||||
|
|
||||||
def canvasx( self, x_root ):
|
def canvasx( self, x_root ):
|
||||||
"Convert root x coordinate to canvas coordinate."
|
"Convert root x coordinate to canvas coordinate."
|
||||||
@@ -223,7 +223,7 @@ class MiniEdit( Frame ):
|
|||||||
for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
|
for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
|
||||||
doCmd = getattr( self, 'do' + cmd )
|
doCmd = getattr( self, 'do' + cmd )
|
||||||
b = Button( toolbar, text=cmd, font=self.smallFont,
|
b = Button( toolbar, text=cmd, font=self.smallFont,
|
||||||
fg=color, command=doCmd )
|
fg=color, command=doCmd )
|
||||||
b.pack( fill='x', side='bottom' )
|
b.pack( fill='x', side='bottom' )
|
||||||
|
|
||||||
return toolbar
|
return toolbar
|
||||||
@@ -289,7 +289,7 @@ class MiniEdit( Frame ):
|
|||||||
def deleteItem( self, item ):
|
def deleteItem( self, item ):
|
||||||
"Delete an item."
|
"Delete an item."
|
||||||
# Don't delete while network is running
|
# Don't delete while network is running
|
||||||
if self.buttons[ 'Select' ][ 'state' ] == 'disabled' :
|
if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
|
||||||
return
|
return
|
||||||
# Delete from model
|
# Delete from model
|
||||||
if item in self.links:
|
if item in self.links:
|
||||||
@@ -299,19 +299,16 @@ class MiniEdit( Frame ):
|
|||||||
# Delete from view
|
# Delete from view
|
||||||
self.canvas.delete( item )
|
self.canvas.delete( item )
|
||||||
|
|
||||||
# Callback ignores event
|
def deleteSelection( self, _event ):
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
def deleteSelection( self, event ):
|
|
||||||
"Delete the selected item."
|
"Delete the selected item."
|
||||||
if self.selection is not None:
|
if self.selection is not None:
|
||||||
self.deleteItem( self.selection )
|
self.deleteItem( self.selection )
|
||||||
self.selectItem( None )
|
self.selectItem( None )
|
||||||
# pylint: enable-msg=W0613
|
|
||||||
|
|
||||||
def nodeIcon( self, node, name ):
|
def nodeIcon( self, node, name ):
|
||||||
"Create a new node icon."
|
"Create a new node icon."
|
||||||
icon = Button( self.canvas, image=self.images[ node ],
|
icon = Button( self.canvas, image=self.images[ node ],
|
||||||
text=name, compound='top' )
|
text=name, compound='top' )
|
||||||
# Unfortunately bindtags wants a tuple
|
# Unfortunately bindtags wants a tuple
|
||||||
bindtags = [ str( self.nodeBindings ) ]
|
bindtags = [ str( self.nodeBindings ) ]
|
||||||
bindtags += list( icon.bindtags() )
|
bindtags += list( icon.bindtags() )
|
||||||
@@ -325,8 +322,8 @@ class MiniEdit( Frame ):
|
|||||||
self.nodeCount += 1
|
self.nodeCount += 1
|
||||||
name = self.nodePrefixes[ node ] + str( self.nodeCount )
|
name = self.nodePrefixes[ node ] + str( self.nodeCount )
|
||||||
icon = self.nodeIcon( node, name )
|
icon = self.nodeIcon( node, name )
|
||||||
item = self.canvas.create_window( x, y, anchor='c',
|
item = self.canvas.create_window( x, y, anchor='c', window=icon,
|
||||||
window=icon, tags=node )
|
tags=node )
|
||||||
self.widgetToItem[ icon ] = item
|
self.widgetToItem[ icon ] = item
|
||||||
self.itemToWidget[ item ] = icon
|
self.itemToWidget[ item ] = icon
|
||||||
self.selectItem( item )
|
self.selectItem( item )
|
||||||
@@ -350,14 +347,11 @@ class MiniEdit( Frame ):
|
|||||||
c = self.canvas
|
c = self.canvas
|
||||||
c.coords( self.link, self.linkx, self.linky, x, y )
|
c.coords( self.link, self.linkx, self.linky, x, y )
|
||||||
|
|
||||||
# Callback ignores event
|
def releaseLink( self, _event ):
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
def releaseLink( self, event ):
|
|
||||||
"Give up on the current link."
|
"Give up on the current link."
|
||||||
if self.link is not None:
|
if self.link is not None:
|
||||||
self.canvas.delete( self.link )
|
self.canvas.delete( self.link )
|
||||||
self.linkWidget = self.linkItem = self.link = None
|
self.linkWidget = self.linkItem = self.link = None
|
||||||
# pylint: enable-msg=W0613
|
|
||||||
|
|
||||||
# Generic node handlers
|
# Generic node handlers
|
||||||
|
|
||||||
@@ -385,12 +379,9 @@ class MiniEdit( Frame ):
|
|||||||
"Select node on entry."
|
"Select node on entry."
|
||||||
self.selectNode( event )
|
self.selectNode( event )
|
||||||
|
|
||||||
# Callback ignores event
|
def leaveNode( self, _event ):
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
def leaveNode( self, event ):
|
|
||||||
"Restore old selection on exit."
|
"Restore old selection on exit."
|
||||||
self.selectItem( self.lastSelection )
|
self.selectItem( self.lastSelection )
|
||||||
# pylint: enable-msg=W0613
|
|
||||||
|
|
||||||
def clickNode( self, event ):
|
def clickNode( self, event ):
|
||||||
"Node click handler."
|
"Node click handler."
|
||||||
@@ -446,7 +437,7 @@ class MiniEdit( Frame ):
|
|||||||
item = self.widgetToItem[ w ]
|
item = self.widgetToItem[ w ]
|
||||||
x, y = self.canvas.coords( item )
|
x, y = self.canvas.coords( item )
|
||||||
self.link = self.canvas.create_line( x, y, x, y, width=4,
|
self.link = self.canvas.create_line( x, y, x, y, width=4,
|
||||||
fill='blue', tag='link' )
|
fill='blue', tag='link' )
|
||||||
self.linkx, self.linky = x, y
|
self.linkx, self.linky = x, y
|
||||||
self.linkWidget = w
|
self.linkWidget = w
|
||||||
self.linkItem = item
|
self.linkItem = item
|
||||||
@@ -454,23 +445,21 @@ class MiniEdit( Frame ):
|
|||||||
# Link bindings
|
# Link bindings
|
||||||
# Selection still needs a bit of work overall
|
# Selection still needs a bit of work overall
|
||||||
# Callbacks ignore event
|
# Callbacks ignore event
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
|
|
||||||
def select( event, link=self.link ):
|
def select( _event, link=self.link ):
|
||||||
"Select item on mouse entry."
|
"Select item on mouse entry."
|
||||||
self.selectItem( link )
|
self.selectItem( link )
|
||||||
|
|
||||||
def highlight( event, link=self.link ):
|
def highlight( _event, link=self.link ):
|
||||||
"Highlight item on mouse entry."
|
"Highlight item on mouse entry."
|
||||||
# self.selectItem( link )
|
# self.selectItem( link )
|
||||||
self.canvas.itemconfig( link, fill='green' )
|
self.canvas.itemconfig( link, fill='green' )
|
||||||
|
|
||||||
def unhighlight( event, link=self.link ):
|
def unhighlight( _event, link=self.link ):
|
||||||
"Unhighlight item on mouse exit."
|
"Unhighlight item on mouse exit."
|
||||||
self.canvas.itemconfig( link, fill='blue' )
|
self.canvas.itemconfig( link, fill='blue' )
|
||||||
# self.selectItem( None )
|
# self.selectItem( None )
|
||||||
|
|
||||||
# pylint: disable-msg=W0613
|
|
||||||
self.canvas.tag_bind( self.link, '<Enter>', highlight )
|
self.canvas.tag_bind( self.link, '<Enter>', highlight )
|
||||||
self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
|
self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
|
||||||
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
|
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
|
||||||
@@ -486,7 +475,7 @@ class MiniEdit( Frame ):
|
|||||||
target = self.findItem( x, y )
|
target = self.findItem( x, y )
|
||||||
dest = self.itemToWidget.get( target, None )
|
dest = self.itemToWidget.get( target, None )
|
||||||
if ( source is None or dest is None or source == dest
|
if ( source is None or dest is None or source == dest
|
||||||
or dest in source.links or source in dest.links ):
|
or dest in source.links or source in dest.links ):
|
||||||
self.releaseLink( event )
|
self.releaseLink( event )
|
||||||
return
|
return
|
||||||
# For now, don't allow hosts to be directly linked
|
# For now, don't allow hosts to be directly linked
|
||||||
|
|||||||
Executable
+86
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
multiping.py: monitor multiple sets of hosts using ping
|
||||||
|
|
||||||
|
This demonstrates how one may send a simple shell script to
|
||||||
|
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 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 ) ]
|
||||||
|
|
||||||
|
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 +
|
||||||
|
' echo -n %s "->" $ip ' % host.IP() +
|
||||||
|
' `ping -c1 -w 1 $ip | grep packets` ;'
|
||||||
|
' sleep 1;'
|
||||||
|
' done; '
|
||||||
|
'done &' )
|
||||||
|
|
||||||
|
print ( '*** Host %s (%s) will be pinging ips: %s' %
|
||||||
|
( host.name, host.IP(), targetips ) )
|
||||||
|
|
||||||
|
host.cmd( cmd )
|
||||||
|
|
||||||
|
def multiping( netsize, chunksize, seconds):
|
||||||
|
"Ping subsets of size chunksize in net of size netsize"
|
||||||
|
|
||||||
|
# Create network and identify subnets
|
||||||
|
topo = SingleSwitchTopo( netsize )
|
||||||
|
net = Mininet( topo=topo )
|
||||||
|
net.start()
|
||||||
|
hosts = net.hosts
|
||||||
|
subnets = chunks( hosts, chunksize )
|
||||||
|
|
||||||
|
# Create polling object
|
||||||
|
fds = [ host.stdout.fileno() for host in hosts ]
|
||||||
|
poller = poll()
|
||||||
|
for fd in fds:
|
||||||
|
poller.register( fd, POLLIN )
|
||||||
|
|
||||||
|
# Start pings
|
||||||
|
for subnet in subnets:
|
||||||
|
ips = [ host.IP() for host in subnet ]
|
||||||
|
for host in subnet:
|
||||||
|
startpings( host, ips )
|
||||||
|
|
||||||
|
# Monitor output
|
||||||
|
endTime = time() + seconds
|
||||||
|
while time() < endTime:
|
||||||
|
readable = poller.poll(1000)
|
||||||
|
for fd, _mask in readable:
|
||||||
|
node = Node.outToNode[ fd ]
|
||||||
|
print '%s:' % node.name, node.monitor().strip()
|
||||||
|
|
||||||
|
# Stop pings
|
||||||
|
for host in hosts:
|
||||||
|
host.cmd( 'kill %while' )
|
||||||
|
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel( 'info' )
|
||||||
|
multiping( netsize=20, chunksize=4, seconds=10 )
|
||||||
Executable
+81
@@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Simple example of sending output to multiple files and
|
||||||
|
monitoring them
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mininet.topo import SingleSwitchTopo
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.log import setLogLevel
|
||||||
|
|
||||||
|
from time import time
|
||||||
|
from select import poll, POLLIN
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
|
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.iteritems():
|
||||||
|
tail = Popen( [ 'tail', '-f', outfile ],
|
||||||
|
stdout=PIPE, stderr=devnull )
|
||||||
|
fd = tail.stdout.fileno()
|
||||||
|
tails[ h ] = tail
|
||||||
|
fdToFile[ fd ] = tail.stdout
|
||||||
|
fdToHost[ fd ] = h
|
||||||
|
# Prepare to poll output files
|
||||||
|
readable = poll()
|
||||||
|
for t in tails.values():
|
||||||
|
readable.register( t.stdout.fileno(), POLLIN )
|
||||||
|
# Run until a set number of seconds have elapsed
|
||||||
|
endTime = time() + seconds
|
||||||
|
while time() < endTime:
|
||||||
|
fdlist = readable.poll(timeoutms)
|
||||||
|
if fdlist:
|
||||||
|
for fd, _flags in fdlist:
|
||||||
|
f = fdToFile[ fd ]
|
||||||
|
host = fdToHost[ fd ]
|
||||||
|
# Wait for a line of output
|
||||||
|
line = f.readline().strip()
|
||||||
|
yield host, line
|
||||||
|
else:
|
||||||
|
# If we timed out, return nothing
|
||||||
|
yield None, ''
|
||||||
|
for t in tails.values():
|
||||||
|
t.terminate()
|
||||||
|
devnull.close() # Not really necessary
|
||||||
|
|
||||||
|
|
||||||
|
def monitorTest( N=3, seconds=3 ):
|
||||||
|
"Run pings and monitor multiple hosts"
|
||||||
|
topo = SingleSwitchTopo( N )
|
||||||
|
net = Mininet( topo )
|
||||||
|
net.start()
|
||||||
|
hosts = net.hosts
|
||||||
|
print "Starting test..."
|
||||||
|
server = hosts[ 0 ]
|
||||||
|
outfiles, errfiles = {}, {}
|
||||||
|
for h in hosts:
|
||||||
|
# Create and/or erase output files
|
||||||
|
outfiles[ h ] = '/tmp/%s.out' % h.name
|
||||||
|
errfiles[ h ] = '/tmp/%s.err' % h.name
|
||||||
|
h.cmd( 'echo >', outfiles[ h ] )
|
||||||
|
h.cmd( 'echo >', errfiles[ h ] )
|
||||||
|
# Start pings
|
||||||
|
h.cmdPrint('ping', server.IP(),
|
||||||
|
'>', outfiles[ h ],
|
||||||
|
'2>', errfiles[ h ],
|
||||||
|
'&' )
|
||||||
|
print "Monitoring output for", seconds, "seconds"
|
||||||
|
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
|
||||||
|
if h:
|
||||||
|
print '%s: %s' % ( h.name, line )
|
||||||
|
for h in hosts:
|
||||||
|
h.cmd('kill %ping')
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel('info')
|
||||||
|
monitorTest()
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
This example monitors a number of hosts using host.popen() and
|
||||||
|
pmonitor()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.node import CPULimitedHost
|
||||||
|
from mininet.topo import SingleSwitchTopo
|
||||||
|
from mininet.log import setLogLevel
|
||||||
|
from mininet.util import custom, pmonitor
|
||||||
|
|
||||||
|
def monitorhosts( hosts=5, sched='cfs' ):
|
||||||
|
"Start a bunch of pings and monitor them using popen"
|
||||||
|
mytopo = SingleSwitchTopo( hosts )
|
||||||
|
cpu = .5 / hosts
|
||||||
|
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
|
||||||
|
net = Mininet( topo=mytopo, host=myhost )
|
||||||
|
net.start()
|
||||||
|
# Start a bunch of pings
|
||||||
|
popens = {}
|
||||||
|
last = net.hosts[ -1 ]
|
||||||
|
for host in net.hosts:
|
||||||
|
popens[ host ] = host.popen( "ping -c5 %s" % last.IP() )
|
||||||
|
last = host
|
||||||
|
# Monitor them and print output
|
||||||
|
for host, line in pmonitor( popens ):
|
||||||
|
if host:
|
||||||
|
print "<%s>: %s" % ( host.name, line.strip() )
|
||||||
|
# Done
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel( 'info' )
|
||||||
|
monitorhosts( hosts=5 )
|
||||||
Executable
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"Monitor multiple hosts using popen()/pmonitor()"
|
||||||
|
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.topo import SingleSwitchTopo
|
||||||
|
from mininet.util import pmonitor
|
||||||
|
from time import time
|
||||||
|
from signal import SIGINT
|
||||||
|
|
||||||
|
def pmonitorTest( N=3, seconds=10 ):
|
||||||
|
"Run pings and monitor multiple hosts using pmonitor"
|
||||||
|
topo = SingleSwitchTopo( N )
|
||||||
|
net = Mininet( topo )
|
||||||
|
net.start()
|
||||||
|
hosts = net.hosts
|
||||||
|
print "Starting test..."
|
||||||
|
server = hosts[ 0 ]
|
||||||
|
popens = {}
|
||||||
|
for h in hosts:
|
||||||
|
popens[ h ] = h.popen('ping', server.IP() )
|
||||||
|
print "Monitoring output for", seconds, "seconds"
|
||||||
|
endTime = time() + seconds
|
||||||
|
for h, line in pmonitor( popens, timeoutms=500 ):
|
||||||
|
if h:
|
||||||
|
print '%s: %s' % ( h.name, line ),
|
||||||
|
if time() >= endTime:
|
||||||
|
for p in popens.values():
|
||||||
|
p.send_signal( SIGINT )
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pmonitorTest()
|
||||||
+28
-18
@@ -8,13 +8,16 @@ but it exposes the configuration details and allows customization.
|
|||||||
For most tasks, the higher-level API will be preferable.
|
For most tasks, the higher-level API will be preferable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from mininet.net import init
|
from mininet.net import Mininet
|
||||||
from mininet.node import Node, OVSKernelSwitch
|
from mininet.node import Node
|
||||||
from mininet.util import createLink
|
from mininet.link import Link
|
||||||
from mininet.log import setLogLevel, info
|
from mininet.log import setLogLevel, info
|
||||||
|
from mininet.util import quietRun
|
||||||
|
|
||||||
def scratchNet( cname='controller', cargs='ptcp:' ):
|
from time import sleep
|
||||||
"Create network from scratch using kernel switch."
|
|
||||||
|
def scratchNet( cname='controller', cargs='-v ptcp:' ):
|
||||||
|
"Create network from scratch using Open vSwitch."
|
||||||
|
|
||||||
info( "*** Creating nodes\n" )
|
info( "*** Creating nodes\n" )
|
||||||
controller = Node( 'c0', inNamespace=False )
|
controller = Node( 'c0', inNamespace=False )
|
||||||
@@ -23,36 +26,43 @@ def scratchNet( cname='controller', cargs='ptcp:' ):
|
|||||||
h1 = Node( 'h1' )
|
h1 = Node( 'h1' )
|
||||||
|
|
||||||
info( "*** Creating links\n" )
|
info( "*** Creating links\n" )
|
||||||
createLink( node1=h0, node2=switch, port1=0, port2=0 )
|
Link( h0, switch )
|
||||||
createLink( node1=h1, node2=switch, port1=0, port2=1 )
|
Link( h1, switch )
|
||||||
|
|
||||||
info( "*** Configuring hosts\n" )
|
info( "*** Configuring hosts\n" )
|
||||||
h0.setIP( h0.intfs[ 0 ], '192.168.123.1', 24 )
|
h0.setIP( '192.168.123.1/24' )
|
||||||
h1.setIP( h1.intfs[ 0 ], '192.168.123.2', 24 )
|
h1.setIP( '192.168.123.2/24' )
|
||||||
info( str( h0 ) + '\n' )
|
info( str( h0 ) + '\n' )
|
||||||
info( str( h1 ) + '\n' )
|
info( str( h1 ) + '\n' )
|
||||||
|
|
||||||
info( "*** Starting network using Open vSwitch kernel datapath\n" )
|
info( "*** Starting network using Open vSwitch\n" )
|
||||||
controller.cmd( cname + ' ' + cargs + '&' )
|
controller.cmd( cname + ' ' + cargs + '&' )
|
||||||
switch.cmd( 'ovs-dpctl del-dp dp0' )
|
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||||
switch.cmd( 'ovs-dpctl add-dp dp0' )
|
switch.cmd( 'ovs-vsctl add-br dp0' )
|
||||||
for intf in switch.intfs.values():
|
for intf in switch.intfs.values():
|
||||||
print switch.cmd( 'ovs-dpctl add-if dp0 ' + intf )
|
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
|
||||||
print switch.cmd( 'ovs-openflowd dp0 tcp:127.0.0.1 &' )
|
|
||||||
|
# Note: controller and switch are in root namespace, and we
|
||||||
|
# can connect via loopback interface
|
||||||
|
switch.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:6633' )
|
||||||
|
|
||||||
|
info( '*** Waiting for switch to connect to controller' )
|
||||||
|
while 'is_connected' not in quietRun( 'ovs-vsctl show' ):
|
||||||
|
sleep( 1 )
|
||||||
|
info( '.' )
|
||||||
|
info( '\n' )
|
||||||
|
|
||||||
info( "*** Running test\n" )
|
info( "*** Running test\n" )
|
||||||
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
|
h0.cmdPrint( 'ping -c1 ' + h1.IP() )
|
||||||
|
|
||||||
info( "*** Stopping network\n" )
|
info( "*** Stopping network\n" )
|
||||||
controller.cmd( 'kill %' + cname )
|
controller.cmd( 'kill %' + cname )
|
||||||
switch.cmd( 'ovs-dpctl del-dp dp0' )
|
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||||
switch.cmd( 'kill %ovs-openflowd' )
|
|
||||||
switch.deleteIntfs()
|
switch.deleteIntfs()
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
setLogLevel( 'info' )
|
setLogLevel( 'info' )
|
||||||
info( '*** Scratch network demo (kernel datapath)\n' )
|
info( '*** Scratch network demo (kernel datapath)\n' )
|
||||||
OVSKernelSwitch.setup()
|
Mininet.init()
|
||||||
init()
|
|
||||||
scratchNet()
|
scratchNet()
|
||||||
|
|||||||
+16
-11
@@ -10,11 +10,16 @@ For most tasks, the higher-level API will be preferable.
|
|||||||
This version uses the user datapath and an explicit control network.
|
This version uses the user datapath and an explicit control network.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from mininet.net import init
|
from mininet.net import Mininet
|
||||||
from mininet.node import Node
|
from mininet.node import Node
|
||||||
from mininet.util import createLink
|
from mininet.link import Link
|
||||||
from mininet.log import setLogLevel, info
|
from mininet.log import setLogLevel, info
|
||||||
|
|
||||||
|
def linkIntfs( node1, node2 ):
|
||||||
|
"Create link from node1 to node2 and return intfs"
|
||||||
|
link = Link( node1, node2 )
|
||||||
|
return link.intf1, link.intf2
|
||||||
|
|
||||||
def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||||
"Create network from scratch using user switch."
|
"Create network from scratch using user switch."
|
||||||
|
|
||||||
@@ -28,17 +33,17 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
|||||||
switch = Node( 's0')
|
switch = Node( 's0')
|
||||||
h0 = Node( 'h0' )
|
h0 = Node( 'h0' )
|
||||||
h1 = Node( 'h1' )
|
h1 = Node( 'h1' )
|
||||||
cintf, sintf = createLink( controller, switch )
|
cintf, sintf = linkIntfs( controller, switch )
|
||||||
h0intf, sintf1 = createLink( h0, switch )
|
h0intf, sintf1 = linkIntfs( h0, switch )
|
||||||
h1intf, sintf2 = createLink( h1, switch )
|
h1intf, sintf2 = linkIntfs( h1, switch )
|
||||||
|
|
||||||
info( '*** Configuring control network\n' )
|
info( '*** Configuring control network\n' )
|
||||||
controller.setIP( cintf, '10.0.123.1', 24 )
|
controller.setIP( '10.0.123.1/24', intf=cintf )
|
||||||
switch.setIP( sintf, '10.0.123.2', 24 )
|
switch.setIP( '10.0.123.2/24', intf=sintf)
|
||||||
|
|
||||||
info( '*** Configuring hosts\n' )
|
info( '*** Configuring hosts\n' )
|
||||||
h0.setIP( h0intf, '192.168.123.1', 24 )
|
h0.setIP( '192.168.123.1/24', intf=h0intf )
|
||||||
h1.setIP( h1intf, '192.168.123.2', 24 )
|
h1.setIP( '192.168.123.2/24', intf=h1intf )
|
||||||
|
|
||||||
info( '*** Network state:\n' )
|
info( '*** Network state:\n' )
|
||||||
for node in controller, switch, h0, h1:
|
for node in controller, switch, h0, h1:
|
||||||
@@ -47,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
|||||||
info( '*** Starting controller and user datapath\n' )
|
info( '*** Starting controller and user datapath\n' )
|
||||||
controller.cmd( cname + ' ' + cargs + '&' )
|
controller.cmd( cname + ' ' + cargs + '&' )
|
||||||
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
||||||
intfs = [ sintf1, sintf2 ]
|
intfs = [ str( i ) for i in sintf1, sintf2 ]
|
||||||
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
|
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
|
||||||
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
|
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
|
||||||
|
|
||||||
@@ -64,5 +69,5 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
setLogLevel( 'info' )
|
setLogLevel( 'info' )
|
||||||
info( '*** Scratch network demo (user datapath)\n' )
|
info( '*** Scratch network demo (user datapath)\n' )
|
||||||
init()
|
Mininet.init()
|
||||||
scratchNetUser()
|
scratchNetUser()
|
||||||
|
|||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
class SingleSwitchTopo(Topo):
|
||||||
|
"Single switch connected to n hosts."
|
||||||
|
def __init__(self, n=2, **opts):
|
||||||
|
Topo.__init__(self, **opts)
|
||||||
|
switch = self.addSwitch('s1')
|
||||||
|
for h in range(n):
|
||||||
|
# Each host gets 50%/n of system CPU
|
||||||
|
host = self.addHost('h%s' % (h + 1),
|
||||||
|
cpu=.5 / n)
|
||||||
|
# 10 Mbps, 5ms delay, 10% loss
|
||||||
|
self.addLink(host, switch,
|
||||||
|
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||||
|
|
||||||
|
def perfTest():
|
||||||
|
"Create network and run simple performance test"
|
||||||
|
topo = SingleSwitchTopo(n=4)
|
||||||
|
net = Mininet(topo=topo,
|
||||||
|
host=CPULimitedHost, link=TCLink)
|
||||||
|
net.start()
|
||||||
|
print "Dumping host connections"
|
||||||
|
dumpNodeConnections(net.hosts)
|
||||||
|
print "Testing network connectivity"
|
||||||
|
net.pingAll()
|
||||||
|
print "Testing bandwidth between h1 and h4"
|
||||||
|
h1, h4 = net.getNodeByName('h1', 'h4')
|
||||||
|
net.iperf((h1, h4))
|
||||||
|
net.stop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel('info')
|
||||||
|
perfTest()
|
||||||
+4
-4
@@ -21,7 +21,7 @@ from mininet.cli import CLI
|
|||||||
from mininet.log import lg
|
from mininet.log import lg
|
||||||
from mininet.node import Node, OVSKernelSwitch
|
from mininet.node import Node, OVSKernelSwitch
|
||||||
from mininet.topolib import TreeTopo
|
from mininet.topolib import TreeTopo
|
||||||
from mininet.util import createLink
|
from mininet.link import Link
|
||||||
|
|
||||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||||
"Convenience function for creating tree networks."
|
"Convenience function for creating tree networks."
|
||||||
@@ -37,13 +37,13 @@ def connectToRootNS( network, switch, ip, prefixLen, routes ):
|
|||||||
routes: host networks to route to"""
|
routes: host networks to route to"""
|
||||||
# Create a node in root namespace and link to switch 0
|
# Create a node in root namespace and link to switch 0
|
||||||
root = Node( 'root', inNamespace=False )
|
root = Node( 'root', inNamespace=False )
|
||||||
intf = createLink( root, switch )[ 0 ]
|
intf = Link( root, switch ).intf1
|
||||||
root.setIP( intf, ip, prefixLen )
|
root.setIP( ip, prefixLen, intf )
|
||||||
# Start network that now includes link to root namespace
|
# Start network that now includes link to root namespace
|
||||||
network.start()
|
network.start()
|
||||||
# Add routes from root ns to hosts
|
# Add routes from root ns to hosts
|
||||||
for route in routes:
|
for route in routes:
|
||||||
root.cmd( 'route add -net ' + route + ' dev ' + intf )
|
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
|
||||||
|
|
||||||
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
|
def sshd( network, cmd='/usr/sbin/sshd', opts='-D' ):
|
||||||
"Start a network, connect it to root ns, and run sshd on all hosts."
|
"Start a network, connect it to root ns, and run sshd on all hosts."
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ def treePing64():
|
|||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
switches = { # 'reference kernel': KernelSwitch,
|
switches = { # 'reference kernel': KernelSwitch,
|
||||||
'reference user': UserSwitch,
|
'reference user': UserSwitch,
|
||||||
'Open vSwitch kernel': OVSKernelSwitch }
|
'Open vSwitch kernel': OVSKernelSwitch }
|
||||||
|
|
||||||
for name in switches:
|
for name in switches:
|
||||||
print "*** Testing", name, "datapath"
|
print "*** Testing", name, "datapath"
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ def cleanup():
|
|||||||
if dp != '':
|
if dp != '':
|
||||||
sh( 'dpctl deldp ' + dp )
|
sh( 'dpctl deldp ' + dp )
|
||||||
|
|
||||||
|
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" )
|
info( "*** Removing all links of the pattern foo-ethX\n" )
|
||||||
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
|
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
|
||||||
for link in links:
|
for link in links:
|
||||||
|
|||||||
+39
-29
@@ -30,10 +30,11 @@ from cmd import Cmd
|
|||||||
from os import isatty
|
from os import isatty
|
||||||
from select import poll, POLLIN
|
from select import poll, POLLIN
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
from mininet.log import info, output, error
|
from mininet.log import info, output, error
|
||||||
from mininet.term import makeTerms
|
from mininet.term import makeTerms
|
||||||
from mininet.util import quietRun, isShellBuiltin
|
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
|
||||||
|
|
||||||
class CLI( Cmd ):
|
class CLI( Cmd ):
|
||||||
"Simple command-line interface to talk to nodes."
|
"Simple command-line interface to talk to nodes."
|
||||||
@@ -46,6 +47,9 @@ class CLI( Cmd ):
|
|||||||
self.nodemap = {} # map names to Node objects
|
self.nodemap = {} # map names to Node objects
|
||||||
for node in self.nodelist:
|
for node in self.nodelist:
|
||||||
self.nodemap[ node.name ] = node
|
self.nodemap[ node.name ] = node
|
||||||
|
# Local variable bindings for py command
|
||||||
|
self.locals = { 'net': mininet }
|
||||||
|
self.locals.update( self.nodemap )
|
||||||
# Attempt to handle input
|
# Attempt to handle input
|
||||||
self.stdin = stdin
|
self.stdin = stdin
|
||||||
self.inPoller = poll()
|
self.inPoller = poll()
|
||||||
@@ -77,7 +81,7 @@ class CLI( Cmd ):
|
|||||||
# Disable pylint "Unused argument: 'arg's'" messages, as well as
|
# Disable pylint "Unused argument: 'arg's'" messages, as well as
|
||||||
# "method could be a function" warning, since each CLI function
|
# "method could be a function" warning, since each CLI function
|
||||||
# must have the same interface
|
# must have the same interface
|
||||||
# pylint: disable-msg=W0613,R0201
|
# pylint: disable-msg=R0201
|
||||||
|
|
||||||
helpStr = (
|
helpStr = (
|
||||||
'You may also send a command to a node using:\n'
|
'You may also send a command to a node using:\n'
|
||||||
@@ -104,21 +108,14 @@ class CLI( Cmd ):
|
|||||||
if line is '':
|
if line is '':
|
||||||
output( self.helpStr )
|
output( self.helpStr )
|
||||||
|
|
||||||
def do_nodes( self, line ):
|
def do_nodes( self, _line ):
|
||||||
"List all nodes."
|
"List all nodes."
|
||||||
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
|
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
|
||||||
output( 'available nodes are: \n%s\n' % nodes )
|
output( 'available nodes are: \n%s\n' % nodes )
|
||||||
|
|
||||||
def do_net( self, line ):
|
def do_net( self, _line ):
|
||||||
"List network connections."
|
"List network connections."
|
||||||
for switch in self.mn.switches:
|
dumpNodeConnections( self.nodelist )
|
||||||
output( switch.name, '<->' )
|
|
||||||
for intf in switch.intfs.values():
|
|
||||||
# Ugly, but pylint wants it
|
|
||||||
name = switch.connection.get( intf,
|
|
||||||
( None, 'Unknown ' ) )[ 1 ]
|
|
||||||
output( ' %s' % name )
|
|
||||||
output( '\n' )
|
|
||||||
|
|
||||||
def do_sh( self, line ):
|
def do_sh( self, line ):
|
||||||
"Run an external shell command"
|
"Run an external shell command"
|
||||||
@@ -131,7 +128,7 @@ class CLI( Cmd ):
|
|||||||
"""Evaluate a Python expression.
|
"""Evaluate a Python expression.
|
||||||
Node names may be used, e.g.: h1.cmd('ls')"""
|
Node names may be used, e.g.: h1.cmd('ls')"""
|
||||||
try:
|
try:
|
||||||
result = eval( line, globals(), self.nodemap )
|
result = eval( line, globals(), self.locals )
|
||||||
if not result:
|
if not result:
|
||||||
return
|
return
|
||||||
elif isinstance( result, str ):
|
elif isinstance( result, str ):
|
||||||
@@ -143,14 +140,22 @@ class CLI( Cmd ):
|
|||||||
|
|
||||||
# pylint: enable-msg=W0703
|
# pylint: enable-msg=W0703
|
||||||
|
|
||||||
def do_pingall( self, line ):
|
def do_pingall( self, _line ):
|
||||||
"Ping between all hosts."
|
"Ping between all hosts."
|
||||||
self.mn.pingAll()
|
self.mn.pingAll()
|
||||||
|
|
||||||
def do_pingpair( self, line ):
|
def do_pingpair( self, _line ):
|
||||||
"Ping between first two hosts, useful for testing."
|
"Ping between first two hosts, useful for testing."
|
||||||
self.mn.pingPair()
|
self.mn.pingPair()
|
||||||
|
|
||||||
|
def do_pingallfull( self, _line ):
|
||||||
|
"Ping between first two 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 ):
|
def do_iperf( self, line ):
|
||||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||||
args = line.split()
|
args = line.split()
|
||||||
@@ -191,16 +196,16 @@ class CLI( Cmd ):
|
|||||||
error( 'invalid number of args: iperfudp bw src dst\n' +
|
error( 'invalid number of args: iperfudp bw src dst\n' +
|
||||||
'bw examples: 10M\n' )
|
'bw examples: 10M\n' )
|
||||||
|
|
||||||
def do_intfs( self, line ):
|
def do_intfs( self, _line ):
|
||||||
"List interfaces."
|
"List interfaces."
|
||||||
for node in self.nodelist:
|
for node in self.nodelist:
|
||||||
output( '%s: %s\n' %
|
output( '%s: %s\n' %
|
||||||
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
|
( node.name, ','.join( node.intfNames() ) ) )
|
||||||
|
|
||||||
def do_dump( self, line ):
|
def do_dump( self, _line ):
|
||||||
"Dump node info."
|
"Dump node info."
|
||||||
for node in self.nodelist:
|
for node in self.nodelist:
|
||||||
output( '%s\n' % node )
|
output( '%s\n' % repr( node ) )
|
||||||
|
|
||||||
def do_link( self, line ):
|
def do_link( self, line ):
|
||||||
"Bring link(s) between two nodes up or down."
|
"Bring link(s) between two nodes up or down."
|
||||||
@@ -229,7 +234,7 @@ class CLI( Cmd ):
|
|||||||
"Spawn gnome-terminal(s) for the given node(s)."
|
"Spawn gnome-terminal(s) for the given node(s)."
|
||||||
self.do_xterm( line, term='gterm' )
|
self.do_xterm( line, term='gterm' )
|
||||||
|
|
||||||
def do_exit( self, line ):
|
def do_exit( self, _line ):
|
||||||
"Exit"
|
"Exit"
|
||||||
return 'exited by user command'
|
return 'exited by user command'
|
||||||
|
|
||||||
@@ -275,16 +280,19 @@ class CLI( Cmd ):
|
|||||||
def do_dpctl( self, line ):
|
def do_dpctl( self, line ):
|
||||||
"Run dpctl command on all switches."
|
"Run dpctl command on all switches."
|
||||||
args = line.split()
|
args = line.split()
|
||||||
if len(args) == 0:
|
if len(args) < 1:
|
||||||
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
|
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
|
||||||
return
|
return
|
||||||
if not self.mn.listenPort:
|
|
||||||
error( "can't run dpctl w/no passive listening port\n")
|
|
||||||
return
|
|
||||||
for sw in self.mn.switches:
|
for sw in self.mn.switches:
|
||||||
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
|
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
|
||||||
output( sw.cmd( 'dpctl ' + ' '.join(args) +
|
output( sw.dpctl( *args ) )
|
||||||
' tcp:127.0.0.1:%i' % sw.listenPort ) )
|
|
||||||
|
def do_time( self, line ):
|
||||||
|
"Measure time taken for any command in Mininet."
|
||||||
|
start = time.time()
|
||||||
|
self.onecmd(line)
|
||||||
|
elapsed = time.time() - start
|
||||||
|
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
|
||||||
|
|
||||||
def default( self, line ):
|
def default( self, line ):
|
||||||
"""Called on an input line when the command prefix is not recognized.
|
"""Called on an input line when the command prefix is not recognized.
|
||||||
@@ -293,6 +301,8 @@ class CLI( Cmd ):
|
|||||||
corresponding IP addrs."""
|
corresponding IP addrs."""
|
||||||
|
|
||||||
first, args, line = self.parseline( line )
|
first, args, line = self.parseline( line )
|
||||||
|
if not args:
|
||||||
|
return
|
||||||
if args and len(args) > 0 and args[ -1 ] == '\n':
|
if args and len(args) > 0 and args[ -1 ] == '\n':
|
||||||
args = args[ :-1 ]
|
args = args[ :-1 ]
|
||||||
rest = args.split( ' ' )
|
rest = args.split( ' ' )
|
||||||
@@ -301,8 +311,8 @@ class CLI( Cmd ):
|
|||||||
node = self.nodemap[ first ]
|
node = self.nodemap[ first ]
|
||||||
# Substitute IP addresses for node names in command
|
# Substitute IP addresses for node names in command
|
||||||
rest = [ self.nodemap[ arg ].IP()
|
rest = [ self.nodemap[ arg ].IP()
|
||||||
if arg in self.nodemap else arg
|
if arg in self.nodemap else arg
|
||||||
for arg in rest ]
|
for arg in rest ]
|
||||||
rest = ' '.join( rest )
|
rest = ' '.join( rest )
|
||||||
# Run cmd on node:
|
# Run cmd on node:
|
||||||
builtin = isShellBuiltin( first )
|
builtin = isShellBuiltin( first )
|
||||||
@@ -311,7 +321,7 @@ class CLI( Cmd ):
|
|||||||
else:
|
else:
|
||||||
error( '*** Unknown command: %s\n' % first )
|
error( '*** Unknown command: %s\n' % first )
|
||||||
|
|
||||||
# pylint: enable-msg=W0613,R0201
|
# pylint: enable-msg=R0201
|
||||||
|
|
||||||
def waitForNode( self, node ):
|
def waitForNode( self, node ):
|
||||||
"Wait for a node to finish, and print its output."
|
"Wait for a node to finish, and print its output."
|
||||||
|
|||||||
+397
@@ -0,0 +1,397 @@
|
|||||||
|
"""
|
||||||
|
link.py: interface and link abstractions for mininet
|
||||||
|
|
||||||
|
It seems useful to bundle functionality for interfaces into a single
|
||||||
|
class.
|
||||||
|
|
||||||
|
Also it seems useful to enable the possibility of multiple flavors of
|
||||||
|
links, including:
|
||||||
|
|
||||||
|
- simple veth pairs
|
||||||
|
- tunneled links
|
||||||
|
- patchable links (which can be disconnected and reconnected via a patchbay)
|
||||||
|
- link simulators (e.g. wireless)
|
||||||
|
|
||||||
|
Basic division of labor:
|
||||||
|
|
||||||
|
Nodes: know how to execute commands
|
||||||
|
Intfs: know how to configure themselves
|
||||||
|
Links: know how to connect nodes together
|
||||||
|
|
||||||
|
Intf: basic interface object that can configure itself
|
||||||
|
TCIntf: interface with bandwidth limiting and delay via tc
|
||||||
|
|
||||||
|
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, **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
|
||||||
|
other arguments are passed to config()"""
|
||||||
|
self.node = node
|
||||||
|
self.name = name
|
||||||
|
self.link = link
|
||||||
|
self.mac, self.ip, self.prefixLen = None, None, None
|
||||||
|
# Add to node (and move ourselves if necessary )
|
||||||
|
node.addIntf( self, port=port )
|
||||||
|
# Save params for future reference
|
||||||
|
self.params = params
|
||||||
|
self.config( **params )
|
||||||
|
|
||||||
|
def cmd( self, *args, **kwargs ):
|
||||||
|
"Run a command in our owning node"
|
||||||
|
return self.node.cmd( *args, **kwargs )
|
||||||
|
|
||||||
|
def ifconfig( self, *args ):
|
||||||
|
"Configure ourselves using ifconfig"
|
||||||
|
return self.cmd( 'ifconfig', self.name, *args )
|
||||||
|
|
||||||
|
def setIP( self, ipstr, prefixLen=None ):
|
||||||
|
"""Set our IP address"""
|
||||||
|
# This is a sign that we should perhaps rethink our prefix
|
||||||
|
# mechanism and/or the way we specify IP addresses
|
||||||
|
if '/' in ipstr:
|
||||||
|
self.ip, self.prefixLen = ipstr.split( '/' )
|
||||||
|
return self.ifconfig( ipstr, 'up' )
|
||||||
|
else:
|
||||||
|
self.ip, self.prefixLen = ipstr, prefixLen
|
||||||
|
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
|
||||||
|
|
||||||
|
def setMAC( self, macstr ):
|
||||||
|
"""Set the MAC address for an interface.
|
||||||
|
macstr: MAC address as string"""
|
||||||
|
self.mac = macstr
|
||||||
|
return ( self.ifconfig( 'down' ) +
|
||||||
|
self.ifconfig( 'hw', 'ether', macstr ) +
|
||||||
|
self.ifconfig( 'up' ) )
|
||||||
|
|
||||||
|
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
|
||||||
|
_macMatchRegex = re.compile( r'..:..:..:..:..:..' )
|
||||||
|
|
||||||
|
def updateIP( self ):
|
||||||
|
"Return updated IP address based on ifconfig"
|
||||||
|
ifconfig = self.ifconfig()
|
||||||
|
ips = self._ipMatchRegex.findall( ifconfig )
|
||||||
|
self.ip = ips[ 0 ] if ips else None
|
||||||
|
return self.ip
|
||||||
|
|
||||||
|
def updateMAC( self ):
|
||||||
|
"Return updated MAC address based on ifconfig"
|
||||||
|
ifconfig = self.ifconfig()
|
||||||
|
macs = self._macMatchRegex.findall( ifconfig )
|
||||||
|
self.mac = macs[ 0 ] if macs else None
|
||||||
|
return self.mac
|
||||||
|
|
||||||
|
def IP( self ):
|
||||||
|
"Return IP address"
|
||||||
|
return self.ip
|
||||||
|
|
||||||
|
def MAC( self ):
|
||||||
|
"Return MAC address"
|
||||||
|
return self.mac
|
||||||
|
|
||||||
|
def isUp( self, setUp=False ):
|
||||||
|
"Return whether interface is up"
|
||||||
|
if setUp:
|
||||||
|
self.ifconfig( 'up' )
|
||||||
|
return "UP" in self.ifconfig()
|
||||||
|
|
||||||
|
def rename( self, newname ):
|
||||||
|
"Rename interface"
|
||||||
|
self.ifconfig( 'down' )
|
||||||
|
result = self.cmd( 'ip link set', self.name, 'name', newname )
|
||||||
|
self.name = newname
|
||||||
|
self.ifconfig( 'up' )
|
||||||
|
return result
|
||||||
|
|
||||||
|
# The reason why we configure things in this way is so
|
||||||
|
# That the parameters can be listed and documented in
|
||||||
|
# the config method.
|
||||||
|
# Dealing with subclasses and superclasses is slightly
|
||||||
|
# annoying, but at least the information is there!
|
||||||
|
|
||||||
|
def setParam( self, results, method, **param ):
|
||||||
|
"""Internal method: configure a *single* parameter
|
||||||
|
results: dict of results to update
|
||||||
|
method: config method name
|
||||||
|
param: arg=value (ignore if value=None)
|
||||||
|
value may also be list or dict"""
|
||||||
|
name, value = param.items()[ 0 ]
|
||||||
|
f = getattr( self, method, None )
|
||||||
|
if not f or value is None:
|
||||||
|
return
|
||||||
|
if type( value ) is list:
|
||||||
|
result = f( *value )
|
||||||
|
elif type( value ) is dict:
|
||||||
|
result = f( **value )
|
||||||
|
else:
|
||||||
|
result = f( value )
|
||||||
|
results[ name ] = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
def config( self, mac=None, ip=None, ifconfig=None,
|
||||||
|
up=True, **_params ):
|
||||||
|
"""Configure Node according to (optional) parameters:
|
||||||
|
mac: MAC address
|
||||||
|
ip: IP address
|
||||||
|
ifconfig: arbitrary interface configuration
|
||||||
|
Subclasses should override this method and call
|
||||||
|
the parent class's config(**params)"""
|
||||||
|
# If we were overriding this method, we would call
|
||||||
|
# the superclass config method here as follows:
|
||||||
|
# r = Parent.config( **params )
|
||||||
|
r = {}
|
||||||
|
self.setParam( r, 'setMAC', mac=mac )
|
||||||
|
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 )
|
||||||
|
# Does it help to sleep to let things run?
|
||||||
|
sleep( 0.001 )
|
||||||
|
|
||||||
|
def __repr__( self ):
|
||||||
|
return '<%s %s>' % ( self.__class__.__name__, self.name )
|
||||||
|
|
||||||
|
def __str__( self ):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class TCIntf( Intf ):
|
||||||
|
"""Interface customized by tc (traffic control) utility
|
||||||
|
Allows specification of bandwidth limits (various methods)
|
||||||
|
as well as delay, loss and max queue length"""
|
||||||
|
|
||||||
|
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
|
||||||
|
latency_ms=None, enable_ecn=False, enable_red=False ):
|
||||||
|
"Return tc commands to set bandwidth"
|
||||||
|
|
||||||
|
cmds, parent = [], ' root '
|
||||||
|
|
||||||
|
if bw and ( bw < 0 or bw > 1000 ):
|
||||||
|
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
|
||||||
|
|
||||||
|
elif bw is not None:
|
||||||
|
# BL: this seems a bit brittle...
|
||||||
|
if ( speedup > 0 and
|
||||||
|
self.node.name[0:1] == 's' ):
|
||||||
|
bw = speedup
|
||||||
|
# This may not be correct - we should look more closely
|
||||||
|
# at the semantics of burst (and cburst) to make sure we
|
||||||
|
# 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 1:0 hfsc default 1',
|
||||||
|
'%s class add dev %s parent 1:0 classid 1:1 hfsc sc '
|
||||||
|
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
|
||||||
|
elif use_tbf:
|
||||||
|
if latency_ms is None:
|
||||||
|
latency_ms = 15 * 8 / bw
|
||||||
|
cmds += [ '%s qdisc add dev %s root handle 1: tbf ' +
|
||||||
|
'rate %fMbit burst 15000 latency %fms' %
|
||||||
|
( bw, latency_ms ) ]
|
||||||
|
else:
|
||||||
|
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 10: red limit 1000000 ' +
|
||||||
|
'min 30000 max 35000 avpkt 1500 ' +
|
||||||
|
'burst 20 ' +
|
||||||
|
'bandwidth %fmbit probability 1 ecn' % bw ]
|
||||||
|
parent = ' parent 10: '
|
||||||
|
elif enable_red:
|
||||||
|
cmds += [ '%s qdisc add dev %s' + parent +
|
||||||
|
'handle 10: red limit 1000000 ' +
|
||||||
|
'min 30000 max 35000 avpkt 1500 ' +
|
||||||
|
'burst 20 ' +
|
||||||
|
'bandwidth %fmbit probability 1' % bw ]
|
||||||
|
parent = ' parent 10: '
|
||||||
|
return cmds, parent
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delayCmds( parent, delay=None, jitter=None,
|
||||||
|
loss=None, max_queue_size=None ):
|
||||||
|
"Internal method: return tc commands for delay and loss"
|
||||||
|
cmds = []
|
||||||
|
if delay and delay < 0:
|
||||||
|
error( 'Negative delay', delay, '\n' )
|
||||||
|
elif jitter and jitter < 0:
|
||||||
|
error( 'Negative jitter', jitter, '\n' )
|
||||||
|
elif loss and ( loss < 0 or loss > 100 ):
|
||||||
|
error( 'Bad loss percentage', loss, '%%\n' )
|
||||||
|
else:
|
||||||
|
# Delay/jitter/loss/max queue size
|
||||||
|
netemargs = '%s%s%s%s' % (
|
||||||
|
'delay %s ' % delay if delay is not None else '',
|
||||||
|
'%s ' % jitter if jitter is not None else '',
|
||||||
|
'loss %d ' % loss if loss is not None else '',
|
||||||
|
'limit %d' % max_queue_size if max_queue_size is not None
|
||||||
|
else '' )
|
||||||
|
if netemargs:
|
||||||
|
cmds = [ '%s qdisc add dev %s ' + parent +
|
||||||
|
' handle 10: netem ' +
|
||||||
|
netemargs ]
|
||||||
|
return cmds
|
||||||
|
|
||||||
|
def tc( self, cmd, tc='tc' ):
|
||||||
|
"Execute tc command for our interface"
|
||||||
|
c = cmd % (tc, self) # Add in tc command and our name
|
||||||
|
debug(" *** executing command: %s\n" % c)
|
||||||
|
return self.cmd( c )
|
||||||
|
|
||||||
|
def config( self, bw=None, delay=None, jitter=None, loss=None,
|
||||||
|
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
|
||||||
|
latency_ms=None, enable_ecn=False, enable_red=False,
|
||||||
|
max_queue_size=None, **params ):
|
||||||
|
"Configure the port and set its properties."
|
||||||
|
|
||||||
|
result = Intf.config( self, **params)
|
||||||
|
|
||||||
|
# Disable GRO
|
||||||
|
if disable_gro:
|
||||||
|
self.cmd( 'ethtool -K %s gro off' % self )
|
||||||
|
|
||||||
|
# Optimization: return if nothing else to configure
|
||||||
|
# Question: what happens if we want to reset things?
|
||||||
|
if ( bw is None and not delay and not loss
|
||||||
|
and max_queue_size is None ):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clear existing configuration
|
||||||
|
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 )
|
||||||
|
cmds += bwcmds
|
||||||
|
|
||||||
|
# Delay/jitter/loss/max_queue_size using netem
|
||||||
|
cmds += self.delayCmds( delay=delay, jitter=jitter, loss=loss,
|
||||||
|
max_queue_size=max_queue_size,
|
||||||
|
parent=parent )
|
||||||
|
|
||||||
|
# Ugly but functional: display configuration info
|
||||||
|
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
|
||||||
|
( [ '%s delay' % delay ] if delay is not None else [] ) +
|
||||||
|
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
|
||||||
|
( ['%d%% loss' % loss ] if loss is not None else [] ) +
|
||||||
|
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
|
||||||
|
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 ]
|
||||||
|
debug( "cmds:", cmds, '\n' )
|
||||||
|
debug( "outputs:", tcoutputs, '\n' )
|
||||||
|
result[ 'tcoutputs'] = tcoutputs
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Link( object ):
|
||||||
|
|
||||||
|
"""A basic link is just a veth pair.
|
||||||
|
Other types of links could be tunnels, link emulators, etc.."""
|
||||||
|
|
||||||
|
def __init__( self, node1, node2, port1=None, port2=None,
|
||||||
|
intfName1=None, intfName2=None,
|
||||||
|
intf=Intf, cls1=None, cls2=None, params1=None,
|
||||||
|
params2=None ):
|
||||||
|
"""Create veth link to another node, making two new interfaces.
|
||||||
|
node1: first node
|
||||||
|
node2: second node
|
||||||
|
port1: node1 port number (optional)
|
||||||
|
port2: node2 port number (optional)
|
||||||
|
intf: default interface class/constructor
|
||||||
|
cls1, cls2: optional interface-specific constructors
|
||||||
|
intfName1: node1 interface name (optional)
|
||||||
|
intfName2: node2 interface name (optional)
|
||||||
|
params1: parameters for interface 1
|
||||||
|
params2: parameters for interface 2"""
|
||||||
|
# This is a bit awkward; it seems that having everything in
|
||||||
|
# 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, port1 )
|
||||||
|
if not intfName2:
|
||||||
|
intfName2 = self.intfName( node2, port2 )
|
||||||
|
|
||||||
|
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, 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
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def intfName( cls, node, n ):
|
||||||
|
"Construct a canonical interface name node-ethN for interface n."
|
||||||
|
return node.name + '-eth' + repr( n )
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def makeIntfPair( cls, intf1, intf2 ):
|
||||||
|
"""Create pair of interfaces
|
||||||
|
intf1: name of interface 1
|
||||||
|
intf2: name of interface 2
|
||||||
|
(override this class method [and possibly delete()]
|
||||||
|
to change link type)"""
|
||||||
|
makeIntfPair( intf1, intf2 )
|
||||||
|
|
||||||
|
def delete( self ):
|
||||||
|
"Delete this link"
|
||||||
|
self.intf1.delete()
|
||||||
|
self.intf2.delete()
|
||||||
|
|
||||||
|
def __str__( self ):
|
||||||
|
return '%s<->%s' % ( self.intf1, self.intf2 )
|
||||||
|
|
||||||
|
class TCLink( Link ):
|
||||||
|
"Link with symmetric TC interfaces configured via opts"
|
||||||
|
def __init__( self, node1, node2, port1=None, port2=None,
|
||||||
|
intfName1=None, intfName2=None, **params ):
|
||||||
|
Link.__init__( self, node1, node2, port1=port1, port2=port2,
|
||||||
|
intfName1=intfName1, intfName2=intfName2,
|
||||||
|
cls1=TCIntf,
|
||||||
|
cls2=TCIntf,
|
||||||
|
params1=params,
|
||||||
|
params2=params)
|
||||||
+6
-6
@@ -11,11 +11,11 @@ import types
|
|||||||
OUTPUT = 25
|
OUTPUT = 25
|
||||||
|
|
||||||
LEVELS = { 'debug': logging.DEBUG,
|
LEVELS = { 'debug': logging.DEBUG,
|
||||||
'info': logging.INFO,
|
'info': logging.INFO,
|
||||||
'output': OUTPUT,
|
'output': OUTPUT,
|
||||||
'warning': logging.WARNING,
|
'warning': logging.WARNING,
|
||||||
'error': logging.ERROR,
|
'error': logging.ERROR,
|
||||||
'critical': logging.CRITICAL }
|
'critical': logging.CRITICAL }
|
||||||
|
|
||||||
# change this to logging.INFO to get printouts when running unit tests
|
# change this to logging.INFO to get printouts when running unit tests
|
||||||
LOGLEVELDEFAULT = OUTPUT
|
LOGLEVELDEFAULT = OUTPUT
|
||||||
@@ -117,7 +117,7 @@ class MininetLogger( Logger, object ):
|
|||||||
Convenience function to support lowercase names.
|
Convenience function to support lowercase names.
|
||||||
levelName: level name from LEVELS"""
|
levelName: level name from LEVELS"""
|
||||||
level = LOGLEVELDEFAULT
|
level = LOGLEVELDEFAULT
|
||||||
if levelname != None:
|
if levelname is not None:
|
||||||
if levelname not in LEVELS:
|
if levelname not in LEVELS:
|
||||||
raise Exception( 'unknown levelname seen in setLogLevel' )
|
raise Exception( 'unknown levelname seen in setLogLevel' )
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def modprobe( mod ):
|
|||||||
return quietRun( [ 'modprobe', mod ] )
|
return quietRun( [ 'modprobe', mod ] )
|
||||||
|
|
||||||
OF_KMOD = 'ofdatapath'
|
OF_KMOD = 'ofdatapath'
|
||||||
OVS_KMOD = 'openvswitch_mod'
|
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
|
||||||
TUN = 'tun'
|
TUN = 'tun'
|
||||||
|
|
||||||
def moduleDeps( subtract=None, add=None ):
|
def moduleDeps( subtract=None, add=None ):
|
||||||
@@ -48,8 +48,8 @@ def moduleDeps( subtract=None, add=None ):
|
|||||||
modprobeOutput = modprobe( mod )
|
modprobeOutput = modprobe( mod )
|
||||||
if modprobeOutput:
|
if modprobeOutput:
|
||||||
error( 'Error inserting ' + mod +
|
error( 'Error inserting ' + mod +
|
||||||
' - is it installed and available via modprobe?\n' +
|
' - is it installed and available via modprobe?\n' +
|
||||||
'Error was: "%s"\n' % modprobeOutput )
|
'Error was: "%s"\n' % modprobeOutput )
|
||||||
if mod not in lsmod():
|
if mod not in lsmod():
|
||||||
error( 'Failed to insert ' + mod + ' - quitting.\n' )
|
error( 'Failed to insert ' + mod + ' - quitting.\n' )
|
||||||
exit( 1 )
|
exit( 1 )
|
||||||
@@ -63,6 +63,6 @@ def pathCheck( *args, **kwargs ):
|
|||||||
for arg in args:
|
for arg in args:
|
||||||
if not quietRun( 'which ' + arg ):
|
if not quietRun( 'which ' + arg ):
|
||||||
error( 'Cannot find required executable %s.\n' % arg +
|
error( 'Cannot find required executable %s.\n' % arg +
|
||||||
'Please make sure that %s is installed ' % moduleName +
|
'Please make sure that %s is installed ' % moduleName +
|
||||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||||
exit( 1 )
|
exit( 1 )
|
||||||
|
|||||||
+345
-170
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Mininet: A simple networking testbed for OpenFlow!
|
Mininet: A simple networking testbed for OpenFlow/SDN!
|
||||||
|
|
||||||
author: Bob Lantz (rlantz@cs.stanford.edu)
|
author: Bob Lantz (rlantz@cs.stanford.edu)
|
||||||
author: Brandon Heller (brandonh@stanford.edu)
|
author: Brandon Heller (brandonh@stanford.edu)
|
||||||
@@ -94,225 +94,231 @@ from time import sleep
|
|||||||
|
|
||||||
from mininet.cli import CLI
|
from mininet.cli import CLI
|
||||||
from mininet.log import info, error, debug, output
|
from mininet.log import info, error, debug, output
|
||||||
from mininet.node import Host, UserSwitch, OVSKernelSwitch, Controller
|
from mininet.node import Host, OVSKernelSwitch, Controller
|
||||||
from mininet.node import ControllerParams
|
from mininet.link import Link, Intf
|
||||||
from mininet.util import quietRun, fixLimits
|
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
|
||||||
from mininet.util import createLink, macColonHex, ipStr, ipParse
|
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
|
||||||
from mininet.term import cleanUpScreens, makeTerms
|
from mininet.term import cleanUpScreens, makeTerms
|
||||||
|
|
||||||
|
# Mininet version: should be consistent with README and LICENSE
|
||||||
|
VERSION = "2.0.0rc1"
|
||||||
|
|
||||||
class Mininet( object ):
|
class Mininet( object ):
|
||||||
"Network emulation with hosts spawned in network namespaces."
|
"Network emulation with hosts spawned in network namespaces."
|
||||||
|
|
||||||
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
|
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
|
||||||
controller=Controller,
|
controller=Controller, link=Link, intf=Intf,
|
||||||
cparams=ControllerParams( '10.0.0.0', 8 ),
|
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||||
build=True, xterms=False, cleanup=False,
|
inNamespace=False,
|
||||||
inNamespace=False,
|
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
|
||||||
autoSetMacs=False, autoStaticArp=False, listenPort=None ):
|
listenPort=None ):
|
||||||
"""Create Mininet object.
|
"""Create Mininet object.
|
||||||
topo: Topo (topology) object or None
|
topo: Topo (topology) object or None
|
||||||
switch: Switch class
|
switch: default Switch class
|
||||||
host: Host class
|
host: default Host class/constructor
|
||||||
controller: Controller class
|
controller: default Controller class/constructor
|
||||||
cparams: ControllerParams object
|
link: default Link class/constructor
|
||||||
|
intf: default Intf class/constructor
|
||||||
|
ipBase: base IP address for hosts,
|
||||||
build: build now from topo?
|
build: build now from topo?
|
||||||
xterms: if build now, spawn xterms?
|
xterms: if build now, spawn xterms?
|
||||||
cleanup: if build now, cleanup before creating?
|
cleanup: if build now, cleanup before creating?
|
||||||
inNamespace: spawn switches and controller in net namespaces?
|
inNamespace: spawn switches and controller in net namespaces?
|
||||||
autoSetMacs: set MAC addrs from topo?
|
autoSetMacs: set MAC addrs automatically like IP addresses?
|
||||||
autoStaticArp: set all-pairs static MAC addrs?
|
autoStaticArp: set all-pairs static MAC addrs?
|
||||||
|
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
|
||||||
listenPort: base listening port to open; will be incremented for
|
listenPort: base listening port to open; will be incremented for
|
||||||
each additional switch in the net if inNamespace=False"""
|
each additional switch in the net if inNamespace=False"""
|
||||||
|
self.topo = topo
|
||||||
self.switch = switch
|
self.switch = switch
|
||||||
self.host = host
|
self.host = host
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.cparams = cparams
|
self.link = link
|
||||||
self.topo = topo
|
self.intf = intf
|
||||||
|
self.ipBase = ipBase
|
||||||
|
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
|
||||||
|
self.nextIP = 1 # start for address allocation
|
||||||
self.inNamespace = inNamespace
|
self.inNamespace = inNamespace
|
||||||
self.xterms = xterms
|
self.xterms = xterms
|
||||||
self.cleanup = cleanup
|
self.cleanup = cleanup
|
||||||
self.autoSetMacs = autoSetMacs
|
self.autoSetMacs = autoSetMacs
|
||||||
self.autoStaticArp = autoStaticArp
|
self.autoStaticArp = autoStaticArp
|
||||||
|
self.autoPinCpus = autoPinCpus
|
||||||
|
self.numCores = numCores()
|
||||||
|
self.nextCore = 0 # next core for pinning hosts to CPUs
|
||||||
self.listenPort = listenPort
|
self.listenPort = listenPort
|
||||||
|
|
||||||
self.hosts = []
|
self.hosts = []
|
||||||
self.switches = []
|
self.switches = []
|
||||||
self.controllers = []
|
self.controllers = []
|
||||||
|
|
||||||
self.nameToNode = {} # name to Node (Host/Switch) objects
|
self.nameToNode = {} # name to Node (Host/Switch) objects
|
||||||
self.idToNode = {} # dpid to Node (Host/Switch) objects
|
|
||||||
self.dps = 0 # number of created kernel datapaths
|
|
||||||
self.terms = [] # list of spawned xterm processes
|
self.terms = [] # list of spawned xterm processes
|
||||||
|
|
||||||
init()
|
Mininet.init() # Initialize Mininet if necessary
|
||||||
switch.setup()
|
|
||||||
|
|
||||||
self.built = False
|
self.built = False
|
||||||
if topo and build:
|
if topo and build:
|
||||||
self.build()
|
self.build()
|
||||||
|
|
||||||
def addHost( self, name, mac=None, ip=None ):
|
def addHost( self, name, cls=None, **params ):
|
||||||
"""Add host.
|
"""Add host.
|
||||||
name: name of host to add
|
name: name of host to add
|
||||||
mac: default MAC address for intf 0
|
cls: custom host class/constructor (optional)
|
||||||
ip: default IP address for intf 0
|
params: parameters for host
|
||||||
returns: added host"""
|
returns: added host"""
|
||||||
host = self.host( name, defaultMAC=mac, defaultIP=ip )
|
# Default IP and MAC addresses
|
||||||
self.hosts.append( host )
|
defaults = { 'ip': ipAdd( self.nextIP,
|
||||||
self.nameToNode[ name ] = host
|
ipBaseNum=self.ipBaseNum,
|
||||||
return host
|
prefixLen=self.prefixLen ) +
|
||||||
|
'/%s' % self.prefixLen }
|
||||||
|
if self.autoSetMacs:
|
||||||
|
defaults[ 'mac'] = macColonHex( self.nextIP )
|
||||||
|
if self.autoPinCpus:
|
||||||
|
defaults[ 'cores' ] = self.nextCore
|
||||||
|
self.nextCore = ( self.nextCore + 1 ) % self.numCores
|
||||||
|
self.nextIP += 1
|
||||||
|
defaults.update( params )
|
||||||
|
if not cls:
|
||||||
|
cls = self.host
|
||||||
|
h = cls( name, **defaults )
|
||||||
|
self.hosts.append( h )
|
||||||
|
self.nameToNode[ name ] = h
|
||||||
|
return h
|
||||||
|
|
||||||
def addSwitch( self, name, mac=None, ip=None ):
|
def addSwitch( self, name, cls=None, **params ):
|
||||||
"""Add switch.
|
"""Add switch.
|
||||||
name: name of switch to add
|
name: name of switch to add
|
||||||
mac: default MAC address for kernel/OVS switch intf 0
|
cls: custom switch class/constructor (optional)
|
||||||
returns: added switch
|
returns: added switch
|
||||||
side effect: increments the listenPort member variable."""
|
side effect: increments listenPort ivar ."""
|
||||||
if self.switch == UserSwitch:
|
defaults = { 'listenPort': self.listenPort,
|
||||||
sw = self.switch( name, listenPort=self.listenPort,
|
'inNamespace': self.inNamespace }
|
||||||
defaultMAC=mac, defaultIP=ip, inNamespace=self.inNamespace )
|
defaults.update( params )
|
||||||
else:
|
if not cls:
|
||||||
sw = self.switch( name, listenPort=self.listenPort,
|
cls = self.switch
|
||||||
defaultMAC=mac, defaultIP=ip, dp=self.dps,
|
sw = cls( name, **defaults )
|
||||||
inNamespace=self.inNamespace )
|
|
||||||
if not self.inNamespace and self.listenPort:
|
if not self.inNamespace and self.listenPort:
|
||||||
self.listenPort += 1
|
self.listenPort += 1
|
||||||
self.dps += 1
|
|
||||||
self.switches.append( sw )
|
self.switches.append( sw )
|
||||||
self.nameToNode[ name ] = sw
|
self.nameToNode[ name ] = sw
|
||||||
return sw
|
return sw
|
||||||
|
|
||||||
def addController( self, name='c0', controller=None, **kwargs ):
|
def addController( self, name='c0', controller=None, **params ):
|
||||||
"""Add controller.
|
"""Add controller.
|
||||||
controller: Controller class"""
|
controller: Controller class"""
|
||||||
if not controller:
|
if not controller:
|
||||||
controller = self.controller
|
controller = self.controller
|
||||||
controller_new = controller( name, **kwargs )
|
controller_new = controller( name, **params )
|
||||||
if controller_new: # allow controller-less setups
|
if controller_new: # allow controller-less setups
|
||||||
self.controllers.append( controller_new )
|
self.controllers.append( controller_new )
|
||||||
self.nameToNode[ name ] = controller_new
|
self.nameToNode[ name ] = controller_new
|
||||||
return controller_new
|
return controller_new
|
||||||
|
|
||||||
# Control network support:
|
# BL: is this better than just using nameToNode[] ?
|
||||||
#
|
# Should it have a better name?
|
||||||
# Create an explicit control network. Currently this is only
|
def getNodeByName( self, *args ):
|
||||||
# used by the user datapath configuration.
|
"Return node(s) with given name(s)"
|
||||||
#
|
if len( args ) == 1:
|
||||||
# Notes:
|
return self.nameToNode[ args[ 0 ] ]
|
||||||
#
|
return [ self.nameToNode[ n ] for n in args ]
|
||||||
# 1. If the controller and switches are in the same (e.g. root)
|
|
||||||
# namespace, they can just use the loopback connection.
|
|
||||||
#
|
|
||||||
# 2. If we can get unix domain sockets to work, we can use them
|
|
||||||
# instead of an explicit control network.
|
|
||||||
#
|
|
||||||
# 3. Instead of routing, we could bridge or use 'in-band' control.
|
|
||||||
#
|
|
||||||
# 4. Even if we dispense with this in general, it could still be
|
|
||||||
# useful for people who wish to simulate a separate control
|
|
||||||
# network (since real networks may need one!)
|
|
||||||
|
|
||||||
def configureControlNetwork( self ):
|
def get( self, *args ):
|
||||||
"Configure control network."
|
"Convenience alias for getNodeByName"
|
||||||
self.configureRoutedControlNetwork()
|
return self.getNodeByName( *args )
|
||||||
|
|
||||||
# We still need to figure out the right way to pass
|
def addLink( self, node1, node2, port1=None, port2=None,
|
||||||
# in the control network location.
|
cls=None, **params ):
|
||||||
|
""""Add a link from node1 to node2
|
||||||
def configureRoutedControlNetwork( self, ip='192.168.123.1',
|
node1: source node
|
||||||
prefixLen=16 ):
|
node2: dest node
|
||||||
"""Configure a routed control network on controller and switches.
|
port1: source port
|
||||||
For use with the user datapath only right now.
|
port2: dest port
|
||||||
"""
|
returns: link object"""
|
||||||
controller = self.controllers[ 0 ]
|
defaults = { 'port1': port1,
|
||||||
info( controller.name + ' <->' )
|
'port2': port2,
|
||||||
cip = ip
|
'intf': self.intf }
|
||||||
snum = ipParse( ip )
|
defaults.update( params )
|
||||||
for switch in self.switches:
|
if not cls:
|
||||||
info( ' ' + switch.name )
|
cls = self.link
|
||||||
sintf, cintf = createLink( switch, controller )
|
return cls( node1, node2, **defaults )
|
||||||
snum += 1
|
|
||||||
while snum & 0xff in [ 0, 255 ]:
|
|
||||||
snum += 1
|
|
||||||
sip = ipStr( snum )
|
|
||||||
controller.setIP( cintf, cip, prefixLen )
|
|
||||||
switch.setIP( sintf, sip, prefixLen )
|
|
||||||
controller.setHostRoute( sip, cintf )
|
|
||||||
switch.setHostRoute( cip, sintf )
|
|
||||||
info( '\n' )
|
|
||||||
info( '*** Testing control network\n' )
|
|
||||||
while not controller.intfIsUp( cintf ):
|
|
||||||
info( '*** Waiting for', cintf, 'to come up\n' )
|
|
||||||
sleep( 1 )
|
|
||||||
for switch in self.switches:
|
|
||||||
while not switch.intfIsUp( sintf ):
|
|
||||||
info( '*** Waiting for', sintf, 'to come up\n' )
|
|
||||||
sleep( 1 )
|
|
||||||
if self.ping( hosts=[ switch, controller ] ) != 0:
|
|
||||||
error( '*** Error: control network test failed\n' )
|
|
||||||
exit( 1 )
|
|
||||||
info( '\n' )
|
|
||||||
|
|
||||||
def configHosts( self ):
|
def configHosts( self ):
|
||||||
"Configure a set of hosts."
|
"Configure a set of hosts."
|
||||||
# params were: hosts, ips
|
|
||||||
for host in self.hosts:
|
for host in self.hosts:
|
||||||
hintf = host.intfs[ 0 ]
|
|
||||||
host.setIP( hintf, host.defaultIP, self.cparams.prefixLen )
|
|
||||||
host.setDefaultRoute( hintf )
|
|
||||||
# You're low priority, dude!
|
|
||||||
quietRun( 'renice +18 -p ' + repr( host.pid ) )
|
|
||||||
info( host.name + ' ' )
|
info( host.name + ' ' )
|
||||||
|
intf = host.defaultIntf()
|
||||||
|
if intf:
|
||||||
|
host.configDefault( defaultRoute=intf )
|
||||||
|
else:
|
||||||
|
# Don't configure nonexistent intf
|
||||||
|
host.configDefault( ip=None, mac=None )
|
||||||
|
# You're low priority, dude!
|
||||||
|
# BL: do we want to do this here or not?
|
||||||
|
# May not make sense if we have CPU lmiting...
|
||||||
|
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
|
||||||
|
# This may not be the right place to do this, but
|
||||||
|
# it needs to be done somewhere.
|
||||||
|
host.cmd( 'ifconfig lo up' )
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
def buildFromTopo( self, topo ):
|
def buildFromTopo( self, topo=None ):
|
||||||
"""Build mininet from a topology object
|
"""Build mininet from a topology object
|
||||||
At the end of this function, everything should be connected
|
At the end of this function, everything should be connected
|
||||||
and up."""
|
and up."""
|
||||||
|
|
||||||
def addNode( prefix, addMethod, nodeId ):
|
|
||||||
"Add a host or a switch."
|
|
||||||
name = prefix + topo.name( nodeId )
|
|
||||||
mac = macColonHex( nodeId ) if self.setMacs else None
|
|
||||||
ip = topo.ip( nodeId )
|
|
||||||
node = addMethod( name, mac=mac, ip=ip )
|
|
||||||
self.idToNode[ nodeId ] = node
|
|
||||||
info( name + ' ' )
|
|
||||||
|
|
||||||
# Possibly we should clean up here and/or validate
|
# Possibly we should clean up here and/or validate
|
||||||
# the topo
|
# the topo
|
||||||
if self.cleanup:
|
if self.cleanup:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
info( '*** Adding controller\n' )
|
|
||||||
self.addController( 'c0' )
|
|
||||||
info( '*** Creating network\n' )
|
info( '*** Creating network\n' )
|
||||||
|
|
||||||
|
if not self.controllers:
|
||||||
|
# Add a default controller
|
||||||
|
info( '*** Adding controller\n' )
|
||||||
|
classes = self.controller
|
||||||
|
if type( classes ) is not list:
|
||||||
|
classes = [ classes ]
|
||||||
|
for i, cls in enumerate( classes ):
|
||||||
|
self.addController( 'c%d' % i, cls )
|
||||||
|
|
||||||
info( '*** Adding hosts:\n' )
|
info( '*** Adding hosts:\n' )
|
||||||
for hostId in sorted( topo.hosts() ):
|
for hostName in topo.hosts():
|
||||||
addNode( 'h', self.addHost, hostId )
|
self.addHost( hostName, **topo.nodeInfo( hostName ) )
|
||||||
|
info( hostName + ' ' )
|
||||||
|
|
||||||
info( '\n*** Adding switches:\n' )
|
info( '\n*** Adding switches:\n' )
|
||||||
for switchId in sorted( topo.switches() ):
|
for switchName in topo.switches():
|
||||||
addNode( 's', self.addSwitch, switchId )
|
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
|
||||||
|
info( switchName + ' ' )
|
||||||
|
|
||||||
info( '\n*** Adding links:\n' )
|
info( '\n*** Adding links:\n' )
|
||||||
for srcId, dstId in sorted( topo.edges() ):
|
for srcName, dstName in topo.links(sort=True):
|
||||||
src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
|
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
|
||||||
srcPort, dstPort = topo.port( srcId, dstId )
|
params = topo.linkInfo( srcName, dstName )
|
||||||
createLink( src, dst, srcPort, dstPort )
|
srcPort, dstPort = topo.port( srcName, dstName )
|
||||||
|
self.addLink( src, dst, srcPort, dstPort, **params )
|
||||||
info( '(%s, %s) ' % ( src.name, dst.name ) )
|
info( '(%s, %s) ' % ( src.name, dst.name ) )
|
||||||
|
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
|
|
||||||
|
def configureControlNetwork( self ):
|
||||||
|
"Control net config hook: override in subclass"
|
||||||
|
raise Exception( 'configureControlNetwork: '
|
||||||
|
'should be overriden in subclass', self )
|
||||||
|
|
||||||
def build( self ):
|
def build( self ):
|
||||||
"Build mininet."
|
"Build mininet."
|
||||||
if self.topo:
|
if self.topo:
|
||||||
self.buildFromTopo( self.topo )
|
self.buildFromTopo( self.topo )
|
||||||
if self.inNamespace:
|
if ( self.inNamespace ):
|
||||||
info( '*** Configuring control network\n' )
|
|
||||||
self.configureControlNetwork()
|
self.configureControlNetwork()
|
||||||
info( '*** Configuring hosts\n' )
|
info( '*** Configuring hosts\n' )
|
||||||
self.configHosts()
|
self.configHosts()
|
||||||
if self.xterms:
|
if self.xterms:
|
||||||
self.startTerms()
|
self.startTerms()
|
||||||
if self.autoSetMacs:
|
|
||||||
self.setMacs()
|
|
||||||
if self.autoStaticArp:
|
if self.autoStaticArp:
|
||||||
self.staticArp()
|
self.staticArp()
|
||||||
self.built = True
|
self.built = True
|
||||||
@@ -327,17 +333,10 @@ class Mininet( object ):
|
|||||||
|
|
||||||
def stopXterms( self ):
|
def stopXterms( self ):
|
||||||
"Kill each xterm."
|
"Kill each xterm."
|
||||||
# Kill xterms
|
|
||||||
for term in self.terms:
|
for term in self.terms:
|
||||||
os.kill( term.pid, signal.SIGKILL )
|
os.kill( term.pid, signal.SIGKILL )
|
||||||
cleanUpScreens()
|
cleanUpScreens()
|
||||||
|
|
||||||
def setMacs( self ):
|
|
||||||
"""Set MAC addrs to correspond to default MACs on hosts.
|
|
||||||
Assume that the host only has one interface."""
|
|
||||||
for host in self.hosts:
|
|
||||||
host.setMAC( host.intfs[ 0 ], host.defaultMAC )
|
|
||||||
|
|
||||||
def staticArp( self ):
|
def staticArp( self ):
|
||||||
"Add all-pairs ARP entries to remove the need to handle broadcast."
|
"Add all-pairs ARP entries to remove the need to handle broadcast."
|
||||||
for src in self.hosts:
|
for src in self.hosts:
|
||||||
@@ -365,18 +364,19 @@ class Mininet( object ):
|
|||||||
self.stopXterms()
|
self.stopXterms()
|
||||||
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
||||||
for host in self.hosts:
|
for host in self.hosts:
|
||||||
info( '%s ' % host.name )
|
info( host.name + ' ' )
|
||||||
host.terminate()
|
host.terminate()
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||||
for switch in self.switches:
|
for switch in self.switches:
|
||||||
info( switch.name )
|
info( switch.name + ' ' )
|
||||||
switch.stop()
|
switch.stop()
|
||||||
info( '\n' )
|
info( '\n' )
|
||||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||||
for controller in self.controllers:
|
for controller in self.controllers:
|
||||||
|
info( controller.name + ' ' )
|
||||||
controller.stop()
|
controller.stop()
|
||||||
info( '*** Done\n' )
|
info( '\n*** Done\n' )
|
||||||
|
|
||||||
def run( self, test, *args, **kwargs ):
|
def run( self, test, *args, **kwargs ):
|
||||||
"Perform a complete start/test/stop cycle."
|
"Perform a complete start/test/stop cycle."
|
||||||
@@ -410,6 +410,9 @@ class Mininet( object ):
|
|||||||
if not ready and timeoutms >= 0:
|
if not ready and timeoutms >= 0:
|
||||||
yield None, None
|
yield None, None
|
||||||
|
|
||||||
|
# XXX These test methods should be moved out of this class.
|
||||||
|
# Probably we should create a tests.py for them
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parsePing( pingOutput ):
|
def _parsePing( pingOutput ):
|
||||||
"Parse ping output and return packets sent, received."
|
"Parse ping output and return packets sent, received."
|
||||||
@@ -418,16 +421,17 @@ class Mininet( object ):
|
|||||||
return (1, 0)
|
return (1, 0)
|
||||||
r = r'(\d+) packets transmitted, (\d+) received'
|
r = r'(\d+) packets transmitted, (\d+) received'
|
||||||
m = re.search( r, pingOutput )
|
m = re.search( r, pingOutput )
|
||||||
if m == None:
|
if m is None:
|
||||||
error( '*** Error: could not parse ping output: %s\n' %
|
error( '*** Error: could not parse ping output: %s\n' %
|
||||||
pingOutput )
|
pingOutput )
|
||||||
return (1, 0)
|
return (1, 0)
|
||||||
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
|
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
|
||||||
return sent, received
|
return sent, received
|
||||||
|
|
||||||
def ping( self, hosts=None ):
|
def ping( self, hosts=None, timeout=None ):
|
||||||
"""Ping between all specified hosts.
|
"""Ping between all specified hosts.
|
||||||
hosts: list of hosts
|
hosts: list of hosts
|
||||||
|
timeout: time to wait for a response, as string
|
||||||
returns: ploss packet loss percentage"""
|
returns: ploss packet loss percentage"""
|
||||||
# should we check if running?
|
# should we check if running?
|
||||||
packets = 0
|
packets = 0
|
||||||
@@ -440,7 +444,10 @@ class Mininet( object ):
|
|||||||
output( '%s -> ' % node.name )
|
output( '%s -> ' % node.name )
|
||||||
for dest in hosts:
|
for dest in hosts:
|
||||||
if node != dest:
|
if node != dest:
|
||||||
result = node.cmd( 'ping -c1 ' + dest.IP() )
|
opts = ''
|
||||||
|
if timeout:
|
||||||
|
opts = '-W %s' % timeout
|
||||||
|
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
|
||||||
sent, received = self._parsePing( result )
|
sent, received = self._parsePing( result )
|
||||||
packets += sent
|
packets += sent
|
||||||
if received > sent:
|
if received > sent:
|
||||||
@@ -456,6 +463,61 @@ class Mininet( object ):
|
|||||||
( ploss, lost, packets ) )
|
( ploss, lost, packets ) )
|
||||||
return ploss
|
return ploss
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parsePingFull( pingOutput ):
|
||||||
|
"Parse ping output and return all data."
|
||||||
|
# Check for downed link
|
||||||
|
if 'connect: Network is unreachable' in pingOutput:
|
||||||
|
return (1, 0)
|
||||||
|
r = r'(\d+) packets transmitted, (\d+) received'
|
||||||
|
m = re.search( r, pingOutput )
|
||||||
|
if m is None:
|
||||||
|
error( '*** Error: could not parse ping output: %s\n' %
|
||||||
|
pingOutput )
|
||||||
|
return (1, 0, 0, 0, 0, 0)
|
||||||
|
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 )
|
||||||
|
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 ):
|
def pingAll( self ):
|
||||||
"""Ping between all hosts.
|
"""Ping between all hosts.
|
||||||
returns: ploss packet loss percentage"""
|
returns: ploss packet loss percentage"""
|
||||||
@@ -467,6 +529,17 @@ class Mininet( object ):
|
|||||||
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
|
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
|
||||||
return self.ping( hosts=hosts )
|
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
|
@staticmethod
|
||||||
def _parseIperf( iperfOutput ):
|
def _parseIperf( iperfOutput ):
|
||||||
"""Parse iperf output and return bandwidth.
|
"""Parse iperf output and return bandwidth.
|
||||||
@@ -481,6 +554,8 @@ class Mininet( object ):
|
|||||||
error( 'could not parse iperf output: ' + iperfOutput )
|
error( 'could not parse iperf output: ' + iperfOutput )
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
# XXX This should be cleaned up
|
||||||
|
|
||||||
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
|
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
|
||||||
"""Run iperf between two hosts.
|
"""Run iperf between two hosts.
|
||||||
hosts: list of hosts; if None, uses opposite hosts
|
hosts: list of hosts; if None, uses opposite hosts
|
||||||
@@ -508,12 +583,13 @@ class Mininet( object ):
|
|||||||
servout = ''
|
servout = ''
|
||||||
while server.lastPid is None:
|
while server.lastPid is None:
|
||||||
servout += server.monitor()
|
servout += server.monitor()
|
||||||
while 'Connected' not in client.cmd(
|
if l4Type == 'TCP':
|
||||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
while 'Connected' not in client.cmd(
|
||||||
output('waiting for iperf to start up')
|
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||||
sleep(.5)
|
output('waiting for iperf to start up...')
|
||||||
|
sleep(.5)
|
||||||
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
|
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
|
||||||
bwArgs )
|
bwArgs )
|
||||||
debug( 'Client output: %s\n' % cliout )
|
debug( 'Client output: %s\n' % cliout )
|
||||||
server.sendInt()
|
server.sendInt()
|
||||||
servout += server.waitOutput()
|
servout += server.waitOutput()
|
||||||
@@ -524,6 +600,44 @@ class Mininet( object ):
|
|||||||
output( '*** Results: %s\n' % result )
|
output( '*** Results: %s\n' % result )
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def runCpuLimitTest( self, cpu, duration=5 ):
|
||||||
|
"""run CPU limit test with 'while true' processes.
|
||||||
|
cpu: desired CPU fraction of each host
|
||||||
|
duration: test duration in seconds
|
||||||
|
returns a single list of measured CPU fractions as floats.
|
||||||
|
"""
|
||||||
|
pct = cpu * 100
|
||||||
|
info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
|
||||||
|
hosts = self.hosts
|
||||||
|
for h in hosts:
|
||||||
|
h.cmd( 'while true; do a=1; done &' )
|
||||||
|
pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
|
||||||
|
pids_str = ",".join(["%s" % pid for pid in pids])
|
||||||
|
cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
|
||||||
|
# It's a shame that this is what pylint prefers
|
||||||
|
outputs = []
|
||||||
|
for _ in range( duration ):
|
||||||
|
sleep( 1 )
|
||||||
|
outputs.append( quietRun( cmd ).strip() )
|
||||||
|
for h in hosts:
|
||||||
|
h.cmd( 'kill %1' )
|
||||||
|
cpu_fractions = []
|
||||||
|
for test_output in outputs:
|
||||||
|
# Split by line. Ignore first line, which looks like this:
|
||||||
|
# PID %CPU COMMAND\n
|
||||||
|
for line in test_output.split('\n')[1:]:
|
||||||
|
r = r'\d+ (\d+\.\d+)'
|
||||||
|
m = re.search( r, line )
|
||||||
|
if m is None:
|
||||||
|
error( '*** Error: could not extract CPU fraction: %s\n' %
|
||||||
|
line )
|
||||||
|
return None
|
||||||
|
cpu_fractions.append( float( m.group( 1 ) ) )
|
||||||
|
output( '*** Results: %s\n' % cpu_fractions )
|
||||||
|
return cpu_fractions
|
||||||
|
|
||||||
|
# BL: I think this can be rewritten now that we have
|
||||||
|
# a real link class.
|
||||||
def configLinkStatus( self, src, dst, status ):
|
def configLinkStatus( self, src, dst, status ):
|
||||||
"""Change status of src <-> dst links.
|
"""Change status of src <-> dst links.
|
||||||
src: node name
|
src: node name
|
||||||
@@ -534,15 +648,18 @@ class Mininet( object ):
|
|||||||
elif dst not in self.nameToNode:
|
elif dst not in self.nameToNode:
|
||||||
error( 'dst not in network: %s\n' % dst )
|
error( 'dst not in network: %s\n' % dst )
|
||||||
else:
|
else:
|
||||||
srcNode, dstNode = self.nameToNode[ src ], self.nameToNode[ dst ]
|
if type( src ) is str:
|
||||||
connections = srcNode.connectionsTo( dstNode )
|
src = self.nameToNode[ src ]
|
||||||
|
if type( dst ) is str:
|
||||||
|
dst = self.nameToNode[ dst ]
|
||||||
|
connections = src.connectionsTo( dst )
|
||||||
if len( connections ) == 0:
|
if len( connections ) == 0:
|
||||||
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
|
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
|
||||||
for srcIntf, dstIntf in connections:
|
for srcIntf, dstIntf in connections:
|
||||||
result = srcNode.cmd( 'ifconfig', srcIntf, status )
|
result = srcIntf.ifconfig( status )
|
||||||
if result:
|
if result:
|
||||||
error( 'link src status change failed: %s\n' % result )
|
error( 'link src status change failed: %s\n' % result )
|
||||||
result = dstNode.cmd( 'ifconfig', dstIntf, status )
|
result = dstIntf.ifconfig( status )
|
||||||
if result:
|
if result:
|
||||||
error( 'link dst status change failed: %s\n' % result )
|
error( 'link dst status change failed: %s\n' % result )
|
||||||
|
|
||||||
@@ -553,26 +670,84 @@ class Mininet( object ):
|
|||||||
self.stop()
|
self.stop()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
inited = False
|
||||||
|
|
||||||
# pylint thinks inited is unused
|
@classmethod
|
||||||
# pylint: disable-msg=W0612
|
def init( cls ):
|
||||||
|
"Initialize Mininet"
|
||||||
|
if cls.inited:
|
||||||
|
return
|
||||||
|
ensureRoot()
|
||||||
|
fixLimits()
|
||||||
|
cls.inited = True
|
||||||
|
|
||||||
def init():
|
|
||||||
"Initialize Mininet."
|
|
||||||
if init.inited:
|
|
||||||
return
|
|
||||||
if os.getuid() != 0:
|
|
||||||
# Note: this script must be run as root
|
|
||||||
# Perhaps we should do so automatically!
|
|
||||||
print "*** Mininet must run as root."
|
|
||||||
exit( 1 )
|
|
||||||
# If which produces no output, then mnexec is not in the path.
|
|
||||||
# May want to loosen this to handle mnexec in the current dir.
|
|
||||||
if not quietRun( 'which mnexec' ):
|
|
||||||
raise Exception( "Could not find mnexec - check $PATH" )
|
|
||||||
fixLimits()
|
|
||||||
init.inited = True
|
|
||||||
|
|
||||||
init.inited = False
|
class MininetWithControlNet( Mininet ):
|
||||||
|
|
||||||
# pylint: enable-msg=W0612
|
"""Control network support:
|
||||||
|
|
||||||
|
Create an explicit control network. Currently this is only
|
||||||
|
used/usable with the user datapath.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
1. If the controller and switches are in the same (e.g. root)
|
||||||
|
namespace, they can just use the loopback connection.
|
||||||
|
|
||||||
|
2. If we can get unix domain sockets to work, we can use them
|
||||||
|
instead of an explicit control network.
|
||||||
|
|
||||||
|
3. Instead of routing, we could bridge or use 'in-band' control.
|
||||||
|
|
||||||
|
4. Even if we dispense with this in general, it could still be
|
||||||
|
useful for people who wish to simulate a separate control
|
||||||
|
network (since real networks may need one!)
|
||||||
|
|
||||||
|
5. Basically nobody ever used this code, so it has been moved
|
||||||
|
into its own class.
|
||||||
|
|
||||||
|
6. Ultimately we may wish to extend this to allow us to create a
|
||||||
|
control network which every node's control interface is
|
||||||
|
attached to."""
|
||||||
|
|
||||||
|
def configureControlNetwork( self ):
|
||||||
|
"Configure control network."
|
||||||
|
self.configureRoutedControlNetwork()
|
||||||
|
|
||||||
|
# We still need to figure out the right way to pass
|
||||||
|
# in the control network location.
|
||||||
|
|
||||||
|
def configureRoutedControlNetwork( self, ip='192.168.123.1',
|
||||||
|
prefixLen=16 ):
|
||||||
|
"""Configure a routed control network on controller and switches.
|
||||||
|
For use with the user datapath only right now."""
|
||||||
|
controller = self.controllers[ 0 ]
|
||||||
|
info( controller.name + ' <->' )
|
||||||
|
cip = ip
|
||||||
|
snum = ipParse( ip )
|
||||||
|
for switch in self.switches:
|
||||||
|
info( ' ' + switch.name )
|
||||||
|
link = self.link( switch, controller, port1=0 )
|
||||||
|
sintf, cintf = link.intf1, link.intf2
|
||||||
|
switch.controlIntf = sintf
|
||||||
|
snum += 1
|
||||||
|
while snum & 0xff in [ 0, 255 ]:
|
||||||
|
snum += 1
|
||||||
|
sip = ipStr( snum )
|
||||||
|
cintf.setIP( cip, prefixLen )
|
||||||
|
sintf.setIP( sip, prefixLen )
|
||||||
|
controller.setHostRoute( sip, cintf )
|
||||||
|
switch.setHostRoute( cip, sintf )
|
||||||
|
info( '\n' )
|
||||||
|
info( '*** Testing control network\n' )
|
||||||
|
while not cintf.isUp():
|
||||||
|
info( '*** Waiting for', cintf, 'to come up\n' )
|
||||||
|
sleep( 1 )
|
||||||
|
for switch in self.switches:
|
||||||
|
while not sintf.isUp():
|
||||||
|
info( '*** Waiting for', sintf, 'to come up\n' )
|
||||||
|
sleep( 1 )
|
||||||
|
if self.ping( hosts=[ switch, controller ] ) != 0:
|
||||||
|
error( '*** Error: control network test failed\n' )
|
||||||
|
exit( 1 )
|
||||||
|
info( '\n' )
|
||||||
|
|||||||
+666
-281
File diff suppressed because it is too large
Load Diff
Executable
+127
@@ -0,0 +1,127 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""Package: mininet
|
||||||
|
Test creation and pings for topologies with link and/or CPU options."""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.node import OVSKernelSwitch
|
||||||
|
from mininet.node import CPULimitedHost
|
||||||
|
from mininet.link import TCLink
|
||||||
|
from mininet.topo import Topo
|
||||||
|
from mininet.log import setLogLevel
|
||||||
|
|
||||||
|
|
||||||
|
SWITCH = OVSKernelSwitch
|
||||||
|
# Number of hosts for each test
|
||||||
|
N = 2
|
||||||
|
|
||||||
|
|
||||||
|
class SingleSwitchOptionsTopo(Topo):
|
||||||
|
"Single switch connected to n hosts."
|
||||||
|
def __init__(self, n=2, hopts=None, lopts=None):
|
||||||
|
if not hopts:
|
||||||
|
hopts = {}
|
||||||
|
if not lopts:
|
||||||
|
lopts = {}
|
||||||
|
Topo.__init__(self, hopts=hopts, lopts=lopts)
|
||||||
|
switch = self.addSwitch('s1')
|
||||||
|
for h in range(n):
|
||||||
|
host = self.addHost('h%s' % (h + 1))
|
||||||
|
self.addLink(host, switch)
|
||||||
|
|
||||||
|
|
||||||
|
class testOptionsTopo( unittest.TestCase ):
|
||||||
|
"Verify ability to create networks with host and link options."
|
||||||
|
|
||||||
|
def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
|
||||||
|
"Generic topology-with-options test runner."
|
||||||
|
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
|
||||||
|
lopts=lopts ),
|
||||||
|
host=CPULimitedHost, link=TCLink )
|
||||||
|
dropped = mn.run( mn.ping )
|
||||||
|
self.assertEqual( dropped, 0 )
|
||||||
|
|
||||||
|
def assertWithinTolerance(self, measured, expected, tolerance_frac):
|
||||||
|
"""Check that a given value is within a tolerance of expected
|
||||||
|
tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
|
||||||
|
"""
|
||||||
|
self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
|
||||||
|
self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
|
||||||
|
|
||||||
|
def testCPULimits( self ):
|
||||||
|
"Verify topology creation with CPU limits set for both schedulers."
|
||||||
|
CPU_FRACTION = 0.1
|
||||||
|
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
|
||||||
|
hopts = { 'cpu': CPU_FRACTION }
|
||||||
|
#self.runOptionsTopoTest( N, hopts=hopts )
|
||||||
|
|
||||||
|
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
|
||||||
|
host=CPULimitedHost )
|
||||||
|
mn.start()
|
||||||
|
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
|
||||||
|
mn.stop()
|
||||||
|
for cpu in results:
|
||||||
|
self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE )
|
||||||
|
|
||||||
|
def testLinkBandwidth( self ):
|
||||||
|
"Verify that link bandwidths are accurate within a bound."
|
||||||
|
BW = 5 # Mbps
|
||||||
|
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
|
||||||
|
# Verify ability to create limited-link topo first;
|
||||||
|
lopts = { 'bw': BW, 'use_htb': True }
|
||||||
|
# Also verify correctness of limit limitng within a bound.
|
||||||
|
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||||
|
link=TCLink )
|
||||||
|
bw_strs = mn.run( mn.iperf )
|
||||||
|
for bw_str in bw_strs:
|
||||||
|
bw = float( bw_str.split(' ')[0] )
|
||||||
|
self.assertWithinTolerance( bw, BW, BW_TOLERANCE )
|
||||||
|
|
||||||
|
def testLinkDelay( self ):
|
||||||
|
"Verify that link delays are accurate within a bound."
|
||||||
|
DELAY_MS = 15
|
||||||
|
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
|
||||||
|
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
|
||||||
|
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||||
|
link=TCLink )
|
||||||
|
ping_delays = mn.run( mn.pingFull )
|
||||||
|
test_outputs = ping_delays[0]
|
||||||
|
# Ignore unused variables below
|
||||||
|
# pylint: disable-msg=W0612
|
||||||
|
node, dest, ping_outputs = test_outputs
|
||||||
|
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
|
||||||
|
self.assertEqual( sent, received )
|
||||||
|
# pylint: enable-msg=W0612
|
||||||
|
for rttval in [rttmin, rttavg, rttmax]:
|
||||||
|
# Multiply delay by 4 to cover there & back on two links
|
||||||
|
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
|
||||||
|
DELAY_TOLERANCE)
|
||||||
|
|
||||||
|
def testLinkLoss( self ):
|
||||||
|
"Verify that we see packet drops with a high configured loss rate."
|
||||||
|
LOSS_PERCENT = 99
|
||||||
|
REPS = 1
|
||||||
|
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
|
||||||
|
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||||
|
host=CPULimitedHost, link=TCLink )
|
||||||
|
# Drops are probabilistic, but the chance of no dropped packets is
|
||||||
|
# 1 in 100 million with 4 hops for a link w/99% loss.
|
||||||
|
dropped_total = 0
|
||||||
|
mn.start()
|
||||||
|
for _ in range(REPS):
|
||||||
|
dropped_total += mn.ping(timeout='1')
|
||||||
|
mn.stop()
|
||||||
|
self.assertTrue(dropped_total > 0)
|
||||||
|
|
||||||
|
def testMostOptions( self ):
|
||||||
|
"Verify topology creation with most link options and CPU limits."
|
||||||
|
lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
|
||||||
|
hopts = { 'cpu': 0.5 / N }
|
||||||
|
self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setLogLevel( 'warning' )
|
||||||
|
unittest.main()
|
||||||
@@ -5,16 +5,14 @@
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mininet.net import init, Mininet
|
from mininet.net import Mininet
|
||||||
from mininet.node import Host, Controller, ControllerParams
|
from mininet.node import Host, Controller
|
||||||
# from mininet.node import KernelSwitch
|
|
||||||
from mininet.node import UserSwitch, OVSKernelSwitch
|
from mininet.node import UserSwitch, OVSKernelSwitch
|
||||||
from mininet.topo import SingleSwitchTopo, LinearTopo
|
from mininet.topo import SingleSwitchTopo, LinearTopo
|
||||||
from mininet.log import setLogLevel
|
from mininet.log import setLogLevel
|
||||||
|
|
||||||
SWITCHES = { 'user': UserSwitch,
|
SWITCHES = { 'user': UserSwitch,
|
||||||
'ovsk': OVSKernelSwitch,
|
'ovsk': OVSKernelSwitch,
|
||||||
# 'kernel': KernelSwitch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -23,21 +21,15 @@ class testSingleSwitch( unittest.TestCase ):
|
|||||||
|
|
||||||
def testMinimal( self ):
|
def testMinimal( self ):
|
||||||
"Ping test with both datapaths on minimal topology"
|
"Ping test with both datapaths on minimal topology"
|
||||||
init()
|
|
||||||
for switch in SWITCHES.values():
|
for switch in SWITCHES.values():
|
||||||
controllerParams = ControllerParams( '10.0.0.0', 8 )
|
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller )
|
||||||
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller,
|
|
||||||
controllerParams )
|
|
||||||
dropped = mn.run( mn.ping )
|
dropped = mn.run( mn.ping )
|
||||||
self.assertEqual( dropped, 0 )
|
self.assertEqual( dropped, 0 )
|
||||||
|
|
||||||
def testSingle5( self ):
|
def testSingle5( self ):
|
||||||
"Ping test with both datapaths on 5-host single-switch topology"
|
"Ping test with both datapaths on 5-host single-switch topology"
|
||||||
init()
|
|
||||||
for switch in SWITCHES.values():
|
for switch in SWITCHES.values():
|
||||||
controllerParams = ControllerParams( '10.0.0.0', 8 )
|
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller )
|
||||||
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller,
|
|
||||||
controllerParams )
|
|
||||||
dropped = mn.run( mn.ping )
|
dropped = mn.run( mn.ping )
|
||||||
self.assertEqual( dropped, 0 )
|
self.assertEqual( dropped, 0 )
|
||||||
|
|
||||||
@@ -47,15 +39,12 @@ class testLinear( unittest.TestCase ):
|
|||||||
|
|
||||||
def testLinear5( self ):
|
def testLinear5( self ):
|
||||||
"Ping test with both datapaths on a 5-switch topology"
|
"Ping test with both datapaths on a 5-switch topology"
|
||||||
init()
|
|
||||||
for switch in SWITCHES.values():
|
for switch in SWITCHES.values():
|
||||||
controllerParams = ControllerParams( '10.0.0.0', 8 )
|
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller )
|
||||||
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller,
|
|
||||||
controllerParams )
|
|
||||||
dropped = mn.run( mn.ping )
|
dropped = mn.run( mn.ping )
|
||||||
self.assertEqual( dropped, 0 )
|
self.assertEqual( dropped, 0 )
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
setLogLevel('warning')
|
setLogLevel( 'warning' )
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+142
-317
@@ -16,258 +16,125 @@ setup for testing, and can even be emulated with the Mininet package.
|
|||||||
# from networkx.classes.graph import Graph
|
# from networkx.classes.graph import Graph
|
||||||
|
|
||||||
from networkx import Graph
|
from networkx import Graph
|
||||||
from mininet.node import SWITCH_PORT_BASE
|
from mininet.util import irange, natural, naturalSeq
|
||||||
|
|
||||||
class NodeID(object):
|
|
||||||
'''Topo node identifier.'''
|
|
||||||
|
|
||||||
def __init__(self, dpid = None):
|
|
||||||
'''Init.
|
|
||||||
|
|
||||||
@param dpid dpid
|
|
||||||
'''
|
|
||||||
# DPID-compatible hashable identifier: opaque 64-bit unsigned int
|
|
||||||
self.dpid = dpid
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
'''String conversion.
|
|
||||||
|
|
||||||
@return str dpid as string
|
|
||||||
'''
|
|
||||||
return str(self.dpid)
|
|
||||||
|
|
||||||
def name_str(self):
|
|
||||||
'''Name conversion.
|
|
||||||
|
|
||||||
@return name name as string
|
|
||||||
'''
|
|
||||||
return str(self.dpid)
|
|
||||||
|
|
||||||
def ip_str(self):
|
|
||||||
'''Name conversion.
|
|
||||||
|
|
||||||
@return ip ip as string
|
|
||||||
'''
|
|
||||||
hi = (self.dpid & 0xff0000) >> 16
|
|
||||||
mid = (self.dpid & 0xff00) >> 8
|
|
||||||
lo = self.dpid & 0xff
|
|
||||||
return "10.%i.%i.%i" % (hi, mid, lo)
|
|
||||||
|
|
||||||
|
|
||||||
class Node(object):
|
|
||||||
'''Node-specific vertex metadata for a Topo object.'''
|
|
||||||
|
|
||||||
def __init__(self, connected = False, admin_on = True,
|
|
||||||
power_on = True, fault = False, is_switch = True):
|
|
||||||
'''Init.
|
|
||||||
|
|
||||||
@param connected actively connected to controller
|
|
||||||
@param admin_on administratively on or off
|
|
||||||
@param power_on powered on or off
|
|
||||||
@param fault fault seen on node
|
|
||||||
@param is_switch switch or host
|
|
||||||
'''
|
|
||||||
self.connected = connected
|
|
||||||
self.admin_on = admin_on
|
|
||||||
self.power_on = power_on
|
|
||||||
self.fault = fault
|
|
||||||
self.is_switch = is_switch
|
|
||||||
|
|
||||||
|
|
||||||
class Edge(object):
|
|
||||||
'''Edge-specific metadata for a StructuredTopo graph.'''
|
|
||||||
|
|
||||||
def __init__(self, admin_on = True, power_on = True, fault = False):
|
|
||||||
'''Init.
|
|
||||||
|
|
||||||
@param admin_on administratively on or off; defaults to True
|
|
||||||
@param power_on powered on or off; defaults to True
|
|
||||||
@param fault fault seen on edge; defaults to False
|
|
||||||
'''
|
|
||||||
self.admin_on = admin_on
|
|
||||||
self.power_on = power_on
|
|
||||||
self.fault = fault
|
|
||||||
|
|
||||||
|
|
||||||
class Topo(object):
|
class Topo(object):
|
||||||
'''Data center network representation for structured multi-trees.'''
|
"Data center network representation for structured multi-trees."
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, hopts=None, sopts=None, lopts=None):
|
||||||
'''Create Topo object.
|
"""Topo object:
|
||||||
|
hinfo: default host options
|
||||||
'''
|
sopts: default switch options
|
||||||
|
lopts: default link options"""
|
||||||
self.g = Graph()
|
self.g = Graph()
|
||||||
self.node_info = {} # dpids hash to Node objects
|
self.node_info = {}
|
||||||
self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
|
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
|
||||||
|
self.hopts = {} if hopts is None else hopts
|
||||||
|
self.sopts = {} if sopts is None else sopts
|
||||||
|
self.lopts = {} if lopts is None else lopts
|
||||||
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
||||||
self.id_gen = NodeID # class used to generate dpid
|
|
||||||
|
|
||||||
def add_node(self, dpid, node):
|
def addNode(self, name, **opts):
|
||||||
'''Add Node to graph.
|
"""Add Node to graph.
|
||||||
|
name: name
|
||||||
|
opts: node options
|
||||||
|
returns: node name"""
|
||||||
|
self.g.add_node(name)
|
||||||
|
self.node_info[name] = opts
|
||||||
|
return name
|
||||||
|
|
||||||
@param dpid dpid
|
def addHost(self, name, **opts):
|
||||||
@param node Node object
|
"""Convenience method: Add host to graph.
|
||||||
'''
|
name: host name
|
||||||
self.g.add_node(dpid)
|
opts: host options
|
||||||
self.node_info[dpid] = node
|
returns: host name"""
|
||||||
|
if not opts and self.hopts:
|
||||||
|
opts = self.hopts
|
||||||
|
return self.addNode(name, **opts)
|
||||||
|
|
||||||
def add_edge(self, src, dst, edge = None):
|
def addSwitch(self, name, **opts):
|
||||||
'''Add edge (Node, Node) to graph.
|
"""Convenience method: Add switch to graph.
|
||||||
|
name: switch name
|
||||||
|
opts: switch options
|
||||||
|
returns: switch name"""
|
||||||
|
if not opts and self.sopts:
|
||||||
|
opts = self.sopts
|
||||||
|
result = self.addNode(name, isSwitch=True, **opts)
|
||||||
|
return result
|
||||||
|
|
||||||
@param src src dpid
|
def addLink(self, node1, node2, port1=None, port2=None,
|
||||||
@param dst dst dpid
|
**opts):
|
||||||
@param edge Edge object
|
"""node1, node2: nodes to link together
|
||||||
'''
|
port1, port2: ports (optional)
|
||||||
src, dst = tuple(sorted([src, dst]))
|
opts: link options (optional)
|
||||||
self.g.add_edge(src, dst)
|
returns: link info key"""
|
||||||
if not edge:
|
if not opts and self.lopts:
|
||||||
edge = Edge()
|
opts = self.lopts
|
||||||
self.edge_info[(src, dst)] = edge
|
self.addPort(node1, node2, port1, port2)
|
||||||
self.add_port(src, dst)
|
key = tuple(self.sorted([node1, node2]))
|
||||||
|
self.link_info[key] = opts
|
||||||
|
self.g.add_edge(*key)
|
||||||
|
return key
|
||||||
|
|
||||||
def add_port(self, src, dst):
|
def addPort(self, src, dst, sport=None, dport=None):
|
||||||
'''Generate port mapping for new edge.
|
'''Generate port mapping for new edge.
|
||||||
|
@param src source switch name
|
||||||
@param src source switch DPID
|
@param dst destination switch name
|
||||||
@param dst destination switch DPID
|
|
||||||
'''
|
'''
|
||||||
src_base = SWITCH_PORT_BASE if self.is_switch(src) else 0
|
self.ports.setdefault(src, {})
|
||||||
dst_base = SWITCH_PORT_BASE if self.is_switch(dst) else 0
|
self.ports.setdefault(dst, {})
|
||||||
if src not in self.ports:
|
# New port: number of outlinks + base
|
||||||
self.ports[src] = {}
|
src_base = 1 if self.isSwitch(src) else 0
|
||||||
if dst not in self.ports[src]:
|
dst_base = 1 if self.isSwitch(dst) else 0
|
||||||
# num outlinks
|
if sport is None:
|
||||||
self.ports[src][dst] = len(self.ports[src]) + src_base
|
sport = len(self.ports[src]) + src_base
|
||||||
if dst not in self.ports:
|
if dport is None:
|
||||||
self.ports[dst] = {}
|
dport = len(self.ports[dst]) + dst_base
|
||||||
if src not in self.ports[dst]:
|
self.ports[src][dst] = sport
|
||||||
# num outlinks
|
self.ports[dst][src] = dport
|
||||||
self.ports[dst][src] = len(self.ports[dst]) + dst_base
|
|
||||||
|
|
||||||
def node_enabled(self, dpid):
|
def nodes(self, sort=True):
|
||||||
'''Is node connected, admin on, powered on, and fault-free?
|
"Return nodes in graph"
|
||||||
|
if sort:
|
||||||
@param dpid dpid
|
return self.sorted( self.g.nodes() )
|
||||||
|
|
||||||
@return bool node is enabled
|
|
||||||
'''
|
|
||||||
ni = self.node_info[dpid]
|
|
||||||
return ni.connected and ni.admin_on and ni.power_on and not ni.fault
|
|
||||||
|
|
||||||
def nodes_enabled(self, dpids, enabled = True):
|
|
||||||
'''Return subset of enabled nodes
|
|
||||||
|
|
||||||
@param dpids list of dpids
|
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids filtered list of dpids
|
|
||||||
'''
|
|
||||||
if enabled:
|
|
||||||
return [n for n in dpids if self.node_enabled(n)]
|
|
||||||
else:
|
else:
|
||||||
return dpids
|
return self.g.nodes()
|
||||||
|
|
||||||
def nodes(self, enabled = True):
|
def isSwitch(self, n):
|
||||||
'''Return graph nodes.
|
|
||||||
|
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids list of dpids
|
|
||||||
'''
|
|
||||||
return self.nodes_enabled(self.g.nodes(), enabled)
|
|
||||||
|
|
||||||
def nodes_str(self, dpids):
|
|
||||||
'''Return string of custom-encoded nodes.
|
|
||||||
|
|
||||||
@param dpids list of dpids
|
|
||||||
|
|
||||||
@return str string
|
|
||||||
'''
|
|
||||||
return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
|
|
||||||
|
|
||||||
def is_switch(self, n):
|
|
||||||
'''Returns true if node is a switch.'''
|
'''Returns true if node is a switch.'''
|
||||||
return self.node_info[n].is_switch
|
info = self.node_info[n]
|
||||||
|
return info and info.get('isSwitch', False)
|
||||||
|
|
||||||
def switches(self, enabled = True):
|
def switches(self, sort=True):
|
||||||
'''Return switches.
|
'''Return switches.
|
||||||
|
sort: sort switches alphabetically
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids list of dpids
|
@return dpids list of dpids
|
||||||
'''
|
'''
|
||||||
nodes = [n for n in self.g.nodes() if self.is_switch(n)]
|
return [n for n in self.nodes(sort) if self.isSwitch(n)]
|
||||||
return self.nodes_enabled(nodes, enabled)
|
|
||||||
|
|
||||||
def hosts(self, enabled = True):
|
def hosts(self, sort=True):
|
||||||
'''Return hosts.
|
'''Return hosts.
|
||||||
|
sort: sort hosts alphabetically
|
||||||
@param enabled only return enabled nodes?
|
|
||||||
|
|
||||||
@return dpids list of dpids
|
@return dpids list of dpids
|
||||||
'''
|
'''
|
||||||
|
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
|
||||||
|
|
||||||
def is_host(n):
|
def links(self, sort=True):
|
||||||
'''Returns true if node is a host.'''
|
'''Return links.
|
||||||
return not self.node_info[n].is_switch
|
sort: sort links alphabetically
|
||||||
|
@return links list of name pairs
|
||||||
nodes = [n for n in self.g.nodes() if is_host(n)]
|
|
||||||
return self.nodes_enabled(nodes, enabled)
|
|
||||||
|
|
||||||
def edge_enabled(self, edge):
|
|
||||||
'''Is edge admin on, powered on, and fault-free?
|
|
||||||
|
|
||||||
@param edge (src, dst) dpid tuple
|
|
||||||
|
|
||||||
@return bool edge is enabled
|
|
||||||
'''
|
'''
|
||||||
src, dst = edge
|
if not sort:
|
||||||
src, dst = tuple(sorted([src, dst]))
|
return self.g.edges()
|
||||||
ei = self.edge_info[tuple(sorted([src, dst]))]
|
|
||||||
return ei.admin_on and ei.power_on and not ei.fault
|
|
||||||
|
|
||||||
def edges_enabled(self, edges, enabled = True):
|
|
||||||
'''Return subset of enabled edges
|
|
||||||
|
|
||||||
@param edges list of edges
|
|
||||||
@param enabled only return enabled edges?
|
|
||||||
|
|
||||||
@return edges filtered list of edges
|
|
||||||
'''
|
|
||||||
if enabled:
|
|
||||||
return [e for e in edges if self.edge_enabled(e)]
|
|
||||||
else:
|
else:
|
||||||
return edges
|
links = [tuple(self.sorted(e)) for e in self.g.edges()]
|
||||||
|
return sorted( links, key=naturalSeq )
|
||||||
def edges(self, enabled = True):
|
|
||||||
'''Return edges.
|
|
||||||
|
|
||||||
@param enabled only return enabled edges?
|
|
||||||
|
|
||||||
@return edges list of dpid pairs
|
|
||||||
'''
|
|
||||||
return self.edges_enabled(self.g.edges(), enabled)
|
|
||||||
|
|
||||||
def edges_str(self, dpid_pairs):
|
|
||||||
'''Return string of custom-encoded node pairs.
|
|
||||||
|
|
||||||
@param dpid_pairs list of dpid pairs (src, dst)
|
|
||||||
|
|
||||||
@return str string
|
|
||||||
'''
|
|
||||||
edges = []
|
|
||||||
for pair in dpid_pairs:
|
|
||||||
src, dst = pair
|
|
||||||
src = str(self.id_gen(dpid = src))
|
|
||||||
dst = str(self.id_gen(dpid = dst))
|
|
||||||
edges.append((src, dst))
|
|
||||||
return edges
|
|
||||||
|
|
||||||
def port(self, src, dst):
|
def port(self, src, dst):
|
||||||
'''Get port number.
|
'''Get port number.
|
||||||
|
|
||||||
@param src source switch DPID
|
@param src source switch name
|
||||||
@param dst destination switch DPID
|
@param dst destination switch name
|
||||||
@return tuple (src_port, dst_port):
|
@return tuple (src_port, dst_port):
|
||||||
src_port: port on source switch leading to the destination switch
|
src_port: port on source switch leading to the destination switch
|
||||||
dst_port: port on destination switch leading to the source switch
|
dst_port: port on destination switch leading to the source switch
|
||||||
@@ -276,130 +143,88 @@ class Topo(object):
|
|||||||
assert dst in self.ports and src in self.ports[dst]
|
assert dst in self.ports and src in self.ports[dst]
|
||||||
return (self.ports[src][dst], self.ports[dst][src])
|
return (self.ports[src][dst], self.ports[dst][src])
|
||||||
|
|
||||||
def enable_edges(self):
|
def linkInfo( self, src, dst ):
|
||||||
'''Enable all edges in the network graph.
|
"Return link metadata"
|
||||||
|
src, dst = self.sorted([src, dst])
|
||||||
|
return self.link_info[(src, dst)]
|
||||||
|
|
||||||
Set admin on, power on, and fault off.
|
def setlinkInfo( self, src, dst, info ):
|
||||||
'''
|
"Set link metadata"
|
||||||
for e in self.g.edges():
|
src, dst = self.sorted([src, dst])
|
||||||
src, dst = e
|
self.link_info[(src, dst)] = info
|
||||||
ei = self.edge_info[tuple(sorted([src, dst]))]
|
|
||||||
ei.admin_on = True
|
|
||||||
ei.power_on = True
|
|
||||||
ei.fault = False
|
|
||||||
|
|
||||||
def enable_nodes(self):
|
def nodeInfo( self, name ):
|
||||||
'''Enable all nodes in the network graph.
|
"Return metadata (dict) for node"
|
||||||
|
info = self.node_info[ name ]
|
||||||
|
return info if info is not None else {}
|
||||||
|
|
||||||
Set connected on, admin on, power on, and fault off.
|
def setNodeInfo( self, name, info ):
|
||||||
'''
|
"Set metadata (dict) for node"
|
||||||
for node in self.g.nodes():
|
self.node_info[ name ] = info
|
||||||
ni = self.node_info[node]
|
|
||||||
ni.connected = True
|
|
||||||
ni.admin_on = True
|
|
||||||
ni.power_on = True
|
|
||||||
ni.fault = False
|
|
||||||
|
|
||||||
def enable_all(self):
|
|
||||||
'''Enable all nodes and edges in the network graph.'''
|
|
||||||
self.enable_nodes()
|
|
||||||
self.enable_edges()
|
|
||||||
|
|
||||||
def name(self, dpid):
|
|
||||||
'''Get string name of node ID.
|
|
||||||
|
|
||||||
@param dpid DPID of host or switch
|
|
||||||
@return name_str string name with no dashes
|
|
||||||
'''
|
|
||||||
return self.id_gen(dpid = dpid).name_str()
|
|
||||||
|
|
||||||
def ip(self, dpid):
|
|
||||||
'''Get IP dotted-decimal string of node ID.
|
|
||||||
|
|
||||||
@param dpid DPID of host or switch
|
|
||||||
@return ip_str
|
|
||||||
'''
|
|
||||||
return self.id_gen(dpid = dpid).ip_str()
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sorted( items ):
|
||||||
|
"Items sorted in natural (i.e. alphabetical) order"
|
||||||
|
return sorted(items, key=natural)
|
||||||
|
|
||||||
class SingleSwitchTopo(Topo):
|
class SingleSwitchTopo(Topo):
|
||||||
'''Single switch connected to k hosts.'''
|
'''Single switch connected to k hosts.'''
|
||||||
|
|
||||||
def __init__(self, k = 2, enable_all = True):
|
def __init__(self, k=2, **opts):
|
||||||
'''Init.
|
'''Init.
|
||||||
|
|
||||||
@param k number of hosts
|
@param k number of hosts
|
||||||
@param enable_all enables all nodes and switches?
|
@param enable_all enables all nodes and switches?
|
||||||
'''
|
'''
|
||||||
super(SingleSwitchTopo, self).__init__()
|
super(SingleSwitchTopo, self).__init__(**opts)
|
||||||
|
|
||||||
self.k = k
|
self.k = k
|
||||||
|
|
||||||
self.add_node(1, Node())
|
switch = self.addSwitch('s1')
|
||||||
hosts = range(2, k + 2)
|
for h in irange(1, k):
|
||||||
for h in hosts:
|
host = self.addHost('h%s' % h)
|
||||||
self.add_node(h, Node(is_switch = False))
|
self.addLink(host, switch)
|
||||||
self.add_edge(h, 1, Edge())
|
|
||||||
|
|
||||||
if enable_all:
|
|
||||||
self.enable_all()
|
|
||||||
|
|
||||||
|
|
||||||
class SingleSwitchReversedTopo(SingleSwitchTopo):
|
class SingleSwitchReversedTopo(Topo):
|
||||||
'''Single switch connected to k hosts, with reversed ports.
|
'''Single switch connected to k hosts, with reversed ports.
|
||||||
|
|
||||||
The lowest-numbered host is connected to the highest-numbered port.
|
The lowest-numbered host is connected to the highest-numbered port.
|
||||||
|
|
||||||
Useful to verify that Mininet properly handles custom port numberings.
|
Useful to verify that Mininet properly handles custom port numberings.
|
||||||
'''
|
'''
|
||||||
|
def __init__(self, k=2, **opts):
|
||||||
def port(self, src, dst):
|
|
||||||
'''Get port number.
|
|
||||||
|
|
||||||
@param src source switch DPID
|
|
||||||
@param dst destination switch DPID
|
|
||||||
@return tuple (src_port, dst_port):
|
|
||||||
src_port: port on source switch leading to the destination switch
|
|
||||||
dst_port: port on destination switch leading to the source switch
|
|
||||||
'''
|
|
||||||
if src == 1:
|
|
||||||
if dst in range(2, self.k + 2):
|
|
||||||
dst_index = dst - 2
|
|
||||||
highest = self.k - 1
|
|
||||||
return (highest - dst_index, 0)
|
|
||||||
else:
|
|
||||||
raise Exception('unexpected dst: %i' % dst)
|
|
||||||
elif src in range(2, self.k + 2):
|
|
||||||
if dst == 1:
|
|
||||||
raise Exception('unexpected dst: %i' % dst)
|
|
||||||
else:
|
|
||||||
src_index = src - 2
|
|
||||||
highest = self.k - 1
|
|
||||||
return (0, highest - src_index)
|
|
||||||
|
|
||||||
|
|
||||||
class LinearTopo(Topo):
|
|
||||||
'''Linear topology of k switches, with one host per switch.'''
|
|
||||||
|
|
||||||
def __init__(self, k = 2, enable_all = True):
|
|
||||||
'''Init.
|
'''Init.
|
||||||
|
|
||||||
@param k number of switches (and hosts too)
|
@param k number of hosts
|
||||||
@param enable_all enables all nodes and switches?
|
@param enable_all enables all nodes and switches?
|
||||||
'''
|
'''
|
||||||
super(LinearTopo, self).__init__()
|
super(SingleSwitchReversedTopo, self).__init__(**opts)
|
||||||
|
self.k = k
|
||||||
|
switch = self.addSwitch('s1')
|
||||||
|
for h in irange(1, k):
|
||||||
|
host = self.addHost('h%s' % h)
|
||||||
|
self.addLink(host, switch,
|
||||||
|
port1=0, port2=(k - h + 1))
|
||||||
|
|
||||||
|
class LinearTopo(Topo):
|
||||||
|
"Linear topology of k switches, with one host per switch."
|
||||||
|
|
||||||
|
def __init__(self, k=2, **opts):
|
||||||
|
"""Init.
|
||||||
|
k: number of switches (and hosts)
|
||||||
|
hconf: host configuration options
|
||||||
|
lconf: link configuration options"""
|
||||||
|
|
||||||
|
super(LinearTopo, self).__init__(**opts)
|
||||||
|
|
||||||
self.k = k
|
self.k = k
|
||||||
|
|
||||||
switches = range(1, k + 1)
|
lastSwitch = None
|
||||||
for s in switches:
|
for i in irange(1, k):
|
||||||
h = s + k
|
host = self.addHost('h%s' % i)
|
||||||
self.add_node(s, Node())
|
switch = self.addSwitch('s%s' % i)
|
||||||
self.add_node(h, Node(is_switch = False))
|
self.addLink( host, switch)
|
||||||
self.add_edge(s, h, Edge())
|
if lastSwitch:
|
||||||
for s in switches:
|
self.addLink( switch, lastSwitch)
|
||||||
if s != k:
|
lastSwitch = switch
|
||||||
self.add_edge(s, s + 1, Edge())
|
|
||||||
|
|
||||||
if enable_all:
|
|
||||||
self.enable_all()
|
|
||||||
|
|||||||
+10
-19
@@ -1,6 +1,6 @@
|
|||||||
"Library of potentially useful topologies for Mininet"
|
"Library of potentially useful topologies for Mininet"
|
||||||
|
|
||||||
from mininet.topo import Topo, Node
|
from mininet.topo import Topo
|
||||||
from mininet.net import Mininet
|
from mininet.net import Mininet
|
||||||
|
|
||||||
class TreeTopo( Topo ):
|
class TreeTopo( Topo ):
|
||||||
@@ -8,36 +8,27 @@ class TreeTopo( Topo ):
|
|||||||
|
|
||||||
def __init__( self, depth=1, fanout=2 ):
|
def __init__( self, depth=1, fanout=2 ):
|
||||||
super( TreeTopo, self ).__init__()
|
super( TreeTopo, self ).__init__()
|
||||||
# Numbering: h1..N, sN+1..M
|
# Numbering: h1..N, s1..M
|
||||||
hostCount = fanout ** depth
|
|
||||||
self.hostNum = 1
|
self.hostNum = 1
|
||||||
self.switchNum = hostCount + 1
|
self.switchNum = 1
|
||||||
# Build topology
|
# Build topology
|
||||||
self.addTree( depth, fanout )
|
self.addTree( depth, fanout )
|
||||||
# Consider all switches and hosts 'on'
|
|
||||||
self.enable_all()
|
|
||||||
|
|
||||||
# It is OK that i is "unused" in the for loop.
|
|
||||||
# pylint: disable-msg=W0612
|
|
||||||
|
|
||||||
def addTree( self, depth, fanout ):
|
def addTree( self, depth, fanout ):
|
||||||
"""Add a subtree starting with node n.
|
"""Add a subtree starting with node n.
|
||||||
returns: last node added"""
|
returns: last node added"""
|
||||||
isSwitch = depth > 0
|
isSwitch = depth > 0
|
||||||
if isSwitch:
|
if isSwitch:
|
||||||
num = self.switchNum
|
node = self.addSwitch( 's%s' % self.switchNum )
|
||||||
self.switchNum += 1
|
self.switchNum += 1
|
||||||
else:
|
for _ in range( fanout ):
|
||||||
num = self.hostNum
|
|
||||||
self.hostNum += 1
|
|
||||||
self.add_node( num, Node( is_switch=isSwitch ) )
|
|
||||||
if isSwitch:
|
|
||||||
for i in range( 0, fanout ):
|
|
||||||
child = self.addTree( depth - 1, fanout )
|
child = self.addTree( depth - 1, fanout )
|
||||||
self.add_edge( num, child )
|
self.addLink( node, child )
|
||||||
return num
|
else:
|
||||||
|
node = self.addHost( 'h%s' % self.hostNum )
|
||||||
|
self.hostNum += 1
|
||||||
|
return node
|
||||||
|
|
||||||
# pylint: enable-msg=W0612
|
|
||||||
|
|
||||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||||
"Convenience function for creating tree networks."
|
"Convenience function for creating tree networks."
|
||||||
|
|||||||
+290
-30
@@ -1,11 +1,15 @@
|
|||||||
"Utility functions for Mininet."
|
"Utility functions for Mininet."
|
||||||
|
|
||||||
|
from mininet.log import output, info, error, warn
|
||||||
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||||
import select
|
from select import poll, POLLIN
|
||||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||||
|
import re
|
||||||
from mininet.log import error
|
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||||
|
from os import O_NONBLOCK
|
||||||
|
import os
|
||||||
|
|
||||||
# Command execution support
|
# Command execution support
|
||||||
|
|
||||||
@@ -22,7 +26,7 @@ def checkRun( cmd ):
|
|||||||
# pylint doesn't understand explicit type checking
|
# pylint doesn't understand explicit type checking
|
||||||
# pylint: disable-msg=E1103
|
# pylint: disable-msg=E1103
|
||||||
|
|
||||||
def quietRun( *cmd ):
|
def oldQuietRun( *cmd ):
|
||||||
"""Run a command, routing stderr to stdout, and return the output.
|
"""Run a command, routing stderr to stdout, and return the output.
|
||||||
cmd: list of command params"""
|
cmd: list of command params"""
|
||||||
if len( cmd ) == 1:
|
if len( cmd ) == 1:
|
||||||
@@ -33,22 +37,87 @@ def quietRun( *cmd ):
|
|||||||
# We can't use Popen.communicate() because it uses
|
# We can't use Popen.communicate() because it uses
|
||||||
# select(), which can't handle
|
# select(), which can't handle
|
||||||
# high file descriptor numbers! poll() can, however.
|
# high file descriptor numbers! poll() can, however.
|
||||||
output = ''
|
out = ''
|
||||||
readable = select.poll()
|
readable = poll()
|
||||||
readable.register( popen.stdout )
|
readable.register( popen.stdout )
|
||||||
while True:
|
while True:
|
||||||
while readable.poll():
|
while readable.poll():
|
||||||
data = popen.stdout.read( 1024 )
|
data = popen.stdout.read( 1024 )
|
||||||
if len( data ) == 0:
|
if len( data ) == 0:
|
||||||
break
|
break
|
||||||
output += data
|
out += data
|
||||||
popen.poll()
|
popen.poll()
|
||||||
if popen.returncode != None:
|
if popen.returncode is not None:
|
||||||
break
|
break
|
||||||
return output
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# This is a bit complicated, but it enables us to
|
||||||
|
# monitor command output as it is happening
|
||||||
|
|
||||||
|
def errRun( *cmd, **kwargs ):
|
||||||
|
"""Run a command and return stdout, stderr and return code
|
||||||
|
cmd: string or list of command and args
|
||||||
|
stderr: STDOUT to merge stderr with stdout
|
||||||
|
shell: run command using shell
|
||||||
|
echo: monitor output to console"""
|
||||||
|
# Allow passing in a list or a string
|
||||||
|
if len( cmd ) == 1:
|
||||||
|
cmd = cmd[ 0 ]
|
||||||
|
if isinstance( cmd, str ):
|
||||||
|
cmd = cmd.split( ' ' )
|
||||||
|
cmd = [ str( arg ) for arg in cmd ]
|
||||||
|
# By default we separate stderr, don't run in a shell, and don't echo
|
||||||
|
stderr = kwargs.get( 'stderr', PIPE )
|
||||||
|
shell = kwargs.get( 'shell', False )
|
||||||
|
echo = kwargs.get( 'echo', False )
|
||||||
|
if echo:
|
||||||
|
# cmd goes to stderr, output goes to stdout
|
||||||
|
info( cmd, '\n' )
|
||||||
|
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
|
||||||
|
# We use poll() because select() doesn't work with large fd numbers,
|
||||||
|
# and thus communicate() doesn't work either
|
||||||
|
out, err = '', ''
|
||||||
|
poller = poll()
|
||||||
|
poller.register( popen.stdout, POLLIN )
|
||||||
|
fdtofile = { popen.stdout.fileno(): popen.stdout }
|
||||||
|
outDone, errDone = False, True
|
||||||
|
if popen.stderr:
|
||||||
|
fdtofile[ popen.stderr.fileno() ] = popen.stderr
|
||||||
|
poller.register( popen.stderr, POLLIN )
|
||||||
|
errDone = False
|
||||||
|
while not outDone or not errDone:
|
||||||
|
readable = poller.poll()
|
||||||
|
for fd, _event in readable:
|
||||||
|
f = fdtofile[ fd ]
|
||||||
|
data = f.read( 1024 )
|
||||||
|
if echo:
|
||||||
|
output( data )
|
||||||
|
if f == popen.stdout:
|
||||||
|
out += data
|
||||||
|
if data == '':
|
||||||
|
outDone = True
|
||||||
|
elif f == popen.stderr:
|
||||||
|
err += data
|
||||||
|
if data == '':
|
||||||
|
errDone = True
|
||||||
|
returncode = popen.wait()
|
||||||
|
return out, err, returncode
|
||||||
|
|
||||||
|
def errFail( *cmd, **kwargs ):
|
||||||
|
"Run a command using errRun and raise exception on nonzero exit"
|
||||||
|
out, err, ret = errRun( *cmd, **kwargs )
|
||||||
|
if ret:
|
||||||
|
raise Exception( "errFail: %s failed with return code %s: %s"
|
||||||
|
% ( cmd, ret, err ) )
|
||||||
|
return out, err, ret
|
||||||
|
|
||||||
|
def quietRun( cmd, **kwargs ):
|
||||||
|
"Run a command and return merged stdout and stderr"
|
||||||
|
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
|
||||||
|
|
||||||
# pylint: enable-msg=E1103
|
# pylint: enable-msg=E1103
|
||||||
# pylint: disable-msg=E1101,W0612
|
# pylint: disable-msg=E1101
|
||||||
|
|
||||||
def isShellBuiltin( cmd ):
|
def isShellBuiltin( cmd ):
|
||||||
"Return True if cmd is a bash builtin."
|
"Return True if cmd is a bash builtin."
|
||||||
@@ -61,7 +130,7 @@ def isShellBuiltin( cmd ):
|
|||||||
|
|
||||||
isShellBuiltin.builtIns = None
|
isShellBuiltin.builtIns = None
|
||||||
|
|
||||||
# pylint: enable-msg=E1101,W0612
|
# pylint: enable-msg=E1101
|
||||||
|
|
||||||
# Interface management
|
# Interface management
|
||||||
#
|
#
|
||||||
@@ -113,7 +182,7 @@ def moveIntfNoRetry( intf, node, printError=False ):
|
|||||||
if not ( ' %s:' % intf ) in links:
|
if not ( ' %s:' % intf ) in links:
|
||||||
if printError:
|
if printError:
|
||||||
error( '*** Error: moveIntf: ' + intf +
|
error( '*** Error: moveIntf: ' + intf +
|
||||||
' not successfully moved to ' + node.name + '\n' )
|
' not successfully moved to ' + node.name + '\n' )
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -124,25 +193,41 @@ def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
|
|||||||
printError: if true, print error"""
|
printError: if true, print error"""
|
||||||
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
|
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
|
||||||
|
|
||||||
def createLink( node1, node2, port1=None, port2=None ):
|
# Support for dumping network
|
||||||
"""Create a link between nodes, making an interface for each.
|
|
||||||
node1: Node object
|
|
||||||
node2: Node object
|
|
||||||
port1: node1 port number (optional)
|
|
||||||
port2: node2 port number (optional)
|
|
||||||
returns: intf1 name, intf2 name"""
|
|
||||||
return node1.linkTo( node2, port1, port2 )
|
|
||||||
|
|
||||||
|
def dumpNodeConnections( nodes ):
|
||||||
|
"Dump connections to/from nodes."
|
||||||
|
|
||||||
|
def dumpConnections( node ):
|
||||||
|
"Helper function: dump connections to node"
|
||||||
|
for intf in node.intfList():
|
||||||
|
output( ' %s:' % intf )
|
||||||
|
if intf.link:
|
||||||
|
intfs = [ intf.link.intf1, intf.link.intf2 ]
|
||||||
|
intfs.remove( intf )
|
||||||
|
output( intfs[ 0 ] )
|
||||||
|
else:
|
||||||
|
output( ' ' )
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
output( node.name )
|
||||||
|
dumpConnections( node )
|
||||||
|
output( '\n' )
|
||||||
|
|
||||||
|
def dumpNetConnections( net ):
|
||||||
|
"Dump connections in network"
|
||||||
|
nodes = net.controllers + net.switches + net.hosts
|
||||||
|
dumpNodeConnections( nodes )
|
||||||
|
|
||||||
# IP and Mac address formatting and parsing
|
# IP and Mac address formatting and parsing
|
||||||
|
|
||||||
def _colonHex( val, count ):
|
def _colonHex( val, bytecount ):
|
||||||
"""Generate colon-hex string.
|
"""Generate colon-hex string.
|
||||||
val: input as unsigned int
|
val: input as unsigned int
|
||||||
count: number of bytes to convert
|
bytecount: number of bytes to convert
|
||||||
returns: chStr colon-hex string"""
|
returns: chStr colon-hex string"""
|
||||||
pieces = []
|
pieces = []
|
||||||
for i in range( count - 1, -1, -1 ):
|
for i in range( bytecount - 1, -1, -1 ):
|
||||||
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
|
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
|
||||||
pieces.append( '%02x' % piece )
|
pieces.append( '%02x' % piece )
|
||||||
chStr = ':'.join( pieces )
|
chStr = ':'.join( pieces )
|
||||||
@@ -158,23 +243,44 @@ def ipStr( ip ):
|
|||||||
"""Generate IP address string from an unsigned int.
|
"""Generate IP address string from an unsigned int.
|
||||||
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
|
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
|
||||||
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
|
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
|
||||||
w = ( ip & 0xff000000 ) >> 24
|
w = ( ip >> 24 ) & 0xff
|
||||||
w = 10 if w == 0 else w
|
w = 10 if w == 0 else w
|
||||||
x = ( ip & 0xff0000 ) >> 16
|
x = ( ip >> 16 ) & 0xff
|
||||||
y = ( ip & 0xff00 ) >> 8
|
y = ( ip >> 8 ) & 0xff
|
||||||
z = ip & 0xff
|
z = ip & 0xff
|
||||||
return "%i.%i.%i.%i" % ( w, x, y, z )
|
return "%i.%i.%i.%i" % ( w, x, y, z )
|
||||||
|
|
||||||
def ipNum( w, x, y, z ):
|
def ipNum( w, x, y, z ):
|
||||||
"""Generate unsigned int from components ofIP address
|
"""Generate unsigned int from components of IP address
|
||||||
returns: w << 24 | x << 16 | y << 8 | z"""
|
returns: w << 24 | x << 16 | y << 8 | z"""
|
||||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||||
|
|
||||||
|
def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||||
|
"""Return IP address string from ints
|
||||||
|
i: int to be added to ipbase
|
||||||
|
prefixLen: optional IP prefix length
|
||||||
|
ipBaseNum: option base IP address as int
|
||||||
|
returns IP address as string"""
|
||||||
|
# Ugly but functional
|
||||||
|
assert i < ( 1 << ( 32 - prefixLen ) )
|
||||||
|
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
|
||||||
|
ipnum = i + ( ipBaseNum & mask )
|
||||||
|
return ipStr( ipnum )
|
||||||
|
|
||||||
def ipParse( ip ):
|
def ipParse( ip ):
|
||||||
"Parse an IP address and return an unsigned int."
|
"Parse an IP address and return an unsigned int."
|
||||||
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
||||||
return ipNum( *args )
|
return ipNum( *args )
|
||||||
|
|
||||||
|
def netParse( ipstr ):
|
||||||
|
"""Parse an IP network specification, returning
|
||||||
|
address and prefix len as unsigned ints"""
|
||||||
|
prefixLen = 0
|
||||||
|
if '/' in ipstr:
|
||||||
|
ip, pf = ipstr.split( '/' )
|
||||||
|
prefixLen = int( pf )
|
||||||
|
return ipParse( ip ), prefixLen
|
||||||
|
|
||||||
def checkInt( s ):
|
def checkInt( s ):
|
||||||
"Check if input string is an int"
|
"Check if input string is an int"
|
||||||
try:
|
try:
|
||||||
@@ -200,10 +306,164 @@ def makeNumeric( s ):
|
|||||||
else:
|
else:
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
# Popen support
|
||||||
|
|
||||||
|
def pmonitor(popens, timeoutms=500, readline=True,
|
||||||
|
readmax=1024 ):
|
||||||
|
"""Monitor dict of hosts to popen objects
|
||||||
|
a line at a time
|
||||||
|
timeoutms: timeout for poll()
|
||||||
|
readline: return single line of output
|
||||||
|
yields: host, line/output (if any)
|
||||||
|
terminates: when all EOFs received"""
|
||||||
|
poller = poll()
|
||||||
|
fdToHost = {}
|
||||||
|
for host, popen in popens.iteritems():
|
||||||
|
fd = popen.stdout.fileno()
|
||||||
|
fdToHost[ fd ] = host
|
||||||
|
poller.register( fd, POLLIN )
|
||||||
|
if not readline:
|
||||||
|
# Use non-blocking reads
|
||||||
|
flags = fcntl( fd, F_GETFL )
|
||||||
|
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
|
||||||
|
while True:
|
||||||
|
fds = poller.poll( timeoutms )
|
||||||
|
if fds:
|
||||||
|
for fd, _event in fds:
|
||||||
|
host = fdToHost[ fd ]
|
||||||
|
popen = popens[ host ]
|
||||||
|
if readline:
|
||||||
|
# Attempt to read a line of output
|
||||||
|
# This blocks until we receive a newline!
|
||||||
|
line = popen.stdout.readline()
|
||||||
|
else:
|
||||||
|
line = popen.stdout.read( readmax )
|
||||||
|
yield host, line
|
||||||
|
# Check for EOF
|
||||||
|
if not line:
|
||||||
|
popen.poll()
|
||||||
|
if popen.returncode is not None:
|
||||||
|
poller.unregister( fd )
|
||||||
|
del popens[ host ]
|
||||||
|
if not popens:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
yield None, ''
|
||||||
|
|
||||||
# Other stuff we use
|
# Other stuff we use
|
||||||
|
|
||||||
def fixLimits():
|
def fixLimits():
|
||||||
"Fix ridiculously small resource limits."
|
"Fix ridiculously small resource limits."
|
||||||
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
|
setrlimit( RLIMIT_NPROC, ( 8192, 8192 ) )
|
||||||
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
|
setrlimit( RLIMIT_NOFILE, ( 16384, 16384 ) )
|
||||||
|
|
||||||
|
def mountCgroups():
|
||||||
|
"Make sure cgroups file system is mounted"
|
||||||
|
mounts = quietRun( 'mount' )
|
||||||
|
cgdir = '/sys/fs/cgroup'
|
||||||
|
csdir = cgdir + '/cpuset'
|
||||||
|
if ('cgroup on %s' % cgdir not in mounts and
|
||||||
|
'cgroups on %s' % cgdir not in mounts):
|
||||||
|
raise Exception( "cgroups not mounted on " + cgdir )
|
||||||
|
if 'cpuset on %s' % csdir not in mounts:
|
||||||
|
errRun( 'mkdir -p ' + csdir )
|
||||||
|
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
|
||||||
|
|
||||||
|
def natural( text ):
|
||||||
|
"To sort sanely/alphabetically: sorted( l, key=natural )"
|
||||||
|
def num( s ):
|
||||||
|
"Convert text segment to int if necessary"
|
||||||
|
return int( s ) if s.isdigit() else s
|
||||||
|
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
|
||||||
|
|
||||||
|
def naturalSeq( t ):
|
||||||
|
"Natural sort key function for sequences"
|
||||||
|
return [ natural( x ) for x in t ]
|
||||||
|
|
||||||
|
def numCores():
|
||||||
|
"Returns number of CPU cores based on /proc/cpuinfo"
|
||||||
|
if hasattr( numCores, 'ncores' ):
|
||||||
|
return numCores.ncores
|
||||||
|
try:
|
||||||
|
numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
|
||||||
|
except ValueError:
|
||||||
|
return 0
|
||||||
|
return numCores.ncores
|
||||||
|
|
||||||
|
def irange(start, end):
|
||||||
|
"""Inclusive range from start to end (vs. Python insanity.)
|
||||||
|
irange(1,5) -> 1, 2, 3, 4, 5"""
|
||||||
|
return range( start, end + 1 )
|
||||||
|
|
||||||
|
def custom( cls, **params ):
|
||||||
|
"Returns customized constructor for class cls."
|
||||||
|
# Note: we may wish to see if we can use functools.partial() here
|
||||||
|
# and in customConstructor
|
||||||
|
def customized( *args, **kwargs):
|
||||||
|
"Customized constructor"
|
||||||
|
kwargs = kwargs.copy()
|
||||||
|
kwargs.update( params )
|
||||||
|
return cls( *args, **kwargs )
|
||||||
|
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
|
||||||
|
return customized
|
||||||
|
|
||||||
|
def splitArgs( argstr ):
|
||||||
|
"""Split argument string into usable python arguments
|
||||||
|
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||||
|
returns: fn, args, kwargs"""
|
||||||
|
split = argstr.split( ',' )
|
||||||
|
fn = split[ 0 ]
|
||||||
|
params = split[ 1: ]
|
||||||
|
# Convert int and float args; removes the need for function
|
||||||
|
# to be flexible with input arg formats.
|
||||||
|
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||||
|
kwargs = {}
|
||||||
|
for s in [ p for p in params if '=' in p ]:
|
||||||
|
key, val = s.split( '=' )
|
||||||
|
kwargs[ key ] = makeNumeric( val )
|
||||||
|
return fn, args, kwargs
|
||||||
|
|
||||||
|
def customConstructor( constructors, argStr ):
|
||||||
|
"""Return custom constructor based on argStr
|
||||||
|
The args and key/val pairs in argsStr will be automatically applied
|
||||||
|
when the generated constructor is later used.
|
||||||
|
"""
|
||||||
|
cname, newargs, kwargs = splitArgs( argStr )
|
||||||
|
constructor = constructors.get( cname, None )
|
||||||
|
|
||||||
|
if not constructor:
|
||||||
|
raise Exception( "error: %s is unknown - please specify one of %s" %
|
||||||
|
( cname, constructors.keys() ) )
|
||||||
|
|
||||||
|
def customized( name, *args, **params ):
|
||||||
|
"Customized constructor, useful for Node, Link, and other classes"
|
||||||
|
params = params.copy()
|
||||||
|
params.update( kwargs )
|
||||||
|
if not newargs:
|
||||||
|
return constructor( name, *args, **params )
|
||||||
|
if args:
|
||||||
|
warn( 'warning: %s replacing %s with %s\n' % (
|
||||||
|
constructor, args, newargs ) )
|
||||||
|
return constructor( name, *newargs, **params )
|
||||||
|
|
||||||
|
customized.__name__ = 'customConstructor(%s)' % argStr
|
||||||
|
return customized
|
||||||
|
|
||||||
|
def buildTopo( topos, topoStr ):
|
||||||
|
"""Create topology from string with format (object, arg1, arg2,...).
|
||||||
|
input topos is a dict of topo names to constructors, possibly w/args.
|
||||||
|
"""
|
||||||
|
topo, args, kwargs = splitArgs( topoStr )
|
||||||
|
if topo not in topos:
|
||||||
|
raise Exception( 'Invalid topo name %s' % topo )
|
||||||
|
return topos[ topo ]( *args, **kwargs )
|
||||||
|
|
||||||
|
def ensureRoot():
|
||||||
|
"""Ensure that we are running as root.
|
||||||
|
|
||||||
|
Probably we should only sudo when needed as per Big Switch's patch.
|
||||||
|
"""
|
||||||
|
if os.getuid() != 0:
|
||||||
|
print "*** Mininet must run as root."
|
||||||
|
exit( 1 )
|
||||||
|
return
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
* - detaching from a controlling tty using setsid
|
* - detaching from a controlling tty using setsid
|
||||||
* - running in a network namespace
|
* - running in a network namespace
|
||||||
* - printing out the pid of a process so we can identify it later
|
* - printing out the pid of a process so we can identify it later
|
||||||
|
* - attaching to a namespace and cgroup
|
||||||
|
* - setting RT scheduling
|
||||||
*
|
*
|
||||||
* Partially based on public domain setsid(1)
|
* Partially based on public domain setsid(1)
|
||||||
*/
|
*/
|
||||||
@@ -14,23 +16,89 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <syscall.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sched.h>
|
||||||
|
|
||||||
|
#if !defined(VERSION)
|
||||||
|
#define VERSION "(devel)"
|
||||||
|
#endif
|
||||||
|
|
||||||
void usage(char *name)
|
void usage(char *name)
|
||||||
{
|
{
|
||||||
printf("Execution utility for Mininet.\n"
|
printf("Execution utility for Mininet\n\n"
|
||||||
"usage: %s [-cdnp]\n"
|
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
|
||||||
"-c: close all file descriptors except stdin/out/error\n"
|
"Options:\n"
|
||||||
"-d: detach from tty by calling setsid()\n"
|
" -c: close all file descriptors except stdin/out/error\n"
|
||||||
"-n: run in new network namespace\n"
|
" -d: detach from tty by calling setsid()\n"
|
||||||
"-p: print ^A + pid\n", name);
|
" -n: run in new network namespace\n"
|
||||||
|
" -p: print ^A + pid\n"
|
||||||
|
" -a pid: attach to pid's network namespace\n"
|
||||||
|
" -g group: add to cgroup\n"
|
||||||
|
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
|
||||||
|
" -v: print version\n",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int setns(int fd, int nstype)
|
||||||
|
{
|
||||||
|
return syscall(308, fd, nstype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate alphanumeric path foo1/bar2/baz */
|
||||||
|
void validate(char *path)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
for (s=path; *s; s++) {
|
||||||
|
if (!isalnum(*s) && *s != '/') {
|
||||||
|
fprintf(stderr, "invalid path: %s\n", path);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add our pid to cgroup */
|
||||||
|
int cgroup(char *gname)
|
||||||
|
{
|
||||||
|
static char path[PATH_MAX];
|
||||||
|
static char *groups[] = {
|
||||||
|
"cpu", "cpuacct", "cpuset", NULL
|
||||||
|
};
|
||||||
|
char **gptr;
|
||||||
|
pid_t pid = getpid();
|
||||||
|
int count = 0;
|
||||||
|
validate(gname);
|
||||||
|
for (gptr = groups; *gptr; gptr++) {
|
||||||
|
FILE *f;
|
||||||
|
snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks",
|
||||||
|
*gptr, gname);
|
||||||
|
f = fopen(path, "w");
|
||||||
|
if (f) {
|
||||||
|
count++;
|
||||||
|
fprintf(f, "%d\n", pid);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!count) {
|
||||||
|
fprintf(stderr, "cgroup: could not add to cgroup %s\n",
|
||||||
|
gname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
int fd;
|
int fd;
|
||||||
|
char path[PATH_MAX];
|
||||||
while ((c = getopt(argc, argv, "+cdnp")) != -1)
|
int nsid;
|
||||||
|
int pid;
|
||||||
|
static struct sched_param sp;
|
||||||
|
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
/* close file descriptors except stdin/out/error */
|
/* close file descriptors except stdin/out/error */
|
||||||
@@ -64,16 +132,48 @@ int main(int argc, char *argv[])
|
|||||||
printf("\001%d\n", getpid());
|
printf("\001%d\n", getpid());
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
break;
|
break;
|
||||||
|
case 'a':
|
||||||
|
/* Attach to pid's network namespace */
|
||||||
|
pid = atoi(optarg);
|
||||||
|
sprintf(path, "/proc/%d/ns/net", pid );
|
||||||
|
nsid = open(path, O_RDONLY);
|
||||||
|
if (nsid < 0) {
|
||||||
|
perror(path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (setns(nsid, 0) != 0) {
|
||||||
|
perror("setns");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
/* Attach to cgroup */
|
||||||
|
cgroup(optarg);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
/* Set RT scheduling priority */
|
||||||
|
sp.sched_priority = atoi(optarg);
|
||||||
|
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
|
||||||
|
perror("sched_setscheduler");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf("%s\n", VERSION);
|
||||||
|
exit(0);
|
||||||
|
case 'h':
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(0);
|
||||||
default:
|
default:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
break;
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
execvp(argv[optind], &argv[optind]);
|
execvp(argv[optind], &argv[optind]);
|
||||||
perror(argv[optind]);
|
perror(argv[optind]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,30 +5,37 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
from os.path import join
|
from os.path import join
|
||||||
|
|
||||||
scripts = [ join( 'bin', filename ) for filename in [
|
# Get version number from source tree
|
||||||
'mn', 'mnexec' ] ]
|
import sys
|
||||||
|
sys.path.append( '.' )
|
||||||
|
from mininet.net import VERSION
|
||||||
|
|
||||||
|
scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ]
|
||||||
|
|
||||||
modname = distname = 'mininet'
|
modname = distname = 'mininet'
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name=distname,
|
name=distname,
|
||||||
version='0.0.0',
|
version=VERSION,
|
||||||
description='Process-based OpenFlow emulator',
|
description='Process-based OpenFlow emulator',
|
||||||
author='Bob Lantz',
|
author='Bob Lantz',
|
||||||
author_email='rlantz@cs.stanford.edu',
|
author_email='rlantz@cs.stanford.edu',
|
||||||
packages=find_packages(exclude='test'),
|
packages=find_packages(exclude='test'),
|
||||||
long_description="""
|
long_description="""
|
||||||
Insert longer description here.
|
Mininet is a network emulator which uses lightweight
|
||||||
""",
|
virtualization to create virtual networks for rapid
|
||||||
|
prototyping of Software-Defined Network (SDN) designs
|
||||||
|
using OpenFlow. http://openflow.org/mininet
|
||||||
|
""",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
"License :: OSI Approved :: BSD License",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 2 - Pre-Alpha",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Topic :: Internet",
|
"Topic :: Internet",
|
||||||
],
|
],
|
||||||
keywords='networking protocol Internet OpenFlow',
|
keywords='networking emulator protocol Internet OpenFlow SDN',
|
||||||
license='unspecified',
|
license='BSD',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'setuptools',
|
'setuptools',
|
||||||
'networkx'
|
'networkx'
|
||||||
|
|||||||
Executable
+94
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Attempt to build debian packages for OVS
|
||||||
|
|
||||||
|
set -e # exit on error
|
||||||
|
set -u # exit on undefined variable
|
||||||
|
|
||||||
|
kvers=`uname -r`
|
||||||
|
ksrc=/lib/modules/$kvers/build
|
||||||
|
dist=`lsb_release -is | tr [A-Z] [a-z]`
|
||||||
|
release=`lsb_release -rs`
|
||||||
|
arch=`uname -m`
|
||||||
|
buildsuffix=-2
|
||||||
|
if [ "$arch" = "i686" ]; then arch=i386; fi
|
||||||
|
if [ "$arch" = "x86_64" ]; then arch=amd64; fi
|
||||||
|
|
||||||
|
overs=1.4.0
|
||||||
|
ovs=openvswitch-$overs
|
||||||
|
ovstgz=$ovs.tar.gz
|
||||||
|
ovsurl=http://openvswitch.org/releases/$ovstgz
|
||||||
|
|
||||||
|
install='sudo apt-get install -y'
|
||||||
|
|
||||||
|
echo "*** Installing debian/ubuntu build system"
|
||||||
|
$install build-essential devscripts ubuntu-dev-tools debhelper dh-make
|
||||||
|
$install diff patch cdbs quilt gnupg fakeroot lintian pbuilder piuparts
|
||||||
|
$install module-assistant
|
||||||
|
|
||||||
|
echo "*** Installing OVS dependencies"
|
||||||
|
$install pkg-config gcc make python-dev libssl-dev libtool
|
||||||
|
$install dkms ipsec-tools
|
||||||
|
|
||||||
|
echo "*** Installing headers for $kvers"
|
||||||
|
$install linux-headers-$kvers
|
||||||
|
|
||||||
|
echo "*** Retrieving OVS source"
|
||||||
|
wget -c $ovsurl
|
||||||
|
tar xzf $ovstgz
|
||||||
|
cd $ovs
|
||||||
|
|
||||||
|
echo "*** Patching OVS source"
|
||||||
|
# Not sure why this fails, but off it goes!
|
||||||
|
sed -i -e 's/dh_strip/# dh_strip/' debian/rules
|
||||||
|
if [ "$release" = "10.04" ]; then
|
||||||
|
# Lucid doesn't seem to have all the packages for ovsdbmonitor
|
||||||
|
echo "*** Patching debian/rules to remove dh_python2"
|
||||||
|
sed -i -e 's/dh_python2/dh_pysupport/' debian/rules
|
||||||
|
echo "*** Not building ovsdbmonitor since it's too hard on 10.04"
|
||||||
|
mv debian/ovsdbmonitor.install debian/ovsdbmonitor.install.backup
|
||||||
|
sed -i -e 's/ovsdbmonitor.install/ovsdbmonitor.install.backup/' Makefile.in
|
||||||
|
else
|
||||||
|
# Install a bag of hurt for ovsdbmonitor
|
||||||
|
$install python-pyside.qtcore pyqt4-dev-tools python-twisted python-twisted-bin \
|
||||||
|
python-twisted-core python-twisted-conch python-anyjson python-zope.interface
|
||||||
|
fi
|
||||||
|
# init script was written to assume that commands complete
|
||||||
|
sed -i -e 's/^set -e/#set -e/' debian/openvswitch-controller.init
|
||||||
|
|
||||||
|
echo "*** Building OVS user packages"
|
||||||
|
opts=--with-linux=/lib/modules/`uname -r`/build
|
||||||
|
fakeroot make -f debian/rules DATAPATH_CONFIGURE_OPTS=$opts binary
|
||||||
|
|
||||||
|
echo "*** Building OVS datapath kernel module package"
|
||||||
|
# Still looking for the "right" way to do this...
|
||||||
|
sudo mkdir -p /usr/src/linux
|
||||||
|
ln -sf _debian/openvswitch.tar.gz .
|
||||||
|
sudo make -f debian/rules.modules KSRC=$ksrc KVERS=$kvers binary-modules
|
||||||
|
|
||||||
|
echo "*** Built the following packages:"
|
||||||
|
cd ~
|
||||||
|
ls -l *deb
|
||||||
|
|
||||||
|
archive=ovs-$overs-core-$dist-$release-$arch$buildsuffix.tar
|
||||||
|
ovsbase='common pki switch brcompat controller datapath-dkms'
|
||||||
|
echo "*** Packing up $ovsbase .debs into:"
|
||||||
|
echo " $archive"
|
||||||
|
pkgs=""
|
||||||
|
for component in $ovsbase; do
|
||||||
|
if echo $component | egrep 'dkms|pki'; then
|
||||||
|
# Architecture-independent packages
|
||||||
|
deb=(openvswitch-${component}_$overs*all.deb)
|
||||||
|
else
|
||||||
|
deb=(openvswitch-${component}_$overs*$arch.deb)
|
||||||
|
fi
|
||||||
|
pkgs="$pkgs $deb"
|
||||||
|
done
|
||||||
|
rm -rf $archive
|
||||||
|
tar cf $archive $pkgs
|
||||||
|
|
||||||
|
echo "*** Contents of archive $archive:"
|
||||||
|
tar tf $archive
|
||||||
|
|
||||||
|
echo "*** Done (hopefully)"
|
||||||
|
|
||||||
+1
-1
@@ -82,7 +82,7 @@ if __name__ == '__main__':
|
|||||||
fixLines( infile.readlines(), outfid )
|
fixLines( infile.readlines(), outfid )
|
||||||
infile.close()
|
infile.close()
|
||||||
os.close( outfid )
|
os.close( outfid )
|
||||||
call( [ 'doxypy.py', outname ] )
|
call( [ 'doxypy', outname ] )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+202
-58
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Mininet install script for Ubuntu (and Debian Lenny)
|
# Mininet install script for Ubuntu (and Debian Lenny)
|
||||||
# Brandon Heller (brandonh@stanford.edu)
|
# Brandon Heller (brandonh@stanford.edu)
|
||||||
|
|
||||||
@@ -16,57 +17,78 @@ KERNEL_LOC=http://www.openflow.org/downloads/mininet
|
|||||||
DIST=Unknown
|
DIST=Unknown
|
||||||
RELEASE=Unknown
|
RELEASE=Unknown
|
||||||
CODENAME=Unknown
|
CODENAME=Unknown
|
||||||
|
ARCH=`uname -m`
|
||||||
|
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi
|
||||||
|
if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi
|
||||||
|
|
||||||
test -e /etc/debian_version && DIST="Debian"
|
test -e /etc/debian_version && DIST="Debian"
|
||||||
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
|
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
|
||||||
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
|
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
|
||||||
sudo apt-get install -y lsb-release
|
install='sudo apt-get -y install'
|
||||||
|
remove='sudo apt-get -y remove'
|
||||||
|
pkginst='sudo dpkg -i'
|
||||||
|
# Prereqs for this script
|
||||||
|
if ! which lsb_release &> /dev/null; then
|
||||||
|
$install lsb-release
|
||||||
|
fi
|
||||||
|
if ! which bc &> /dev/null; then
|
||||||
|
$install bc
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if which lsb_release &> /dev/null; then
|
if which lsb_release &> /dev/null; then
|
||||||
DIST=`lsb_release -is`
|
DIST=`lsb_release -is`
|
||||||
RELEASE=`lsb_release -rs`
|
RELEASE=`lsb_release -rs`
|
||||||
CODENAME=`lsb_release -cs`
|
CODENAME=`lsb_release -cs`
|
||||||
fi
|
fi
|
||||||
echo "Detected Linux distribution: $DIST $RELEASE $CODENAME"
|
echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
|
||||||
|
|
||||||
# Kernel params
|
# Kernel params
|
||||||
|
|
||||||
if [ "$DIST" = "Debian" ]; then
|
if [ "$DIST" = "Ubuntu" ]; then
|
||||||
KERNEL_NAME=2.6.33.1-mininet
|
|
||||||
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
|
||||||
KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
|
||||||
elif [ "$DIST" = "Ubuntu" ]; then
|
|
||||||
if [ "$RELEASE" = "10.04" ]; then
|
if [ "$RELEASE" = "10.04" ]; then
|
||||||
KERNEL_NAME='3.0.0-15-generic'
|
KERNEL_NAME='3.0.0-15-generic'
|
||||||
else
|
else
|
||||||
KERNEL_NAME=`uname -r`
|
KERNEL_NAME=`uname -r`
|
||||||
fi
|
fi
|
||||||
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}
|
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}
|
||||||
|
elif [ "$DIST" = "Debian" ] && [ "$ARCH" = "i386" ] && [ "$CODENAME" = "lenny" ]; then
|
||||||
|
KERNEL_NAME=2.6.33.1-mininet
|
||||||
|
KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
||||||
|
KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb
|
||||||
else
|
else
|
||||||
echo "Install.sh currently only supports Ubuntu and Debian."
|
echo "Install.sh currently only supports Ubuntu and Debian Lenny i386."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# More distribution info
|
||||||
|
DIST_LC=`echo $DIST | tr [A-Z] [a-z]` # as lower case
|
||||||
|
|
||||||
# Kernel Deb pkg to be removed:
|
# Kernel Deb pkg to be removed:
|
||||||
KERNEL_IMAGE_OLD=linux-image-2.6.26-2-686
|
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
|
||||||
|
|
||||||
DRIVERS_DIR=/lib/modules/${KERNEL_NAME}/kernel/drivers/net
|
DRIVERS_DIR=/lib/modules/${KERNEL_NAME}/kernel/drivers/net
|
||||||
|
|
||||||
OVS_RELEASE=v1.2.2
|
OVS_RELEASE=1.4.0
|
||||||
|
OVS_PACKAGE_LOC=https://github.com/downloads/mininet/mininet
|
||||||
|
OVS_BUILDSUFFIX=-ignore # was -2
|
||||||
|
OVS_PACKAGE_NAME=ovs-$OVS_RELEASE-core-$DIST_LC-$RELEASE-$ARCH$OVS_BUILDSUFFIX.tar
|
||||||
OVS_SRC=~/openvswitch
|
OVS_SRC=~/openvswitch
|
||||||
|
OVS_TAG=v$OVS_RELEASE
|
||||||
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
|
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
|
||||||
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
|
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
|
||||||
|
|
||||||
function kernel {
|
function kernel {
|
||||||
echo "Install Mininet-compatible kernel if necessary"
|
echo "Install Mininet-compatible kernel if necessary"
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
if [ "$DIST" = "Debian" ]; then
|
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||||
|
$install linux-image-$KERNEL_NAME
|
||||||
|
elif [ "$DIST" = "Debian" ]; then
|
||||||
# The easy approach: download pre-built linux-image and linux-headers packages:
|
# The easy approach: download pre-built linux-image and linux-headers packages:
|
||||||
wget -c $KERNEL_LOC/$KERNEL_HEADERS
|
wget -c $KERNEL_LOC/$KERNEL_HEADERS
|
||||||
wget -c $KERNEL_LOC/$KERNEL_IMAGE
|
wget -c $KERNEL_LOC/$KERNEL_IMAGE
|
||||||
|
|
||||||
# Install custom linux headers and image:
|
# Install custom linux headers and image:
|
||||||
sudo dpkg -i $KERNEL_IMAGE $KERNEL_HEADERS
|
$pkginst $KERNEL_IMAGE $KERNEL_HEADERS
|
||||||
|
|
||||||
# The next two steps are to work around a bug in newer versions of
|
# The next two steps are to work around a bug in newer versions of
|
||||||
# kernel-package, which fails to add initrd images with the latest kernels.
|
# kernel-package, which fails to add initrd images with the latest kernels.
|
||||||
@@ -83,16 +105,15 @@ function kernel {
|
|||||||
# /boot/grub/menu.lst to set the default to the entry corresponding to the
|
# /boot/grub/menu.lst to set the default to the entry corresponding to the
|
||||||
# kernel you just installed.
|
# kernel you just installed.
|
||||||
fi
|
fi
|
||||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
|
||||||
sudo apt-get -y install linux-image-$KERNEL_NAME
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function kernel_clean {
|
function kernel_clean {
|
||||||
echo "Cleaning kernel..."
|
echo "Cleaning kernel..."
|
||||||
|
|
||||||
# To save disk space, remove previous kernel
|
# To save disk space, remove previous kernel
|
||||||
sudo apt-get -y remove $KERNEL_IMAGE_OLD
|
if ! $remove $KERNEL_IMAGE_OLD; then
|
||||||
|
echo $KERNEL_IMAGE_OLD not installed.
|
||||||
|
fi
|
||||||
|
|
||||||
# Also remove downloaded packages:
|
# Also remove downloaded packages:
|
||||||
rm -f ~/linux-headers-* ~/linux-image-*
|
rm -f ~/linux-headers-* ~/linux-image-*
|
||||||
@@ -101,8 +122,9 @@ function kernel_clean {
|
|||||||
# Install Mininet deps
|
# Install Mininet deps
|
||||||
function mn_deps {
|
function mn_deps {
|
||||||
echo "Installing Mininet dependencies"
|
echo "Installing Mininet dependencies"
|
||||||
sudo aptitude install -y gcc make screen psmisc xterm ssh iperf iproute \
|
$install gcc make screen psmisc xterm ssh iperf iproute \
|
||||||
python-setuptools python-networkx
|
python-setuptools python-networkx cgroup-bin ethtool help2man \
|
||||||
|
pyflakes pylint pep8
|
||||||
|
|
||||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||||
echo "Upgrading networkx to avoid deprecation warning"
|
echo "Upgrading networkx to avoid deprecation warning"
|
||||||
@@ -124,16 +146,14 @@ function mn_deps {
|
|||||||
|
|
||||||
# The following will cause a full OF install, covering:
|
# The following will cause a full OF install, covering:
|
||||||
# -user switch
|
# -user switch
|
||||||
# -dissector
|
|
||||||
# The instructions below are an abbreviated version from
|
# The instructions below are an abbreviated version from
|
||||||
# http://www.openflowswitch.org/wk/index.php/Debian_Install
|
# http://www.openflowswitch.org/wk/index.php/Debian_Install
|
||||||
# ... modified to use Debian Lenny rather than unstable.
|
# ... modified to use Debian Lenny rather than unstable.
|
||||||
function of {
|
function of {
|
||||||
echo "Installing OpenFlow and its tools..."
|
echo "Installing OpenFlow reference implementation..."
|
||||||
|
|
||||||
cd ~/
|
cd ~/
|
||||||
sudo apt-get install -y git-core automake m4 pkg-config libtool \
|
$install git-core autoconf automake autotools-dev pkg-config \
|
||||||
make libc6-dev autoconf autotools-dev gcc
|
make gcc libtool libc6-dev
|
||||||
git clone git://openflowswitch.org/openflow.git
|
git clone git://openflowswitch.org/openflow.git
|
||||||
cd ~/openflow
|
cd ~/openflow
|
||||||
|
|
||||||
@@ -147,8 +167,8 @@ function of {
|
|||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||||
# sent during tests, near link status changes:
|
# sent during tests, near link status changes:
|
||||||
sudo apt-get remove -y avahi-daemon
|
$remove avahi-daemon
|
||||||
|
|
||||||
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||||
if [ "$DIST" = "Ubuntu" ]; then
|
if [ "$DIST" = "Ubuntu" ]; then
|
||||||
@@ -157,6 +177,38 @@ function of {
|
|||||||
BLACKLIST=/etc/modprobe.d/blacklist
|
BLACKLIST=/etc/modprobe.d/blacklist
|
||||||
fi
|
fi
|
||||||
sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||||
|
cd ~
|
||||||
|
}
|
||||||
|
|
||||||
|
function wireshark {
|
||||||
|
echo "Installing Wireshark dissector..."
|
||||||
|
|
||||||
|
sudo apt-get install -y wireshark libgtk2.0-dev
|
||||||
|
|
||||||
|
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" != "10.04" ]; then
|
||||||
|
# Install newer version
|
||||||
|
sudo apt-get install -y scons mercurial libglib2.0-dev
|
||||||
|
sudo apt-get install -y libwiretap-dev libwireshark-dev
|
||||||
|
cd ~
|
||||||
|
hg clone https://bitbucket.org/barnstorm/of-dissector
|
||||||
|
cd of-dissector/src
|
||||||
|
export WIRESHARK=/usr/include/wireshark
|
||||||
|
scons
|
||||||
|
# libwireshark0/ on 11.04; libwireshark1/ on later
|
||||||
|
WSDIR=`ls -d /usr/lib/wireshark/libwireshark* | head -1`
|
||||||
|
WSPLUGDIR=$WSDIR/plugins/
|
||||||
|
sudo cp openflow.so $WSPLUGDIR
|
||||||
|
echo "Copied openflow plugin to $WSPLUGDIR"
|
||||||
|
else
|
||||||
|
# Install older version from reference source
|
||||||
|
cd ~/openflow/utilities/wireshark_dissectors/openflow
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy coloring rules: OF is white-on-blue:
|
||||||
|
mkdir -p ~/.wireshark
|
||||||
|
cp ~/mininet/util/colorfilters ~/.wireshark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -194,42 +246,120 @@ function wireshark {
|
|||||||
|
|
||||||
# Install Open vSwitch
|
# Install Open vSwitch
|
||||||
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
|
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
|
||||||
|
|
||||||
function ovs {
|
function ovs {
|
||||||
echo "Installing Open vSwitch..."
|
echo "Installing Open vSwitch..."
|
||||||
|
|
||||||
if [ "$DIST" = "Debian" ] && [ "$CODENAME" == "lenny" ]; then
|
# Required for module build/dkms install
|
||||||
sudo aptitude -y install pkg-config gcc make git-core python-dev libssl-dev
|
$install $KERNEL_HEADERS
|
||||||
# Install Autoconf 2.63+ backport from Debian Backports repo:
|
|
||||||
# Instructions from http://backports.org/dokuwiki/doku.php?id=instructions
|
ovspresent=0
|
||||||
sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list"
|
|
||||||
sudo apt-get update
|
# First see if we have packages
|
||||||
sudo apt-get -y --force-yes install debian-backports-keyring
|
# XXX wget -c seems to fail from github/amazon s3
|
||||||
sudo apt-get -y --force-yes -t lenny-backports install autoconf
|
cd /tmp
|
||||||
|
if wget $OVS_PACKAGE_LOC/$OVS_PACKAGE_NAME 2> /dev/null; then
|
||||||
|
$install patch dkms fakeroot python-argparse
|
||||||
|
tar xf $OVS_PACKAGE_NAME
|
||||||
|
orig=`tar tf $OVS_PACKAGE_NAME`
|
||||||
|
# Now install packages in reasonable dependency order
|
||||||
|
order='dkms common pki openvswitch-switch brcompat controller'
|
||||||
|
pkgs=""
|
||||||
|
for p in $order; do
|
||||||
|
pkg=`echo "$orig" | grep $p`
|
||||||
|
# Annoyingly, things seem to be missing without this flag
|
||||||
|
$pkginst --force-confmiss $pkg
|
||||||
|
done
|
||||||
|
ovspresent=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DIST" = "Ubuntu" ]; then
|
# Otherwise try distribution's OVS packages
|
||||||
sudo apt-get -y install $KERNEL_HEADERS
|
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 11.10` = 1 ]; then
|
||||||
|
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||||
|
# If you've already installed a datapath, assume you
|
||||||
|
# know what you're doing and don't need dkms datapath.
|
||||||
|
# Otherwise, install it.
|
||||||
|
$install openvswitch-datapath-dkms
|
||||||
|
fi
|
||||||
|
if $install openvswitch-switch openvswitch-controller; then
|
||||||
|
echo "Ignoring error installing openvswitch-controller"
|
||||||
|
fi
|
||||||
|
ovspresent=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Switch can run on its own, but
|
||||||
|
# Mininet should control the controller
|
||||||
|
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||||
|
if sudo service openvswitch-controller stop; then
|
||||||
|
echo "Stopped running controller"
|
||||||
|
fi
|
||||||
|
sudo update-rc.d openvswitch-controller disable
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $ovspresent = 1 ]; then
|
||||||
|
echo "Done (hopefully) installing packages"
|
||||||
|
cd ~
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Otherwise attempt to install from source
|
||||||
|
|
||||||
|
$install pkg-config gcc make python-dev libssl-dev libtool
|
||||||
|
|
||||||
|
if [ "$DIST" = "Debian" ]; then
|
||||||
|
if [ "$CODENAME" = "lenny" ]; then
|
||||||
|
$install git-core
|
||||||
|
# Install Autoconf 2.63+ backport from Debian Backports repo:
|
||||||
|
# Instructions from http://backports.org/dokuwiki/doku.php?id=instructions
|
||||||
|
sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list"
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get -y --force-yes install debian-backports-keyring
|
||||||
|
sudo apt-get -y --force-yes -t lenny-backports install autoconf
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
$install git
|
||||||
|
fi
|
||||||
|
|
||||||
# Install OVS from release
|
# Install OVS from release
|
||||||
cd ~/
|
cd ~/
|
||||||
git clone git://openvswitch.org/openvswitch
|
git clone git://openvswitch.org/openvswitch $OVS_SRC
|
||||||
cd $OVS_SRC
|
cd $OVS_SRC
|
||||||
git checkout $OVS_RELEASE
|
git checkout $OVS_TAG
|
||||||
./boot.sh
|
./boot.sh
|
||||||
BUILDDIR=/lib/modules/${KERNEL_NAME}/build
|
BUILDDIR=/lib/modules/${KERNEL_NAME}/build
|
||||||
if [ ! -e $BUILDDIR ]; then
|
if [ ! -e $BUILDDIR ]; then
|
||||||
echo "Creating build sdirectory $BUILDDIR"
|
echo "Creating build sdirectory $BUILDDIR"
|
||||||
sudo mkdir -p $BUILDDIR
|
sudo mkdir -p $BUILDDIR
|
||||||
fi
|
fi
|
||||||
opts="--with-linux=$BUILDDIR"
|
opts="--with-linux=$BUILDDIR"
|
||||||
mkdir -p $OVS_BUILD
|
mkdir -p $OVS_BUILD
|
||||||
cd $OVS_BUILD
|
cd $OVS_BUILD
|
||||||
../configure $opts
|
../configure $opts
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
# openflowd is deprecated, but for now copy it in
|
|
||||||
sudo cp tests/test-openflowd /usr/local/bin/ovs-openflowd
|
modprobe
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_ovs {
|
||||||
|
pkgs=`dpkg --get-selections | grep openvswitch | awk '{ print $1;}'`
|
||||||
|
echo "Removing existing Open vSwitch packages:"
|
||||||
|
echo $pkgs
|
||||||
|
if ! $remove $pkgs; then
|
||||||
|
echo "Not all packages removed correctly"
|
||||||
|
fi
|
||||||
|
# For some reason this doesn't happen
|
||||||
|
if scripts=`ls /etc/init.d/*openvswitch* 2>/dev/null`; then
|
||||||
|
echo $scripts
|
||||||
|
for s in $scripts; do
|
||||||
|
s=$(basename $s)
|
||||||
|
echo SCRIPT $s
|
||||||
|
sudo service $s stop
|
||||||
|
sudo rm -f /etc/init.d/$s
|
||||||
|
sudo update-rc.d -f $s remove
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo "Done removing OVS"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Install NOX with tutorial files
|
# Install NOX with tutorial files
|
||||||
@@ -237,17 +367,17 @@ function nox {
|
|||||||
echo "Installing NOX w/tutorial files..."
|
echo "Installing NOX w/tutorial files..."
|
||||||
|
|
||||||
# Install NOX deps:
|
# Install NOX deps:
|
||||||
sudo apt-get -y install autoconf automake g++ libtool python python-twisted \
|
$install autoconf automake g++ libtool python python-twisted \
|
||||||
swig libssl-dev make
|
swig libssl-dev make
|
||||||
if [ "$DIST" = "Debian" ]; then
|
if [ "$DIST" = "Debian" ]; then
|
||||||
sudo apt-get -y install libboost1.35-dev
|
$install libboost1.35-dev
|
||||||
elif [ "$DIST" = "Ubuntu" ]; then
|
elif [ "$DIST" = "Ubuntu" ]; then
|
||||||
sudo apt-get -y install python-dev libboost-dev
|
$install python-dev libboost-dev
|
||||||
sudo apt-get -y install libboost-filesystem-dev
|
$install libboost-filesystem-dev
|
||||||
sudo apt-get -y install libboost-test-dev
|
$install libboost-test-dev
|
||||||
fi
|
fi
|
||||||
# Install NOX optional deps:
|
# Install NOX optional deps:
|
||||||
sudo apt-get install -y libsqlite3-dev python-simplejson
|
$install libsqlite3-dev python-simplejson
|
||||||
|
|
||||||
# Fetch NOX destiny
|
# Fetch NOX destiny
|
||||||
cd ~/
|
cd ~/
|
||||||
@@ -259,7 +389,10 @@ function nox {
|
|||||||
|
|
||||||
# Apply patches
|
# Apply patches
|
||||||
git checkout -b tutorial-destiny
|
git checkout -b tutorial-destiny
|
||||||
git am ~/mininet/util/nox-patches/*.patch
|
git am ~/mininet/util/nox-patches/*tutorial-port-nox-destiny*.patch
|
||||||
|
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
|
||||||
|
git am ~/mininet/util/nox-patches/*nox-ubuntu12-hacks.patch
|
||||||
|
fi
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
./boot.sh
|
./boot.sh
|
||||||
@@ -277,12 +410,19 @@ function nox {
|
|||||||
#./nox_core -v -i ptcp:
|
#./nox_core -v -i ptcp:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# "Install" POX
|
||||||
|
function pox {
|
||||||
|
echo "Installing POX into $HOME/pox..."
|
||||||
|
cd ~
|
||||||
|
git clone https://github.com/noxrepo/pox.git
|
||||||
|
}
|
||||||
|
|
||||||
# Install OFtest
|
# Install OFtest
|
||||||
function oftest {
|
function oftest {
|
||||||
echo "Installing oftest..."
|
echo "Installing oftest..."
|
||||||
|
|
||||||
# Install deps:
|
# Install deps:
|
||||||
sudo apt-get install -y tcpdump python-scapy
|
$install tcpdump python-scapy
|
||||||
|
|
||||||
# Install oftest:
|
# Install oftest:
|
||||||
cd ~/
|
cd ~/
|
||||||
@@ -296,7 +436,7 @@ function oftest {
|
|||||||
function cbench {
|
function cbench {
|
||||||
echo "Installing cbench..."
|
echo "Installing cbench..."
|
||||||
|
|
||||||
sudo apt-get install -y libsnmp-dev libpcap-dev libconfig-dev
|
$install libsnmp-dev libpcap-dev libconfig-dev
|
||||||
cd ~/
|
cd ~/
|
||||||
git clone git://openflow.org/oflops.git
|
git clone git://openflow.org/oflops.git
|
||||||
cd oflops
|
cd oflops
|
||||||
@@ -315,13 +455,13 @@ function other {
|
|||||||
|
|
||||||
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
|
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
|
||||||
# a graphical git history viewer.
|
# a graphical git history viewer.
|
||||||
sudo apt-get install -y tcpdump tshark gitk
|
$install tcpdump tshark gitk
|
||||||
|
|
||||||
# Install common text editors
|
# Install common text editors
|
||||||
sudo apt-get install -y vim nano emacs
|
$install vim nano emacs
|
||||||
|
|
||||||
# Install NTP
|
# Install NTP
|
||||||
sudo apt-get install -y ntp
|
$install ntp
|
||||||
|
|
||||||
# Set git to colorize everything.
|
# Set git to colorize everything.
|
||||||
git config --global color.diff auto
|
git config --global color.diff auto
|
||||||
@@ -358,8 +498,9 @@ function all {
|
|||||||
of
|
of
|
||||||
wireshark
|
wireshark
|
||||||
ovs
|
ovs
|
||||||
modprobe
|
# NOX-classic is deprecated, but you can install it manually if desired.
|
||||||
nox
|
# nox
|
||||||
|
pox
|
||||||
oftest
|
oftest
|
||||||
cbench
|
cbench
|
||||||
other
|
other
|
||||||
@@ -412,8 +553,9 @@ function usage {
|
|||||||
printf -- ' -f: install open(F)low\n' >&2
|
printf -- ' -f: install open(F)low\n' >&2
|
||||||
printf -- ' -h: print this (H)elp message\n' >&2
|
printf -- ' -h: print this (H)elp message\n' >&2
|
||||||
printf -- ' -k: install new (K)ernel\n' >&2
|
printf -- ' -k: install new (K)ernel\n' >&2
|
||||||
printf -- ' -m: install Open vSwitch kernel (M)odule\n' >&2
|
printf -- ' -m: install Open vSwitch kernel (M)odule from source dir\n' >&2
|
||||||
printf -- ' -n: install mini(N)et dependencies + core files\n' >&2
|
printf -- ' -n: install mini(N)et dependencies + core files\n' >&2
|
||||||
|
printf -- ' -r: remove existing Open vSwitch packages\n' >&2
|
||||||
printf -- ' -t: install o(T)her stuff\n' >&2
|
printf -- ' -t: install o(T)her stuff\n' >&2
|
||||||
printf -- ' -v: install open (V)switch\n' >&2
|
printf -- ' -v: install open (V)switch\n' >&2
|
||||||
printf -- ' -w: install OpenFlow (w)ireshark dissector\n' >&2
|
printf -- ' -w: install OpenFlow (w)ireshark dissector\n' >&2
|
||||||
@@ -427,7 +569,7 @@ if [ $# -eq 0 ]
|
|||||||
then
|
then
|
||||||
all
|
all
|
||||||
else
|
else
|
||||||
while getopts 'abcdfhkmntvwx' OPTION
|
while getopts 'abcdfhkmnprtvwx' OPTION
|
||||||
do
|
do
|
||||||
case $OPTION in
|
case $OPTION in
|
||||||
a) all;;
|
a) all;;
|
||||||
@@ -439,6 +581,8 @@ else
|
|||||||
k) kernel;;
|
k) kernel;;
|
||||||
m) modprobe;;
|
m) modprobe;;
|
||||||
n) mn_deps;;
|
n) mn_deps;;
|
||||||
|
p) pox;;
|
||||||
|
r) remove_ovs;;
|
||||||
t) other;;
|
t) other;;
|
||||||
v) ovs;;
|
v) ovs;;
|
||||||
w) wireshark;;
|
w) wireshark;;
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
From 166693d7cb640d4a41251b87e92c52d9c688196b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Bob Lantz <rlantz@cs.stanford.edu>
|
||||||
|
Date: Mon, 14 May 2012 15:30:44 -0700
|
||||||
|
Subject: [PATCH] Hacks to get NOX classic/destiny to compile under Ubuntu
|
||||||
|
12.04
|
||||||
|
|
||||||
|
Thanks to Srinivasu R. Kanduru for the initial patch.
|
||||||
|
|
||||||
|
Apologies for the hacks - it is my hope that this will be fixed
|
||||||
|
upstream eventually.
|
||||||
|
|
||||||
|
---
|
||||||
|
config/ac_pkg_swig.m4 | 7 ++++---
|
||||||
|
src/Make.vars | 2 +-
|
||||||
|
src/nox/coreapps/pyrt/deferredcallback.cc | 2 +-
|
||||||
|
src/nox/coreapps/pyrt/pyglue.cc | 2 +-
|
||||||
|
src/nox/coreapps/pyrt/pyrt.cc | 2 +-
|
||||||
|
src/nox/netapps/authenticator/auth.i | 2 ++
|
||||||
|
src/nox/netapps/authenticator/flow_util.i | 1 +
|
||||||
|
src/nox/netapps/routing/routing.i | 2 ++
|
||||||
|
.../switch_management/pyswitch_management.i | 2 ++
|
||||||
|
src/nox/netapps/tests/tests.cc | 2 +-
|
||||||
|
src/nox/netapps/topology/pytopology.i | 2 ++
|
||||||
|
11 files changed, 18 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/config/ac_pkg_swig.m4 b/config/ac_pkg_swig.m4
|
||||||
|
index d12556e..9b608f2 100644
|
||||||
|
--- a/config/ac_pkg_swig.m4
|
||||||
|
+++ b/config/ac_pkg_swig.m4
|
||||||
|
@@ -78,9 +78,10 @@ AC_DEFUN([AC_PROG_SWIG],[
|
||||||
|
if test -z "$available_patch" ; then
|
||||||
|
[available_patch=0]
|
||||||
|
fi
|
||||||
|
- if test $available_major -ne $required_major \
|
||||||
|
- -o $available_minor -ne $required_minor \
|
||||||
|
- -o $available_patch -lt $required_patch ; then
|
||||||
|
+ major_done=`test $available_major -gt $required_major`
|
||||||
|
+ minor_done=`test $available_minor -gt $required_minor`
|
||||||
|
+ if test !$major_done -a !$minor_done \
|
||||||
|
+ -a $available_patch -lt $required_patch ; then
|
||||||
|
AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org])
|
||||||
|
SWIG=''
|
||||||
|
else
|
||||||
|
diff --git a/src/Make.vars b/src/Make.vars
|
||||||
|
index d70d6aa..93b2879 100644
|
||||||
|
--- a/src/Make.vars
|
||||||
|
+++ b/src/Make.vars
|
||||||
|
@@ -53,7 +53,7 @@ AM_LDFLAGS += -export-dynamic
|
||||||
|
endif
|
||||||
|
|
||||||
|
# set python runtimefiles to be installed in the same directory as pkg
|
||||||
|
-pkglib_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||||
|
+pkgdata_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||||
|
BUILT_SOURCES = $(NOX_PYBUILDFILES)
|
||||||
|
|
||||||
|
# Runtime-files build and clean rules
|
||||||
|
diff --git a/src/nox/coreapps/pyrt/deferredcallback.cc b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||||
|
index 3a40fa7..111a586 100644
|
||||||
|
--- a/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||||
|
+++ b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||||
|
@@ -69,7 +69,7 @@ DeferredCallback::get_instance(const Callback& c)
|
||||||
|
DeferredCallback* cb = new DeferredCallback(c);
|
||||||
|
|
||||||
|
// flag as used in *_wrap.cc....correct?
|
||||||
|
- return SWIG_Python_NewPointerObj(cb, s, SWIG_POINTER_OWN | 0);
|
||||||
|
+ return SWIG_Python_NewPointerObj(m, cb, s, SWIG_POINTER_OWN | 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
diff --git a/src/nox/coreapps/pyrt/pyglue.cc b/src/nox/coreapps/pyrt/pyglue.cc
|
||||||
|
index 48b9716..317fd04 100644
|
||||||
|
--- a/src/nox/coreapps/pyrt/pyglue.cc
|
||||||
|
+++ b/src/nox/coreapps/pyrt/pyglue.cc
|
||||||
|
@@ -874,7 +874,7 @@ to_python(const Flow& flow)
|
||||||
|
if (!s) {
|
||||||
|
throw std::runtime_error("Could not find Flow SWIG type_info");
|
||||||
|
}
|
||||||
|
- return SWIG_Python_NewPointerObj(f, s, SWIG_POINTER_OWN | 0);
|
||||||
|
+ return SWIG_Python_NewPointerObj(m, f, s, SWIG_POINTER_OWN | 0);
|
||||||
|
|
||||||
|
// PyObject* dict = PyDict_New();
|
||||||
|
// if (!dict) {
|
||||||
|
diff --git a/src/nox/coreapps/pyrt/pyrt.cc b/src/nox/coreapps/pyrt/pyrt.cc
|
||||||
|
index fbda461..8ec05d6 100644
|
||||||
|
--- a/src/nox/coreapps/pyrt/pyrt.cc
|
||||||
|
+++ b/src/nox/coreapps/pyrt/pyrt.cc
|
||||||
|
@@ -776,7 +776,7 @@ Python_event_manager::create_python_context(const Context* ctxt,
|
||||||
|
pretty_print_python_exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||||
|
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||||
|
Py_INCREF(pyctxt); // XXX needed?
|
||||||
|
|
||||||
|
//Py_DECREF(m);
|
||||||
|
diff --git a/src/nox/netapps/authenticator/auth.i b/src/nox/netapps/authenticator/auth.i
|
||||||
|
index 1de1a17..bfa04e2 100644
|
||||||
|
--- a/src/nox/netapps/authenticator/auth.i
|
||||||
|
+++ b/src/nox/netapps/authenticator/auth.i
|
||||||
|
@@ -18,6 +18,8 @@
|
||||||
|
|
||||||
|
%module "nox.netapps.authenticator.pyauth"
|
||||||
|
|
||||||
|
+// Hack to get it to compile -BL
|
||||||
|
+%include "std_list.i"
|
||||||
|
%{
|
||||||
|
#include "core_events.hh"
|
||||||
|
#include "pyrt/pycontext.hh"
|
||||||
|
diff --git a/src/nox/netapps/authenticator/flow_util.i b/src/nox/netapps/authenticator/flow_util.i
|
||||||
|
index f67c3ef..2a314e2 100644
|
||||||
|
--- a/src/nox/netapps/authenticator/flow_util.i
|
||||||
|
+++ b/src/nox/netapps/authenticator/flow_util.i
|
||||||
|
@@ -32,6 +32,7 @@ using namespace vigil::applications;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%include "common-defs.i"
|
||||||
|
+%include "std_list.i"
|
||||||
|
|
||||||
|
%import "netinet/netinet.i"
|
||||||
|
%import "pyrt/event.i"
|
||||||
|
diff --git a/src/nox/netapps/routing/routing.i b/src/nox/netapps/routing/routing.i
|
||||||
|
index 44ccb3d..f9221a2 100644
|
||||||
|
--- a/src/nox/netapps/routing/routing.i
|
||||||
|
+++ b/src/nox/netapps/routing/routing.i
|
||||||
|
@@ -17,6 +17,8 @@
|
||||||
|
*/
|
||||||
|
%module "nox.netapps.routing.pyrouting"
|
||||||
|
|
||||||
|
+// Hack to get it to compile -BL
|
||||||
|
+%include "std_list.i"
|
||||||
|
%{
|
||||||
|
#include "pyrouting.hh"
|
||||||
|
#include "routing.hh"
|
||||||
|
diff --git a/src/nox/netapps/switch_management/pyswitch_management.i b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||||
|
index 72bfed4..ad2c90d 100644
|
||||||
|
--- a/src/nox/netapps/switch_management/pyswitch_management.i
|
||||||
|
+++ b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||||
|
@@ -18,6 +18,8 @@
|
||||||
|
|
||||||
|
%module "nox.netapps.pyswitch_management"
|
||||||
|
|
||||||
|
+// Hack to get it to compile -BL
|
||||||
|
+%include "std_list.i"
|
||||||
|
%{
|
||||||
|
#include "switch_management_proxy.hh"
|
||||||
|
#include "pyrt/pycontext.hh"
|
||||||
|
diff --git a/src/nox/netapps/tests/tests.cc b/src/nox/netapps/tests/tests.cc
|
||||||
|
index 20e900d..f027028 100644
|
||||||
|
--- a/src/nox/netapps/tests/tests.cc
|
||||||
|
+++ b/src/nox/netapps/tests/tests.cc
|
||||||
|
@@ -306,7 +306,7 @@ private:
|
||||||
|
throw runtime_error("Could not find PyContext SWIG type_info.");
|
||||||
|
}
|
||||||
|
|
||||||
|
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||||
|
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||||
|
assert(pyctxt);
|
||||||
|
|
||||||
|
Py_DECREF(m);
|
||||||
|
diff --git a/src/nox/netapps/topology/pytopology.i b/src/nox/netapps/topology/pytopology.i
|
||||||
|
index 94a9f4b..7a8cd94 100644
|
||||||
|
--- a/src/nox/netapps/topology/pytopology.i
|
||||||
|
+++ b/src/nox/netapps/topology/pytopology.i
|
||||||
|
@@ -18,6 +18,8 @@
|
||||||
|
|
||||||
|
%module "nox.netapps.topology"
|
||||||
|
|
||||||
|
+// Hack to get it to compile -BL
|
||||||
|
+%include "std_list.i"
|
||||||
|
%{
|
||||||
|
#include "pytopology.hh"
|
||||||
|
#include "pyrt/pycontext.hh"
|
||||||
|
--
|
||||||
|
1.7.5.4
|
||||||
|
|
||||||
@@ -1 +1,2 @@
|
|||||||
This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
0001: This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||||
|
0002: This patch hacks nox-destiny to compile on Ubuntu 12.04.
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
obj-m = sch_htb.o
|
||||||
|
KVERSION = $(shell uname -r)
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
||||||
|
install:
|
||||||
|
test -e /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak || mv /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak
|
||||||
|
cp sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko
|
||||||
|
rmmod sch_htb
|
||||||
|
modprobe sch_htb
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Modified sch_htb implementation with ofbuf support.
|
||||||
|
|
||||||
|
To compile, just type make. To use this module instead
|
||||||
|
of regular sch_htb, do:
|
||||||
|
|
||||||
|
0. make
|
||||||
|
1. rmmod sch_htb
|
||||||
|
2. insmod ./sch_htb.ko
|
||||||
|
|
||||||
|
To revert, just rmmod sch_htb.
|
||||||
File diff suppressed because it is too large
Load Diff
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
from subprocess import check_output as co
|
||||||
|
from sys import exit
|
||||||
|
|
||||||
|
# Actually run bin/mn rather than importing via python path
|
||||||
|
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
|
||||||
|
version = version.strip()
|
||||||
|
|
||||||
|
# Find all Mininet path references
|
||||||
|
lines = co( "grep -or 'Mininet \w\.\w\.\w\w*' *", shell=True )
|
||||||
|
|
||||||
|
error = False
|
||||||
|
|
||||||
|
for line in lines.split( '\n' ):
|
||||||
|
if line and 'Binary' not in line:
|
||||||
|
fname, fversion = line.split( ':' )
|
||||||
|
if version != fversion:
|
||||||
|
print "%s: incorrect version '%s' (should be '%s')" % (
|
||||||
|
fname, fversion, version )
|
||||||
|
error = True
|
||||||
|
|
||||||
|
if error:
|
||||||
|
exit( 1 )
|
||||||
Reference in New Issue
Block a user