Merge branch '2.0dev' into 2.0merge

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