Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8137a9219 | |||
| 891ea4854d | |||
| 2e0e2d97ec | |||
| 71b3128122 | |||
| 5ca1fb7e0f | |||
| 18a0b7e4ac | |||
| e27673aeec | |||
| 035d4d20fe | |||
| 550ee2469e | |||
| d8d9035242 | |||
| f1aca0d9a6 |
@@ -1 +0,0 @@
|
||||
*.py diff=python
|
||||
+2
-8
@@ -1,14 +1,8 @@
|
||||
mnexec
|
||||
*.pyc
|
||||
*~
|
||||
*.1
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
\#*\#
|
||||
mininet.egg-info
|
||||
build
|
||||
dist
|
||||
doc/html
|
||||
doc/latex
|
||||
trunk
|
||||
build/*
|
||||
dist/*
|
||||
|
||||
|
||||
@@ -1,123 +1,273 @@
|
||||
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.0.0
|
||||
Mininet 1.0.0
|
||||
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
pre-built VM image, and 2) native installation on Ubuntu. You can also
|
||||
easily create your own Mininet VM image (4).
|
||||
|
||||
The supported installation methods for Mininet are 1) using
|
||||
a pre-built VM image, and 2) native installation on Ubuntu or Debian.
|
||||
(Other distributions may be supported in the future - if you would
|
||||
like to contribute an installation script, we would welcome it!)
|
||||
|
||||
1. Easiest "installation" - use our pre-built VM image!
|
||||
1. Easiest "install" - use our pre-built VM image!
|
||||
|
||||
The easiest way to get Mininet running is to start with one of our
|
||||
pre-built virtual machine images from <http://openflow.org/mininet>
|
||||
The easiest way to get Mininet running is to start with one of our pre-built
|
||||
virtual machine images from http://openflow.org/mininet
|
||||
|
||||
Boot up the VM image, log in, and follow the instructions on the
|
||||
Mininet web site.
|
||||
Boot up the VM image, log in, and follow the instructions on the wiki page.
|
||||
|
||||
One advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
An additional advantage of using the VM image is that it doesn't mess with
|
||||
your native OS installation or damage it in any way.
|
||||
|
||||
Although a single Mininet instance can simulate multiple networks
|
||||
with multiple controllers, only one Mininet instance may currently
|
||||
be run at a time, and Mininet requires root access in the machine
|
||||
it's running on. Therefore, if you have a multiuser system, you
|
||||
may wish to consider running Mininet in a VM.
|
||||
2. Native installation (experimental!) for Ubuntu 10.04 LTS
|
||||
|
||||
2. Next-easiest option: use our Ubuntu package!
|
||||
If you are running Ubuntu 10.04 LTS (or possibly Debian 5), you may be
|
||||
able to use our handy install.sh script, which is in mininet/util.
|
||||
|
||||
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
|
||||
12.10+:
|
||||
WARNING: USE AT YOUR OWN RISK!
|
||||
|
||||
sudo apt-get install mininet
|
||||
install.sh is a bit intrusive and may possibly damage your OS and/or
|
||||
home directory, by creating/modifying several directories such as
|
||||
mininet, openflow, openvswitch and noxcore. Although we hope it won't
|
||||
do anything completely terrible, you may want to look at the script
|
||||
before you run it, and you should make sure your system and home
|
||||
directory are backed up just in case!
|
||||
|
||||
Note: if you are upgrading from an older version of Mininet, make
|
||||
sure you remove the old OVS from `/usr/local`:
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
you may use
|
||||
|
||||
sudo rm /usr/local/bin/ovs*
|
||||
sudo rm /usr/local/sbin/ovs*
|
||||
$ mininet/util/install.sh
|
||||
|
||||
3. Native installation from source on Ubuntu 11.10+
|
||||
This takes about 20-30 minutes.
|
||||
|
||||
If you're reading this, you've probably already done so, but the
|
||||
command to download the Mininet source code is:
|
||||
Alternately, you can install just the pieces you need.
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
We recommend the following steps, in order:
|
||||
|
||||
If you are running Ubuntu, you may be able to use our handy
|
||||
`install.sh` script, which is in `mininet/util`.
|
||||
[a) On Debian 5, first install a Mininet-compatible kernel:
|
||||
$ mininet/util/install.sh -k
|
||||
Reboot and run 'uname -r' to make sure you're running the new kernel.]
|
||||
|
||||
*WARNING: USE AT YOUR OWN RISK!*
|
||||
b) Install mininet and its dependencies:
|
||||
$ mininet/util/install.sh -n
|
||||
|
||||
c) Install OpenFlow 1.0 and associated useful software
|
||||
$ mininet/util/install.sh -f
|
||||
|
||||
`install.sh` is a bit intrusive and may possibly damage your OS
|
||||
and/or home directory, by creating/modifying several directories
|
||||
such as `mininet`, `openflow`, `oftest`, `pox`, or `noxcosre`.
|
||||
Although we hope it won't do anything completely terrible, you may
|
||||
want to look at the script before you run it, and you should make
|
||||
sure your system and home directory are backed up just in case!
|
||||
d) Install Open vSwitch and its kernel module
|
||||
$ mininet/util/install.sh -vm
|
||||
|
||||
To install Mininet itself, the OpenFlow reference implementation, and
|
||||
Open vSwitch, you may use:
|
||||
e) If you wish to install the version of NOX we use in the tutorial:
|
||||
$ mininet/util/install.sh -x
|
||||
|
||||
mininet/util/install.sh -fnv
|
||||
Note: NOX development is progressing over time, so after you complete
|
||||
the tutorial you may wish to install the latest and greatest NOX from
|
||||
noxrepo.org.
|
||||
|
||||
This should be reasonably quick, and the following command should
|
||||
work after the installation:
|
||||
Good luck! Some additional installation notes are provided below, for
|
||||
the brave and/or Linux-savvy, or those who are trying to understand what
|
||||
is installed and why.
|
||||
|
||||
sudo mn --test pingall
|
||||
p.s. Note that only one instance of Mininet is currently supported on a single
|
||||
machine - that's one reason we recommend using a VM to run it.
|
||||
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
including POX, the OpenFlow WireShark dissector, the `oftest`
|
||||
framework, and other potentially useful software (and to add some
|
||||
stuff to `/etc/sysctl.conf` which may or may not be useful) you may
|
||||
use:
|
||||
---
|
||||
|
||||
mininet/util/install.sh -a
|
||||
Mininet Manual Installation Notes
|
||||
|
||||
This takes about 4 minutes on our test system.
|
||||
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.)
|
||||
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||
If you wish to try to create a VM to run Mininet, you may also wish
|
||||
to look at the Wiki page:
|
||||
|
||||
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
|
||||
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
|
||||
http://openflow.org/foswiki/bin/view/OpenFlow/MininetVMCreationNotes
|
||||
|
||||
wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh
|
||||
time install-mininet-vm.sh
|
||||
0. Obtaining Mininet
|
||||
|
||||
Finally, verify that Mininet is installed and working in the VM:
|
||||
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
|
||||
|
||||
sudo mn --test pingall
|
||||
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
|
||||
|
||||
5. Installation on other Linux distributions
|
||||
To install Mininet itself, with root privileges:
|
||||
|
||||
# cd mininet
|
||||
# make install
|
||||
|
||||
Although we don't support other Linux distributions directly, it
|
||||
should be possible to install and run Mininet with some degree of
|
||||
manual effort.
|
||||
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
|
||||
|
||||
In general, you must have:
|
||||
2. Installation script for Ubuntu/Debian Lenny
|
||||
|
||||
* A Linux kernel compiled with network namespace support enabled
|
||||
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.
|
||||
|
||||
* 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.
|
||||
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
|
||||
|
||||
* Python, `bash`, `ping`, `iperf`, etc.`
|
||||
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.
|
||||
|
||||
* Root privileges (required for network device access)
|
||||
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.
|
||||
|
||||
We encourage contribution of patches to the `install.sh` script to
|
||||
support other Linux distributions.
|
||||
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!
|
||||
|
||||
Mininet Team
|
||||
|
||||
---
|
||||
|
||||
Historical information on OpenFlow 0.8.9 and the reference kernel module:
|
||||
|
||||
The kernel reference implementation has been deprecated, but it may
|
||||
be possible to get it work with Mininet.
|
||||
|
||||
To switch to the most recent OpenFlow 0.8.9 release branch (the most
|
||||
recent one with full NOX support and kernel datapath support) in your
|
||||
OpenFlow git tree:
|
||||
|
||||
git checkout -b release/0.8.9 remotes/origin/release/0.8.9
|
||||
|
||||
A patch to enable datapath.c to compile with recent kernels
|
||||
is included in util/openflow-patches/datapath.patch.
|
||||
|
||||
In OpenFlow 1.0, switch port numbering starts at 1 (for better or for worse.)
|
||||
To run with previous versions of OpenFlow, it may be necessary
|
||||
to change SWITCH_PORT_BASE from 1 to 0 in node.py.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
Mininet 2.0.0 License
|
||||
Mininet 1.0.0 License
|
||||
|
||||
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
|
||||
Copyright (c) 2009-2011 Bob Lantz and Brandon Heller
|
||||
|
||||
We are making Mininet available for public use and benefit with the
|
||||
expectation that others will use, modify and enhance the Software and
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
MININET = mininet/*.py
|
||||
TEST = mininet/test/*.py
|
||||
EXAMPLES = examples/*.py
|
||||
MN = bin/mn
|
||||
BIN = $(MN)
|
||||
BIN = bin/mn
|
||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||
MNEXEC = mnexec
|
||||
MANPAGES = mn.1 mnexec.1
|
||||
P8IGN = E251,E201,E302,E202
|
||||
BINDIR = /usr/bin
|
||||
MANDIR = /usr/share/man/man1
|
||||
DOCDIRS = doc/html doc/latex
|
||||
PDF = doc/latex/refman.pdf
|
||||
|
||||
all: codecheck test
|
||||
|
||||
clean:
|
||||
rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS)
|
||||
rm -rf build dist *.egg-info *.pyc $(MNEXEC)
|
||||
|
||||
codecheck: $(PYSRC)
|
||||
-echo "Running code check"
|
||||
util/versioncheck.py
|
||||
pyflakes $(PYSRC)
|
||||
pylint --rcfile=.pylint $(PYSRC)
|
||||
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
|
||||
@@ -32,34 +25,15 @@ errcheck: $(PYSRC)
|
||||
test: $(MININET) $(TEST)
|
||||
-echo "Running tests"
|
||||
mininet/test/test_nets.py
|
||||
mininet/test/test_hifi.py
|
||||
|
||||
mnexec: mnexec.c $(MN) mininet/net.py
|
||||
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
|
||||
|
||||
install: $(MNEXEC) $(MANPAGES)
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
install: $(MNEXEC)
|
||||
install $(MNEXEC) /usr/local/bin/
|
||||
python setup.py install
|
||||
|
||||
develop: $(MNEXEC) $(MANPAGES)
|
||||
# Perhaps we should link these as well
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
develop: $(MNEXEC)
|
||||
install $(MNEXEC) /usr/local/bin/
|
||||
python setup.py develop
|
||||
|
||||
man: $(MANPAGES)
|
||||
doc:
|
||||
doxygen doxygen.cfg
|
||||
|
||||
mn.1: $(MN)
|
||||
PYTHONPATH=. help2man -N -n "create a Mininet network." \
|
||||
--no-discard-stderr $< -o $@
|
||||
|
||||
mnexec.1: mnexec
|
||||
help2man -N -n "execution utility for Mininet." \
|
||||
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
|
||||
|
||||
.PHONY: doc
|
||||
|
||||
doc: man
|
||||
doxygen doc/doxygen.cfg
|
||||
make -C doc/latex
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
Mininet: A Simple Virtual Testbed for OpenFlow/SDN
|
||||
or
|
||||
How to Squeeze a 1024-node OpenFlow Network onto your Laptop
|
||||
|
||||
Mininet 1.0.0
|
||||
|
||||
---
|
||||
Welcome to Mininet!
|
||||
|
||||
Mininet creates OpenFlow test networks by using process-based
|
||||
virtualization and network namespaces.
|
||||
|
||||
Simulated hosts (as well as switches and controllers with the user
|
||||
datapath) are created as processes in separate network namespaces. This
|
||||
allows a complete OpenFlow network to be simulated on top of a single
|
||||
Linux kernel.
|
||||
|
||||
Mininet may be invoked directly from the command line, and also provides a
|
||||
handy Python API for creating networks of varying sizes and topologies.
|
||||
|
||||
Mininet is currently in *limited alpha release*. We encourage you to
|
||||
experiment with it and hope that you will provide us with feedback on
|
||||
features, documentation, and how you're using it. We plan to make it
|
||||
available publicly via a GPL or BSD license (probably in April), but please
|
||||
don't distribute the code or URLs yet! The feedback you provide will help
|
||||
us improve Mininet for general release.
|
||||
|
||||
In order to run Mininet, you must have:
|
||||
|
||||
* A Linux 2.6.26 or greater kernel compiled with network namespace support
|
||||
enabled (see INSTALL for additional information.)
|
||||
|
||||
* An OpenFlow implementation (either the reference user or kernel
|
||||
space implementations, or Open vSwitch.) Appropriate kernel modules
|
||||
(e.g. tun and ofdatapath for the reference kernel implementation) must
|
||||
be loaded.
|
||||
|
||||
* Python, bash, ping, iperf, etc.
|
||||
|
||||
* Root privileges (required for network device access)
|
||||
|
||||
Currently Mininet includes:
|
||||
|
||||
- A simple node infrastructure (Host, Switch, Controller classes) for
|
||||
creating virtual OpenFlow networks
|
||||
|
||||
- A simple network infrastructure (Mininet class) supporting parametrized
|
||||
topologies (Topo subclasses.) For example, a tree network may be created
|
||||
with the command
|
||||
|
||||
# mn --topo tree,depth=2,fanout=3
|
||||
|
||||
- Basic tests, including connectivity (ping) and bandwidth (iperf)
|
||||
|
||||
- A command-line interface (CLI class) which provides useful
|
||||
diagnostic commands, as well as the ability to send a command to a
|
||||
node. For example,
|
||||
|
||||
mininet> h11 ifconfig -a
|
||||
|
||||
tells host h11 to run the command 'ifconfig -a'
|
||||
|
||||
- A 'cleanup' command to get rid of junk (interfaces, processes, files in
|
||||
/tmp, etc.) which might be left around by Mininet or Linux. Try this if
|
||||
things stop working!
|
||||
|
||||
# mn -c
|
||||
|
||||
- Examples (in the examples/ directory) to help you get started.
|
||||
|
||||
Batteries are not included (yet!)
|
||||
|
||||
However, some preliminary installation notes are included in the INSTALL
|
||||
file.
|
||||
|
||||
Additionally, much useful information, including a Mininet tutorial,
|
||||
is available on the Mininet wiki:
|
||||
|
||||
http://openflow.org/mininet
|
||||
|
||||
Enjoy, and good luck!
|
||||
|
||||
---
|
||||
Bob Lantz
|
||||
rlantz@cs.stanford.edu
|
||||
@@ -1,134 +0,0 @@
|
||||
|
||||
Mininet: Rapid Prototyping for Software Defined Networks
|
||||
========================================================
|
||||
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
Version 2.0.0
|
||||
|
||||
### What is Mininet?
|
||||
|
||||
Mininet emulates a complete network of hosts, links, and switches
|
||||
on a single machine. To create a sample two-host, one-switch network,
|
||||
just run:
|
||||
|
||||
`sudo mn`
|
||||
|
||||
Mininet is useful for interactive development, testing, and demos,
|
||||
especially those using OpenFlow and SDN. OpenFlow-based network
|
||||
controllers prototyped in Mininet can usually be transferred to
|
||||
hardware with minimal changes for full line-rate execution.
|
||||
|
||||
### How does it work?
|
||||
|
||||
Mininet creates virtual networks using process-based virtualization
|
||||
and network namespaces - features that are available in recent Linux
|
||||
kernels. In Mininet, hosts are emulated as `bash` processes running in
|
||||
a network namespace, so any code that would normally run on a Linux
|
||||
server (like a web server or client program) should run just fine
|
||||
within a Mininet "Host". The Mininet "Host" will have its own private
|
||||
network interface and can only see its own processes. Switches in
|
||||
Mininet are software-based switches like Open vSwitch or the OpenFlow
|
||||
reference switch. Links are virtual ethernet pairs, which live in the
|
||||
Linux kernel and connect our emulated switches to emulated hosts
|
||||
(processes).
|
||||
|
||||
### Features
|
||||
|
||||
Mininet includes:
|
||||
|
||||
* A command-line launcher (`mn`) to instantiate networks.
|
||||
|
||||
* A handy Python API for creating networks of varying sizes and
|
||||
topologies.
|
||||
|
||||
* Examples (in the `examples/` directory) to help you get started.
|
||||
|
||||
* Full API documentation via Python `help()` docstrings, as well as
|
||||
the ability to generate PDF/HTML documentation with `make doc`.
|
||||
|
||||
* Parametrized topologies (`Topo` subclasses) using the Mininet
|
||||
object. For example, a tree network may be created with the
|
||||
command:
|
||||
|
||||
`mn --topo tree,depth=2,fanout=3`
|
||||
|
||||
* A command-line interface (`CLI` class) which provides useful
|
||||
diagnostic commands (like `iperf` and `ping`), as well as the
|
||||
ability to run a command to a node. For example,
|
||||
|
||||
`mininet> h11 ifconfig -a`
|
||||
|
||||
tells host h11 to run the command `ifconfig -a`
|
||||
|
||||
* A "cleanup" command to get rid of junk (interfaces, processes, files
|
||||
in /tmp, etc.) which might be left around by Mininet or Linux. Try
|
||||
this if things stop working!
|
||||
|
||||
`mn -c`
|
||||
|
||||
### New features in 2.0.0
|
||||
|
||||
Mininet 2.0.0 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,39 +12,56 @@ Example to pull custom params (topo, switch, etc.) from a file:
|
||||
"""
|
||||
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Fix setuptools' evil madness, and open up (more?) security holes
|
||||
if 'PYTHONPATH' in os.environ:
|
||||
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||
|
||||
from mininet.clean import cleanup
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, LEVELS, info
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.log import lg, LEVELS, info, warn
|
||||
from mininet.net import Mininet, MininetWithControlNet
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
NOX, RemoteController, UserSwitch, OVSKernelSwitch,
|
||||
OVSLegacyKernelSwitch )
|
||||
from mininet.link import Link, TCLink
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, customConstructor
|
||||
from mininet.util import buildTopo
|
||||
from mininet.util import makeNumeric, custom
|
||||
|
||||
|
||||
def customNode( constructors, argStr ):
|
||||
"Return custom Node constructor based on argStr"
|
||||
cname, newargs, kwargs = splitArgs( argStr )
|
||||
constructor = constructors.get( cname, None )
|
||||
|
||||
if not constructor:
|
||||
raise Exception( "error: %s is unknown - please specify one of %s" %
|
||||
( cname, constructors.keys() ) )
|
||||
|
||||
def customized( name, *args, **params ):
|
||||
"Customized Node constructor"
|
||||
params.update( kwargs )
|
||||
if not newargs:
|
||||
return constructor( name, *args, **params )
|
||||
if args:
|
||||
warn( 'warning: %s replacing %s with %s\n' % (
|
||||
constructor, args, newargs ) )
|
||||
return constructor( name, *newargs, **params )
|
||||
|
||||
return customized
|
||||
|
||||
|
||||
# built in topologies, created only when run
|
||||
TOPODEF = 'minimal'
|
||||
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo }
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo }
|
||||
|
||||
SWITCHDEF = 'ovsk'
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
'ovsk': OVSKernelSwitch,
|
||||
'ovsk': OVSKernelSwitch,
|
||||
'ovsl': OVSLegacyKernelSwitch }
|
||||
|
||||
HOSTDEF = 'proc'
|
||||
@@ -52,7 +69,7 @@ HOSTS = { 'proc': Host,
|
||||
'rt': custom( CPULimitedHost, sched='rt' ),
|
||||
'cfs': custom( CPULimitedHost, sched='cfs' ) }
|
||||
|
||||
CONTROLLERDEF = 'ovsc'
|
||||
CONTROLLERDEF = 'ref'
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
@@ -66,12 +83,35 @@ LINKS = { 'default': Link,
|
||||
|
||||
# optional tests to run
|
||||
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||
'none' ]
|
||||
'none' ]
|
||||
|
||||
ALTSPELLING = { 'pingall': 'pingAll',
|
||||
'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp',
|
||||
'iperfUDP': 'iperfUdp' }
|
||||
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
|
||||
|
||||
|
||||
def splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
split = argstr.split( ',' )
|
||||
fn = split[ 0 ]
|
||||
params = split[ 1: ]
|
||||
# Convert int and float args; removes the need for function
|
||||
# to be flexible with input arg formats.
|
||||
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||
kwargs = {}
|
||||
for s in [ p for p in params if '=' in p ]:
|
||||
key, val = s.split( '=' )
|
||||
kwargs[ key ] = makeNumeric( val )
|
||||
return fn, args, kwargs
|
||||
|
||||
|
||||
def buildTopo( topoStr ):
|
||||
"Create topology from string with format (object, arg1, arg2,...)."
|
||||
topo, args, kwargs = splitArgs( topoStr )
|
||||
if topo not in TOPOS:
|
||||
raise Exception( 'Invalid topo name %s' % topo )
|
||||
return TOPOS[ topo ]( *args, **kwargs )
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
@@ -83,21 +123,16 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
help: string"""
|
||||
if default not in choicesDict:
|
||||
raise Exception( 'Invalid default %s for choices dict: %s' %
|
||||
( default, name ) )
|
||||
( default, name ) )
|
||||
if not helpStr:
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
'[,param=value...]' )
|
||||
opts.add_option( '--' + name,
|
||||
type='string',
|
||||
default = default,
|
||||
help = helpStr )
|
||||
type='string',
|
||||
default = default,
|
||||
help = helpStr )
|
||||
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
print "%s" % VERSION
|
||||
exit()
|
||||
|
||||
class MininetRunner( object ):
|
||||
"Build, setup, and run Mininet."
|
||||
|
||||
@@ -145,14 +180,7 @@ class MininetRunner( object ):
|
||||
else:
|
||||
raise Exception( 'Custom file name not found' )
|
||||
|
||||
desc = ( "The %prog utility creates Mininet network from the\n"
|
||||
"command line. It can create parametrized topologies,\n"
|
||||
"invoke the Mininet CLI, and run tests." )
|
||||
|
||||
usage = ( '%prog [options]\n'
|
||||
'(type %prog -h for details)' )
|
||||
|
||||
opts = OptionParser( description=desc, usage=usage )
|
||||
opts = OptionParser()
|
||||
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
||||
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
||||
@@ -160,39 +188,42 @@ class MininetRunner( object ):
|
||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||
|
||||
opts.add_option( '--clean', '-c', action='store_true',
|
||||
default=False, help='clean and exit' )
|
||||
default=False, help='clean and exit' )
|
||||
opts.add_option( '--custom', type='string', default=None,
|
||||
help='read custom topo and node params from .py' +
|
||||
'file' )
|
||||
help='read custom topo and node params from .py file' )
|
||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||
default=TESTS[ 0 ],
|
||||
help='|'.join( TESTS ) )
|
||||
default=TESTS[ 0 ],
|
||||
help='|'.join( TESTS ) )
|
||||
opts.add_option( '--xterms', '-x', action='store_true',
|
||||
default=False, help='spawn xterms for each node' )
|
||||
default=False, help='spawn xterms for each node' )
|
||||
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
|
||||
help='base IP address for hosts' )
|
||||
opts.add_option( '--mac', action='store_true',
|
||||
default=False, help='automatically set host MACs' )
|
||||
default=False, help='automatically set host MACs' )
|
||||
opts.add_option( '--arp', action='store_true',
|
||||
default=False, help='set all-pairs ARP entries' )
|
||||
default=False, help='set all-pairs ARP entries' )
|
||||
opts.add_option( '--verbosity', '-v', type='choice',
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
opts.add_option( '--ip', type='string', default='127.0.0.1',
|
||||
help='ip address as a dotted decimal string for a'
|
||||
'remote controller' )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6634,
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6635,
|
||||
help='base port for passive switch listening' )
|
||||
opts.add_option( '--nolistenport', action='store_true',
|
||||
default=False, help="don't use passive listening " +
|
||||
"port")
|
||||
default=False, help="don't use passive listening port")
|
||||
opts.add_option( '--pre', type='string', default=None,
|
||||
help='CLI script to run before tests' )
|
||||
help='CLI script to run before tests' )
|
||||
opts.add_option( '--post', type='string', default=None,
|
||||
help='CLI script to run after tests' )
|
||||
help='CLI script to run after tests' )
|
||||
opts.add_option( '--prefixlen', type='int', default=8,
|
||||
help='prefix length (e.g. /8) for automatic '
|
||||
'network configuration' )
|
||||
opts.add_option( '--pin', action='store_true',
|
||||
default=False, help="pin hosts to CPU cores "
|
||||
"(requires --host cfs or --host rt)" )
|
||||
opts.add_option( '--version', action='callback', callback=version )
|
||||
|
||||
self.options, self.args = opts.parse_args()
|
||||
|
||||
@@ -216,11 +247,11 @@ class MininetRunner( object ):
|
||||
|
||||
start = time.time()
|
||||
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customConstructor( SWITCHES, self.options.switch )
|
||||
host = customConstructor( HOSTS, self.options.host )
|
||||
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||
link = customConstructor( LINKS, self.options.link )
|
||||
topo = buildTopo( self.options.topo )
|
||||
switch = customNode( SWITCHES, self.options.switch )
|
||||
host = customNode( HOSTS, self.options.host )
|
||||
controller = customNode( CONTROLLERS, self.options.controller )
|
||||
link = customNode( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( self.options )
|
||||
|
||||
+24
-13
@@ -1,5 +1,7 @@
|
||||
"""Custom topology example
|
||||
|
||||
author: Brandon Heller (brandonh@stanford.edu)
|
||||
|
||||
Two directly connected switches plus a host for each switch:
|
||||
|
||||
host --- switch --- switch --- host
|
||||
@@ -8,27 +10,36 @@ Adding the 'topos' dict with a key/value pair to generate our newly defined
|
||||
topology enables one to pass in '--topo=mytopo' from the command line.
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.topo import Topo, Node
|
||||
|
||||
class MyTopo( Topo ):
|
||||
"Simple topology example."
|
||||
|
||||
def __init__( self ):
|
||||
def __init__( self, enable_all = True ):
|
||||
"Create custom topo."
|
||||
|
||||
# Initialize topology
|
||||
Topo.__init__( self )
|
||||
# Add default members to class.
|
||||
super( MyTopo, self ).__init__()
|
||||
|
||||
# Add hosts and switches
|
||||
leftHost = self.addHost( 'h1' )
|
||||
rightHost = self.addHost( 'h2' )
|
||||
leftSwitch = self.addSwitch( 's3' )
|
||||
rightSwitch = self.addSwitch( 's4' )
|
||||
# Set Node IDs for hosts and switches
|
||||
leftHost = 1
|
||||
leftSwitch = 2
|
||||
rightSwitch = 3
|
||||
rightHost = 4
|
||||
|
||||
# Add links
|
||||
self.addLink( leftHost, leftSwitch )
|
||||
self.addLink( leftSwitch, rightSwitch )
|
||||
self.addLink( rightSwitch, rightHost )
|
||||
# Add nodes
|
||||
self.add_node( leftSwitch, Node( is_switch=True ) )
|
||||
self.add_node( rightSwitch, Node( is_switch=True ) )
|
||||
self.add_node( leftHost, Node( is_switch=False ) )
|
||||
self.add_node( rightHost, Node( is_switch=False ) )
|
||||
|
||||
# Add edges
|
||||
self.add_edge( leftHost, leftSwitch )
|
||||
self.add_edge( leftSwitch, rightSwitch )
|
||||
self.add_edge( rightSwitch, rightHost )
|
||||
|
||||
# Consider all switches and hosts 'on'
|
||||
self.enable_all()
|
||||
|
||||
|
||||
topos = { 'mytopo': ( lambda: MyTopo() ) }
|
||||
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
mininet (2.0.0~rc1-0ubuntu1) quantal; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
* Update copyright to match upstream release
|
||||
* Fix this message
|
||||
|
||||
-- Bob Lantz <rlantz@cs.stanford.edu> Sun, 18 Nov 2012 00:15:09 -0800
|
||||
|
||||
mininet (2.0.0~d4-0ubuntu1) quantal; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Bob Lantz <rlantz@cs.stanford.edu> Tue, 07 Aug 2012 14:11:27 -0700
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
9
|
||||
Vendored
-30
@@ -1,30 +0,0 @@
|
||||
Source: mininet
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||
XSBC-Original-Maintainer: Bob Lantz <rlantz@cs.stanford.edu>
|
||||
Standards-Version: 3.9.3
|
||||
Build-Depends:
|
||||
debhelper (>= 9~),
|
||||
help2man,
|
||||
python-dev,
|
||||
python-pkg-resources,
|
||||
python-setuptools,
|
||||
python-networkx
|
||||
Homepage: http://openflow.org/mininet
|
||||
|
||||
Package: mininet
|
||||
Architecture: any
|
||||
Depends:
|
||||
openvswitch-switch,
|
||||
python-networkx,
|
||||
telnet,
|
||||
${misc:Depends},
|
||||
${python:Depends},
|
||||
${shlibs:Depends}
|
||||
Recommends: iperf, openvswitch-controller, socat
|
||||
Description: Process-based network emulator
|
||||
Mininet is a network emulator which uses lightweight
|
||||
virtualization to create virtual networks for rapid
|
||||
prototyping of Software-Defined Network (SDN) designs
|
||||
using OpenFlow.
|
||||
Vendored
-37
@@ -1,37 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
|
||||
Upstream-Name: mininet
|
||||
Source: https://github.com/mininet/mininet
|
||||
|
||||
Files: *
|
||||
Copyright: 2012 Open Networking Laboratory,
|
||||
2009-2012 Bob Lantz,
|
||||
2009-2012 The Board of Trustees of the Leland Stanford Junior
|
||||
University
|
||||
License:
|
||||
Original authors: Bob Lantz and Brandon Heller
|
||||
.
|
||||
We are making Mininet available for public use and benefit with the
|
||||
expectation that others will use, modify and enhance the Software and
|
||||
contribute those enhancements back to the community. However, since we
|
||||
would like to make the Software available for broadest use, with as few
|
||||
restrictions as possible permission is hereby granted, free of charge, to
|
||||
any person obtaining a copy of this Software to deal in the Software
|
||||
under the copyrights without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
.
|
||||
The name and trademarks of copyright holder(s) may NOT be used in
|
||||
advertising or publicity pertaining to the Software or any derivatives
|
||||
without specific, written prior permission.
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
README.md
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
examples/*
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
mnexec /usr/bin
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
*.1
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=python_distutils --with=python2
|
||||
|
||||
override_dh_auto_build:
|
||||
make man
|
||||
make mnexec
|
||||
dh_auto_build
|
||||
|
||||
get-orig-source:
|
||||
uscan --force-download --rename
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
version=3
|
||||
opts=filenamemangle=s/(.*)\/archive/$1/,uversionmangle=s/([abdr].*)\.tar\.gz/~$1/ \
|
||||
https://github.com/mininet/mininet/tags .*/archive\/(\d.*\.tar\.gz)
|
||||
@@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
PROJECT_NAME = "Mininet Python API Reference Manual"
|
||||
PROJECT_NAME = Mininet
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
||||
# This could be handy for archiving the generated documentation or
|
||||
@@ -114,7 +114,7 @@ FULL_PATH_NAMES = YES
|
||||
# If left blank the directory from which doxygen is run is used as the
|
||||
# path to strip.
|
||||
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_PATH =
|
||||
|
||||
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
|
||||
# the path mentioned in the documentation of a class, which tells
|
||||
@@ -919,7 +919,7 @@ COMPACT_LATEX = NO
|
||||
# by the printer. Possible values are: a4, a4wide, letter, legal and
|
||||
# executive. If left blank a4wide will be used.
|
||||
|
||||
PAPER_TYPE = letter
|
||||
PAPER_TYPE = a4wide
|
||||
|
||||
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
|
||||
# packages that should be included in the LaTeX output.
|
||||
+1
-31
@@ -19,33 +19,13 @@ graphical monitoring.
|
||||
|
||||
controllers.py:
|
||||
|
||||
This example creates a network with multiple controllers, by
|
||||
using a custom Switch() subclass.
|
||||
|
||||
controllers2.py:
|
||||
|
||||
This example creates a network with multiple controllers by
|
||||
creating an empty network, adding nodes to it, and manually
|
||||
starting the switches.
|
||||
|
||||
cpu.py:
|
||||
|
||||
This example tests iperf bandwidth for varying CPU limits.
|
||||
This example creates a network and adds multiple controllers to it.
|
||||
|
||||
emptynet.py:
|
||||
|
||||
This example demonstrates creating an empty network (i.e. with no
|
||||
topology object) and adding nodes to it.
|
||||
|
||||
hwintf.py:
|
||||
|
||||
This example shows how to add an interface (for example a real
|
||||
hardware interface) to a network after the network is created.
|
||||
|
||||
limit.py:
|
||||
|
||||
This example shows how to use link and CPU limits.
|
||||
|
||||
linearbandwidth.py:
|
||||
|
||||
This example shows how to create a custom topology programatically
|
||||
@@ -68,16 +48,6 @@ multitest.py:
|
||||
|
||||
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:
|
||||
|
||||
These two examples demonstrate how to create a network by using the lowest-
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
||||
|
||||
from mininet.node import Host
|
||||
from mininet.util import ensureRoot
|
||||
|
||||
ensureRoot()
|
||||
|
||||
print "*** Creating nodes"
|
||||
h1 = Host( 'h1' )
|
||||
|
||||
+12
-9
@@ -74,11 +74,11 @@ class Console( Frame ):
|
||||
"Pop up a new terminal window for a node."
|
||||
net.terms += makeTerms( [ node ], title )
|
||||
label = Button( self, text=self.node.name, command=newTerm,
|
||||
**self.buttonStyle )
|
||||
**self.buttonStyle )
|
||||
label.pack( side='top', fill='x' )
|
||||
text = Text( self, wrap='word', **self.textStyle )
|
||||
ybar = Scrollbar( self, orient='vertical', width=7,
|
||||
command=text.yview )
|
||||
command=text.yview )
|
||||
text.configure( yscrollcommand=ybar.set )
|
||||
text.pack( side='left', expand=True, fill='both' )
|
||||
ybar.pack( side='right', fill='y' )
|
||||
@@ -95,7 +95,7 @@ class Console( Frame ):
|
||||
# way to trigger a file event handler from Tk's
|
||||
# event loop!
|
||||
self.tk.createfilehandler( self.node.stdout, READABLE,
|
||||
self.handleReadable )
|
||||
self.handleReadable )
|
||||
|
||||
# We're not a terminal (yet?), so we ignore the following
|
||||
# control characters other than [\b\n\r]
|
||||
@@ -169,8 +169,11 @@ class Graph( Frame ):
|
||||
|
||||
"Graph that we can add bars to over time."
|
||||
|
||||
def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500,
|
||||
barwidth=10, ymax=3.5,):
|
||||
def __init__( self, parent=None,
|
||||
bg = 'white',
|
||||
gheight=200, gwidth=500,
|
||||
barwidth=10,
|
||||
ymax=3.5,):
|
||||
|
||||
Frame.__init__( self, parent )
|
||||
|
||||
@@ -192,7 +195,7 @@ class Graph( Frame ):
|
||||
width = 25
|
||||
ymax = self.ymax
|
||||
scale = Canvas( self, width=width, height=height,
|
||||
background=self.bg )
|
||||
background=self.bg )
|
||||
opts = { 'fill': 'red' }
|
||||
# Draw scale line
|
||||
scale.create_line( width - 1, height, width - 1, 0, **opts )
|
||||
@@ -208,7 +211,7 @@ class Graph( Frame ):
|
||||
ofs = 20
|
||||
height = self.gheight + ofs
|
||||
self.graph.configure( scrollregion=( 0, -ofs,
|
||||
self.xpos * self.barwidth, height ) )
|
||||
self.xpos * self.barwidth, height ) )
|
||||
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
|
||||
|
||||
def yview( self, *args ):
|
||||
@@ -228,7 +231,7 @@ class Graph( Frame ):
|
||||
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
|
||||
ybar = Scrollbar( self, orient='vertical', command=self.yview )
|
||||
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
|
||||
scrollregion=(0, 0, width, height ) )
|
||||
scrollregion=(0, 0, width, height ) )
|
||||
scale.configure( yscrollcommand=ybar.set )
|
||||
|
||||
# Layout
|
||||
@@ -249,7 +252,7 @@ class Graph( Frame ):
|
||||
x1 = x0 + self.barwidth
|
||||
y0 = self.gheight
|
||||
y1 = ( 1 - percent ) * self.gheight
|
||||
c.create_rectangle( x0, y0, x1, y1, fill='green' )
|
||||
c.create_rectangle( x0 , y0, x1, y1, fill='green' )
|
||||
self.xpos += 1
|
||||
self.updateScrollRegions()
|
||||
self.graph.xview( 'moveto', '1.0' )
|
||||
|
||||
+54
-18
@@ -1,28 +1,64 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Create a network where different switches are connected to
|
||||
different controllers, by creating a custom Switch() subclass.
|
||||
This example creates a multi-controller network from
|
||||
semi-scratch; note a topo object could also be used and
|
||||
would be passed into the Mininet() constructor.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import OVSSwitch, Controller
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.node import Controller, OVSKernelSwitch
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
c0 = Controller( 'c0' )
|
||||
c1 = Controller( 'c1', ip='127.0.0.2' )
|
||||
cmap = { 's1': c0, 's2': c1, 's3': c1 }
|
||||
Switch = OVSKernelSwitch
|
||||
|
||||
class MultiSwitch( OVSSwitch ):
|
||||
"Custom Switch() subclass that connects to different controllers"
|
||||
def start( self, controllers ):
|
||||
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
|
||||
def addHost( net, N ):
|
||||
"Create host hN and add to net."
|
||||
name = 'h%d' % N
|
||||
ip = '10.0.0.%d' % N
|
||||
return net.addHost( name, ip=ip )
|
||||
|
||||
topo = TreeTopo( depth=2, fanout=2 )
|
||||
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
|
||||
net.controllers = [ c0, c1 ]
|
||||
net.build()
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
def multiControllerNet():
|
||||
"Create a network with multiple controllers."
|
||||
|
||||
net = Mininet( controller=Controller, switch=Switch)
|
||||
|
||||
print "*** Creating controllers"
|
||||
c1 = net.addController( 'c1', port=6633 )
|
||||
c2 = net.addController( 'c2', port=6634 )
|
||||
|
||||
print "*** Creating switches"
|
||||
s1 = net.addSwitch( 's1' )
|
||||
s2 = net.addSwitch( 's2' )
|
||||
|
||||
print "*** Creating hosts"
|
||||
hosts1 = [ addHost( net, n ) for n in 3, 4 ]
|
||||
hosts2 = [ addHost( net, n ) for n in 5, 6 ]
|
||||
|
||||
print "*** Creating links"
|
||||
for h in hosts1:
|
||||
s1.linkTo( h )
|
||||
for h in hosts2:
|
||||
s2.linkTo( h )
|
||||
s1.linkTo( s2 )
|
||||
|
||||
print "*** Starting network"
|
||||
net.build()
|
||||
c1.start()
|
||||
c2.start()
|
||||
s1.start( [ c1 ] )
|
||||
s2.start( [ c2 ] )
|
||||
|
||||
print "*** Testing network"
|
||||
net.pingAll()
|
||||
|
||||
print "*** Running CLI"
|
||||
CLI( net )
|
||||
|
||||
print "*** Stopping network"
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' ) # for CLI output
|
||||
multiControllerNet()
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example creates a multi-controller network from semi-scratch by
|
||||
using the net.add*() API and manually starting the switches and controllers.
|
||||
|
||||
This is the "mid-level" API, which is an alternative to the "high-level"
|
||||
Topo() API which supports parametrized topology classes.
|
||||
|
||||
Note that one could also create a custom switch class and pass it into
|
||||
the Mininet() constructor.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller, OVSSwitch
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
def multiControllerNet():
|
||||
"Create a network from semi-scratch with multiple controllers."
|
||||
|
||||
net = Mininet( controller=Controller, switch=OVSSwitch, build=False )
|
||||
|
||||
print "*** Creating (reference) controllers"
|
||||
c1 = net.addController( 'c1', port=6633 )
|
||||
c2 = net.addController( 'c2', port=6634 )
|
||||
|
||||
print "*** Creating switches"
|
||||
s1 = net.addSwitch( 's1' )
|
||||
s2 = net.addSwitch( 's2' )
|
||||
|
||||
print "*** Creating hosts"
|
||||
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
|
||||
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
|
||||
|
||||
print "*** Creating links"
|
||||
for h in hosts1:
|
||||
net.addLink( s1, h )
|
||||
for h in hosts2:
|
||||
net.addLink( s2, h )
|
||||
net.addLink( s1, s2 )
|
||||
|
||||
print "*** Starting network"
|
||||
net.build()
|
||||
c1.start()
|
||||
c2.start()
|
||||
s1.start( [ c1 ] )
|
||||
s2.start( [ c2 ] )
|
||||
|
||||
print "*** Testing network"
|
||||
net.pingAll()
|
||||
|
||||
print "*** Running CLI"
|
||||
CLI( net )
|
||||
|
||||
print "*** Stopping network"
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' ) # for CLI output
|
||||
multiControllerNet()
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
cpu.py: test iperf bandwidth for varying cpu limits
|
||||
cpu.py: test iperf bandwidth for varying cpu limtis
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
@@ -46,7 +46,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
|
||||
server.cmd( 'iperf -s -p 5001 &' )
|
||||
waitListening( client, server, 5001 )
|
||||
result = client.cmd( 'iperf -yc -t %s -c %s' % (
|
||||
seconds, server.IP() ) ).split( ',' )
|
||||
seconds, server.IP() ) ).split( ',' )
|
||||
bps = float( result[ -1 ] )
|
||||
server.cmdPrint( 'kill %iperf' )
|
||||
net.stop()
|
||||
|
||||
+6
-11
@@ -10,7 +10,6 @@ import re
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel, info, error
|
||||
from mininet.net import Mininet
|
||||
from mininet.link import Intf
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import quietRun
|
||||
|
||||
@@ -22,26 +21,22 @@ def checkIntf( intf ):
|
||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
|
||||
if ips:
|
||||
error( 'Error:', intf, 'has an IP address,'
|
||||
'and is probably in use!\n' )
|
||||
'and is probably in use!\n' )
|
||||
exit( 1 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
|
||||
intfName = 'eth1'
|
||||
info( '*** Checking', intfName, '\n' )
|
||||
checkIntf( intfName )
|
||||
newIntf = 'eth1'
|
||||
info( '*** Checking', newIntf, '\n' )
|
||||
checkIntf( newIntf )
|
||||
|
||||
info( '*** Creating network\n' )
|
||||
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
|
||||
|
||||
switch = net.switches[ 0 ]
|
||||
info( '*** Adding hardware interface', intfName, 'to switch',
|
||||
switch.name, '\n' )
|
||||
_intf = Intf( intfName, node=switch )
|
||||
|
||||
info( '*** Note: you may need to reconfigure the interfaces for '
|
||||
'the Mininet hosts:\n', net.hosts, '\n' )
|
||||
info( '*** Adding', newIntf, 'to switch', switch.name, '\n' )
|
||||
switch.addIntf( newIntf )
|
||||
|
||||
net.start()
|
||||
CLI( net )
|
||||
|
||||
+20
-4
@@ -8,17 +8,33 @@ from mininet.net import Mininet
|
||||
from mininet.link import TCIntf
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom
|
||||
from mininet.util import custom, quietRun
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from time import sleep
|
||||
|
||||
def testLinkLimit( net, bw ):
|
||||
"Run bandwidth limit test"
|
||||
print '*** Testing network %.2f Mbps bandwidth limit' % bw
|
||||
net.iperf( )
|
||||
|
||||
def testCpuLimit( net, cpu ):
|
||||
"run CPU limit test"
|
||||
pct = cpu * 100
|
||||
print '*** Testing CPU %.0f%% bandwidth limit' % pct
|
||||
h1, h2 = net.hosts
|
||||
h1.cmd( 'while true; do a=1; done &' )
|
||||
h2.cmd( 'while true; do a=1; done &' )
|
||||
pid1 = h1.cmd( 'echo $!' ).strip()
|
||||
pid2 = h2.cmd( 'echo $!' ).strip()
|
||||
cmd = 'ps -p %s,%s -o pid,%%cpu,args' % ( pid1, pid2 )
|
||||
# It's a shame that this is what pylint prefers
|
||||
for _ in range( 5 ):
|
||||
sleep( 1 )
|
||||
print quietRun( cmd ).strip()
|
||||
h1.cmd( 'kill %1')
|
||||
h2.cmd( 'kill %1')
|
||||
|
||||
def limit( bw=10, cpu=.1 ):
|
||||
def limit( bw=10, cpu=.4 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
bw: interface bandwidth limit in Mbps
|
||||
cpu: cpu limit as fraction of overall CPU time"""
|
||||
@@ -30,7 +46,7 @@ def limit( bw=10, cpu=.1 ):
|
||||
net = Mininet( topo=myTopo, intf=intf, host=host )
|
||||
net.start()
|
||||
testLinkLimit( net, bw=bw )
|
||||
net.runCpuLimitTest( cpu=cpu )
|
||||
testCpuLimit( net, cpu=cpu )
|
||||
net.stop()
|
||||
|
||||
def verySimpleLimit( bw=150 ):
|
||||
|
||||
@@ -41,22 +41,22 @@ class LinearTestTopo( Topo ):
|
||||
Topo.__init__( self, **params )
|
||||
|
||||
# Create switches and hosts
|
||||
hosts = [ self.addHost( 'h%s' % h )
|
||||
hosts = [ self.add_host( 'h%s' % h )
|
||||
for h in irange( 1, N ) ]
|
||||
switches = [ self.addSwitch( 's%s' % s )
|
||||
switches = [ self.add_switch( 's%s' % s )
|
||||
for s in irange( 1, N - 1 ) ]
|
||||
|
||||
# Wire up switches
|
||||
last = None
|
||||
for switch in switches:
|
||||
if last:
|
||||
self.addLink( last, switch )
|
||||
self.add_link( last, switch )
|
||||
last = switch
|
||||
|
||||
# Wire up hosts
|
||||
self.addLink( hosts[ 0 ], switches[ 0 ] )
|
||||
self.add_link( hosts[ 0 ], switches[ 0 ] )
|
||||
for host, switch in zip( hosts[ 1: ], switches ):
|
||||
self.addLink( host, switch )
|
||||
self.add_link( host, switch )
|
||||
|
||||
|
||||
def linearBandwidthTest( lengths ):
|
||||
@@ -68,7 +68,7 @@ def linearBandwidthTest( lengths ):
|
||||
hostCount = switchCount + 1
|
||||
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
topo = LinearTestTopo( hostCount )
|
||||
|
||||
|
||||
+12
-12
@@ -112,7 +112,7 @@ class MiniEdit( Frame ):
|
||||
appMenu = Menu( mbar, tearoff=False )
|
||||
mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
|
||||
appMenu.add_command( label='About MiniEdit', command=self.about,
|
||||
font=font)
|
||||
font=font)
|
||||
appMenu.add_separator()
|
||||
appMenu.add_command( label='Quit', command=self.quit, font=font )
|
||||
|
||||
@@ -127,7 +127,7 @@ class MiniEdit( Frame ):
|
||||
editMenu = Menu( mbar, tearoff=False )
|
||||
mbar.add_cascade( label="Edit", font=font, menu=editMenu )
|
||||
editMenu.add_command( label="Cut", font=font,
|
||||
command=lambda: self.deleteSelection( None ) )
|
||||
command=lambda: self.deleteSelection( None ) )
|
||||
|
||||
runMenu = Menu( mbar, tearoff=False )
|
||||
mbar.add_cascade( label="Run", font=font, menu=runMenu )
|
||||
@@ -143,7 +143,7 @@ class MiniEdit( Frame ):
|
||||
f = Frame( self )
|
||||
|
||||
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
|
||||
bg=self.bg )
|
||||
bg=self.bg )
|
||||
|
||||
# Scroll bars
|
||||
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
|
||||
@@ -177,7 +177,7 @@ class MiniEdit( Frame ):
|
||||
bbox = self.canvas.bbox( 'all' )
|
||||
if bbox is not None:
|
||||
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
|
||||
bbox[ 3 ] ) )
|
||||
bbox[ 3 ] ) )
|
||||
|
||||
def canvasx( self, x_root ):
|
||||
"Convert root x coordinate to canvas coordinate."
|
||||
@@ -223,7 +223,7 @@ class MiniEdit( Frame ):
|
||||
for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
|
||||
doCmd = getattr( self, 'do' + cmd )
|
||||
b = Button( toolbar, text=cmd, font=self.smallFont,
|
||||
fg=color, command=doCmd )
|
||||
fg=color, command=doCmd )
|
||||
b.pack( fill='x', side='bottom' )
|
||||
|
||||
return toolbar
|
||||
@@ -289,7 +289,7 @@ class MiniEdit( Frame ):
|
||||
def deleteItem( self, item ):
|
||||
"Delete an item."
|
||||
# Don't delete while network is running
|
||||
if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
|
||||
if self.buttons[ 'Select' ][ 'state' ] == 'disabled' :
|
||||
return
|
||||
# Delete from model
|
||||
if item in self.links:
|
||||
@@ -308,7 +308,7 @@ class MiniEdit( Frame ):
|
||||
def nodeIcon( self, node, name ):
|
||||
"Create a new node icon."
|
||||
icon = Button( self.canvas, image=self.images[ node ],
|
||||
text=name, compound='top' )
|
||||
text=name, compound='top' )
|
||||
# Unfortunately bindtags wants a tuple
|
||||
bindtags = [ str( self.nodeBindings ) ]
|
||||
bindtags += list( icon.bindtags() )
|
||||
@@ -322,8 +322,8 @@ class MiniEdit( Frame ):
|
||||
self.nodeCount += 1
|
||||
name = self.nodePrefixes[ node ] + str( self.nodeCount )
|
||||
icon = self.nodeIcon( node, name )
|
||||
item = self.canvas.create_window( x, y, anchor='c', window=icon,
|
||||
tags=node )
|
||||
item = self.canvas.create_window( x, y, anchor='c',
|
||||
window=icon, tags=node )
|
||||
self.widgetToItem[ icon ] = item
|
||||
self.itemToWidget[ item ] = icon
|
||||
self.selectItem( item )
|
||||
@@ -437,7 +437,7 @@ class MiniEdit( Frame ):
|
||||
item = self.widgetToItem[ w ]
|
||||
x, y = self.canvas.coords( item )
|
||||
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.linkWidget = w
|
||||
self.linkItem = item
|
||||
@@ -475,7 +475,7 @@ class MiniEdit( Frame ):
|
||||
target = self.findItem( x, y )
|
||||
dest = self.itemToWidget.get( target, None )
|
||||
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 )
|
||||
return
|
||||
# For now, don't allow hosts to be directly linked
|
||||
@@ -591,7 +591,7 @@ class MiniEdit( Frame ):
|
||||
cleanUpScreens()
|
||||
self.net = None
|
||||
|
||||
def xterm( self, _ignore=None ):
|
||||
def xterm( self, _=None ):
|
||||
"Make an xterm when a button is pressed."
|
||||
if ( self.selection is None or
|
||||
self.net is None or
|
||||
|
||||
@@ -18,7 +18,7 @@ from time import time
|
||||
|
||||
def chunks( l, n ):
|
||||
"Divide list l into chunks of size n - thanks Stackoverflow"
|
||||
return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
|
||||
return [ l[ i : i + n ] for i in range( 0, len( l ), n ) ]
|
||||
|
||||
def startpings( host, targetips ):
|
||||
"Tell host to repeatedly ping targets"
|
||||
@@ -40,7 +40,7 @@ def startpings( host, targetips ):
|
||||
'done &' )
|
||||
|
||||
print ( '*** Host %s (%s) will be pinging ips: %s' %
|
||||
( host.name, host.IP(), targetips ) )
|
||||
( host.name, host.IP(), targetips ) )
|
||||
|
||||
host.cmd( cmd )
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
tails, fdToFile, fdToHost = {}, {}, {}
|
||||
for h, outfile in outfiles.iteritems():
|
||||
tail = Popen( [ 'tail', '-f', outfile ],
|
||||
stdout=PIPE, stderr=devnull )
|
||||
stdout=PIPE, stderr=devnull )
|
||||
fd = tail.stdout.fileno()
|
||||
tails[ h ] = tail
|
||||
fdToFile[ fd ] = tail.stdout
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
This example monitors a number of hosts using host.popen() and
|
||||
pmonitor()
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import custom, pmonitor
|
||||
|
||||
def monitorhosts( hosts=5, sched='cfs' ):
|
||||
"Start a bunch of pings and monitor them using popen"
|
||||
mytopo = SingleSwitchTopo( hosts )
|
||||
cpu = .5 / hosts
|
||||
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
|
||||
net = Mininet( topo=mytopo, host=myhost )
|
||||
net.start()
|
||||
# Start a bunch of pings
|
||||
popens = {}
|
||||
last = net.hosts[ -1 ]
|
||||
for host in net.hosts:
|
||||
popens[ host ] = host.popen( "ping -c5 %s" % last.IP() )
|
||||
last = host
|
||||
# Monitor them and print output
|
||||
for host, line in pmonitor( popens ):
|
||||
if host:
|
||||
print "<%s>: %s" % ( host.name, line.strip() )
|
||||
# Done
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
monitorhosts( hosts=5 )
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"Monitor multiple hosts using popen()/pmonitor()"
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.util import pmonitor
|
||||
from time import time
|
||||
from signal import SIGINT
|
||||
|
||||
def pmonitorTest( N=3, seconds=10 ):
|
||||
"Run pings and monitor multiple hosts using pmonitor"
|
||||
topo = SingleSwitchTopo( N )
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
popens = {}
|
||||
for h in hosts:
|
||||
popens[ h ] = h.popen('ping', server.IP() )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
endTime = time() + seconds
|
||||
for h, line in pmonitor( popens, timeoutms=500 ):
|
||||
if h:
|
||||
print '%s: %s' % ( h.name, line ),
|
||||
if time() >= endTime:
|
||||
for p in popens.values():
|
||||
p.send_signal( SIGINT )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pmonitorTest()
|
||||
@@ -38,12 +38,12 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
h1intf, sintf2 = linkIntfs( h1, switch )
|
||||
|
||||
info( '*** Configuring control network\n' )
|
||||
controller.setIP( '10.0.123.1/24', intf=cintf )
|
||||
switch.setIP( '10.0.123.2/24', intf=sintf)
|
||||
controller.setIP( '10.0.123.1/24', cintf )
|
||||
switch.setIP( '10.0.123.2/24', sintf)
|
||||
|
||||
info( '*** Configuring hosts\n' )
|
||||
h0.setIP( '192.168.123.1/24', intf=h0intf )
|
||||
h1.setIP( '192.168.123.2/24', intf=h1intf )
|
||||
h0.setIP( '192.168.123.1/24', h0intf )
|
||||
h1.setIP( '192.168.123.2/24', h1intf )
|
||||
|
||||
info( '*** Network state:\n' )
|
||||
for node in controller, switch, h0, h1:
|
||||
|
||||
Executable → Regular
+5
-10
@@ -2,11 +2,6 @@
|
||||
|
||||
"""
|
||||
Simple example of setting network and CPU parameters
|
||||
|
||||
NOTE: link params limit BW, add latency, and loss.
|
||||
There is a high chance that pings WILL fail and that
|
||||
iperf will hang indefinitely if the TCP handshake fails
|
||||
to complete.
|
||||
"""
|
||||
|
||||
from mininet.topo import Topo
|
||||
@@ -20,14 +15,14 @@ class SingleSwitchTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def __init__(self, n=2, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
switch = self.addSwitch('s1')
|
||||
switch = self.add_switch('s1')
|
||||
for h in range(n):
|
||||
# Each host gets 50%/n of system CPU
|
||||
host = self.addHost('h%s' % (h + 1),
|
||||
cpu=.5 / n)
|
||||
host = self.add_host('h%s' % (h + 1),
|
||||
cpu=.5 / n)
|
||||
# 10 Mbps, 5ms delay, 10% loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
self.add_link(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
|
||||
def perfTest():
|
||||
"Create network and run simple performance test"
|
||||
|
||||
@@ -11,8 +11,8 @@ def treePing64():
|
||||
|
||||
results = {}
|
||||
switches = { # 'reference kernel': KernelSwitch,
|
||||
'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
for name in switches:
|
||||
print "*** Testing", name, "datapath"
|
||||
|
||||
+18
-50
@@ -10,83 +10,51 @@ It may also get rid of 'false positives', but hopefully
|
||||
nothing irreplaceable!
|
||||
"""
|
||||
|
||||
from subprocess import Popen, PIPE, STDOUT, check_output as co
|
||||
from sys import stdout, exit
|
||||
from time import sleep
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from mininet.log import info, error
|
||||
from mininet.log import info
|
||||
from mininet.term import cleanUpScreens
|
||||
|
||||
def sh( cmd ):
|
||||
"Run a command in the shell and return non-empty output lines"
|
||||
"Print a command and send it to the shell"
|
||||
info( cmd + '\n' )
|
||||
output = ( Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE )
|
||||
.communicate()[ 0 ]
|
||||
.strip()
|
||||
.split( '\n' ) )
|
||||
return [ s for s in output if s ]
|
||||
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
|
||||
def cleanup():
|
||||
"""Clean up junk which might be left over from old runs;
|
||||
do fast stuff before slow dp and link removal!"""
|
||||
|
||||
info( "*** Removing excess "
|
||||
"controllers/ofprotocols/ofdatapaths/pings/noxes\n" )
|
||||
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
|
||||
"\n")
|
||||
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
||||
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec'
|
||||
zombies += 'ovs-openflowd udpbwtest'
|
||||
# Note: real zombie processes can't actually be killed, since they
|
||||
# are already (un)dead. Then again,
|
||||
# you can't connect to them either, so they're mostly harmless.
|
||||
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
|
||||
|
||||
# And kill off sudo mnexec
|
||||
sh( 'pkill -9 -f "sudo mnexec"')
|
||||
|
||||
info( "*** Removing junk from /tmp\n" )
|
||||
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
|
||||
|
||||
info( "*** Removing old X11 tunnels\n" )
|
||||
info( "*** Removing old screen sessions\n" )
|
||||
cleanUpScreens()
|
||||
|
||||
info( "*** Removing excess kernel datapaths\n" )
|
||||
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" )
|
||||
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' )
|
||||
for dp in dps:
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
if dp != '':
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
|
||||
info( "*** Removing OVS datapaths\n" )
|
||||
dps = sh("ovs-vsctl list-br")
|
||||
info( "*** Removing OVS datapaths" )
|
||||
dps = sh("ovs-vsctl list-br").split( '\n' )
|
||||
for dp in dps:
|
||||
sh( 'ovs-vsctl del-br ' + dp )
|
||||
if co( 'ovs-vsctl list-br', shell=True ):
|
||||
raise Excpetion( "Error: could not remove all OVS datapaths" )
|
||||
if dp:
|
||||
sh( 'ovs-vsctl del-br ' + dp )
|
||||
|
||||
info( "*** Removing all links of the pattern foo-ethX\n" )
|
||||
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" )
|
||||
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
|
||||
for link in links:
|
||||
sh( "ip link del " + link )
|
||||
if sh( "ip link show | egrep -o '(\w+-eth\w+)'" ):
|
||||
raise Exception( "Error could not remove stale links")
|
||||
|
||||
info( "*** Killing stale mininet node processes\n" )
|
||||
sh( 'pkill -9 -f mininet:' )
|
||||
# Make sure they are gone
|
||||
while True:
|
||||
try:
|
||||
pids = co( 'pgrep -f mininet:'.split() )
|
||||
except:
|
||||
pids = ''
|
||||
if pids:
|
||||
sh( 'pkill -f 9 mininet:' )
|
||||
sleep( .5 )
|
||||
else:
|
||||
break
|
||||
|
||||
info( "*** Removing stale namespaces\n" )
|
||||
nses = sh( "ip netns list" )
|
||||
for ns in nses:
|
||||
sh( "ip netns del " + ns )
|
||||
if co( "ip netns list", shell=True ):
|
||||
error( "Error: could not remove all namespaces - exiting\n" )
|
||||
exit( 1 )
|
||||
if link != '':
|
||||
sh( "ip link del " + link )
|
||||
|
||||
info( "*** Cleanup complete.\n" )
|
||||
|
||||
+8
-35
@@ -30,7 +30,6 @@ from cmd import Cmd
|
||||
from os import isatty
|
||||
from select import poll, POLLIN
|
||||
import sys
|
||||
import time
|
||||
|
||||
from mininet.log import info, output, error
|
||||
from mininet.term import makeTerms
|
||||
@@ -121,12 +120,12 @@ class CLI( Cmd ):
|
||||
"Run an external shell command"
|
||||
call( line, shell=True )
|
||||
|
||||
# do_py() and do_px() need to catch any exception during eval()/exec()
|
||||
# do_py() needs to catch any exception during eval()
|
||||
# pylint: disable-msg=W0703
|
||||
|
||||
def do_py( self, line ):
|
||||
"""Evaluate a Python expression.
|
||||
Node names may be used, e.g.: py h1.cmd('ls')"""
|
||||
Node names may be used, e.g.: h1.cmd('ls')"""
|
||||
try:
|
||||
result = eval( line, globals(), self.locals )
|
||||
if not result:
|
||||
@@ -138,18 +137,7 @@ class CLI( Cmd ):
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# We are in fact using the exec() pseudo-function
|
||||
# pylint: disable-msg=W0122
|
||||
|
||||
def do_px( self, line ):
|
||||
"""Execute a Python statement.
|
||||
Node names may be used, e.g.: px print h1.cmd('ls')"""
|
||||
try:
|
||||
exec( line, globals(), self.locals )
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# pylint: enable-msg=W0703,W0122
|
||||
# pylint: enable-msg=W0703
|
||||
|
||||
def do_pingall( self, _line ):
|
||||
"Ping between all hosts."
|
||||
@@ -159,14 +147,6 @@ class CLI( Cmd ):
|
||||
"Ping between first two hosts, useful for testing."
|
||||
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 ):
|
||||
"Simple iperf TCP test between two (optionally specified) hosts."
|
||||
args = line.split()
|
||||
@@ -211,7 +191,7 @@ class CLI( Cmd ):
|
||||
"List interfaces."
|
||||
for node in self.nodelist:
|
||||
output( '%s: %s\n' %
|
||||
( node.name, ','.join( node.intfNames() ) ) )
|
||||
( node.name, ','.join( node.intfNames() ) ) )
|
||||
|
||||
def do_dump( self, _line ):
|
||||
"Dump node info."
|
||||
@@ -298,13 +278,6 @@ class CLI( Cmd ):
|
||||
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
|
||||
output( sw.dpctl( *args ) )
|
||||
|
||||
def do_time( self, line ):
|
||||
"Measure time taken for any command in Mininet."
|
||||
start = time.time()
|
||||
self.onecmd(line)
|
||||
elapsed = time.time() - start
|
||||
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
|
||||
|
||||
def default( self, line ):
|
||||
"""Called on an input line when the command prefix is not recognized.
|
||||
Overridden to run shell commands when a node is the first CLI argument.
|
||||
@@ -322,8 +295,8 @@ class CLI( Cmd ):
|
||||
node = self.nodemap[ first ]
|
||||
# Substitute IP addresses for node names in command
|
||||
rest = [ self.nodemap[ arg ].IP()
|
||||
if arg in self.nodemap else arg
|
||||
for arg in rest ]
|
||||
if arg in self.nodemap else arg
|
||||
for arg in rest ]
|
||||
rest = ' '.join( rest )
|
||||
# Run cmd on node:
|
||||
builtin = isShellBuiltin( first )
|
||||
@@ -340,8 +313,8 @@ class CLI( Cmd ):
|
||||
nodePoller = poll()
|
||||
nodePoller.register( node.stdout )
|
||||
bothPoller = poll()
|
||||
bothPoller.register( self.stdin, POLLIN )
|
||||
bothPoller.register( node.stdout, POLLIN )
|
||||
bothPoller.register( self.stdin )
|
||||
bothPoller.register( node.stdout )
|
||||
if self.isatty():
|
||||
# Buffer by character, so that interactive
|
||||
# commands sort of work
|
||||
|
||||
+43
-64
@@ -25,7 +25,7 @@ Link: basic link class for creating veth pairs
|
||||
"""
|
||||
|
||||
from mininet.log import info, error, debug
|
||||
from mininet.util import makeIntfPair, errFail, quietRun
|
||||
from mininet.util import makeIntfPair
|
||||
from time import sleep
|
||||
import re
|
||||
|
||||
@@ -109,7 +109,7 @@ class Intf( object ):
|
||||
def rename( self, newname ):
|
||||
"Rename interface"
|
||||
self.ifconfig( 'down' )
|
||||
result = self.cmd( 'ip link set dev', self.name, 'name', newname )
|
||||
result = self.cmd( 'ip link set', self.name, 'name', newname )
|
||||
self.name = newname
|
||||
self.ifconfig( 'up' )
|
||||
return result
|
||||
@@ -178,7 +178,7 @@ class TCIntf( Intf ):
|
||||
as well as delay, loss and max queue length"""
|
||||
|
||||
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False ):
|
||||
enable_ecn=False, enable_red=False ):
|
||||
"Return tc commands to set bandwidth"
|
||||
|
||||
cmds, parent = [], ' root '
|
||||
@@ -196,63 +196,59 @@ class TCIntf( Intf ):
|
||||
# are specifying the correct sizes. For now I have used
|
||||
# the same settings we had in the mininet-hifi code.
|
||||
if use_hfsc:
|
||||
cmds += [ '%s qdisc add dev %s root handle 5:0 hfsc default 1',
|
||||
'%s class add dev %s parent 5:0 classid 5:1 hfsc sc '
|
||||
cmds = [ '%s qdisc add dev %s root handle 1:0 hfsc default 1',
|
||||
'%s class add dev %s parent 1:0 classid 1:1 hfsc sc '
|
||||
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
|
||||
elif use_tbf:
|
||||
if latency_ms is None:
|
||||
latency_ms = 15 * 8 / bw
|
||||
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
|
||||
'rate %fMbit burst 15000 latency %fms' %
|
||||
( bw, latency_ms ) ]
|
||||
latency_us = 10 * 1500 * 8 / bw
|
||||
cmds = ['%s qdisc add dev %s root handle 1: tbf ' +
|
||||
'rate %fMbit burst 15000 latency %fus' %
|
||||
( bw, latency_us ) ]
|
||||
else:
|
||||
cmds += [ '%s qdisc add dev %s root handle 5:0 htb default 1',
|
||||
'%s class add dev %s parent 5:0 classid 5:1 htb ' +
|
||||
'rate %fMbit burst 15k' % bw ]
|
||||
parent = ' parent 5:1 '
|
||||
cmds = [ '%s qdisc add dev %s root handle 1:0 htb default 1',
|
||||
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
|
||||
'rate %fMbit burst 15k' % bw ]
|
||||
parent = ' parent 1:1 '
|
||||
|
||||
# ECN or RED
|
||||
if enable_ecn:
|
||||
cmds += [ '%s qdisc add dev %s' + parent +
|
||||
'handle 6: red limit 1000000 ' +
|
||||
'min 30000 max 35000 avpkt 1500 ' +
|
||||
cmds = [ '%s qdisc add dev %s' + parent +
|
||||
'handle 10: red limit 1000000 ' +
|
||||
'min 20000 max 25000 avpkt 1000 ' +
|
||||
'burst 20 ' +
|
||||
'bandwidth %fmbit probability 1 ecn' % bw ]
|
||||
parent = ' parent 6: '
|
||||
parent = ' parent 10: '
|
||||
elif enable_red:
|
||||
cmds += [ '%s qdisc add dev %s' + parent +
|
||||
'handle 6: red limit 1000000 ' +
|
||||
'min 30000 max 35000 avpkt 1500 ' +
|
||||
cmds = [ '%s qdisc add dev %s' + parent +
|
||||
'handle 10: red limit 1000000 ' +
|
||||
'min 20000 max 25000 avpkt 1000 ' +
|
||||
'burst 20 ' +
|
||||
'bandwidth %fmbit probability 1' % bw ]
|
||||
parent = ' parent 6: '
|
||||
parent = ' parent 10: '
|
||||
|
||||
return cmds, parent
|
||||
|
||||
@staticmethod
|
||||
def delayCmds( parent, delay=None, jitter=None,
|
||||
loss=None, max_queue_size=None ):
|
||||
def delayCmds( parent, delay=None, loss=None,
|
||||
max_queue_size=None ):
|
||||
"Internal method: return tc commands for delay and loss"
|
||||
cmds = []
|
||||
if delay and delay < 0:
|
||||
error( 'Negative delay', delay, '\n' )
|
||||
elif jitter and jitter < 0:
|
||||
error( 'Negative jitter', jitter, '\n' )
|
||||
elif loss and ( loss < 0 or loss > 100 ):
|
||||
error( 'Bad loss percentage', loss, '%%\n' )
|
||||
else:
|
||||
# Delay/jitter/loss/max queue size
|
||||
netemargs = '%s%s%s%s' % (
|
||||
# Delay/loss/max queue size
|
||||
netemargs = '%s%s%s' % (
|
||||
'delay %s ' % delay if delay is not None else '',
|
||||
'%s ' % jitter if jitter is not None else '',
|
||||
'loss %d ' % loss if loss is not None else '',
|
||||
'limit %d' % max_queue_size if max_queue_size is not None
|
||||
else '' )
|
||||
else '' )
|
||||
if netemargs:
|
||||
cmds = [ '%s qdisc add dev %s ' + parent +
|
||||
' handle 10: netem ' +
|
||||
netemargs ]
|
||||
parent = ' parent 10:1 '
|
||||
return cmds, parent
|
||||
netemargs ]
|
||||
return cmds
|
||||
|
||||
def tc( self, cmd, tc='tc' ):
|
||||
"Execute tc command for our interface"
|
||||
@@ -260,10 +256,9 @@ class TCIntf( Intf ):
|
||||
debug(" *** executing command: %s\n" % c)
|
||||
return self.cmd( c )
|
||||
|
||||
def config( self, bw=None, delay=None, jitter=None, loss=None,
|
||||
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False,
|
||||
max_queue_size=None, **params ):
|
||||
def config( self, bw=None, delay=None, loss=None, disable_gro=True,
|
||||
speedup=0, use_hfsc=False, use_tbf=False, enable_ecn=False,
|
||||
enable_red=False, max_queue_size=None, **params ):
|
||||
"Configure the port and set its properties."
|
||||
|
||||
result = Intf.config( self, **params)
|
||||
@@ -283,24 +278,21 @@ class TCIntf( Intf ):
|
||||
|
||||
# Bandwidth limits via various methods
|
||||
bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup,
|
||||
use_hfsc=use_hfsc, use_tbf=use_tbf,
|
||||
latency_ms=latency_ms,
|
||||
enable_ecn=enable_ecn,
|
||||
enable_red=enable_red )
|
||||
use_hfsc=use_hfsc, use_tbf=use_tbf,
|
||||
enable_ecn=enable_ecn,
|
||||
enable_red=enable_red )
|
||||
cmds += bwcmds
|
||||
|
||||
# Delay/jitter/loss/max_queue_size using netem
|
||||
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter, loss=loss,
|
||||
max_queue_size=max_queue_size,
|
||||
parent=parent )
|
||||
cmds += delaycmds
|
||||
# Delay/loss/max_queue_size using netem
|
||||
cmds += self.delayCmds( delay=delay, loss=loss,
|
||||
max_queue_size=max_queue_size,
|
||||
parent=parent )
|
||||
|
||||
# Ugly but functional: display configuration info
|
||||
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
|
||||
( [ '%s delay' % delay ] if delay is not None else [] ) +
|
||||
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
|
||||
( ['%d%% loss' % loss ] if loss is not None else [] ) +
|
||||
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
|
||||
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
|
||||
if enable_red else [] ) )
|
||||
info( '(' + ' '.join( stuff ) + ') ' )
|
||||
|
||||
@@ -310,7 +302,6 @@ class TCIntf( Intf ):
|
||||
debug( "cmds:", cmds, '\n' )
|
||||
debug( "outputs:", tcoutputs, '\n' )
|
||||
result[ 'tcoutputs'] = tcoutputs
|
||||
result[ 'parent' ] = parent
|
||||
|
||||
return result
|
||||
|
||||
@@ -347,7 +338,7 @@ class Link( object ):
|
||||
if not intfName2:
|
||||
intfName2 = self.intfName( node2, port2 )
|
||||
|
||||
self.makeIntfPair( intfName1, intfName2, node1, node2 )
|
||||
self.makeIntfPair( intfName1, intfName2 )
|
||||
|
||||
if not cls1:
|
||||
cls1 = intf
|
||||
@@ -372,24 +363,13 @@ class Link( object ):
|
||||
return node.name + '-eth' + repr( n )
|
||||
|
||||
@classmethod
|
||||
def makeIntfPair( cls, intf1, intf2, node1=None, node2=None ):
|
||||
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)"""
|
||||
# To be compatible with pid namespaces and chroot in the future
|
||||
# we create links in the root namespace and then move
|
||||
# the ends as needed.
|
||||
# First, make sure there aren't stale links sitting around
|
||||
quietRun( 'ip link delete %s type veth' % intf1 )
|
||||
quietRun( 'ip link delete %s type veth' % intf2 )
|
||||
cmd = 'ip link add %s type veth peer name %s' % ( intf1, intf2 )
|
||||
if node2 and node2.inNamespace:
|
||||
cmd += ' netns %s' % node2
|
||||
errFail( cmd )
|
||||
if node1 and node1.inNamespace:
|
||||
errFail( 'ip link set dev %s netns %s' % ( intf1, node1 ) )
|
||||
makeIntfPair( intf1, intf2 )
|
||||
|
||||
def delete( self ):
|
||||
"Delete this link"
|
||||
@@ -399,7 +379,6 @@ class Link( object ):
|
||||
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,
|
||||
|
||||
+6
-6
@@ -11,11 +11,11 @@ import types
|
||||
OUTPUT = 25
|
||||
|
||||
LEVELS = { 'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
|
||||
# change this to logging.INFO to get printouts when running unit tests
|
||||
LOGLEVELDEFAULT = OUTPUT
|
||||
@@ -117,7 +117,7 @@ class MininetLogger( Logger, object ):
|
||||
Convenience function to support lowercase names.
|
||||
levelName: level name from LEVELS"""
|
||||
level = LOGLEVELDEFAULT
|
||||
if levelname is not None:
|
||||
if levelname != None:
|
||||
if levelname not in LEVELS:
|
||||
raise Exception( 'unknown levelname seen in setLogLevel' )
|
||||
else:
|
||||
|
||||
@@ -19,7 +19,7 @@ def modprobe( mod ):
|
||||
return quietRun( [ 'modprobe', mod ] )
|
||||
|
||||
OF_KMOD = 'ofdatapath'
|
||||
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
|
||||
OVS_KMOD = 'openvswitch_mod'
|
||||
TUN = 'tun'
|
||||
|
||||
def moduleDeps( subtract=None, add=None ):
|
||||
@@ -48,8 +48,8 @@ def moduleDeps( subtract=None, add=None ):
|
||||
modprobeOutput = modprobe( mod )
|
||||
if modprobeOutput:
|
||||
error( 'Error inserting ' + mod +
|
||||
' - is it installed and available via modprobe?\n' +
|
||||
'Error was: "%s"\n' % modprobeOutput )
|
||||
' - is it installed and available via modprobe?\n' +
|
||||
'Error was: "%s"\n' % modprobeOutput )
|
||||
if mod not in lsmod():
|
||||
error( 'Failed to insert ' + mod + ' - quitting.\n' )
|
||||
exit( 1 )
|
||||
@@ -63,6 +63,6 @@ def pathCheck( *args, **kwargs ):
|
||||
for arg in args:
|
||||
if not quietRun( 'which ' + arg ):
|
||||
error( 'Cannot find required executable %s.\n' % arg +
|
||||
'Please make sure that %s is installed ' % moduleName +
|
||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||
'Please make sure that %s is installed ' % moduleName +
|
||||
'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] )
|
||||
exit( 1 )
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
from time import sleep, time
|
||||
from subprocess import Popen, PIPE
|
||||
from multiprocessing import Process
|
||||
import re
|
||||
import os
|
||||
|
||||
from mininet.log import info, error, debug, output
|
||||
from mininet.util import quietRun
|
||||
|
||||
class Monitor(object):
|
||||
|
||||
def __init__(self, output_dir='/tmp'):
|
||||
self.monitors = []
|
||||
self.output_dir = output_dir
|
||||
|
||||
# Add general process monitors
|
||||
# Bandwidth monitor
|
||||
self.monitors.append(Process(target=self.monitor_devs_ng,
|
||||
args=('%s/bwm.txt' % self.output_dir, 1.0)))
|
||||
|
||||
# CPU monitor
|
||||
self.monitors.append(Process(target=self.monitor_cpu,
|
||||
args=('%s/cpu.txt' % self.output_dir, )))
|
||||
|
||||
# cwnd monitor: tcp_probe
|
||||
self.monitors.append(Process(target=self.monitor_cwnd,
|
||||
args=('%s/tcp_probe.txt' % self.output_dir, )))
|
||||
|
||||
def start(self):
|
||||
'''Start all the system monitors'''
|
||||
|
||||
# Set output directory
|
||||
self.set_output_dir(self.output_dir)
|
||||
|
||||
# Start the monitors
|
||||
for m in self.monitors:
|
||||
m.start()
|
||||
|
||||
def stop(self):
|
||||
'''Terminate all the system monitors'''
|
||||
# Stop the monitors
|
||||
for m in self.monitors:
|
||||
m.terminate()
|
||||
self.monitors = []
|
||||
Popen("killall -9 bwm-ng top", shell=True).wait()
|
||||
Popen("killall -9 cat; rmmod tcp_probe > /dev/null 2>&1;", shell=True).wait()
|
||||
|
||||
def set_output_dir(self, output_dir):
|
||||
# Create output directory if it doesn't exist already
|
||||
self.output_dir = output_dir
|
||||
debug('Monitoring output dir: %s' % self.output_dir)
|
||||
if not os.path.isdir(self.output_dir):
|
||||
os.makedirs(self.output_dir)
|
||||
|
||||
def monitor_qlen(self, iface, interval_sec = 0.01, fname='%s/qlen.txt' % '.'):
|
||||
pat_queued = re.compile(r'backlog\s[^\s]+\s([\d]+)p')
|
||||
cmd = "tc -s qdisc show dev %s" % (iface)
|
||||
ret = []
|
||||
open(fname, 'w').write('')
|
||||
while 1:
|
||||
p = Popen(cmd, shell=True, stdout=PIPE)
|
||||
output = p.stdout.read()
|
||||
# Not quite right, but will do for now
|
||||
matches = pat_queued.findall(output)
|
||||
if matches and len(matches) > 1:
|
||||
ret.append(matches[1])
|
||||
t = "%f" % time()
|
||||
open(fname, 'a').write(t + ',' + matches[1] + '\n')
|
||||
sleep(interval_sec)
|
||||
return
|
||||
|
||||
def monitor_cwnd(self, fname='%s/tcp_probe.txt' % '.'):
|
||||
Popen("rmmod tcp_probe > /dev/null 2>&1; modprobe tcp_probe;", shell=True).wait()
|
||||
Popen("cat /proc/net/tcpprobe > %s" % fname, shell=True).wait()
|
||||
|
||||
def monitor_devs_ng(self, fname="%s/txrate.txt" % '.', interval_sec=0.01):
|
||||
"""Uses bwm-ng tool to collect iface tx rate stats. Very reliable."""
|
||||
cmd = "sleep 1; bwm-ng -t %s -o csv -u bits -T rate -C ',' > %s" % (interval_sec * 1000, fname)
|
||||
Popen(cmd, shell=True).wait()
|
||||
|
||||
def monitor_cpu(self, fname="%s/cpu.txt" % '.'):
|
||||
cmd = "(top -b -p 1 -d 1 | grep --line-buffered \"^Cpu\") > %s" % fname
|
||||
Popen(cmd, shell=True).wait()
|
||||
|
||||
def monitor_cpuacct(self, hosts, fname="%s/cpuacct.txt" % '.', interval_sec=1.0):
|
||||
prereqs = ['cgget']
|
||||
for p in prereqs:
|
||||
if not quietRun('which ' + p):
|
||||
error('Could not find %s... not monitoring cpuacct' % p)
|
||||
return
|
||||
hnames = ' '.join([h.name for h in hosts])
|
||||
cpuacct_cmd = 'cgget -g cpuacct %s >> %s' % (hnames, fname)
|
||||
prev_time = time()
|
||||
while 1:
|
||||
sleep(interval_sec - (time() - prev_time))
|
||||
prev_time = time()
|
||||
cpu_usage = Popen(cpuacct_cmd, shell=True).wait()
|
||||
return
|
||||
|
||||
# Obsolete: Use bwm-ng instead (monitor_devs_ng), for reliable stats
|
||||
def monitor_count(self, ipt_args="--src 10.0.0.0/8", interval_sec=0.01, fname='%s/bytes_sent.txt' % '.', chain="OUTPUT"):
|
||||
cmd = "iptables -I %(chain)s 1 %(filter)s -j RETURN" % {
|
||||
"filter": ipt_args,
|
||||
"chain": chain,
|
||||
}
|
||||
# We always erase the first rule; will fix this later
|
||||
Popen("iptables -D %s 1" % chain, shell=True).wait()
|
||||
# Add our rule
|
||||
Popen(cmd, shell=True).wait()
|
||||
open(fname, 'w').write('')
|
||||
cmd = "iptables -vnL %s 1 -Z" % (chain)
|
||||
while 1:
|
||||
p = Popen(cmd, shell=True, stdout=PIPE)
|
||||
output = p.stdout.read().strip()
|
||||
values = output.split(' ')
|
||||
if len(values) > 2:
|
||||
t = "%f" % time()
|
||||
pkts, bytes = values[0], values[1]
|
||||
open(fname, 'a').write(','.join([t, pkts, bytes]) + '\n')
|
||||
sleep(interval_sec)
|
||||
return
|
||||
|
||||
# Obsolete: Use bwm-ng instead (monitor_devs_ng), for reliable stats
|
||||
def monitor_devs(self, dev_pattern='^sw', fname="%s/bytes_sent.txt" % '.', interval_sec=0.01):
|
||||
"""Aggregates (sums) all txed bytes and rate (in Mbps) from devices whose name
|
||||
matches @dev_pattern and writes to @fname"""
|
||||
pat = re.compile(dev_pattern)
|
||||
spaces = re.compile('\s+')
|
||||
open(fname, 'w').write('')
|
||||
prev_tx = {}
|
||||
while 1:
|
||||
lines = open('/proc/net/dev').read().split('\n')
|
||||
t = str(time())
|
||||
total = 0
|
||||
for line in lines:
|
||||
line = spaces.split(line.strip())
|
||||
iface = line[0]
|
||||
if pat.match(iface) and len(line) > 9:
|
||||
tx_bytes = int(line[9])
|
||||
total += tx_bytes - prev_tx.get(iface, tx_bytes)
|
||||
prev_tx[iface] = tx_bytes
|
||||
open(fname, 'a').write(','.join([t, str(total * 8 / interval_sec / 1e6), str(total)]) + "\n")
|
||||
sleep(interval_sec)
|
||||
return
|
||||
|
||||
+58
-181
@@ -91,28 +91,27 @@ import re
|
||||
import select
|
||||
import signal
|
||||
from time import sleep
|
||||
from itertools import chain
|
||||
from datetime import datetime
|
||||
from multiprocessing import Process
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import info, error, debug, output
|
||||
from mininet.node import Host, OVSKernelSwitch, Controller
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.util import quietRun, errFail, fixLimits, numCores, ensureRoot
|
||||
from mininet.util import quietRun, fixLimits, numCores
|
||||
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
|
||||
from mininet.term import cleanUpScreens, makeTerms
|
||||
|
||||
# Mininet version: should be consistent with README and LICENSE
|
||||
VERSION = "2.0.0"
|
||||
from mininet.monitor import Monitor
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
|
||||
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
|
||||
controller=Controller, link=Link, intf=Intf,
|
||||
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||
inNamespace=False,
|
||||
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
|
||||
listenPort=None ):
|
||||
controller=Controller, link=Link, intf=Intf,
|
||||
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||
inNamespace=False,
|
||||
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
|
||||
listenPort=None ):
|
||||
"""Create Mininet object.
|
||||
topo: Topo (topology) object or None
|
||||
switch: default Switch class
|
||||
@@ -148,12 +147,12 @@ class Mininet( object ):
|
||||
self.numCores = numCores()
|
||||
self.nextCore = 0 # next core for pinning hosts to CPUs
|
||||
self.listenPort = listenPort
|
||||
self.monitoring = None
|
||||
|
||||
self.hosts = []
|
||||
self.switches = []
|
||||
self.controllers = []
|
||||
self.links = []
|
||||
|
||||
|
||||
self.nameToNode = {} # name to Node (Host/Switch) objects
|
||||
|
||||
self.terms = [] # list of spawned xterm processes
|
||||
@@ -164,6 +163,13 @@ class Mininet( object ):
|
||||
if topo and build:
|
||||
self.build()
|
||||
|
||||
def set_debug(self, output_dir=None):
|
||||
'''Enable debugging, with output to the specified directory'''
|
||||
dt = datetime.now()
|
||||
if not output_dir:
|
||||
output_dir = '/tmp/mininet-%s-%s' % (str(dt.date()), str(dt.time()))
|
||||
self.monitoring = Monitor(output_dir=output_dir)
|
||||
|
||||
def addHost( self, name, cls=None, **params ):
|
||||
"""Add host.
|
||||
name: name of host to add
|
||||
@@ -173,8 +179,7 @@ class Mininet( object ):
|
||||
# Default IP and MAC addresses
|
||||
defaults = { 'ip': ipAdd( self.nextIP,
|
||||
ipBaseNum=self.ipBaseNum,
|
||||
prefixLen=self.prefixLen ) +
|
||||
'/%s' % self.prefixLen }
|
||||
prefixLen=self.prefixLen ) }
|
||||
if self.autoSetMacs:
|
||||
defaults[ 'mac'] = macColonHex( self.nextIP )
|
||||
if self.autoPinCpus:
|
||||
@@ -218,27 +223,14 @@ class Mininet( object ):
|
||||
self.nameToNode[ name ] = controller_new
|
||||
return controller_new
|
||||
|
||||
# BL: We now have four ways to look up nodes
|
||||
# This may (should?) be cleaned up in the future.
|
||||
# BL: is this better than just using nameToNode[] ?
|
||||
# Should it have a better name?
|
||||
def getNodeByName( self, *args ):
|
||||
"Return node(s) with given name(s)"
|
||||
if len( args ) == 1:
|
||||
return self.nameToNode[ args[ 0 ] ]
|
||||
return [ self.nameToNode[ n ] for n in args ]
|
||||
|
||||
def get( self, *args ):
|
||||
"Convenience alias for getNodeByName"
|
||||
return self.getNodeByName( *args )
|
||||
|
||||
# Even more convenient syntax for node lookup and iteration
|
||||
def __getitem__( self, *args ):
|
||||
"""net [ name ] operator: Return node(s) with given name(s)"""
|
||||
return self.getNodeByName( *args )
|
||||
|
||||
def __iter__( self ):
|
||||
"return iterator over nodes"
|
||||
return chain( self.hosts, self.switches, self.controllers )
|
||||
|
||||
def addLink( self, node1, node2, port1=None, port2=None,
|
||||
cls=None, **params ):
|
||||
""""Add a link from node1 to node2
|
||||
@@ -253,20 +245,13 @@ class Mininet( object ):
|
||||
defaults.update( params )
|
||||
if not cls:
|
||||
cls = self.link
|
||||
link = cls( node1, node2, **defaults )
|
||||
self.links.append( link )
|
||||
return link
|
||||
return cls( node1, node2, **defaults )
|
||||
|
||||
def configHosts( self ):
|
||||
"Configure a set of hosts."
|
||||
for host in self.hosts:
|
||||
info( host.name + ' ' )
|
||||
intf = host.defaultIntf()
|
||||
if intf:
|
||||
host.configDefault( defaultRoute=intf )
|
||||
else:
|
||||
# Don't configure nonexistent intf
|
||||
host.configDefault( ip=None, mac=None )
|
||||
host.configDefault( defaultRoute=host.defaultIntf() )
|
||||
# You're low priority, dude!
|
||||
# BL: do we want to do this here or not?
|
||||
# May not make sense if we have CPU lmiting...
|
||||
@@ -291,11 +276,7 @@ class Mininet( object ):
|
||||
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 )
|
||||
self.addController( 'c0' )
|
||||
|
||||
info( '*** Adding hosts:\n' )
|
||||
for hostName in topo.hosts():
|
||||
@@ -320,7 +301,7 @@ class Mininet( object ):
|
||||
def configureControlNetwork( self ):
|
||||
"Control net config hook: override in subclass"
|
||||
raise Exception( 'configureControlNetwork: '
|
||||
'should be overriden in subclass', self )
|
||||
'should be overriden in subclass', self )
|
||||
|
||||
def build( self ):
|
||||
"Build mininet."
|
||||
@@ -369,40 +350,37 @@ class Mininet( object ):
|
||||
info( switch.name + ' ')
|
||||
switch.start( self.controllers )
|
||||
info( '\n' )
|
||||
info( '*** Starting system monitor\n' )
|
||||
if self.monitoring:
|
||||
m = self.monitoring
|
||||
m.monitors.append(Process(target=m.monitor_cpuacct,
|
||||
args=(self.hosts, '%s/cpuacct.txt' % m.output_dir)))
|
||||
m.start()
|
||||
info( 'Logging monitoring info in: %s\n' % m.output_dir )
|
||||
|
||||
def stop( self ):
|
||||
"Stop the controller(s), switches and hosts"
|
||||
info( '*** Stopping system monitor\n' )
|
||||
if self.monitoring:
|
||||
self.monitoring.stop()
|
||||
if self.terms:
|
||||
info( '*** Stopping %i terms\n' % len( self.terms ) )
|
||||
self.stopXterms()
|
||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||
for controller in self.controllers:
|
||||
info( controller.name + ' ' )
|
||||
controller.stop()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
for switch in self.switches:
|
||||
info( switch.name + ' ' )
|
||||
switch.stop( deleteIntfs=False )
|
||||
info( '\n' )
|
||||
info( '*** Removing links\n' )
|
||||
for link in self.links:
|
||||
info( '.' )
|
||||
link.delete()
|
||||
info( '\n*** Terminating switches\n' )
|
||||
for switch in self.switches:
|
||||
info( '.' )
|
||||
switch.terminate()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
|
||||
for host in self.hosts:
|
||||
info( host.name + ' ' )
|
||||
host.terminate()
|
||||
info( '\n' )
|
||||
nses = quietRun( 'ip netns list').strip().split()
|
||||
info( '*** Removing namespaces' )
|
||||
for ns in nses:
|
||||
errFail( 'ip netns del ' + ns )
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
for switch in self.switches:
|
||||
info( switch.name + ' ' )
|
||||
switch.stop()
|
||||
info( '\n' )
|
||||
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
|
||||
for controller in self.controllers:
|
||||
info( controller.name + ' ' )
|
||||
controller.stop()
|
||||
info( '\n' )
|
||||
info( '\n*** Done\n' )
|
||||
|
||||
def run( self, test, *args, **kwargs ):
|
||||
@@ -448,17 +426,16 @@ class Mininet( object ):
|
||||
return (1, 0)
|
||||
r = r'(\d+) packets transmitted, (\d+) received'
|
||||
m = re.search( r, pingOutput )
|
||||
if m is None:
|
||||
if m == None:
|
||||
error( '*** Error: could not parse ping output: %s\n' %
|
||||
pingOutput )
|
||||
pingOutput )
|
||||
return (1, 0)
|
||||
sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
|
||||
return sent, received
|
||||
|
||||
def ping( self, hosts=None, timeout=None ):
|
||||
def ping( self, hosts=None ):
|
||||
"""Ping between all specified hosts.
|
||||
hosts: list of hosts
|
||||
timeout: time to wait for a response, as string
|
||||
returns: ploss packet loss percentage"""
|
||||
# should we check if running?
|
||||
packets = 0
|
||||
@@ -471,10 +448,7 @@ class Mininet( object ):
|
||||
output( '%s -> ' % node.name )
|
||||
for dest in hosts:
|
||||
if node != dest:
|
||||
opts = ''
|
||||
if timeout:
|
||||
opts = '-W %s' % timeout
|
||||
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
|
||||
result = node.cmd( 'ping -c1 ' + dest.IP() )
|
||||
sent, received = self._parsePing( result )
|
||||
packets += sent
|
||||
if received > sent:
|
||||
@@ -490,61 +464,6 @@ class Mininet( object ):
|
||||
( ploss, lost, packets ) )
|
||||
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 ):
|
||||
"""Ping between all hosts.
|
||||
returns: ploss packet loss percentage"""
|
||||
@@ -556,17 +475,6 @@ class Mininet( object ):
|
||||
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
|
||||
return self.ping( hosts=hosts )
|
||||
|
||||
def pingAllFull( self ):
|
||||
"""Ping between all hosts.
|
||||
returns: ploss packet loss percentage"""
|
||||
return self.pingFull()
|
||||
|
||||
def pingPairFull( self ):
|
||||
"""Ping between first two hosts, useful for testing.
|
||||
returns: ploss packet loss percentage"""
|
||||
hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
|
||||
return self.pingFull( hosts=hosts )
|
||||
|
||||
@staticmethod
|
||||
def _parseIperf( iperfOutput ):
|
||||
"""Parse iperf output and return bandwidth.
|
||||
@@ -612,11 +520,11 @@ class Mininet( object ):
|
||||
servout += server.monitor()
|
||||
if l4Type == 'TCP':
|
||||
while 'Connected' not in client.cmd(
|
||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||
output('waiting for iperf to start up...')
|
||||
sleep(.5)
|
||||
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
|
||||
bwArgs )
|
||||
bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
server.sendInt()
|
||||
servout += server.waitOutput()
|
||||
@@ -627,42 +535,6 @@ class Mininet( object ):
|
||||
output( '*** Results: %s\n' % result )
|
||||
return result
|
||||
|
||||
def runCpuLimitTest( self, cpu, duration=5 ):
|
||||
"""run CPU limit test with 'while true' processes.
|
||||
cpu: desired CPU fraction of each host
|
||||
duration: test duration in seconds
|
||||
returns a single list of measured CPU fractions as floats.
|
||||
"""
|
||||
pct = cpu * 100
|
||||
info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
|
||||
hosts = self.hosts
|
||||
for h in hosts:
|
||||
h.cmd( 'while true; do a=1; done &' )
|
||||
pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
|
||||
pids_str = ",".join(["%s" % pid for pid in pids])
|
||||
cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
|
||||
# It's a shame that this is what pylint prefers
|
||||
outputs = []
|
||||
for _ in range( duration ):
|
||||
sleep( 1 )
|
||||
outputs.append( quietRun( cmd ).strip() )
|
||||
for h in hosts:
|
||||
h.cmd( 'kill %1' )
|
||||
cpu_fractions = []
|
||||
for test_output in outputs:
|
||||
# Split by line. Ignore first line, which looks like this:
|
||||
# PID %CPU COMMAND\n
|
||||
for line in test_output.split('\n')[1:]:
|
||||
r = r'\d+\s*(\d+\.\d+)'
|
||||
m = re.search( r, line )
|
||||
if m is None:
|
||||
error( '*** Error: could not extract CPU fraction: %s\n' %
|
||||
line )
|
||||
return None
|
||||
cpu_fractions.append( float( m.group( 1 ) ) )
|
||||
output( '*** Results: %s\n' % cpu_fractions )
|
||||
return cpu_fractions
|
||||
|
||||
# BL: I think this can be rewritten now that we have
|
||||
# a real link class.
|
||||
def configLinkStatus( self, src, dst, status ):
|
||||
@@ -704,7 +576,12 @@ class Mininet( object ):
|
||||
"Initialize Mininet"
|
||||
if cls.inited:
|
||||
return
|
||||
ensureRoot()
|
||||
if os.getuid() != 0:
|
||||
# Note: this script must be run as root
|
||||
# Probably we should only sudo when we need
|
||||
# to as per Big Switch's patch
|
||||
print "*** Mininet must run as root."
|
||||
exit( 1 )
|
||||
fixLimits()
|
||||
cls.inited = True
|
||||
|
||||
@@ -745,7 +622,7 @@ class MininetWithControlNet( Mininet ):
|
||||
# in the control network location.
|
||||
|
||||
def configureRoutedControlNetwork( self, ip='192.168.123.1',
|
||||
prefixLen=16 ):
|
||||
prefixLen=16 ):
|
||||
"""Configure a routed control network on controller and switches.
|
||||
For use with the user datapath only right now."""
|
||||
controller = self.controllers[ 0 ]
|
||||
|
||||
+80
-196
@@ -49,12 +49,10 @@ import re
|
||||
import signal
|
||||
import select
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
from sys import stdout
|
||||
from time import sleep
|
||||
|
||||
from mininet.log import info, error, warn, debug
|
||||
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
|
||||
numCores, retry, mountCgroups )
|
||||
numCores, retry, mountCgroups )
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
|
||||
from mininet.link import Link, Intf, TCIntf
|
||||
|
||||
@@ -85,8 +83,8 @@ class Node( object ):
|
||||
|
||||
# Make pylint happy
|
||||
( self.shell, self.execed, self.pid, self.stdin, self.stdout,
|
||||
self.lastPid, self.lastCmd, self.pollOut ) = (
|
||||
None, None, None, None, None, None, None, None )
|
||||
self.lastPid, self.lastCmd, self.pollOut ) = (
|
||||
None, None, None, None, None, None, None, None )
|
||||
self.waiting = False
|
||||
self.readbuf = ''
|
||||
|
||||
@@ -109,27 +107,20 @@ class Node( object ):
|
||||
|
||||
# Command support via shell process in namespace
|
||||
|
||||
def _popen( self, *args, **kwargs ):
|
||||
"Internal wrapper for Popen"
|
||||
old = signal.signal( signal.SIGINT, signal.SIG_IGN )
|
||||
result = Popen( *args, **kwargs )
|
||||
signal.signal( signal.SIGINT, old )
|
||||
return result
|
||||
|
||||
def startShell( self ):
|
||||
"Start a shell process for running commands"
|
||||
if self.shell:
|
||||
error( "%s: shell is already running" )
|
||||
return
|
||||
# bash -m: enable job control
|
||||
# -s: pass $* to shell, and make process easy to find in ps
|
||||
cmd = [ 'bash', '-ms', 'mininet:' + self.name ]
|
||||
# mnexec: (c)lose descriptors, (d)etach from tty,
|
||||
# (p)rint pid, and run in (n)amespace
|
||||
opts = '-cdp'
|
||||
if self.inNamespace:
|
||||
quietRun( 'ip netns del ' + self.name )
|
||||
errFail( 'ip netns add ' + self.name )
|
||||
cmd = [ 'ip', 'netns', 'exec', self.name ] + cmd
|
||||
self.shell = self._popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
|
||||
close_fds=True )
|
||||
opts += 'n'
|
||||
# bash -m: enable job control
|
||||
cmd = [ 'mnexec', opts, 'bash', '-m' ]
|
||||
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
|
||||
close_fds=True )
|
||||
self.stdin = self.shell.stdin
|
||||
self.stdout = self.shell.stdout
|
||||
self.pid = self.shell.pid
|
||||
@@ -148,16 +139,11 @@ class Node( object ):
|
||||
|
||||
def cleanup( self ):
|
||||
"Help python collect its garbage."
|
||||
if self.shell:
|
||||
self.shell.terminate()
|
||||
self.shell.wait()
|
||||
self.shell = None
|
||||
if self.inNamespace:
|
||||
# Note: this will only work if there are no other
|
||||
# processes running in namespaces, but we'll do it anyway
|
||||
quietRun( 'ip netns del ' + self.name )
|
||||
|
||||
# Subshell I/O, commands and control
|
||||
if not self.inNamespace:
|
||||
for intfName in self.intfNames():
|
||||
if self.name in intfName:
|
||||
quietRun( 'ip link del ' + intfName )
|
||||
self.shell = None
|
||||
|
||||
def read( self, maxbytes=1024 ):
|
||||
"""Buffered read from node, non-blocking.
|
||||
@@ -181,7 +167,7 @@ class Node( object ):
|
||||
if '\n' not in self.readbuf:
|
||||
return None
|
||||
pos = self.readbuf.find( '\n' )
|
||||
line = self.readbuf[ 0: pos ]
|
||||
line = self.readbuf[ 0 : pos ]
|
||||
self.readbuf = self.readbuf[ pos + 1: ]
|
||||
return line
|
||||
|
||||
@@ -296,43 +282,6 @@ class Node( object ):
|
||||
cmd: string"""
|
||||
return self.cmd( *args, **{ 'verbose': True } )
|
||||
|
||||
def popen( self, *args, **kwargs ):
|
||||
"""Return a Popen() object in our namespace
|
||||
args: Popen() args, single list, or string
|
||||
kwargs: Popen() keyword args"""
|
||||
defaults = { 'stdout': PIPE, 'stderr': PIPE,
|
||||
'mncmd':
|
||||
[ 'mnexec', '-da', str( self.pid ) ] }
|
||||
defaults.update( kwargs )
|
||||
if len( args ) == 1:
|
||||
if type( args[ 0 ] ) is list:
|
||||
# popen([cmd, arg1, arg2...])
|
||||
cmd = args[ 0 ]
|
||||
elif type( args[ 0 ] ) is str:
|
||||
# popen("cmd arg1 arg2...")
|
||||
cmd = args[ 0 ].split()
|
||||
else:
|
||||
raise Exception( 'popen() requires a string or list' )
|
||||
elif len( args ) > 0:
|
||||
# popen( cmd, arg1, arg2... )
|
||||
cmd = list( args )
|
||||
# Attach to our namespace using mnexec -a
|
||||
mncmd = defaults[ 'mncmd' ]
|
||||
del defaults[ 'mncmd' ]
|
||||
cmd = mncmd + cmd
|
||||
# Shell requires a string, not a list!
|
||||
if defaults.get( 'shell', False ):
|
||||
cmd = ' '.join( cmd )
|
||||
return self._popen( cmd, **defaults )
|
||||
|
||||
def pexec( self, *args, **kwargs ):
|
||||
"""Execute a command using popen
|
||||
returns: out, err, exitcode"""
|
||||
popen = self.popen( *args, **kwargs)
|
||||
out, err = popen.communicate()
|
||||
exitcode = popen.wait()
|
||||
return out, err, exitcode
|
||||
|
||||
# Interface management, configuration, and routing
|
||||
|
||||
# BL notes: This might be a bit redundant or over-complicated.
|
||||
@@ -357,8 +306,10 @@ class Node( object ):
|
||||
self.ports[ intf ] = port
|
||||
self.nameToIntf[ intf.name ] = intf
|
||||
debug( '\n' )
|
||||
assert intf.name in self.cmd( 'ip link show' )
|
||||
debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
|
||||
if self.inNamespace:
|
||||
debug( 'moving', intf, 'into namespace for', self.name, '\n' )
|
||||
moveIntf( intf.name, self )
|
||||
|
||||
def defaultIntf( self ):
|
||||
"Return interface for lowest port"
|
||||
@@ -367,23 +318,17 @@ class Node( object ):
|
||||
return self.intfs[ min( ports ) ]
|
||||
else:
|
||||
warn( '*** defaultIntf: warning:', self.name,
|
||||
'has no interfaces\n' )
|
||||
'has no interfaces\n' )
|
||||
|
||||
def intf( self, intf='' ):
|
||||
"""Return our interface object with given string name,
|
||||
default intf if name is falsy (None, empty string, etc).
|
||||
or the input intf arg.
|
||||
|
||||
Having this fcn return its arg for Intf objects makes it
|
||||
easier to construct functions with flexible input args for
|
||||
interfaces (those that accept both string names and Intf objects).
|
||||
"""
|
||||
"""Return our interface object with given name,
|
||||
or default intf if name is empty"""
|
||||
if not intf:
|
||||
return self.defaultIntf()
|
||||
elif type( intf) is str:
|
||||
return self.nameToIntf[ intf ]
|
||||
else:
|
||||
return intf
|
||||
return None
|
||||
|
||||
def connectionsTo( self, node):
|
||||
"Return [ intf1, intf2... ] for all intfs that connect self to node."
|
||||
@@ -409,7 +354,6 @@ class Node( object ):
|
||||
for intf in self.intfs.values():
|
||||
intf.delete()
|
||||
info( '.' )
|
||||
stdout.flush()
|
||||
|
||||
# Routing support
|
||||
|
||||
@@ -444,7 +388,7 @@ class Node( object ):
|
||||
|
||||
def setIP( self, ip, prefixLen=8, intf=None ):
|
||||
"""Set the IP address for an interface.
|
||||
intf: intf or intf name
|
||||
intf: interface name
|
||||
ip: IP address as a string
|
||||
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
|
||||
# This should probably be rethought
|
||||
@@ -458,7 +402,7 @@ class Node( object ):
|
||||
|
||||
def MAC( self, intf=None ):
|
||||
"Return MAC address of a node or specific interface."
|
||||
return self.intf( intf ).MAC()
|
||||
return self.intf( intf ).IP()
|
||||
|
||||
def intfIsUp( self, intf=None ):
|
||||
"Check if an interface is up."
|
||||
@@ -532,9 +476,9 @@ class Node( object ):
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
for i in self.intfList() ] ) )
|
||||
for i in self.intfList() ] ) )
|
||||
return '<%s %s: %s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
|
||||
def __str__( self ):
|
||||
"Abbreviated string representation"
|
||||
@@ -585,7 +529,6 @@ class CPULimitedHost( Host ):
|
||||
# still does better with larger period values.
|
||||
self.period_us = kwargs.get( 'period_us', 100000 )
|
||||
self.sched = sched
|
||||
self.rtprio = 20
|
||||
|
||||
def cgroupSet( self, param, value, resource='cpu' ):
|
||||
"Set a cgroup parameter and return its value"
|
||||
@@ -610,24 +553,13 @@ class CPULimitedHost( Host ):
|
||||
_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
|
||||
return exitcode != 0
|
||||
|
||||
def popen( self, *args, **kwargs ):
|
||||
"""Return a Popen() object in node's namespace
|
||||
args: Popen() args, single list, or string
|
||||
kwargs: Popen() keyword args"""
|
||||
# Tell mnexec to execute command in our cgroup
|
||||
mncmd = [ 'mnexec', '-da', str( self.pid ),
|
||||
'-g', self.name ]
|
||||
if self.sched == 'rt':
|
||||
mncmd += [ '-r', str( self.rtprio ) ]
|
||||
return Host.popen( self, *args, mncmd=mncmd, **kwargs )
|
||||
|
||||
def cleanup( self ):
|
||||
"Clean up our cgroup"
|
||||
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
|
||||
|
||||
def chrt( self ):
|
||||
def chrt( self, prio=20 ):
|
||||
"Set RT scheduling priority"
|
||||
quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
|
||||
quietRun( 'chrt -p %s %s' % ( prio, self.pid ) )
|
||||
result = quietRun( 'chrt -p %s' % self.pid )
|
||||
firstline = result.split( '\n' )[ 0 ]
|
||||
lastword = firstline.split( ' ' )[ -1 ]
|
||||
@@ -686,7 +618,7 @@ class CPULimitedHost( Host ):
|
||||
self.cgroupSet( qstr, quota )
|
||||
if sched == 'rt':
|
||||
# Set RT priority if necessary
|
||||
self.chrt()
|
||||
self.chrt( prio=20 )
|
||||
info( '(%s %d/%dus) ' % ( sched, quota, period ) )
|
||||
|
||||
def setCPUs( self, cores, mems=0 ):
|
||||
@@ -702,7 +634,7 @@ class CPULimitedHost( Host ):
|
||||
# We have to do this here after we've specified
|
||||
# cpus and mems
|
||||
errFail( 'cgclassify -g cpuset:/%s %s' % (
|
||||
self.name, self.pid ) )
|
||||
self.name, self.pid ) )
|
||||
|
||||
def config( self, cpu=None, cores=None, **params ):
|
||||
"""cpu: desired overall system CPU fraction
|
||||
@@ -749,10 +681,9 @@ class Switch( Node ):
|
||||
an OpenFlow switch."""
|
||||
|
||||
portBase = 1 # Switches start with port 1 in OpenFlow
|
||||
dpidLen = 16 # digits in dpid passed to switch
|
||||
|
||||
def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
|
||||
"""dpid: dpid for switch (or None to derive from name, e.g. s1 -> 1)
|
||||
"""dpid: dpid for switch (or None for default)
|
||||
opts: additional switch options
|
||||
listenPort: port to listen on for dpctl connections"""
|
||||
Node.__init__( self, name, **params )
|
||||
@@ -764,15 +695,10 @@ class Switch( Node ):
|
||||
|
||||
def defaultDpid( self ):
|
||||
"Derive dpid from switch name, s1 -> 1"
|
||||
try:
|
||||
dpid = int( re.findall( '\d+', self.name )[ 0 ] )
|
||||
dpid = hex( dpid )[ 2: ]
|
||||
dpid = '0' * ( self.dpidLen - len( dpid ) ) + dpid
|
||||
return dpid
|
||||
except IndexError:
|
||||
raise Exception( 'Unable to derive default datapath ID - '
|
||||
'please either specify a dpid or use a '
|
||||
'canonical switch name such as s23.' )
|
||||
dpid = int( re.findall( '\d+', self.name )[ 0 ] )
|
||||
dpid = hex( dpid )[ 2: ]
|
||||
dpid = '0' * ( 16 - len( dpid ) ) + dpid
|
||||
return dpid
|
||||
|
||||
def defaultIntf( self ):
|
||||
"Return control interface"
|
||||
@@ -789,27 +715,24 @@ class Switch( Node ):
|
||||
return Node.sendCmd( self, *cmd, **kwargs )
|
||||
else:
|
||||
error( '*** Error: %s has execed and cannot accept commands' %
|
||||
self.name )
|
||||
self.name )
|
||||
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
for i in self.intfList() ] ) )
|
||||
for i in self.intfList() ] ) )
|
||||
return '<%s %s: %s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
|
||||
class UserSwitch( Switch ):
|
||||
"User-space switch."
|
||||
|
||||
dpidLen = 12
|
||||
|
||||
def __init__( self, name, **kwargs ):
|
||||
"""Init.
|
||||
name: name for the switch"""
|
||||
Switch.__init__( self, name, **kwargs )
|
||||
pathCheck( 'ofdatapath', 'ofprotocol',
|
||||
moduleName='the OpenFlow reference user switch' +
|
||||
'(openflow.org)' )
|
||||
moduleName='the OpenFlow reference user switch (openflow.org)' )
|
||||
if self.listenPort:
|
||||
self.opts += ' --listen=ptcp:%i ' % self.listenPort
|
||||
|
||||
@@ -830,27 +753,25 @@ class UserSwitch( Switch ):
|
||||
"""Start OpenFlow reference user datapath.
|
||||
Log to /tmp/sN-{ofd,ofp}.log.
|
||||
controllers: list of controller objects"""
|
||||
# Add controllers
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
controller = controllers[ 0 ]
|
||||
ofdlog = '/tmp/' + self.name + '-ofd.log'
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
|
||||
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
|
||||
' punix:/tmp/' + self.name + ' -d ' + self.dpid +
|
||||
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||
' punix:/tmp/' + self.name + ' -d ' + self.dpid +
|
||||
' --no-slicing ' +
|
||||
' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||
self.cmd( 'ofprotocol unix:/tmp/' + self.name +
|
||||
' ' + clist +
|
||||
' --fail=closed ' + self.opts +
|
||||
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
|
||||
' --fail=closed ' + self.opts +
|
||||
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||
|
||||
def stop( self, deleteIntfs=True ):
|
||||
def stop( self ):
|
||||
"Stop OpenFlow reference user datapath."
|
||||
self.cmd( 'kill %ofdatapath' )
|
||||
self.cmd( 'kill %ofprotocol' )
|
||||
if deleteIntfs:
|
||||
self.deleteIntfs()
|
||||
self.deleteIntfs()
|
||||
|
||||
|
||||
class OVSLegacyKernelSwitch( Switch ):
|
||||
@@ -867,14 +788,14 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
self.intf = self.dp
|
||||
if self.inNamespace:
|
||||
error( "OVSKernelSwitch currently only works"
|
||||
" in the root namespace.\n" )
|
||||
" in the root namespace.\n" )
|
||||
exit( 1 )
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Ensure any dependencies are loaded; if not, try to load them."
|
||||
pathCheck( 'ovs-dpctl', 'ovs-openflowd',
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
|
||||
def start( self, controllers ):
|
||||
@@ -888,21 +809,19 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
|
||||
self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
|
||||
# Run protocol daemon
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
controller = controllers[ 0 ]
|
||||
self.cmd( 'ovs-openflowd ' + self.dp +
|
||||
' ' + clist +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --datapath-id=' + self.dpid +
|
||||
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --datapath-id=' + self.dpid +
|
||||
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||
self.execed = False
|
||||
|
||||
def stop( self, deleteIntfs=True ):
|
||||
def stop( self ):
|
||||
"Terminate kernel datapath."
|
||||
quietRun( 'ovs-dpctl del-dp ' + self.dp )
|
||||
self.cmd( 'kill %ovs-openflowd' )
|
||||
if deleteIntfs:
|
||||
self.deleteIntfs()
|
||||
self.deleteIntfs()
|
||||
|
||||
|
||||
class OVSSwitch( Switch ):
|
||||
@@ -919,10 +838,8 @@ class OVSSwitch( Switch ):
|
||||
def setup( cls ):
|
||||
"Make sure Open vSwitch is installed and working"
|
||||
pathCheck( 'ovs-vsctl',
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
# This should no longer be needed, and it breaks
|
||||
# with OVS 1.7 which has renamed the kernel module:
|
||||
# moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
|
||||
if exitcode:
|
||||
error( out + err +
|
||||
@@ -968,24 +885,19 @@ class OVSSwitch( Switch ):
|
||||
# Annoyingly, --if-exists option seems not to work
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
self.cmd( 'ovs-vsctl add-br', self )
|
||||
self.cmd( 'ovs-vsctl -- set Bridge', self,
|
||||
'other_config:datapath-id=' + self.dpid )
|
||||
self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode )
|
||||
for intf in self.intfList():
|
||||
if not intf.IP():
|
||||
self.attach( intf )
|
||||
# Add controllers
|
||||
clist = ' '.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
if self.listenPort:
|
||||
clist += ' ptcp:%s' % self.listenPort
|
||||
self.cmd( 'ovs-vsctl set-controller', self, clist )
|
||||
|
||||
def stop( self, deleteIntfs=True ):
|
||||
"Stop OVS switch."
|
||||
def stop( self ):
|
||||
"Terminate OVS switch."
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
if deleteIntfs:
|
||||
self.deleteIntfs()
|
||||
self.deleteIntfs()
|
||||
|
||||
OVSKernelSwitch = OVSSwitch
|
||||
|
||||
@@ -995,35 +907,15 @@ class Controller( Node ):
|
||||
OpenFlow controller."""
|
||||
|
||||
def __init__( self, name, inNamespace=False, command='controller',
|
||||
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
|
||||
port=6633, **params ):
|
||||
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
|
||||
port=6633, **params ):
|
||||
self.command = command
|
||||
self.cargs = cargs
|
||||
self.cdir = cdir
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
Node.__init__( self, name, inNamespace=inNamespace,
|
||||
ip=ip, **params )
|
||||
self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary
|
||||
self.checkListening()
|
||||
|
||||
def checkListening( self ):
|
||||
"Make sure no controllers are running on our port"
|
||||
# Verify that Telnet is installed first:
|
||||
out, _err, returnCode = errRun( "which telnet" )
|
||||
if 'telnet' not in out or returnCode != 0:
|
||||
raise Exception( "Error running telnet to check for listening "
|
||||
"controllers; please check that it is "
|
||||
"installed." )
|
||||
listening = self.cmd( "echo A | telnet -e A %s %d" %
|
||||
( self.ip, self.port ) )
|
||||
if 'Unable' not in listening:
|
||||
servers = self.cmd( 'netstat -atp' ).split( '\n' )
|
||||
pstr = ':%d ' % self.port
|
||||
clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
|
||||
raise Exception( "Please shut down the controller which is"
|
||||
" running on port %d:\n" % self.port +
|
||||
'\n'.join( clist ) )
|
||||
ip=ip, **params )
|
||||
|
||||
def start( self ):
|
||||
"""Start <controller> <args> on controller.
|
||||
@@ -1033,7 +925,7 @@ class Controller( Node ):
|
||||
if self.cdir is not None:
|
||||
self.cmd( 'cd ' + self.cdir )
|
||||
self.cmd( self.command + ' ' + self.cargs % self.port +
|
||||
' 1>' + cout + ' 2>' + cout + '&' )
|
||||
' 1>' + cout + ' 2>' + cout + '&' )
|
||||
self.execed = False
|
||||
|
||||
def stop( self ):
|
||||
@@ -1052,8 +944,8 @@ class Controller( Node ):
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
return '<%s %s: %s:%s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name,
|
||||
self.IP(), self.port, self.pid )
|
||||
self.__class__.__name__, self.name,
|
||||
self.IP(), self.port, self.pid )
|
||||
|
||||
|
||||
class OVSController( Controller ):
|
||||
@@ -1081,24 +973,24 @@ class NOX( Controller ):
|
||||
noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
|
||||
|
||||
Controller.__init__( self, name,
|
||||
command=noxCoreDir + '/nox_core',
|
||||
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
|
||||
' '.join( noxArgs ),
|
||||
cdir=noxCoreDir,
|
||||
**kwargs )
|
||||
command=noxCoreDir + '/nox_core',
|
||||
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
|
||||
' '.join( noxArgs ),
|
||||
cdir=noxCoreDir, **kwargs )
|
||||
|
||||
|
||||
class RemoteController( Controller ):
|
||||
"Controller running outside of Mininet's control."
|
||||
|
||||
def __init__( self, name, ip='127.0.0.1',
|
||||
port=6633, **kwargs):
|
||||
def __init__( self, name, defaultIP='127.0.0.1',
|
||||
port=6633, **kwargs):
|
||||
"""Init.
|
||||
name: name to give controller
|
||||
ip: the IP address where the remote controller is
|
||||
defaultIP: the IP address where the remote controller is
|
||||
listening
|
||||
port: the port where the remote controller is listening"""
|
||||
Controller.__init__( self, name, ip=ip, port=port, **kwargs )
|
||||
Controller.__init__( self, name, defaultIP=defaultIP, port=port,
|
||||
**kwargs )
|
||||
|
||||
def start( self ):
|
||||
"Overridden to do nothing."
|
||||
@@ -1107,11 +999,3 @@ class RemoteController( Controller ):
|
||||
def stop( self ):
|
||||
"Overridden to do nothing."
|
||||
return
|
||||
|
||||
def checkListening( self ):
|
||||
"Warn if remote controller is not accessible"
|
||||
listening = self.cmd( "echo A | telnet -e A %s %d" %
|
||||
( self.ip, self.port ) )
|
||||
if 'Unable' in listening:
|
||||
warn( "Unable to contact the remote controller"
|
||||
" at %s:%d\n" % ( self.ip, self.port ) )
|
||||
|
||||
+32
-39
@@ -1,67 +1,60 @@
|
||||
"""
|
||||
Terminal creation and cleanup.
|
||||
Utility functions to run a terminal (connected via socat(1)) on each host.
|
||||
Utility functions to run a term (connected via screen(1)) on each host.
|
||||
|
||||
Requires socat(1) and xterm(1).
|
||||
Requires GNU screen(1) and xterm(1).
|
||||
Optionally uses gnome-terminal.
|
||||
"""
|
||||
|
||||
from os import environ
|
||||
import re
|
||||
from subprocess import Popen
|
||||
|
||||
from mininet.log import error
|
||||
from mininet.util import quietRun, errRun
|
||||
from mininet.util import quietRun
|
||||
|
||||
def tunnelX11( node, display=None):
|
||||
"""Create an X11 tunnel from node:6000 to the root host
|
||||
display: display on root host (optional)
|
||||
returns: node $DISPLAY, Popen object for tunnel"""
|
||||
if display is None:
|
||||
display = environ[ 'DISPLAY' ]
|
||||
host, screen = display.split( ':' )
|
||||
# Unix sockets should work
|
||||
if not host or host == 'unix':
|
||||
# GDM3 doesn't put credentials in .Xauthority,
|
||||
# so allow root to just connect
|
||||
quietRun( 'xhost +si:localuser:root' )
|
||||
return display, None
|
||||
else:
|
||||
# Create a tunnel for the TCP connection
|
||||
port = 6000 + int( float( screen ) )
|
||||
connection = r'TCP\:%s\:%s' % ( host, port )
|
||||
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
|
||||
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
|
||||
return 'localhost:' + screen, node.popen( cmd )
|
||||
def quoteArg( arg ):
|
||||
"Quote an argument if it contains spaces."
|
||||
return repr( arg ) if ' ' in arg else arg
|
||||
|
||||
def makeTerm( node, title='Node', term='xterm', display=None ):
|
||||
"""Create an X11 tunnel to the node and start up a terminal.
|
||||
def makeTerm( node, title='Node', term='xterm' ):
|
||||
"""Run screen on a node, and hook up a terminal.
|
||||
node: Node object
|
||||
title: base title
|
||||
term: 'xterm' or 'gterm'
|
||||
returns: two Popen objects, tunnel and terminal"""
|
||||
returns: process created"""
|
||||
title += ': ' + node.name
|
||||
if not node.inNamespace:
|
||||
title += ' (root)'
|
||||
cmds = {
|
||||
'xterm': [ 'xterm', '-title', title, '-display' ],
|
||||
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
|
||||
'xterm': [ 'xterm', '-title', title, '-e' ],
|
||||
'gterm': [ 'gnome-terminal', '--title', title, '-e' ]
|
||||
}
|
||||
if term not in cmds:
|
||||
error( 'invalid terminal type: %s' % term )
|
||||
return
|
||||
display, tunnel = tunnelX11( node, display )
|
||||
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
|
||||
return [ tunnel, term ] if tunnel else [ term ]
|
||||
if not node.execed:
|
||||
node.cmd( 'screen -dmS ' + 'mininet.' + node.name)
|
||||
args = [ 'screen', '-D', '-RR', '-S', 'mininet.' + node.name ]
|
||||
else:
|
||||
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
|
||||
if term == 'gterm':
|
||||
# Compress these for gnome-terminal, which expects one token
|
||||
# to follow the -e option
|
||||
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
|
||||
return Popen( cmds[ term ] + args )
|
||||
|
||||
def cleanUpScreens():
|
||||
"Remove moldy socat X11 tunnels."
|
||||
errRun( "pkill -9 -f mnexec.*socat" )
|
||||
"Remove moldy old screen sessions."
|
||||
r = r'(\d+\.mininet\.[hsc]\d+)'
|
||||
output = quietRun( 'screen -ls' ).split( '\n' )
|
||||
for line in output:
|
||||
m = re.search( r, line )
|
||||
if m:
|
||||
quietRun( 'screen -S ' + m.group( 1 ) + ' -X quit' )
|
||||
|
||||
def makeTerms( nodes, title='Node', term='xterm' ):
|
||||
"""Create terminals.
|
||||
nodes: list of Node objects
|
||||
title: base title for each
|
||||
returns: list of created tunnel/terminal processes"""
|
||||
terms = []
|
||||
for node in nodes:
|
||||
terms += makeTerm( node, title, term )
|
||||
return terms
|
||||
returns: list of created terminal processes"""
|
||||
return [ makeTerm( node, title, term ) for node in nodes ]
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
#!/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()
|
||||
+26
-31
@@ -34,7 +34,7 @@ class Topo(object):
|
||||
self.lopts = {} if lopts is None else lopts
|
||||
self.ports = {} # ports[src][dst] is port on src that connects to dst
|
||||
|
||||
def addNode(self, name, **opts):
|
||||
def add_node(self, name, **opts):
|
||||
"""Add Node to graph.
|
||||
name: name
|
||||
opts: node options
|
||||
@@ -43,40 +43,40 @@ class Topo(object):
|
||||
self.node_info[name] = opts
|
||||
return name
|
||||
|
||||
def addHost(self, name, **opts):
|
||||
def add_host(self, name, **opts):
|
||||
"""Convenience method: Add host to graph.
|
||||
name: host name
|
||||
opts: host options
|
||||
returns: host name"""
|
||||
if not opts and self.hopts:
|
||||
opts = self.hopts
|
||||
return self.addNode(name, **opts)
|
||||
return self.add_node(name, **opts)
|
||||
|
||||
def addSwitch(self, name, **opts):
|
||||
def add_switch(self, name, **opts):
|
||||
"""Convenience method: Add switch to graph.
|
||||
name: switch name
|
||||
opts: switch options
|
||||
returns: switch name"""
|
||||
if not opts and self.sopts:
|
||||
opts = self.sopts
|
||||
result = self.addNode(name, isSwitch=True, **opts)
|
||||
result = self.add_node(name, is_switch=True, **opts)
|
||||
return result
|
||||
|
||||
def addLink(self, node1, node2, port1=None, port2=None,
|
||||
**opts):
|
||||
def add_link(self, node1, node2, port1=None, port2=None,
|
||||
**opts):
|
||||
"""node1, node2: nodes to link together
|
||||
port1, port2: ports (optional)
|
||||
opts: link options (optional)
|
||||
returns: link info key"""
|
||||
if not opts and self.lopts:
|
||||
opts = self.lopts
|
||||
self.addPort(node1, node2, port1, port2)
|
||||
self.add_port(node1, node2, port1, port2)
|
||||
key = tuple(self.sorted([node1, node2]))
|
||||
self.link_info[key] = opts
|
||||
self.g.add_edge(*key)
|
||||
return key
|
||||
|
||||
def addPort(self, src, dst, sport=None, dport=None):
|
||||
def add_port(self, src, dst, sport=None, dport=None):
|
||||
'''Generate port mapping for new edge.
|
||||
@param src source switch name
|
||||
@param dst destination switch name
|
||||
@@ -84,8 +84,8 @@ class Topo(object):
|
||||
self.ports.setdefault(src, {})
|
||||
self.ports.setdefault(dst, {})
|
||||
# New port: number of outlinks + base
|
||||
src_base = 1 if self.isSwitch(src) else 0
|
||||
dst_base = 1 if self.isSwitch(dst) else 0
|
||||
src_base = 1 if self.is_switch(src) else 0
|
||||
dst_base = 1 if self.is_switch(dst) else 0
|
||||
if sport is None:
|
||||
sport = len(self.ports[src]) + src_base
|
||||
if dport is None:
|
||||
@@ -100,24 +100,24 @@ class Topo(object):
|
||||
else:
|
||||
return self.g.nodes()
|
||||
|
||||
def isSwitch(self, n):
|
||||
def is_switch(self, n):
|
||||
'''Returns true if node is a switch.'''
|
||||
info = self.node_info[n]
|
||||
return info and info.get('isSwitch', False)
|
||||
return info and info.get('is_switch', False)
|
||||
|
||||
def switches(self, sort=True):
|
||||
'''Return switches.
|
||||
sort: sort switches alphabetically
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
return [n for n in self.nodes(sort) if self.isSwitch(n)]
|
||||
return [n for n in self.nodes(sort) if self.is_switch(n)]
|
||||
|
||||
def hosts(self, sort=True):
|
||||
'''Return hosts.
|
||||
sort: sort hosts alphabetically
|
||||
@return dpids list of dpids
|
||||
'''
|
||||
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
|
||||
return [n for n in self.nodes(sort) if not self.is_switch(n)]
|
||||
|
||||
def links(self, sort=True):
|
||||
'''Return links.
|
||||
@@ -148,11 +148,6 @@ class Topo(object):
|
||||
src, dst = self.sorted([src, dst])
|
||||
return self.link_info[(src, dst)]
|
||||
|
||||
def setlinkInfo( self, src, dst, info ):
|
||||
"Set link metadata"
|
||||
src, dst = self.sorted([src, dst])
|
||||
self.link_info[(src, dst)] = info
|
||||
|
||||
def nodeInfo( self, name ):
|
||||
"Return metadata (dict) for node"
|
||||
info = self.node_info[ name ]
|
||||
@@ -180,10 +175,10 @@ class SingleSwitchTopo(Topo):
|
||||
|
||||
self.k = k
|
||||
|
||||
switch = self.addSwitch('s1')
|
||||
switch = self.add_switch('s1')
|
||||
for h in irange(1, k):
|
||||
host = self.addHost('h%s' % h)
|
||||
self.addLink(host, switch)
|
||||
host = self.add_host('h%s' % h)
|
||||
self.add_link(host, switch)
|
||||
|
||||
|
||||
class SingleSwitchReversedTopo(Topo):
|
||||
@@ -201,11 +196,11 @@ class SingleSwitchReversedTopo(Topo):
|
||||
'''
|
||||
super(SingleSwitchReversedTopo, self).__init__(**opts)
|
||||
self.k = k
|
||||
switch = self.addSwitch('s1')
|
||||
switch = self.add_switch('s1')
|
||||
for h in irange(1, k):
|
||||
host = self.addHost('h%s' % h)
|
||||
self.addLink(host, switch,
|
||||
port1=0, port2=(k - h + 1))
|
||||
host = self.add_host('h%s' % h)
|
||||
self.add_link(host, switch,
|
||||
port1=0, port2=(k - h + 1))
|
||||
|
||||
class LinearTopo(Topo):
|
||||
"Linear topology of k switches, with one host per switch."
|
||||
@@ -222,9 +217,9 @@ class LinearTopo(Topo):
|
||||
|
||||
lastSwitch = None
|
||||
for i in irange(1, k):
|
||||
host = self.addHost('h%s' % i)
|
||||
switch = self.addSwitch('s%s' % i)
|
||||
self.addLink( host, switch)
|
||||
host = self.add_host('h%s' % i)
|
||||
switch = self.add_switch('s%s' % i)
|
||||
self.add_link( host, switch)
|
||||
if lastSwitch:
|
||||
self.addLink( switch, lastSwitch)
|
||||
self.add_link( switch, lastSwitch)
|
||||
lastSwitch = switch
|
||||
|
||||
+3
-3
@@ -19,13 +19,13 @@ class TreeTopo( Topo ):
|
||||
returns: last node added"""
|
||||
isSwitch = depth > 0
|
||||
if isSwitch:
|
||||
node = self.addSwitch( 's%s' % self.switchNum )
|
||||
node = self.add_switch( 's%s' % self.switchNum )
|
||||
self.switchNum += 1
|
||||
for _ in range( fanout ):
|
||||
child = self.addTree( depth - 1, fanout )
|
||||
self.addLink( node, child )
|
||||
self.add_link( node, child )
|
||||
else:
|
||||
node = self.addHost( 'h%s' % self.hostNum )
|
||||
node = self.add_host( 'h%s' % self.hostNum )
|
||||
self.hostNum += 1
|
||||
return node
|
||||
|
||||
|
||||
+34
-153
@@ -1,15 +1,11 @@
|
||||
"Utility functions for Mininet."
|
||||
|
||||
from mininet.log import output, info, error, warn
|
||||
|
||||
from time import sleep
|
||||
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
from select import poll, POLLIN, POLLHUP
|
||||
from select import poll, POLLIN
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
from mininet.log import output, info, error
|
||||
import re
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import os
|
||||
|
||||
# Command execution support
|
||||
|
||||
@@ -47,13 +43,13 @@ def oldQuietRun( *cmd ):
|
||||
break
|
||||
out += data
|
||||
popen.poll()
|
||||
if popen.returncode is not None:
|
||||
if popen.returncode != None:
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
# This is a bit complicated, but it enables us to
|
||||
# monitor command output as it is happening
|
||||
# monitor commount output as it is happening
|
||||
|
||||
def errRun( *cmd, **kwargs ):
|
||||
"""Run a command and return stdout, stderr and return code
|
||||
@@ -75,33 +71,31 @@ def errRun( *cmd, **kwargs ):
|
||||
# cmd goes to stderr, output goes to stdout
|
||||
info( cmd, '\n' )
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
|
||||
# We use poll() because select() doesn't work with large fd numbers,
|
||||
# and thus communicate() doesn't work either
|
||||
# We use poll() because select() doesn't work with large fd numbers
|
||||
out, err = '', ''
|
||||
poller = poll()
|
||||
poller.register( popen.stdout, POLLIN )
|
||||
fdtofile = { popen.stdout.fileno(): popen.stdout }
|
||||
outDone, errDone = False, True
|
||||
if popen.stderr:
|
||||
fdtofile[ popen.stderr.fileno() ] = popen.stderr
|
||||
poller.register( popen.stderr, POLLIN )
|
||||
errDone = False
|
||||
while not outDone or not errDone:
|
||||
while True:
|
||||
readable = poller.poll()
|
||||
for fd, _event in readable:
|
||||
# Tell pylint to ignore unused variable event
|
||||
# pylint: disable-msg=W0612
|
||||
for fd, event in readable:
|
||||
# pylint: enable-msg=W0612
|
||||
f = fdtofile[ fd ]
|
||||
data = f.read( 1024 )
|
||||
if echo:
|
||||
output( data )
|
||||
if f == popen.stdout:
|
||||
out += data
|
||||
if data == '':
|
||||
outDone = True
|
||||
elif f == popen.stderr:
|
||||
err += data
|
||||
if data == '':
|
||||
errDone = True
|
||||
returncode = popen.wait()
|
||||
returncode = popen.poll()
|
||||
if returncode is not None:
|
||||
break
|
||||
return out, err, returncode
|
||||
|
||||
def errFail( *cmd, **kwargs ):
|
||||
@@ -117,7 +111,7 @@ def quietRun( cmd, **kwargs ):
|
||||
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
|
||||
|
||||
# pylint: enable-msg=E1103
|
||||
# pylint: disable-msg=E1101
|
||||
# pylint: disable-msg=E1101,W0612
|
||||
|
||||
def isShellBuiltin( cmd ):
|
||||
"Return True if cmd is a bash builtin."
|
||||
@@ -130,7 +124,7 @@ def isShellBuiltin( cmd ):
|
||||
|
||||
isShellBuiltin.builtIns = None
|
||||
|
||||
# pylint: enable-msg=E1101
|
||||
# pylint: enable-msg=E1101,W0612
|
||||
|
||||
# Interface management
|
||||
#
|
||||
@@ -171,35 +165,27 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
|
||||
error( "*** gave up after %i retries\n" % tries )
|
||||
exit( 1 )
|
||||
|
||||
def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
|
||||
def moveIntfNoRetry( intf, node, printError=False ):
|
||||
"""Move interface to node, without retrying.
|
||||
intf: string, interface
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns
|
||||
printError: if true, print error"""
|
||||
intf = str( intf )
|
||||
cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
|
||||
if srcNode:
|
||||
srcNode.cmd( cmd )
|
||||
else:
|
||||
quietRun( cmd )
|
||||
links = dstNode.cmd( 'ip link show' )
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
|
||||
quietRun( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not ( ' %s:' % intf ) in links:
|
||||
if printError:
|
||||
error( '*** Error: moveIntf: ' + intf +
|
||||
' not successfully moved to ' + dstNode.name + '\n' )
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
return False
|
||||
return True
|
||||
|
||||
def moveIntf( intf, dstNode, srcNode=None, printError=False,
|
||||
retries=3, delaySecs=0.001 ):
|
||||
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
|
||||
"""Move interface to node, retrying on failure.
|
||||
intf: string, interface
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
|
||||
srcNode=srcNode, printError=printError )
|
||||
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
|
||||
|
||||
# Support for dumping network
|
||||
|
||||
@@ -232,7 +218,7 @@ def dumpNetConnections( net ):
|
||||
def _colonHex( val, bytecount ):
|
||||
"""Generate colon-hex string.
|
||||
val: input as unsigned int
|
||||
bytecount: number of bytes to convert
|
||||
bytescount: number of bytes to convert
|
||||
returns: chStr colon-hex string"""
|
||||
pieces = []
|
||||
for i in range( bytecount - 1, -1, -1 ):
|
||||
@@ -250,8 +236,9 @@ def macColonHex( mac ):
|
||||
def ipStr( ip ):
|
||||
"""Generate IP address string from an unsigned int.
|
||||
ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
|
||||
returns: ip address string w.x.y.z"""
|
||||
returns: ip address string w.x.y.z, or 10.x.y.z if w==0"""
|
||||
w = ( ip >> 24 ) & 0xff
|
||||
w = 10 if w == 0 else w
|
||||
x = ( ip >> 16 ) & 0xff
|
||||
y = ( ip >> 8 ) & 0xff
|
||||
z = ip & 0xff
|
||||
@@ -260,7 +247,7 @@ def ipStr( ip ):
|
||||
def ipNum( w, x, y, z ):
|
||||
"""Generate unsigned int from components of IP address
|
||||
returns: w << 24 | x << 16 | y << 8 | z"""
|
||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||
return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
|
||||
|
||||
def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||
"""Return IP address string from ints
|
||||
@@ -268,10 +255,10 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
|
||||
prefixLen: optional IP prefix length
|
||||
ipBaseNum: option base IP address as int
|
||||
returns IP address as string"""
|
||||
imax = 0xffffffff >> prefixLen
|
||||
assert i <= imax
|
||||
mask = 0xffffffff ^ imax
|
||||
ipnum = ( ipBaseNum & mask ) + i
|
||||
# Ugly but functional
|
||||
assert i < ( 1 << ( 32 - prefixLen ) )
|
||||
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
|
||||
ipnum = i + ( ipBaseNum & mask )
|
||||
return ipStr( ipnum )
|
||||
|
||||
def ipParse( ip ):
|
||||
@@ -313,46 +300,6 @@ def makeNumeric( s ):
|
||||
else:
|
||||
return s
|
||||
|
||||
# Popen support
|
||||
|
||||
def pmonitor(popens, timeoutms=500, readline=True,
|
||||
readmax=1024 ):
|
||||
"""Monitor dict of hosts to popen objects
|
||||
a line at a time
|
||||
timeoutms: timeout for poll()
|
||||
readline: return single line of output
|
||||
yields: host, line/output (if any)
|
||||
terminates: when all EOFs received"""
|
||||
poller = poll()
|
||||
fdToHost = {}
|
||||
for host, popen in popens.iteritems():
|
||||
fd = popen.stdout.fileno()
|
||||
fdToHost[ fd ] = host
|
||||
poller.register( fd, POLLIN )
|
||||
if not readline:
|
||||
# Use non-blocking reads
|
||||
flags = fcntl( fd, F_GETFL )
|
||||
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
|
||||
while popens:
|
||||
fds = poller.poll( timeoutms )
|
||||
if fds:
|
||||
for fd, event in fds:
|
||||
host = fdToHost[ fd ]
|
||||
popen = popens[ host ]
|
||||
if event & POLLIN:
|
||||
if readline:
|
||||
# Attempt to read a line of output
|
||||
# This blocks until we receive a newline!
|
||||
line = popen.stdout.readline()
|
||||
else:
|
||||
line = popen.stdout.read( readmax )
|
||||
yield host, line
|
||||
# Check for EOF
|
||||
elif event & POLLHUP:
|
||||
poller.unregister( fd )
|
||||
del popens[ host ]
|
||||
else:
|
||||
yield None, ''
|
||||
|
||||
# Other stuff we use
|
||||
|
||||
@@ -366,8 +313,7 @@ def mountCgroups():
|
||||
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):
|
||||
if '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 )
|
||||
@@ -401,73 +347,8 @@ def irange(start, end):
|
||||
|
||||
def custom( cls, **params ):
|
||||
"Returns customized constructor for class cls."
|
||||
# Note: we may wish to see if we can use functools.partial() here
|
||||
# and in customConstructor
|
||||
def customized( *args, **kwargs):
|
||||
"Customized constructor"
|
||||
kwargs = kwargs.copy()
|
||||
kwargs.update( params )
|
||||
return cls( *args, **kwargs )
|
||||
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
|
||||
return customized
|
||||
|
||||
def splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
split = argstr.split( ',' )
|
||||
fn = split[ 0 ]
|
||||
params = split[ 1: ]
|
||||
# Convert int and float args; removes the need for function
|
||||
# to be flexible with input arg formats.
|
||||
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||
kwargs = {}
|
||||
for s in [ p for p in params if '=' in p ]:
|
||||
key, val = s.split( '=' )
|
||||
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,8 +7,6 @@
|
||||
* - detaching from a controlling tty using setsid
|
||||
* - running in a network namespace
|
||||
* - printing out the pid of a process so we can identify it later
|
||||
* - attaching to a namespace and cgroup
|
||||
* - setting RT scheduling
|
||||
*
|
||||
* Partially based on public domain setsid(1)
|
||||
*/
|
||||
@@ -16,89 +14,23 @@
|
||||
#include <stdio.h>
|
||||
#include <linux/sched.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)
|
||||
{
|
||||
printf("Execution utility for Mininet\n\n"
|
||||
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
|
||||
"Options:\n"
|
||||
" -c: close all file descriptors except stdin/out/error\n"
|
||||
" -d: detach from tty by calling setsid()\n"
|
||||
" -n: run in new network 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(__NR_setns, fd, nstype);
|
||||
}
|
||||
|
||||
/* Validate alphanumeric path foo1/bar2/baz */
|
||||
void validate(char *path)
|
||||
{
|
||||
char *s;
|
||||
for (s=path; *s; s++) {
|
||||
if (!isalnum(*s) && *s != '/') {
|
||||
fprintf(stderr, "invalid path: %s\n", path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add our pid to cgroup */
|
||||
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);
|
||||
}
|
||||
printf("Execution utility for Mininet.\n"
|
||||
"usage: %s [-cdnp]\n"
|
||||
"-c: close all file descriptors except stdin/out/error\n"
|
||||
"-d: detach from tty by calling setsid()\n"
|
||||
"-n: run in new network namespace\n"
|
||||
"-p: print ^A + pid\n", name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char c;
|
||||
int fd;
|
||||
char path[PATH_MAX];
|
||||
int nsid;
|
||||
int pid;
|
||||
static struct sched_param sp;
|
||||
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
|
||||
|
||||
while ((c = getopt(argc, argv, "+cdnp")) != -1)
|
||||
switch(c) {
|
||||
case 'c':
|
||||
/* close file descriptors except stdin/out/error */
|
||||
@@ -132,48 +64,16 @@ int main(int argc, char *argv[])
|
||||
printf("\001%d\n", getpid());
|
||||
fflush(stdout);
|
||||
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:
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
perror(argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
perror(argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
@@ -5,37 +5,29 @@
|
||||
from setuptools import setup, find_packages
|
||||
from os.path import join
|
||||
|
||||
# Get version number from source tree
|
||||
import sys
|
||||
sys.path.append( '.' )
|
||||
from mininet.net import VERSION
|
||||
|
||||
scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ]
|
||||
|
||||
modname = distname = 'mininet'
|
||||
|
||||
setup(
|
||||
name=distname,
|
||||
version=VERSION,
|
||||
version='0.0.0',
|
||||
description='Process-based OpenFlow emulator',
|
||||
author='Bob Lantz',
|
||||
author_email='rlantz@cs.stanford.edu',
|
||||
packages=find_packages(exclude='test'),
|
||||
long_description="""
|
||||
Mininet is a network emulator which uses lightweight
|
||||
virtualization to create virtual networks for rapid
|
||||
prototyping of Software-Defined Network (SDN) designs
|
||||
using OpenFlow. http://openflow.org/mininet
|
||||
""",
|
||||
Insert longer description here.
|
||||
""",
|
||||
classifiers=[
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
||||
"Programming Language :: Python",
|
||||
"Development Status :: 2 - Pre-Alpha",
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Internet",
|
||||
],
|
||||
keywords='networking emulator protocol Internet OpenFlow SDN',
|
||||
license='BSD',
|
||||
keywords='networking protocol Internet OpenFlow',
|
||||
license='unspecified',
|
||||
install_requires=[
|
||||
'setuptools',
|
||||
'networkx'
|
||||
|
||||
+62
-185
@@ -5,7 +5,7 @@
|
||||
|
||||
# Fail on error
|
||||
set -e
|
||||
|
||||
|
||||
# Fail on unset var usage
|
||||
set -o nounset
|
||||
|
||||
@@ -33,7 +33,7 @@ if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
|
||||
fi
|
||||
if ! which bc &> /dev/null; then
|
||||
$install bc
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if which lsb_release &> /dev/null; then
|
||||
DIST=`lsb_release -is`
|
||||
@@ -97,11 +97,11 @@ function kernel {
|
||||
if ! test -e /boot/initrd.img-${KERNEL_NAME}; then
|
||||
sudo update-initramfs -c -k ${KERNEL_NAME}
|
||||
fi
|
||||
|
||||
|
||||
# Ensure /boot/grub/menu.lst boots with initrd image:
|
||||
sudo update-grub
|
||||
|
||||
# The default should be the new kernel. Otherwise, you may need to modify
|
||||
# The default should be the new kernel. Otherwise, you may need to modify
|
||||
# /boot/grub/menu.lst to set the default to the entry corresponding to the
|
||||
# kernel you just installed.
|
||||
fi
|
||||
@@ -122,24 +122,21 @@ function kernel_clean {
|
||||
# Install Mininet deps
|
||||
function mn_deps {
|
||||
echo "Installing Mininet dependencies"
|
||||
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
|
||||
python-setuptools python-networkx cgroup-bin ethtool help2man \
|
||||
pyflakes pylint pep8
|
||||
$install gcc make screen psmisc xterm ssh iperf iproute \
|
||||
python-setuptools python-networkx cgroup-bin ethtool
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
|
||||
echo "Upgrading networkx to avoid deprecation warning"
|
||||
sudo easy_install --upgrade networkx
|
||||
fi
|
||||
|
||||
# Add sysctl parameters as noted in the INSTALL file to increase kernel
|
||||
# Add sysctl parameters as noted in the INSTALL file to increase kernel
|
||||
# limits to support larger setups:
|
||||
if ! grep Mininet /etc/sysctl.conf; then
|
||||
echo "Adding Mininet sysctl settings"
|
||||
sudo su -c "cat $HOME/mininet/util/sysctl_addon >> /etc/sysctl.conf"
|
||||
fi
|
||||
sudo su -c "cat $HOME/mininet/util/sysctl_addon >> /etc/sysctl.conf"
|
||||
|
||||
# Load new sysctl settings:
|
||||
sudo sysctl -p
|
||||
|
||||
|
||||
echo "Installing Mininet core"
|
||||
pushd ~/mininet
|
||||
sudo make install
|
||||
@@ -155,7 +152,7 @@ function of {
|
||||
echo "Installing OpenFlow reference implementation..."
|
||||
cd ~/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc libtool libc6-dev
|
||||
make gcc libtool libc6-dev
|
||||
git clone git://openflowswitch.org/openflow.git
|
||||
cd ~/openflow
|
||||
|
||||
@@ -167,51 +164,32 @@ function of {
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd ~
|
||||
}
|
||||
|
||||
function of13 {
|
||||
echo "Installing OpenFlow 1.3 soft switch implementation..."
|
||||
cd ~/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
|
||||
unzip libpcre3-dev flex bison libboost-dev
|
||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||
# sent during tests, near link status changes:
|
||||
$remove avahi-daemon
|
||||
|
||||
if [ ! -d "ofsoftswitch13" ]; then
|
||||
git clone https://github.com/CPqD/ofsoftswitch13.git
|
||||
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||
if [ "$DIST" = "Ubuntu" ]; then
|
||||
BLACKLIST=/etc/modprobe.d/blacklist.conf
|
||||
else
|
||||
BLACKLIST=/etc/modprobe.d/blacklist
|
||||
fi
|
||||
|
||||
# Install netbee
|
||||
wget -nc http://www.nbee.org/download/nbeesrc-12-05-16.zip
|
||||
unzip nbeesrc-12-05-16.zip
|
||||
cd ~/nbeesrc/src
|
||||
cmake .
|
||||
make
|
||||
cd ~/
|
||||
sudo cp nbeesrc/bin/libn*.so /usr/local/lib
|
||||
sudo ldconfig
|
||||
sudo cp -R nbeesrc/include/ /usr/
|
||||
|
||||
# Resume the install:
|
||||
cd ~/ofsoftswitch13
|
||||
./boot.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||
cd ~
|
||||
}
|
||||
|
||||
function wireshark {
|
||||
echo "Installing Wireshark dissector..."
|
||||
|
||||
sudo apt-get install -y wireshark tshark libgtk2.0-dev
|
||||
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
|
||||
hg clone https://bitbucket.org/onlab/of-dissector
|
||||
cd of-dissector/src
|
||||
export WIRESHARK=/usr/include/wireshark
|
||||
scons
|
||||
@@ -235,19 +213,16 @@ function wireshark {
|
||||
|
||||
# Install Open vSwitch
|
||||
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
|
||||
|
||||
function ovs {
|
||||
echo "Installing Open vSwitch..."
|
||||
|
||||
# Required for module build/dkms install
|
||||
$install $KERNEL_HEADERS
|
||||
|
||||
ovspresent=0
|
||||
|
||||
# First see if we have packages
|
||||
# XXX wget -c seems to fail from github/amazon s3
|
||||
cd /tmp
|
||||
if wget $OVS_PACKAGE_LOC/$OVS_PACKAGE_NAME 2> /dev/null; then
|
||||
if wget $OVS_PACKAGE_LOC/$OVS_PACKAGE_NAME; then
|
||||
$install patch dkms fakeroot python-argparse
|
||||
tar xf $OVS_PACKAGE_NAME
|
||||
orig=`tar tf $OVS_PACKAGE_NAME`
|
||||
@@ -259,11 +234,21 @@ function ovs {
|
||||
# Annoyingly, things seem to be missing without this flag
|
||||
$pkginst --force-confmiss $pkg
|
||||
done
|
||||
ovspresent=1
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
echo "Done (hopefully) installing packages"
|
||||
cd ~
|
||||
return
|
||||
fi
|
||||
|
||||
# Otherwise try distribution's OVS packages
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 11.10` = 1 ]; then
|
||||
if [ "$DIST" = "Ubuntu" ] && [ `echo "$RELEASE >= 11.10" | bc` = 1 ]; then
|
||||
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||
# If you've already installed a datapath, assume you
|
||||
# know what you're doing and don't need dkms datapath.
|
||||
@@ -271,28 +256,10 @@ function ovs {
|
||||
$install openvswitch-datapath-dkms
|
||||
fi
|
||||
if $install openvswitch-switch openvswitch-controller; then
|
||||
echo "Ignoring error installing openvswitch-controller"
|
||||
return
|
||||
fi
|
||||
ovspresent=1
|
||||
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
|
||||
@@ -307,7 +274,7 @@ function ovs {
|
||||
fi
|
||||
else
|
||||
$install git
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install OVS from release
|
||||
cd ~/
|
||||
@@ -361,7 +328,7 @@ function nox {
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install python-dev libboost-dev
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
@@ -370,18 +337,13 @@ function nox {
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd ~/
|
||||
git clone https://github.com/noxrepo/nox-classic.git noxcore
|
||||
git clone git://noxrepo.org/nox noxcore
|
||||
cd noxcore
|
||||
if ! git checkout -b destiny remotes/origin/destiny ; then
|
||||
echo "Did not check out a new destiny branch - assuming current branch is destiny"
|
||||
fi
|
||||
git checkout -b destiny remotes/origin/destiny
|
||||
|
||||
# Apply patches
|
||||
git checkout -b tutorial-destiny
|
||||
git am ~/mininet/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
|
||||
git am ~/mininet/util/nox-patches/*.patch
|
||||
|
||||
# Build
|
||||
./boot.sh
|
||||
@@ -399,47 +361,6 @@ function nox {
|
||||
#./nox_core -v -i ptcp:
|
||||
}
|
||||
|
||||
# Install NOX Classic/Zaku for OpenFlow 1.3
|
||||
function nox13 {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
|
||||
# Install NOX deps:
|
||||
$install autoconf automake g++ libtool python python-twisted \
|
||||
swig libssl-dev make
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd ~/
|
||||
git clone https://github.com/CPqD/nox13oflib.git
|
||||
cd nox13oflib
|
||||
|
||||
# Build
|
||||
./boot.sh
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
make -j3
|
||||
#make check
|
||||
|
||||
# To verify this install:
|
||||
#cd ~/nox13oflib/build/src
|
||||
#./nox_core -v -i ptcp:
|
||||
}
|
||||
|
||||
|
||||
# "Install" POX
|
||||
function pox {
|
||||
echo "Installing POX into $HOME/pox..."
|
||||
cd ~
|
||||
git clone https://github.com/noxrepo/pox.git
|
||||
}
|
||||
|
||||
# Install OFtest
|
||||
function oftest {
|
||||
echo "Installing oftest..."
|
||||
@@ -449,14 +370,17 @@ function oftest {
|
||||
|
||||
# Install oftest:
|
||||
cd ~/
|
||||
git clone git://github.com/floodlight/oftest
|
||||
git clone git://openflow.org/oftest
|
||||
cd oftest
|
||||
cd tools/munger
|
||||
sudo make install
|
||||
}
|
||||
|
||||
# Install cbench
|
||||
function cbench {
|
||||
echo "Installing cbench..."
|
||||
|
||||
$install libsnmp-dev libpcap-dev libconfig-dev
|
||||
|
||||
$install libsnmp-dev libpcap-dev
|
||||
cd ~/
|
||||
git clone git://openflow.org/oflops.git
|
||||
cd oflops
|
||||
@@ -468,45 +392,14 @@ function cbench {
|
||||
}
|
||||
|
||||
function other {
|
||||
echo "Doing other Mininet VM setup tasks..."
|
||||
|
||||
# Remove avahi-daemon, which may cause unwanted discovery packets to be
|
||||
# sent during tests, near link status changes:
|
||||
echo "Removing avahi-daemon"
|
||||
$remove avahi-daemon
|
||||
|
||||
# was: Disable IPv6. Add to /etc/modprobe.d/blacklist:
|
||||
#echo "Attempting to disable IPv6"
|
||||
#if [ "$DIST" = "Ubuntu" ]; then
|
||||
# BLACKLIST=/etc/modprobe.d/blacklist.conf
|
||||
#else
|
||||
# BLACKLIST=/etc/modprobe.d/blacklist
|
||||
#fi
|
||||
#sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
|
||||
|
||||
# Disable IPv6
|
||||
if ! grep 'disable IPv6' /etc/sysctl.conf; then
|
||||
echo 'Disabling IPv6'
|
||||
echo '
|
||||
# Mininet: disable IPv6
|
||||
net.ipv6.conf.all.disable_ipv6 = 1
|
||||
net.ipv6.conf.default.disable_ipv6 = 1
|
||||
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
fi
|
||||
# Disabling IPv6 breaks X11 forwarding via ssh
|
||||
line='AddressFamily inet'
|
||||
file='/etc/ssh/sshd_config'
|
||||
echo "Adding $line to $file"
|
||||
if ! grep "$line" $file > /dev/null; then
|
||||
echo "$line" | sudo tee -a $file > /dev/null
|
||||
fi
|
||||
echo "Doing other setup tasks..."
|
||||
|
||||
# Enable command auto completion using sudo; modify ~/.bashrc:
|
||||
sed -i -e 's|# for examples$|&\ncomplete -cf sudo|' ~/.bashrc
|
||||
|
||||
# Install tcpdump, cmd-line packet dump tool. Also install gitk,
|
||||
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
|
||||
# a graphical git history viewer.
|
||||
$install tcpdump gitk
|
||||
$install tcpdump tshark gitk
|
||||
|
||||
# Install common text editors
|
||||
$install vim nano emacs
|
||||
@@ -549,9 +442,7 @@ function all {
|
||||
of
|
||||
wireshark
|
||||
ovs
|
||||
# NOX-classic is deprecated, but you can install it manually if desired.
|
||||
# nox
|
||||
pox
|
||||
nox
|
||||
oftest
|
||||
cbench
|
||||
other
|
||||
@@ -588,11 +479,11 @@ function vm_clean {
|
||||
}
|
||||
|
||||
function usage {
|
||||
printf '\nUsage: %s [-abcdfhkmnprtvwx03]\n\n' $(basename $0) >&2
|
||||
|
||||
printf 'Usage: %s [-acdfhkmntvxy]\n\n' $(basename $0) >&2
|
||||
|
||||
printf 'This install script attempts to install useful packages\n' >&2
|
||||
printf 'for Mininet. It should (hopefully) work on Ubuntu 11.10+\n' >&2
|
||||
printf 'If you run into trouble, try\n' >&2
|
||||
printf 'for Mininet. It should (hopefully) work on Ubuntu 10.04, 11.10\n' >&2
|
||||
printf 'and Debian 5.0 (Lenny). If you run into trouble, try\n' >&2
|
||||
printf 'installing one thing at a time, and looking at the \n' >&2
|
||||
printf 'specific installation function in this script.\n\n' >&2
|
||||
|
||||
@@ -600,57 +491,43 @@ function usage {
|
||||
printf -- ' -a: (default) install (A)ll packages - good luck!\n' >&2
|
||||
printf -- ' -b: install controller (B)enchmark (oflops)\n' >&2
|
||||
printf -- ' -c: (C)lean up after kernel install\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -f: install open(F)low\n' >&2
|
||||
printf -- ' -h: print this (H)elp message\n' >&2
|
||||
printf -- ' -k: install new (K)ernel\n' >&2
|
||||
printf -- ' -m: install Open vSwitch kernel (M)odule from source dir\n' >&2
|
||||
printf -- ' -n: install mini(N)et dependencies + core files\n' >&2
|
||||
printf -- ' -p: install (P)OX OpenFlow Controller\n' >&2
|
||||
printf -- ' -r: remove existing Open vSwitch packages\n' >&2
|
||||
printf -- ' -t: install o(T)her stuff\n' >&2
|
||||
printf -- ' -v: install open (V)switch\n' >&2
|
||||
printf -- ' -w: install OpenFlow (w)ireshark dissector\n' >&2
|
||||
printf -- ' -x: install NO(X) Classic OpenFlow controller\n' >&2
|
||||
printf -- ' -0: (default) -0[fx] installs OpenFlow 1.0 versions\n' >&2
|
||||
printf -- ' -3: -3[fx] installs OpenFlow 1.3 versions\n' >&2
|
||||
printf -- ' -x: install NO(X) OpenFlow controller\n' >&2
|
||||
printf -- ' -y: install (A)ll packages\n' >&2
|
||||
|
||||
exit 2
|
||||
}
|
||||
|
||||
OF_VERSION=1.0
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
all
|
||||
else
|
||||
while getopts 'abcdfhkmnprtvwx03' OPTION
|
||||
while getopts 'abcdfhkmnrtvwx' OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
a) all;;
|
||||
b) cbench;;
|
||||
c) kernel_clean;;
|
||||
d) vm_clean;;
|
||||
f) case $OF_VERSION in
|
||||
1.0) of;;
|
||||
1.3) of13;;
|
||||
*) echo "Invalid OpenFlow version $OF_VERSION";;
|
||||
esac;;
|
||||
f) of;;
|
||||
h) usage;;
|
||||
k) kernel;;
|
||||
m) modprobe;;
|
||||
n) mn_deps;;
|
||||
p) pox;;
|
||||
r) remove_ovs;;
|
||||
t) other;;
|
||||
v) ovs;;
|
||||
w) wireshark;;
|
||||
x) case $OF_VERSION in
|
||||
1.0) nox;;
|
||||
1.3) nox13;;
|
||||
*) echo "Invalid OpenFlow version $OF_VERSION";;
|
||||
esac;;
|
||||
0) OF_VERSION=1.0;;
|
||||
3) OF_VERSION=1.3;;
|
||||
x) nox;;
|
||||
?) usage;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Attach to a Mininet host and run a command
|
||||
|
||||
if [ -z $1 ]; then
|
||||
echo "usage: $0 host cmd [args...]"
|
||||
exit 1
|
||||
else
|
||||
host=$1
|
||||
fi
|
||||
|
||||
if [ -z $2 ]; then
|
||||
cmd='bash'
|
||||
else
|
||||
shift
|
||||
cmd=$*
|
||||
fi
|
||||
|
||||
# We could do this in this script, and we may want to eventually,
|
||||
# but for now we'll use mnexec to attach to the host's cgroup
|
||||
cgroup=/sys/fs/cgroup/cpu/$host
|
||||
if [ -d "$cgroup" ]; then
|
||||
cg="mnexec -g $host"
|
||||
fi
|
||||
|
||||
exec sudo ip netns exec $host $cg $cmd
|
||||
@@ -1,175 +0,0 @@
|
||||
From 166693d7cb640d4a41251b87e92c52d9c688196b Mon Sep 17 00:00:00 2001
|
||||
From: Bob Lantz <rlantz@cs.stanford.edu>
|
||||
Date: Mon, 14 May 2012 15:30:44 -0700
|
||||
Subject: [PATCH] Hacks to get NOX classic/destiny to compile under Ubuntu
|
||||
12.04
|
||||
|
||||
Thanks to Srinivasu R. Kanduru for the initial patch.
|
||||
|
||||
Apologies for the hacks - it is my hope that this will be fixed
|
||||
upstream eventually.
|
||||
|
||||
---
|
||||
config/ac_pkg_swig.m4 | 7 ++++---
|
||||
src/Make.vars | 2 +-
|
||||
src/nox/coreapps/pyrt/deferredcallback.cc | 2 +-
|
||||
src/nox/coreapps/pyrt/pyglue.cc | 2 +-
|
||||
src/nox/coreapps/pyrt/pyrt.cc | 2 +-
|
||||
src/nox/netapps/authenticator/auth.i | 2 ++
|
||||
src/nox/netapps/authenticator/flow_util.i | 1 +
|
||||
src/nox/netapps/routing/routing.i | 2 ++
|
||||
.../switch_management/pyswitch_management.i | 2 ++
|
||||
src/nox/netapps/tests/tests.cc | 2 +-
|
||||
src/nox/netapps/topology/pytopology.i | 2 ++
|
||||
11 files changed, 18 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/config/ac_pkg_swig.m4 b/config/ac_pkg_swig.m4
|
||||
index d12556e..9b608f2 100644
|
||||
--- a/config/ac_pkg_swig.m4
|
||||
+++ b/config/ac_pkg_swig.m4
|
||||
@@ -78,9 +78,10 @@ AC_DEFUN([AC_PROG_SWIG],[
|
||||
if test -z "$available_patch" ; then
|
||||
[available_patch=0]
|
||||
fi
|
||||
- if test $available_major -ne $required_major \
|
||||
- -o $available_minor -ne $required_minor \
|
||||
- -o $available_patch -lt $required_patch ; then
|
||||
+ major_done=`test $available_major -gt $required_major`
|
||||
+ minor_done=`test $available_minor -gt $required_minor`
|
||||
+ if test !$major_done -a !$minor_done \
|
||||
+ -a $available_patch -lt $required_patch ; then
|
||||
AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org])
|
||||
SWIG=''
|
||||
else
|
||||
diff --git a/src/Make.vars b/src/Make.vars
|
||||
index d70d6aa..93b2879 100644
|
||||
--- a/src/Make.vars
|
||||
+++ b/src/Make.vars
|
||||
@@ -53,7 +53,7 @@ AM_LDFLAGS += -export-dynamic
|
||||
endif
|
||||
|
||||
# set python runtimefiles to be installed in the same directory as pkg
|
||||
-pkglib_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||
+pkgdata_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES)
|
||||
BUILT_SOURCES = $(NOX_PYBUILDFILES)
|
||||
|
||||
# Runtime-files build and clean rules
|
||||
diff --git a/src/nox/coreapps/pyrt/deferredcallback.cc b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
index 3a40fa7..111a586 100644
|
||||
--- a/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
+++ b/src/nox/coreapps/pyrt/deferredcallback.cc
|
||||
@@ -69,7 +69,7 @@ DeferredCallback::get_instance(const Callback& c)
|
||||
DeferredCallback* cb = new DeferredCallback(c);
|
||||
|
||||
// flag as used in *_wrap.cc....correct?
|
||||
- return SWIG_Python_NewPointerObj(cb, s, SWIG_POINTER_OWN | 0);
|
||||
+ return SWIG_Python_NewPointerObj(m, cb, s, SWIG_POINTER_OWN | 0);
|
||||
}
|
||||
|
||||
bool
|
||||
diff --git a/src/nox/coreapps/pyrt/pyglue.cc b/src/nox/coreapps/pyrt/pyglue.cc
|
||||
index 48b9716..317fd04 100644
|
||||
--- a/src/nox/coreapps/pyrt/pyglue.cc
|
||||
+++ b/src/nox/coreapps/pyrt/pyglue.cc
|
||||
@@ -874,7 +874,7 @@ to_python(const Flow& flow)
|
||||
if (!s) {
|
||||
throw std::runtime_error("Could not find Flow SWIG type_info");
|
||||
}
|
||||
- return SWIG_Python_NewPointerObj(f, s, SWIG_POINTER_OWN | 0);
|
||||
+ return SWIG_Python_NewPointerObj(m, f, s, SWIG_POINTER_OWN | 0);
|
||||
|
||||
// PyObject* dict = PyDict_New();
|
||||
// if (!dict) {
|
||||
diff --git a/src/nox/coreapps/pyrt/pyrt.cc b/src/nox/coreapps/pyrt/pyrt.cc
|
||||
index fbda461..8ec05d6 100644
|
||||
--- a/src/nox/coreapps/pyrt/pyrt.cc
|
||||
+++ b/src/nox/coreapps/pyrt/pyrt.cc
|
||||
@@ -776,7 +776,7 @@ Python_event_manager::create_python_context(const Context* ctxt,
|
||||
pretty_print_python_exception());
|
||||
}
|
||||
|
||||
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||
Py_INCREF(pyctxt); // XXX needed?
|
||||
|
||||
//Py_DECREF(m);
|
||||
diff --git a/src/nox/netapps/authenticator/auth.i b/src/nox/netapps/authenticator/auth.i
|
||||
index 1de1a17..bfa04e2 100644
|
||||
--- a/src/nox/netapps/authenticator/auth.i
|
||||
+++ b/src/nox/netapps/authenticator/auth.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.authenticator.pyauth"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "core_events.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
diff --git a/src/nox/netapps/authenticator/flow_util.i b/src/nox/netapps/authenticator/flow_util.i
|
||||
index f67c3ef..2a314e2 100644
|
||||
--- a/src/nox/netapps/authenticator/flow_util.i
|
||||
+++ b/src/nox/netapps/authenticator/flow_util.i
|
||||
@@ -32,6 +32,7 @@ using namespace vigil::applications;
|
||||
%}
|
||||
|
||||
%include "common-defs.i"
|
||||
+%include "std_list.i"
|
||||
|
||||
%import "netinet/netinet.i"
|
||||
%import "pyrt/event.i"
|
||||
diff --git a/src/nox/netapps/routing/routing.i b/src/nox/netapps/routing/routing.i
|
||||
index 44ccb3d..f9221a2 100644
|
||||
--- a/src/nox/netapps/routing/routing.i
|
||||
+++ b/src/nox/netapps/routing/routing.i
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
%module "nox.netapps.routing.pyrouting"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "pyrouting.hh"
|
||||
#include "routing.hh"
|
||||
diff --git a/src/nox/netapps/switch_management/pyswitch_management.i b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
index 72bfed4..ad2c90d 100644
|
||||
--- a/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
+++ b/src/nox/netapps/switch_management/pyswitch_management.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.pyswitch_management"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "switch_management_proxy.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
diff --git a/src/nox/netapps/tests/tests.cc b/src/nox/netapps/tests/tests.cc
|
||||
index 20e900d..f027028 100644
|
||||
--- a/src/nox/netapps/tests/tests.cc
|
||||
+++ b/src/nox/netapps/tests/tests.cc
|
||||
@@ -306,7 +306,7 @@ private:
|
||||
throw runtime_error("Could not find PyContext SWIG type_info.");
|
||||
}
|
||||
|
||||
- PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0);
|
||||
+ PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0);
|
||||
assert(pyctxt);
|
||||
|
||||
Py_DECREF(m);
|
||||
diff --git a/src/nox/netapps/topology/pytopology.i b/src/nox/netapps/topology/pytopology.i
|
||||
index 94a9f4b..7a8cd94 100644
|
||||
--- a/src/nox/netapps/topology/pytopology.i
|
||||
+++ b/src/nox/netapps/topology/pytopology.i
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
%module "nox.netapps.topology"
|
||||
|
||||
+// Hack to get it to compile -BL
|
||||
+%include "std_list.i"
|
||||
%{
|
||||
#include "pytopology.hh"
|
||||
#include "pyrt/pycontext.hh"
|
||||
--
|
||||
1.7.5.4
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
0001: This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||
0002: This patch hacks nox-destiny to compile on Ubuntu 12.04.
|
||||
This patch adds the OpenFlow tutorial module source code to nox-destiny.
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
obj-m = sch_htb.o
|
||||
KVERSION = $(shell uname -r)
|
||||
all:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
||||
install:
|
||||
test -e /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak || mv /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak
|
||||
cp sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko
|
||||
rmmod sch_htb
|
||||
modprobe sch_htb
|
||||
clean:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
||||
@@ -1,10 +0,0 @@
|
||||
Modified sch_htb implementation with ofbuf support.
|
||||
|
||||
To compile, just type make. To use this module instead
|
||||
of regular sch_htb, do:
|
||||
|
||||
0. make
|
||||
1. rmmod sch_htb
|
||||
2. insmod ./sch_htb.ko
|
||||
|
||||
To revert, just rmmod sch_htb.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,4 +15,3 @@ net.ipv4.neigh.default.gc_thresh3 = 16384
|
||||
|
||||
# Mininet: increase routing table size
|
||||
net.ipv4.route.max_size=32768
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/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 )
|
||||
Executable → Regular
+10
-14
@@ -1,10 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is intended to install Mininet into
|
||||
# a brand-new Ubuntu virtual machine,
|
||||
# a brand-new Ubuntu (10.04 or 11.10) virtual machine,
|
||||
# to create a fully usable "tutorial" VM.
|
||||
|
||||
set -e
|
||||
echo `whoami` ALL=NOPASSWD: ALL | sudo tee -a /etc/sudoers
|
||||
sudo sh -c 'cat >> /etc/sudoers' <<EOF
|
||||
openflow ALL=NOPASSWD: ALL
|
||||
EOF
|
||||
sudo sed -i -e 's/Default/#Default/' /etc/sudoers
|
||||
sudo sed -i -e 's/ubuntu/mininet-vm/' /etc/hostname
|
||||
sudo sed -i -e 's/ubuntu/mininet-vm/g' /etc/hosts
|
||||
@@ -14,23 +17,16 @@ sudo update-grub
|
||||
sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
|
||||
/etc/apt/sources.list
|
||||
sudo apt-get update
|
||||
# Clean up vmware easy install junk if present
|
||||
if [ -e /etc/issue.backup ]; then
|
||||
sudo mv /etc/issue.backup /etc/issue
|
||||
fi
|
||||
if [ -e /etc/rc.local.backup ]; then
|
||||
sudo mv /etc/rc.local.backup /etc/rc.local
|
||||
fi
|
||||
# Install Mininet
|
||||
sudo apt-get -y install git-core openssh-server
|
||||
git clone git://github.com/mininet/mininet
|
||||
cd mininet
|
||||
# Check out branch for cs244
|
||||
git checkout -b cs244 origin/class/cs244
|
||||
cd
|
||||
time mininet/util/install.sh
|
||||
# Ignoring this since NOX classic is deprecated
|
||||
#if ! grep NOX_CORE_DIR .bashrc; then
|
||||
# echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc
|
||||
#fi
|
||||
if ! grep NOX_CORE_DIR .bashrc; then
|
||||
echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc
|
||||
fi
|
||||
echo <<EOF
|
||||
You may need to reboot and then:
|
||||
sudo dpkg-reconfigure openvswitch-datapath-dkms
|
||||
|
||||
Reference in New Issue
Block a user