Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ce07884f4 | |||
| be53ac31d6 | |||
| 50bfbd0566 | |||
| a4d8049661 | |||
| da4c53b3c9 | |||
| 1b96ff3539 | |||
| 93d04123c6 | |||
| 22655cf9fd | |||
| fe82d2c04a | |||
| 0534932bf3 |
@@ -1,19 +0,0 @@
|
||||
**Mininet uses GitHub issues for bug reports and feature requests only.**
|
||||
These issues can be viewed at bugs.mininet.org
|
||||
|
||||
If you have a question that is not a **bug report** or a **feature request**,
|
||||
please use the documentation at docs.mininet.org, the FAQ
|
||||
at faq.mininet.org, and the mininet-discuss mailing list.
|
||||
|
||||
For bug reports, please fill in the following information in detail,
|
||||
and also feel free to include additional information such as debug
|
||||
output from mn -v debug, etc.
|
||||
--- Cut Here ---
|
||||
### Expected/Desired Behavior:
|
||||
|
||||
### Actual Behavior:
|
||||
|
||||
### Detailed Steps to Reproduce the Behavior:
|
||||
|
||||
### Additional Information:
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
name: code-check
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
code-check:
|
||||
name: Mininet Code Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Check out Mininet source
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Mininet code check dependencies
|
||||
run: |
|
||||
PYTHON=`which python` util/install.sh -n
|
||||
python -m pip install pylint==2.4.4
|
||||
- name: Run code check
|
||||
run: make codecheck
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
name: mininet-tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Mininet Tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
|
||||
python-version: [3.x, 2.x]
|
||||
steps:
|
||||
- name: Check out Mininet source
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Mininet and base dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
# This seems too slow unfortunately:
|
||||
# sudo apt-get upgrade -y -qq
|
||||
PYTHON=`which python` util/install.sh -nv
|
||||
- name: Sanity test
|
||||
run: |
|
||||
export sudo="sudo env PATH=$PATH"
|
||||
# Verify major python-version number matches python and mininet
|
||||
export PYVER=`echo ${{ matrix.python-version }} | cut -d . -f 1`
|
||||
export CHKVER='px import platform; print(platform.python_version())'
|
||||
python --version |& grep " $PYVER\."
|
||||
echo $CHKVER | $sudo mn -v output | grep " $PYVER\."
|
||||
# Newer OvS tries OpenFlow15 which crashes ovsc on ubuntu-20.04
|
||||
$sudo mn --switch ovs,protocols=OpenFlow13 --test pingall
|
||||
- name: Install test dependencies
|
||||
run: |
|
||||
sudo apt-get install -qq vlan
|
||||
pip install pexpect
|
||||
util/install.sh -fw
|
||||
- name: Run core tests
|
||||
run: |
|
||||
export sudo="sudo env PATH=$PATH"
|
||||
export PYTHON=`which python`
|
||||
$sudo $PYTHON mininet/test/runner.py -v
|
||||
- name: Run examples tests (quick)
|
||||
run: |
|
||||
export sudo="sudo env PATH=$PATH"
|
||||
export PYTHON=`which python`
|
||||
$sudo $PYTHON examples/test/runner.py -v -quick
|
||||
@@ -41,18 +41,10 @@ load-plugins=
|
||||
# can either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once).
|
||||
#
|
||||
# Note: we may want to re-enable some of these at some point, but many of them
|
||||
# are just style issues rather than errors.
|
||||
#
|
||||
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
|
||||
too-many-instance-attributes, too-few-public-methods,
|
||||
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
|
||||
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
|
||||
locally-disabled, locally-enabled, bad-continuation,
|
||||
useless-object-inheritance, unnecessary-pass, no-else-return,
|
||||
no-else-raise, no-else-continue, super-with-arguments
|
||||
|
||||
# bad-continuation, wrong-import-order
|
||||
locally-disabled
|
||||
|
||||
[REPORTS]
|
||||
|
||||
@@ -69,7 +61,7 @@ include-ids=yes
|
||||
# written in a file name "pylint_global.[txt|html]".
|
||||
files-output=no
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
# Tells wether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highes
|
||||
@@ -119,10 +111,10 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Regular expression which should only match correct function names
|
||||
function-rgx=[a-z_][a-z0-9]{2,30}$
|
||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct method names
|
||||
method-rgx=[a-z_][a-z0-9]{2,30}$
|
||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct instance attribute names
|
||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
@@ -131,7 +123,7 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9]{2,30}$
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct list comprehension /
|
||||
# generator expression variable names
|
||||
|
||||
+1
-30
@@ -18,63 +18,34 @@ Cody Burkard
|
||||
|
||||
Additional Mininet Contributors
|
||||
|
||||
Joseph Beshay
|
||||
M S Vishwanath Bhat
|
||||
Muhammad Umair Bhatti
|
||||
Arie Bregman
|
||||
Tomasz Buchert
|
||||
Gustavo Pantuza Coelho Pinto
|
||||
Fernando Cappi
|
||||
HW Chiu
|
||||
Ryan Cox
|
||||
Shaun Crampton
|
||||
Jason Croft
|
||||
Hantao Cui
|
||||
Nirmoy Das
|
||||
Lenoardo D'avila
|
||||
Giuseppe Di Lena
|
||||
David Erickson
|
||||
Juan Gascon
|
||||
Glen Gibb
|
||||
Andrew Ferguson
|
||||
Eder Leao Fernandes
|
||||
Julian Filter
|
||||
Ben Frankel
|
||||
Tim Gates
|
||||
Gregory Gee
|
||||
Jon Hall
|
||||
Roan Huang
|
||||
Vitaly Ivanov
|
||||
Theo Jepsen
|
||||
Mathieu Jadin
|
||||
Babis Kaidos
|
||||
Rich Lane
|
||||
Rémy Léone
|
||||
Xiaozhou Li
|
||||
Zi Shen Lim
|
||||
David Mahler
|
||||
Felix Maurer
|
||||
Murphy McCauley
|
||||
Alex Moijes
|
||||
Felician Nemeth
|
||||
José Pedro Oliveira
|
||||
James Page
|
||||
Gustavo Pantuza Coelho Pinto
|
||||
Ramon Pujianto
|
||||
Stempha Reiter
|
||||
Damien Saucez
|
||||
Shan Sikdar
|
||||
Angad Singh
|
||||
Piyush Srivastava
|
||||
Ed Swierk
|
||||
Darshan Thaker
|
||||
Olivier Tl]ilmans
|
||||
Niels van Adrichem
|
||||
Brad Walker
|
||||
Andreas Wundsam
|
||||
Vikas Yadav
|
||||
Isaku Yamahata
|
||||
Baohua Yang
|
||||
Zhuo
|
||||
|
||||
Thanks also to everyone who has submitted issues and pull
|
||||
requests on github, and to our friendly mininet-discuss
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.3.0b2
|
||||
Mininet 2.2.1
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
@@ -32,21 +32,19 @@ like to contribute an installation script, we would welcome it!)
|
||||
2. Next-easiest option: use our Ubuntu package!
|
||||
|
||||
To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu
|
||||
16.04+:
|
||||
12.10+:
|
||||
|
||||
sudo apt-get install mininet
|
||||
|
||||
Note: this may install an older version of Mininet which may not
|
||||
support Python 3. If you would like the latest version of Mininet,
|
||||
consider installing from source as described in the next section.
|
||||
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
|
||||
|
||||
If you are running Ubuntu, Debian, or Fedora, you may be able to use
|
||||
our handy `install.sh` script, which is in `util/`. Please read the
|
||||
following sections first.
|
||||
|
||||
3.1. Obtaining the Mininet source code
|
||||
3.1. Native installation from source on Ubuntu 12.04+
|
||||
|
||||
If you're reading this, you've probably already done so, but the
|
||||
command to download the Mininet source code is:
|
||||
@@ -54,9 +52,8 @@ like to contribute an installation script, we would welcome it!)
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
|
||||
Note that the above git command will check out the latest and greatest
|
||||
Mininet (which we recommend!) If you want to run the last
|
||||
tagged/released version of Mininet, you can look at the release tags
|
||||
using
|
||||
Mininet (which we recommend!) If you want to run the last tagged/released
|
||||
version of Mininet, you can look at the release tags using
|
||||
|
||||
cd mininet
|
||||
git tag
|
||||
@@ -67,79 +64,44 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
where <release tag> is the release you want to check out.
|
||||
|
||||
3.1.1 *CAUTION: USE AT YOUR OWN RISK!*
|
||||
If you are running Ubuntu, Debian, or Fedora, you may be able to use
|
||||
our handy `install.sh` script, which is in `mininet/util`.
|
||||
|
||||
`install.sh` can be a bit intrusive and may possibly damage your OS
|
||||
*WARNING: USE AT YOUR OWN RISK!*
|
||||
|
||||
`install.sh` is a bit intrusive and may possibly damage your OS
|
||||
and/or home directory, by creating/modifying several directories
|
||||
such as `mininet`, `openflow`, `oftest`, `pox`, etc.. We recommend
|
||||
trying it in a VM before trying it on a system you use from day to
|
||||
day.
|
||||
trying it in a VM before trying it on a system you use from day to day.
|
||||
|
||||
Although we hope it won't do anything completely terrible, you may
|
||||
want to look at the script before you run it, and you should make
|
||||
sure your system and home directory are backed up just in case!
|
||||
|
||||
You can change the directory where the dependencies are installed
|
||||
using the -s <directory> flag.
|
||||
|
||||
util/install.sh -s <directory> ...
|
||||
|
||||
3.1.2 Running `install.sh`
|
||||
|
||||
Installing a "minimal" version of Mininet with Open vSwitch should
|
||||
be reasonably non-perturbing since it should not create directories
|
||||
for other tools:
|
||||
|
||||
util/install.sh -nv
|
||||
|
||||
Note this will not install a controller, so you will have to either
|
||||
install your own controller, or use a switch such OVSBridge that does
|
||||
not require a controller:
|
||||
|
||||
sudo mn --switch ovsbr --test pingall
|
||||
|
||||
To install Mininet itself, the OpenFlow reference controller, and
|
||||
To install Mininet itself, the OpenFlow reference implementation, and
|
||||
Open vSwitch, you may use:
|
||||
|
||||
util/install.sh -fnv
|
||||
mininet/util/install.sh -fnv
|
||||
|
||||
This should be reasonably quick, and the following command should
|
||||
work after the installation:
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
3.1.3 Python 3 and Python 2 support
|
||||
|
||||
Mininet supports Python 3 and Python 2. By default, `install.sh`
|
||||
will use whatever `python` is on your system. To specify a
|
||||
specific version of Pythonm, you can set the PYTHON environment
|
||||
variable:
|
||||
|
||||
PYTHON=python3 util/install.sh -fnv
|
||||
|
||||
You can install Mininet for both Python 3 and Python 2:
|
||||
|
||||
PYTHON=python2 util/install.sh -fnv
|
||||
PYTHON=python3 util/install.sh -n
|
||||
|
||||
Whichever version was installed last will be the default for `mn`.
|
||||
As long as Mininet is installed for the appropriate version of
|
||||
Python, you can run it using that versinon of Python:
|
||||
|
||||
python3 `which mn`
|
||||
python2 `which mn`
|
||||
|
||||
To install ALL of the software which we use for OpenFlow tutorials,
|
||||
including POX, the OpenFlow WireShark dissector, the `oftest`
|
||||
framework, and other potentially useful software, you may use:
|
||||
|
||||
util/install.sh -a
|
||||
mininet/util/install.sh -a
|
||||
|
||||
This takes about 4 minutes on our test system.
|
||||
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
You can change the directory where the dependencies are installed using
|
||||
the -s <directory> flag.
|
||||
|
||||
*This may be out of date.*
|
||||
mininet/util/install.sh -s <directory> -a
|
||||
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
|
||||
As root execute the following operations:
|
||||
|
||||
@@ -166,7 +128,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
* install Mininet, the OpenFlow reference implementation, and
|
||||
Open vSwitch
|
||||
|
||||
util/install.sh -fnv
|
||||
mininet/util/install.sh -fnv
|
||||
|
||||
* enable and start openvswitch
|
||||
|
||||
@@ -193,8 +155,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
Although we don't support other Linux distributions directly, it
|
||||
should be possible to install and run Mininet with some degree of
|
||||
manual effort. People have even gotten `mn --switch user` to run
|
||||
in a ChromeOS container.
|
||||
manual effort.
|
||||
|
||||
In general, you must have:
|
||||
|
||||
@@ -213,6 +174,6 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
Good luck!
|
||||
|
||||
Mininet Developers
|
||||
Mininet Team
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Mininet 2.3.0b2 License
|
||||
Mininet 2.2.1 License
|
||||
|
||||
Copyright (c) 2013-2020 Open Networking Foundation
|
||||
Copyright (c) 2013-2015 Open Networking Laboratory
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
|
||||
The Leland Stanford Junior University
|
||||
|
||||
|
||||
@@ -2,19 +2,15 @@ MININET = mininet/*.py
|
||||
TEST = mininet/test/*.py
|
||||
EXAMPLES = mininet/examples/*.py
|
||||
MN = bin/mn
|
||||
PYTHON ?= python
|
||||
PYMN = $(PYTHON) -B bin/mn
|
||||
BIN = $(MN)
|
||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||
MNEXEC = mnexec
|
||||
MANPAGES = mn.1 mnexec.1
|
||||
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226,E402,W504,W503,E731
|
||||
PREFIX ?= /usr
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
MANDIR ?= $(PREFIX)/share/man/man1
|
||||
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
|
||||
BINDIR = /usr/bin
|
||||
MANDIR = /usr/share/man/man1
|
||||
DOCDIRS = doc/html doc/latex
|
||||
PDF = doc/latex/refman.pdf
|
||||
CC ?= cc
|
||||
|
||||
CFLAGS += -Wall -Wextra
|
||||
|
||||
@@ -47,28 +43,24 @@ slowtest: $(MININET)
|
||||
mininet/examples/test/runner.py -v
|
||||
|
||||
mnexec: mnexec.c $(MN) mininet/net.py
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
|
||||
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
|
||||
|
||||
install-mnexec: $(MNEXEC)
|
||||
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
|
||||
|
||||
install-manpages: $(MANPAGES)
|
||||
install -D -t $(MANDIR) $(MANPAGES)
|
||||
|
||||
install: install-mnexec install-manpages
|
||||
$(PYTHON) -m pip install .
|
||||
install: $(MNEXEC) $(MANPAGES)
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
python setup.py install
|
||||
|
||||
develop: $(MNEXEC) $(MANPAGES)
|
||||
# Perhaps we should link these as well
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
$(PYTHON) -m pip install -e . --no-binary :all:
|
||||
python setup.py develop
|
||||
|
||||
man: $(MANPAGES)
|
||||
|
||||
mn.1: $(MN)
|
||||
PYTHONPATH=. help2man -N -n "create a Mininet network." \
|
||||
--no-discard-stderr "$(PYMN)" -o $@
|
||||
--no-discard-stderr $< -o $@
|
||||
|
||||
mnexec.1: mnexec
|
||||
help2man -N -n "execution utility for Mininet." \
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
Mininet: Rapid Prototyping for Software Defined Networks
|
||||
========================================================
|
||||
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
Mininet 2.3.0b2
|
||||
|
||||
[![Build Status][1]](https://github.com/mininet/mininet/actions)
|
||||
|
||||
Mininet 2.2.1
|
||||
|
||||
### What is Mininet?
|
||||
|
||||
@@ -68,28 +66,27 @@ Mininet includes:
|
||||
|
||||
`mn -c`
|
||||
|
||||
### Python 3 Support
|
||||
### New features in this release
|
||||
|
||||
- Mininet 2.3.0b2 supports Python 3 and Python 2.
|
||||
This is primarily a performance improvement and bug fix release.
|
||||
|
||||
- You can install both the Python 3 and Python 2 versions of
|
||||
Mininet side by side, but the most recent installation will
|
||||
determine which Python version is used by default by `mn`.
|
||||
- Batch startup has been implemented for Open vSwitch, improving
|
||||
startup performance.
|
||||
|
||||
- You can run `mn` directly with Python 2 or Python 3,
|
||||
as long as the appropriate version of Mininet is installed,
|
||||
e.g.
|
||||
- OVS patch links have been implemented via OVSLink and --link ovs
|
||||
|
||||
$ sudo python2 `which mn`
|
||||
Warning! These links have *serious limitations* compared to
|
||||
virtual Ethernet pairs: they are not attached to real Linux
|
||||
interfaces so you cannot use tcpdump or wireshark with them;
|
||||
they also cannot be used in long chains - we don't recommend more
|
||||
than 64 OVSLinks, for example --linear,64. However, they can offer
|
||||
significantly better performance than veth pairs, for certain
|
||||
configurations.
|
||||
|
||||
### Other Enhancements and Information
|
||||
- You can now easily install Mininet on a Raspberry Pi ;-)
|
||||
|
||||
- Support for Ubuntu 20.04 LTS (and 18.04 and 16.04)
|
||||
|
||||
- More reliable testing and CI via github actions
|
||||
|
||||
- Additional information about this release and previous releases
|
||||
may be found in the release notes on http://docs.mininet.org.
|
||||
- Additional information for this release and previous releases
|
||||
may be found in the release notes on docs.mininet.org
|
||||
|
||||
### Installation
|
||||
|
||||
@@ -102,8 +99,7 @@ information, including a Mininet walkthrough and an introduction
|
||||
to the Python API, is available on the
|
||||
[Mininet Web Site](http://mininet.org).
|
||||
There is also a wiki which you are encouraged to read and to
|
||||
contribute to, particularly the Frequently Asked Questions
|
||||
(FAQ) at http://faq.mininet.org.
|
||||
contribute to, particularly the Frequently Asked Questions (FAQ.)
|
||||
|
||||
### Support
|
||||
|
||||
@@ -130,6 +126,4 @@ Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
|
||||
Bob Lantz
|
||||
on behalf of the Mininet Contributors
|
||||
|
||||
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
|
||||
Mininet Core Team
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Mininet runner
|
||||
@@ -11,23 +11,18 @@ Example to pull custom params (topo, switch, etc.) from a file:
|
||||
sudo mn --custom ~/mininet/custom/custom_example.py
|
||||
"""
|
||||
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from functools import partial
|
||||
from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
|
||||
# Fix setuptools' evil madness, and open up (more?) security holes
|
||||
if 'PYTHONPATH' in os.environ:
|
||||
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
|
||||
from mininet.clean import cleanup
|
||||
import mininet.cli
|
||||
from mininet.log import lg, LEVELS, info, debug, warn, error, output
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, LEVELS, info, debug, warn, error
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
Ryu, NOX, RemoteController, findController,
|
||||
@@ -35,11 +30,14 @@ from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
UserSwitch, OVSSwitch, OVSBridge,
|
||||
IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink, TCULink, OVSLink
|
||||
from mininet.link import Link, TCLink, OVSLink
|
||||
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
|
||||
SingleSwitchReversedTopo, MinimalTopo )
|
||||
from mininet.topolib import TreeTopo, TorusTopo
|
||||
from mininet.util import customClass, specialClass, splitArgs, buildTopo
|
||||
from mininet.util import customClass, specialClass, splitArgs
|
||||
from mininet.util import buildTopo
|
||||
|
||||
from functools import partial
|
||||
|
||||
# Experimental! cluster edition prototype
|
||||
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
|
||||
@@ -48,7 +46,6 @@ from mininet.examples.cluster import ( MininetCluster, RemoteHost,
|
||||
ClusterCleanup )
|
||||
from mininet.examples.clustercli import ClusterCLI
|
||||
|
||||
|
||||
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
|
||||
|
||||
# built in topologies, created only when run
|
||||
@@ -76,66 +73,29 @@ HOSTS = { 'proc': Host,
|
||||
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
|
||||
|
||||
CONTROLLERDEF = 'default'
|
||||
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'ryu': Ryu,
|
||||
'default': DefaultController, # Note: overridden below
|
||||
'default': DefaultController, # Note: replaced below
|
||||
'none': NullController }
|
||||
|
||||
LINKDEF = 'default'
|
||||
LINKS = { 'default': Link, # Note: overridden below
|
||||
LINKS = { 'default': Link,
|
||||
'tc': TCLink,
|
||||
'tcu': TCULink,
|
||||
'ovs': OVSLink }
|
||||
|
||||
# TESTS dict can contain functions and/or Mininet() method names
|
||||
# XXX: it would be nice if we could specify a default test, but
|
||||
# this may be tricky
|
||||
TESTS = { name: True
|
||||
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
|
||||
|
||||
CLI = None # Set below if needed
|
||||
# optional tests to run
|
||||
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
|
||||
'none' ]
|
||||
|
||||
# Locally defined tests
|
||||
def allTest( net ):
|
||||
"Run ping and iperf tests"
|
||||
net.waitConnected()
|
||||
net.start()
|
||||
net.ping()
|
||||
net.iperf()
|
||||
|
||||
def nullTest( _net ):
|
||||
"Null 'test' (does nothing)"
|
||||
pass
|
||||
|
||||
|
||||
TESTS.update( all=allTest, none=nullTest, build=nullTest )
|
||||
|
||||
# Map to alternate spellings of Mininet() methods
|
||||
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp' }
|
||||
|
||||
def runTests( mn, options ):
|
||||
"""Run tests
|
||||
mn: Mininet object
|
||||
option: list of test optinos """
|
||||
# Split option into test name and parameters
|
||||
for option in options:
|
||||
# Multiple tests may be separated by '+' for now
|
||||
for test in option.split( '+' ):
|
||||
test, args, kwargs = splitArgs( test )
|
||||
test = ALTSPELLING.get( test.lower(), test )
|
||||
testfn = TESTS.get( test, test )
|
||||
if callable( testfn ):
|
||||
testfn( mn, *args, **kwargs )
|
||||
elif hasattr( mn, test ):
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )( *args, **kwargs )
|
||||
else:
|
||||
raise Exception( 'Test %s is unknown - please specify one of '
|
||||
'%s ' % ( test, TESTS.keys() ) )
|
||||
ALTSPELLING = { 'pingall': 'pingAll',
|
||||
'pingpair': 'pingPair',
|
||||
'iperfudp': 'iperfUdp',
|
||||
'iperfUDP': 'iperfUdp' }
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
@@ -156,7 +116,7 @@ def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
output( "%s\n" % VERSION )
|
||||
print "%s" % VERSION
|
||||
exit()
|
||||
|
||||
|
||||
@@ -190,18 +150,15 @@ class MininetRunner( object ):
|
||||
for fileName in files:
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
# pylint: disable=exec-used
|
||||
exec( compile( open( fileName ).read(), fileName, 'exec' ),
|
||||
customs, customs )
|
||||
for name, val in customs.items():
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
def setCustom( self, name, value ):
|
||||
"Set custom parameters for MininetRunner."
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links'
|
||||
'testnames', 'tests' ):
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links' ):
|
||||
# Update dictionaries
|
||||
param = name.upper()
|
||||
globals()[ param ].update( value )
|
||||
@@ -251,8 +208,10 @@ class MininetRunner( object ):
|
||||
type='string',
|
||||
help='read custom classes or params from .py file(s)'
|
||||
)
|
||||
opts.add_option( '--test', default=[], action='append',
|
||||
dest='test', help='|'.join( TESTS.keys() ) )
|
||||
|
||||
opts.add_option( '--test', type='choice', choices=TESTS,
|
||||
default=TESTS[ 0 ],
|
||||
help='|'.join( TESTS ) )
|
||||
opts.add_option( '--xterms', '-x', action='store_true',
|
||||
default=False, help='spawn xterms for each node' )
|
||||
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
|
||||
@@ -262,11 +221,11 @@ class MininetRunner( object ):
|
||||
opts.add_option( '--arp', action='store_true',
|
||||
default=False, help='set all-pairs ARP entries' )
|
||||
opts.add_option( '--verbosity', '-v', type='choice',
|
||||
choices=list( LEVELS.keys() ), default = 'info',
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6654,
|
||||
opts.add_option( '--listenport', type='int', default=6634,
|
||||
help='base port for passive switch listening' )
|
||||
opts.add_option( '--nolistenport', action='store_true',
|
||||
default=False, help="don't use passive listening " +
|
||||
@@ -288,13 +247,11 @@ class MininetRunner( object ):
|
||||
" Mininet's IP subnet, see the --ipbase option." )
|
||||
opts.add_option( '--version', action='callback', callback=version,
|
||||
help='prints the version and exits' )
|
||||
opts.add_option( '--wait', '-w', action='store_true',
|
||||
default=False, help='wait for switches to connect' )
|
||||
opts.add_option( '--cluster', type='string', default=None,
|
||||
metavar='server1,server2...',
|
||||
help=( 'run on multiple servers (experimental!)' ) )
|
||||
opts.add_option( '--placement', type='choice',
|
||||
choices=list( PLACEMENT.keys() ), default='block',
|
||||
choices=PLACEMENT.keys(), default='block',
|
||||
metavar='block|random',
|
||||
help=( 'node placement for --cluster '
|
||||
'(experimental!) ' ) )
|
||||
@@ -311,111 +268,116 @@ class MininetRunner( object ):
|
||||
|
||||
# set logging verbosity
|
||||
if LEVELS[self.options.verbosity] > LEVELS['output']:
|
||||
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
|
||||
'output!\n'
|
||||
'Please restart Mininet with -v [debug, info, output].\n'
|
||||
'Please restart Mininet with -v [debug, info, output].'
|
||||
% self.options.verbosity )
|
||||
lg.setLogLevel( self.options.verbosity )
|
||||
|
||||
# Maybe we'll reorganize this someday...
|
||||
# pylint: disable=too-many-branches,too-many-statements,global-statement
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
|
||||
def begin( self ):
|
||||
"Create and run mininet."
|
||||
|
||||
global CLI
|
||||
|
||||
opts = self.options
|
||||
|
||||
if opts.cluster:
|
||||
servers = opts.cluster.split( ',' )
|
||||
if self.options.cluster:
|
||||
servers = self.options.cluster.split( ',' )
|
||||
for server in servers:
|
||||
ClusterCleanup.add( server )
|
||||
|
||||
if opts.clean:
|
||||
if self.options.clean:
|
||||
cleanup()
|
||||
exit()
|
||||
|
||||
start = time.time()
|
||||
|
||||
if not opts.controller:
|
||||
if not self.options.controller:
|
||||
# Update default based on available controllers
|
||||
CONTROLLERS[ 'default' ] = findController()
|
||||
opts.controller = [ 'default' ]
|
||||
self.options.controller = [ 'default' ]
|
||||
if not CONTROLLERS[ 'default' ]:
|
||||
opts.controller = [ 'none' ]
|
||||
if opts.switch == 'default':
|
||||
self.options.controller = [ 'none' ]
|
||||
if self.options.switch == 'default':
|
||||
info( '*** No default OpenFlow controller found '
|
||||
'for default switch!\n' )
|
||||
info( '*** Falling back to OVS Bridge\n' )
|
||||
opts.switch = 'ovsbr'
|
||||
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
self.options.switch = 'ovsbr'
|
||||
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
raise Exception( "Could not find a default controller "
|
||||
"for switch %s" %
|
||||
opts.switch )
|
||||
self.options.switch )
|
||||
|
||||
topo = buildTopo( TOPOS, opts.topo )
|
||||
switch = customClass( SWITCHES, opts.switch )
|
||||
host = customClass( HOSTS, opts.host )
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customClass( SWITCHES, self.options.switch )
|
||||
host = customClass( HOSTS, self.options.host )
|
||||
controller = [ customClass( CONTROLLERS, c )
|
||||
for c in opts.controller ]
|
||||
|
||||
if opts.switch == 'user' and opts.link == 'default':
|
||||
debug( '*** Using TCULink with UserSwitch\n' )
|
||||
# Use link configured correctly for UserSwitch
|
||||
opts.link = 'tcu'
|
||||
|
||||
link = customClass( LINKS, opts.link )
|
||||
for c in self.options.controller ]
|
||||
link = customClass( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( opts )
|
||||
self.validate( self.options )
|
||||
|
||||
if opts.nolistenport:
|
||||
opts.listenport = None
|
||||
ipBase = self.options.ipbase
|
||||
xterms = self.options.xterms
|
||||
mac = self.options.mac
|
||||
arp = self.options.arp
|
||||
pin = self.options.pin
|
||||
listenPort = None
|
||||
if not self.options.nolistenport:
|
||||
listenPort = self.options.listenport
|
||||
|
||||
# Handle innamespace, cluster options
|
||||
if opts.innamespace and opts.cluster:
|
||||
error( "Please specify --innamespace OR --cluster\n" )
|
||||
# Handle inNamespace, cluster options
|
||||
inNamespace = self.options.innamespace
|
||||
cluster = self.options.cluster
|
||||
if inNamespace and cluster:
|
||||
print "Please specify --innamespace OR --cluster"
|
||||
exit()
|
||||
Net = MininetWithControlNet if opts.innamespace else Mininet
|
||||
if opts.cluster:
|
||||
Net = MininetWithControlNet if inNamespace else Mininet
|
||||
cli = ClusterCLI if cluster else CLI
|
||||
if cluster:
|
||||
warn( '*** WARNING: Experimental cluster mode!\n'
|
||||
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
|
||||
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
|
||||
Net = partial( MininetCluster, servers=servers,
|
||||
placement=PLACEMENT[ opts.placement ] )
|
||||
mininet.cli.CLI = ClusterCLI
|
||||
placement=PLACEMENT[ self.options.placement ] )
|
||||
|
||||
mn = Net( topo=topo,
|
||||
switch=switch, host=host, controller=controller, link=link,
|
||||
ipBase=opts.ipbase, inNamespace=opts.innamespace,
|
||||
xterms=opts.xterms, autoSetMacs=opts.mac,
|
||||
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
|
||||
waitConnected=opts.wait,
|
||||
listenPort=opts.listenport )
|
||||
switch=switch, host=host, controller=controller,
|
||||
link=link,
|
||||
ipBase=ipBase,
|
||||
inNamespace=inNamespace,
|
||||
xterms=xterms, autoSetMacs=mac,
|
||||
autoStaticArp=arp, autoPinCpus=pin,
|
||||
listenPort=listenPort )
|
||||
|
||||
if opts.ensure_value( 'nat', False ):
|
||||
with open( '/etc/resolv.conf' ) as f:
|
||||
if 'nameserver 127.' in f.read():
|
||||
warn( '*** Warning: loopback address in /etc/resolv.conf '
|
||||
'may break host DNS over NAT\n')
|
||||
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
|
||||
if self.options.ensure_value( 'nat', False ):
|
||||
nat = mn.addNAT( *self.options.nat_args,
|
||||
**self.options.nat_kwargs )
|
||||
nat.configDefault()
|
||||
|
||||
# --custom files can set CLI or change mininet.cli.CLI
|
||||
CLI = mininet.cli.CLI if CLI is None else CLI
|
||||
if self.options.pre:
|
||||
cli( mn, script=self.options.pre )
|
||||
|
||||
if opts.pre:
|
||||
CLI( mn, script=opts.pre )
|
||||
test = self.options.test
|
||||
test = ALTSPELLING.get( test, test )
|
||||
|
||||
mn.start()
|
||||
|
||||
if opts.test:
|
||||
runTests( mn, opts.test )
|
||||
else:
|
||||
CLI( mn )
|
||||
if test == 'none':
|
||||
pass
|
||||
elif test == 'all':
|
||||
mn.waitConnected()
|
||||
mn.start()
|
||||
mn.ping()
|
||||
mn.iperf()
|
||||
elif test == 'cli':
|
||||
cli( mn )
|
||||
elif test != 'build':
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )()
|
||||
|
||||
if opts.post:
|
||||
CLI( mn, script=opts.post )
|
||||
if self.options.post:
|
||||
cli( mn, script=self.options.post )
|
||||
|
||||
mn.stop()
|
||||
|
||||
@@ -429,7 +391,7 @@ if __name__ == "__main__":
|
||||
except KeyboardInterrupt:
|
||||
info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
|
||||
cleanup()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except Exception:
|
||||
# Print exception
|
||||
type_, val_, trace_ = sys.exc_info()
|
||||
errorMsg = ( "-"*80 + "\n" +
|
||||
|
||||
@@ -13,9 +13,12 @@ from mininet.topo import Topo
|
||||
class MyTopo( Topo ):
|
||||
"Simple topology example."
|
||||
|
||||
def build( self ):
|
||||
def __init__( self ):
|
||||
"Create custom topo."
|
||||
|
||||
# Initialize topology
|
||||
Topo.__init__( self )
|
||||
|
||||
# Add hosts and switches
|
||||
leftHost = self.addHost( 'h1' )
|
||||
rightHost = self.addHost( 'h2' )
|
||||
|
||||
+9
-12
@@ -3,35 +3,32 @@
|
||||
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
||||
|
||||
import sys
|
||||
|
||||
from mininet.node import Host
|
||||
from mininet.util import ensureRoot, waitListening
|
||||
from mininet.log import info, warn, output
|
||||
|
||||
|
||||
ensureRoot()
|
||||
timeout = 5
|
||||
|
||||
info( "*** Creating nodes\n" )
|
||||
print "*** Creating nodes"
|
||||
h1 = Host( 'h1' )
|
||||
|
||||
root = Host( 'root', inNamespace=False )
|
||||
|
||||
info( "*** Creating link\n" )
|
||||
print "*** Creating links"
|
||||
h1.linkTo( root )
|
||||
|
||||
info( h1 )
|
||||
print h1
|
||||
|
||||
info( "*** Configuring nodes\n" )
|
||||
print "*** Configuring nodes"
|
||||
h1.setIP( '10.0.0.1', 8 )
|
||||
root.setIP( '10.0.0.2', 8 )
|
||||
|
||||
info( "*** Creating banner file\n" )
|
||||
print "*** Creating banner file"
|
||||
f = open( '/tmp/%s.banner' % h1.name, 'w' )
|
||||
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
|
||||
f.close()
|
||||
|
||||
info( "*** Running sshd\n" )
|
||||
print "*** Running sshd"
|
||||
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
|
||||
# add arguments from the command line
|
||||
if len( sys.argv ) > 1:
|
||||
@@ -40,7 +37,7 @@ h1.cmd( cmd )
|
||||
listening = waitListening( server=h1, port=22, timeout=timeout )
|
||||
|
||||
if listening:
|
||||
output( "*** You may now ssh into", h1.name, "at", h1.IP(), '\n' )
|
||||
print "*** You may now ssh into", h1.name, "at", h1.IP()
|
||||
else:
|
||||
warn( "*** Warning: after %s seconds, %s is not listening on port 22"
|
||||
% ( timeout, h1.name ), '\n' )
|
||||
print ( "*** Warning: after %s seconds, %s is not listening on port 22"
|
||||
% ( timeout, h1.name ) )
|
||||
|
||||
+3
-4
@@ -34,14 +34,14 @@ and '/var/run'. It also has a temporary private directory mounted
|
||||
on '/var/mn'
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Host
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from functools import partial
|
||||
|
||||
|
||||
# Sample usage
|
||||
|
||||
@@ -53,7 +53,7 @@ def testHostWithPrivateDirs():
|
||||
'/var/mn' ]
|
||||
host = partial( Host,
|
||||
privateDirs=privateDirs )
|
||||
net = Mininet( topo=topo, host=host, waitConnected=True )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
net.start()
|
||||
directories = [ directory[ 0 ] if isinstance( directory, tuple )
|
||||
else directory for directory in privateDirs ]
|
||||
@@ -61,7 +61,6 @@ def testHostWithPrivateDirs():
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testHostWithPrivateDirs()
|
||||
|
||||
+77
-190
@@ -74,16 +74,6 @@ Things to do:
|
||||
- hifi support (e.g. delay compensation)
|
||||
"""
|
||||
|
||||
from signal import signal, SIGINT, SIG_IGN
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
import os
|
||||
from random import randrange
|
||||
import sys
|
||||
import re
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
from mininet.node import Node, Host, OVSSwitch, Controller
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.net import Mininet
|
||||
@@ -94,7 +84,15 @@ from mininet.examples.clustercli import CLI
|
||||
from mininet.log import setLogLevel, debug, info, error
|
||||
from mininet.clean import addCleanupCallback
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
from signal import signal, SIGINT, SIG_IGN
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
import os
|
||||
from random import randrange
|
||||
import sys
|
||||
import re
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
|
||||
def findUser():
|
||||
@@ -105,7 +103,7 @@ def findUser():
|
||||
# Logged-in user (if we have a tty)
|
||||
( quietRun( 'who am i' ).split() or [ False ] )[ 0 ] or
|
||||
# Give up and return effective user
|
||||
quietRun( 'whoami' ).strip() )
|
||||
quietRun( 'whoami' ) )
|
||||
|
||||
|
||||
class ClusterCleanup( object ):
|
||||
@@ -127,7 +125,7 @@ class ClusterCleanup( object ):
|
||||
def cleanup( cls ):
|
||||
"Clean up"
|
||||
info( '*** Cleaning up cluster\n' )
|
||||
for server, user in cls.serveruser.items():
|
||||
for server, user in cls.serveruser.iteritems():
|
||||
if server == 'localhost':
|
||||
# Handled by mininet.clean.cleanup()
|
||||
continue
|
||||
@@ -240,7 +238,7 @@ class RemoteMixin( object ):
|
||||
args: string or list of strings
|
||||
returns: stdout and stderr"""
|
||||
popen = self.rpopen( *cmd, **opts )
|
||||
# info( 'RCMD: POPEN:', popen, '\n' )
|
||||
# print 'RCMD: POPEN:', popen
|
||||
# These loops are tricky to get right.
|
||||
# Once the process exits, we can read
|
||||
# EOF twice if necessary.
|
||||
@@ -262,7 +260,7 @@ class RemoteMixin( object ):
|
||||
cmd: remote command to run (list)
|
||||
**params: parameters to Popen()
|
||||
returns: Popen() object"""
|
||||
if isinstance( cmd, str):
|
||||
if type( cmd ) is str:
|
||||
cmd = cmd.split()
|
||||
if self.isRemote:
|
||||
if sudo:
|
||||
@@ -289,8 +287,7 @@ class RemoteMixin( object ):
|
||||
|
||||
def addIntf( self, *args, **kwargs ):
|
||||
"Override: use RemoteLink.moveIntf"
|
||||
# kwargs.update( moveIntfFn=RemoteLink.moveIntf )
|
||||
# pylint: disable=useless-super-delegation
|
||||
kwargs.update( moveIntfFn=RemoteLink.moveIntf )
|
||||
return super( RemoteMixin, self).addIntf( *args, **kwargs )
|
||||
|
||||
|
||||
@@ -327,7 +324,6 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
|
||||
StrictVersion( '1.10' ) )
|
||||
|
||||
@classmethod
|
||||
# pylint: disable=arguments-differ
|
||||
def batchStartup( cls, switches, **_kwargs ):
|
||||
"Start up switches in per-server batches"
|
||||
key = attrgetter( 'server' )
|
||||
@@ -339,7 +335,6 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
|
||||
return switches
|
||||
|
||||
@classmethod
|
||||
# pylint: disable=arguments-differ
|
||||
def batchShutdown( cls, switches, **_kwargs ):
|
||||
"Stop switches in per-server batches"
|
||||
key = attrgetter( 'server' )
|
||||
@@ -397,31 +392,33 @@ class RemoteLink( Link ):
|
||||
return self.tunnel
|
||||
|
||||
@staticmethod
|
||||
def moveIntf( intf, node ):
|
||||
def moveIntf( intf, node, printError=True ):
|
||||
"""Move remote interface from root ns to node
|
||||
intf: string, interface
|
||||
dstNode: destination Node
|
||||
srcNode: source Node or None (default) for root ns"""
|
||||
srcNode: source Node or None (default) for root ns
|
||||
printError: if true, print error"""
|
||||
intf = str( intf )
|
||||
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
|
||||
result = node.rcmd( cmd )
|
||||
if result:
|
||||
raise Exception('error executing command %s' % cmd)
|
||||
node.rcmd( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not ' %s:' % intf in links:
|
||||
if printError:
|
||||
error( '*** Error: RemoteLink.moveIntf: ' + intf +
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
return False
|
||||
return True
|
||||
|
||||
def makeTunnel( self, node1, node2, intfname1, intfname2,
|
||||
addr1=None, addr2=None ):
|
||||
"Make a tunnel across switches on different servers"
|
||||
# We should never try to create a tunnel to ourselves!
|
||||
assert node1.server != node2.server
|
||||
assert node1.server != 'localhost' or node2.server != 'localhost'
|
||||
# And we can't ssh into this server remotely as 'localhost',
|
||||
# so try again swappping node1 and node2
|
||||
if node2.server == 'localhost':
|
||||
return self.makeTunnel( node1=node2, node2=node1,
|
||||
intfname1=intfname2, intfname2=intfname1,
|
||||
addr1=addr2, addr2=addr1 )
|
||||
debug( '\n*** Make SSH tunnel ' + node1.server + ':' + intfname1 +
|
||||
' == ' + node2.server + ':' + intfname2 )
|
||||
return self.makeTunnel( node2, node1, intfname2, intfname1,
|
||||
addr2, addr1 )
|
||||
# 1. Create tap interfaces
|
||||
for node in node1, node2:
|
||||
# For now we are hard-wiring tap9, which we will rename
|
||||
@@ -477,93 +474,6 @@ class RemoteLink( Link ):
|
||||
return result
|
||||
|
||||
|
||||
class RemoteSSHLink( RemoteLink ):
|
||||
"Remote link using SSH tunnels"
|
||||
def __init__(self, node1, node2, **kwargs):
|
||||
RemoteLink.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
|
||||
class RemoteGRELink( RemoteLink ):
|
||||
"Remote link using GRE tunnels"
|
||||
|
||||
GRE_KEY = 0
|
||||
|
||||
def __init__(self, node1, node2, **kwargs):
|
||||
RemoteLink.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
def stop( self ):
|
||||
"Stop this link"
|
||||
if self.tunnel:
|
||||
self.intf1.delete()
|
||||
self.intf2.delete()
|
||||
else:
|
||||
Link.stop( self )
|
||||
self.tunnel = None
|
||||
|
||||
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
|
||||
node1=None, node2=None, deleteIntfs=True ):
|
||||
"""Create pair of interfaces
|
||||
intfname1: name of interface 1
|
||||
intfname2: name of interface 2
|
||||
(override this method [and possibly delete()]
|
||||
to change link type)"""
|
||||
node1 = self.node1 if node1 is None else node1
|
||||
node2 = self.node2 if node2 is None else node2
|
||||
server1 = getattr( node1, 'server', 'localhost' )
|
||||
server2 = getattr( node2, 'server', 'localhost' )
|
||||
if server1 == server2:
|
||||
# Link within same server
|
||||
Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
|
||||
node1, node2, deleteIntfs=deleteIntfs )
|
||||
# Need to reduce the MTU of all emulated hosts to 1450 for GRE
|
||||
# tunneling, otherwise packets larger than 1400 bytes cannot be
|
||||
# successfully transmitted through the tunnel.
|
||||
node1.cmd('ip link set dev %s mtu 1450' % intfname1)
|
||||
node2.cmd('ip link set dev %s mtu 1450' % intfname2)
|
||||
else:
|
||||
# Otherwise, make a tunnel
|
||||
self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
|
||||
self.tunnel = 1
|
||||
|
||||
def makeTunnel(self, node1, node2, intfname1, intfname2,
|
||||
addr1=None, addr2=None):
|
||||
"Make a tunnel across switches on different servers"
|
||||
# We should never try to create a tunnel to ourselves!
|
||||
assert node1.server != node2.server
|
||||
if node2.server == 'localhost':
|
||||
return self.makeTunnel( node1=node2, node2=node1,
|
||||
intfname1=intfname2, intfname2=intfname1,
|
||||
addr1=addr2, addr2=addr1 )
|
||||
IP1, IP2 = node1.serverIP, node2.serverIP
|
||||
# GRE tunnel needs to be set up with the IP of the local interface
|
||||
# that connects the remote node, NOT '127.0.0.1' of localhost
|
||||
if node1.server == 'localhost':
|
||||
output = quietRun('ip route get %s' % node2.serverIP)
|
||||
IP1 = output.split(' src ')[1].split()[0]
|
||||
debug( '\n*** Make GRE tunnel ' + node1.server + ':' + intfname1 +
|
||||
' == ' + node2.server + ':' + intfname2 )
|
||||
tun1 = 'local ' + IP1 + ' remote ' + IP2
|
||||
tun2 = 'local ' + IP2 + ' remote ' + IP1
|
||||
self.__class__.GRE_KEY += 1
|
||||
for (node, intfname, addr, tun) in [(node1, intfname1, addr1, tun1),
|
||||
(node2, intfname2, addr2, tun2)]:
|
||||
node.rcmd('ip link delete ' + intfname)
|
||||
result = node.rcmd('ip link add name ' + intfname + ' type gretap '
|
||||
+ tun + ' ttl 64 key '
|
||||
+ str( self.__class__.GRE_KEY) )
|
||||
if result:
|
||||
raise Exception('error creating gretap on %s: %s'
|
||||
% (node, result))
|
||||
if addr:
|
||||
node.rcmd('ip link set %s address %s' % (intfname, addr))
|
||||
|
||||
node.rcmd('ip link set dev %s up' % intfname)
|
||||
node.rcmd('ip link set dev %s mtu 1450' % intfname)
|
||||
if not self.moveIntf(intfname, node):
|
||||
raise Exception('interface move failed on node %s' % node)
|
||||
return None # May want to return something useful here
|
||||
|
||||
|
||||
# Some simple placement algorithms for MininetCluster
|
||||
|
||||
class Placer( object ):
|
||||
@@ -596,10 +506,10 @@ class Placer( object ):
|
||||
|
||||
class RandomPlacer( Placer ):
|
||||
"Random placement"
|
||||
def place( self, node ):
|
||||
def place( self, nodename ):
|
||||
"""Random placement function
|
||||
node: node"""
|
||||
assert node # please pylint
|
||||
nodename: node name"""
|
||||
assert nodename # please pylint
|
||||
# This may be slow with lots of servers
|
||||
return self.servers[ randrange( 0, len( self.servers ) ) ]
|
||||
|
||||
@@ -613,10 +523,10 @@ class RoundRobinPlacer( Placer ):
|
||||
Placer.__init__( self, *args, **kwargs )
|
||||
self.next = 0
|
||||
|
||||
def place( self, node ):
|
||||
def place( self, nodename ):
|
||||
"""Round-robin placement function
|
||||
node: node"""
|
||||
assert node # please pylint
|
||||
nodename: node name"""
|
||||
assert nodename # please pylint
|
||||
# This may be slow with lots of servers
|
||||
server = self.servers[ self.next ]
|
||||
self.next = ( self.next + 1 ) % len( self.servers )
|
||||
@@ -654,7 +564,7 @@ class SwitchBinPlacer( Placer ):
|
||||
tickets = sum( [ binsizes[ server ] * [ server ]
|
||||
for server in servers ], [] )
|
||||
# And assign one ticket to each node
|
||||
return dict( zip( nodes, tickets ) )
|
||||
return { node: ticket for node, ticket in zip( nodes, tickets ) }
|
||||
|
||||
def calculatePlacement( self ):
|
||||
"Pre-calculate node placement"
|
||||
@@ -711,21 +621,21 @@ class HostSwitchBinPlacer( Placer ):
|
||||
self.cset = frozenset( self.controllers )
|
||||
self.hind, self.sind, self.cind = 0, 0, 0
|
||||
|
||||
def place( self, node ):
|
||||
def place( self, nodename ):
|
||||
"""Simple placement algorithm:
|
||||
place nodes into evenly sized bins"""
|
||||
# Place nodes into bins
|
||||
if node in self.hset:
|
||||
if nodename in self.hset:
|
||||
server = self.servdict[ self.hind / self.hbin ]
|
||||
self.hind += 1
|
||||
elif node in self.sset:
|
||||
elif nodename in self.sset:
|
||||
server = self.servdict[ self.sind / self.sbin ]
|
||||
self.sind += 1
|
||||
elif node in self.cset:
|
||||
elif nodename in self.cset:
|
||||
server = self.servdict[ self.cind / self.cbin ]
|
||||
self.cind += 1
|
||||
else:
|
||||
info( 'warning: unknown node', node )
|
||||
info( 'warning: unknown node', nodename )
|
||||
server = self.servdict[ 0 ]
|
||||
return server
|
||||
|
||||
@@ -771,7 +681,6 @@ class MininetCluster( Mininet ):
|
||||
# Make sure control directory exists
|
||||
self.cdir = os.environ[ 'HOME' ] + '/.ssh/mn'
|
||||
errRun( [ 'mkdir', '-p', self.cdir ] )
|
||||
# pylint: disable=unexpected-keyword-arg
|
||||
Mininet.__init__( self, *args, **params )
|
||||
|
||||
def popen( self, cmd ):
|
||||
@@ -848,33 +757,16 @@ class MininetCluster( Mininet ):
|
||||
if cfile:
|
||||
config.setdefault( 'controlPath', cfile )
|
||||
|
||||
# pylint: disable=arguments-differ,signature-differs
|
||||
def addController( self, *args, **kwargs ):
|
||||
"Patch to update IP address to global IP address"
|
||||
controller = Mininet.addController( self, *args, **kwargs )
|
||||
loopback = '127.0.0.1'
|
||||
if ( not isinstance( controller, Controller ) or
|
||||
controller.IP() != loopback ):
|
||||
return None
|
||||
# Find route to a different server IP address
|
||||
serverIPs = [ ip for ip in self.serverIP.values()
|
||||
if ip is not controller.IP() ]
|
||||
if not serverIPs:
|
||||
return None # no remote servers - loopback is fine
|
||||
remoteIP = serverIPs[ 0 ]
|
||||
# Route should contain 'dev <intfname>'
|
||||
route = controller.cmd( 'ip route get', remoteIP,
|
||||
r'| egrep -o "dev\s[^[:space:]]+"' )
|
||||
if not route:
|
||||
raise Exception('addController: no route from', controller,
|
||||
'to', remoteIP )
|
||||
intf = route.split()[ 1 ].strip()
|
||||
debug( 'adding', intf, 'to', controller )
|
||||
Intf( intf, node=controller ).updateIP()
|
||||
debug( controller, 'IP address updated to', controller.IP() )
|
||||
# Update IP address for controller that may not be local
|
||||
if ( isinstance( controller, Controller)
|
||||
and controller.IP() == '127.0.0.1'
|
||||
and ' eth0:' in controller.cmd( 'ip link show' ) ):
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
# pylint: disable=arguments-differ,signature-differs
|
||||
def buildFromTopo( self, *args, **kwargs ):
|
||||
"Start network"
|
||||
info( '*** Placing nodes\n' )
|
||||
@@ -883,11 +775,11 @@ class MininetCluster( Mininet ):
|
||||
Mininet.buildFromTopo( self, *args, **kwargs )
|
||||
|
||||
|
||||
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testNsTunnels():
|
||||
"Test tunnels between nodes in namespaces"
|
||||
net = Mininet( host=RemoteHost, link=link, waitConnected=True )
|
||||
h1 = net.addHost( 'h1')
|
||||
h2 = net.addHost( 'h2', server=remote )
|
||||
net = Mininet( host=RemoteHost, link=RemoteLink )
|
||||
h1 = net.addHost( 'h1' )
|
||||
h2 = net.addHost( 'h2', server='ubuntu2' )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
@@ -898,31 +790,30 @@ def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
|
||||
# This shows how node options may be used to manage
|
||||
# cluster placement using the net.add*() API
|
||||
|
||||
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testRemoteNet( remote='ubuntu2' ):
|
||||
"Test remote Node classes"
|
||||
info( '*** Remote Node Test\n' )
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link,
|
||||
waitConnected=True )
|
||||
print '*** Remote Node Test'
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
|
||||
link=RemoteLink )
|
||||
c0 = net.addController( 'c0' )
|
||||
# Make sure controller knows its non-loopback address
|
||||
Intf( 'eth0', node=c0 ).updateIP()
|
||||
info( "*** Creating local h1\n" )
|
||||
print "*** Creating local h1"
|
||||
h1 = net.addHost( 'h1' )
|
||||
info( "*** Creating remote h2\n" )
|
||||
print "*** Creating remote h2"
|
||||
h2 = net.addHost( 'h2', server=remote )
|
||||
info( "*** Creating local s1\n" )
|
||||
print "*** Creating local s1"
|
||||
s1 = net.addSwitch( 's1' )
|
||||
info( "*** Creating remote s2\n" )
|
||||
print "*** Creating remote s2"
|
||||
s2 = net.addSwitch( 's2', server=remote )
|
||||
info( "*** Adding links\n" )
|
||||
print "*** Adding links"
|
||||
net.addLink( h1, s1 )
|
||||
net.addLink( s1, s2 )
|
||||
net.addLink( h2, s2 )
|
||||
net.start()
|
||||
info( 'Mininet is running on', quietRun( 'hostname' ).strip(), '\n' )
|
||||
print 'Mininet is running on', quietRun( 'hostname' ).strip()
|
||||
for node in c0, h1, h2, s1, s2:
|
||||
info( 'Node', node, 'is running on',
|
||||
node.cmd( 'hostname' ).strip(), '\n' )
|
||||
print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip()
|
||||
net.pingAll()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
@@ -960,11 +851,11 @@ def ClusterController( *args, **kwargs):
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
def testRemoteTopo( link=RemoteGRELink ):
|
||||
def testRemoteTopo():
|
||||
"Test remote Node classes using Mininet()/Topo() API"
|
||||
topo = LinearTopo( 2 )
|
||||
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
|
||||
link=link, controller=ClusterController )
|
||||
link=RemoteLink, controller=ClusterController )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
@@ -974,17 +865,18 @@ def testRemoteTopo( link=RemoteGRELink ):
|
||||
# do random switch placement rather than completely random
|
||||
# host placement.
|
||||
|
||||
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testRemoteSwitches():
|
||||
"Test with local hosts and remote switches"
|
||||
servers = [ 'localhost', remote]
|
||||
servers = [ 'localhost', 'ubuntu2']
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
net = MininetCluster( topo=topo, servers=servers, link=link,
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
placement=RoundRobinPlacer )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
|
||||
#
|
||||
# For testing and demo purposes it would be nice to draw the
|
||||
# network graph and color it based on server.
|
||||
|
||||
@@ -992,36 +884,31 @@ def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
|
||||
# functions, for maximum ease of use. MininetCluster() also
|
||||
# pre-flights and multiplexes server connections.
|
||||
|
||||
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testMininetCluster():
|
||||
"Test MininetCluster()"
|
||||
servers = [ 'localhost', remote ]
|
||||
servers = [ 'localhost', 'ubuntu2' ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers, link=link,
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
def signalTest( remote='ubuntu2'):
|
||||
def signalTest():
|
||||
"Make sure hosts are robust to signals"
|
||||
h = RemoteHost( 'h0', server=remote )
|
||||
h = RemoteHost( 'h0', server='ubuntu1' )
|
||||
h.shell.send_signal( SIGINT )
|
||||
h.shell.poll()
|
||||
if h.shell.returncode is None:
|
||||
info( 'signalTest: SUCCESS: ', h, 'has not exited after SIGINT', '\n' )
|
||||
print 'OK: ', h, 'has not exited'
|
||||
else:
|
||||
info( 'signalTest: FAILURE:', h, 'exited with code',
|
||||
h.shell.returncode, '\n' )
|
||||
print 'FAILURE:', h, 'exited with code', h.shell.returncode
|
||||
h.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
remoteServer = 'ubuntu2'
|
||||
remoteLink = RemoteSSHLink
|
||||
testRemoteTopo(link=remoteLink)
|
||||
testNsTunnels( remote=remoteServer, link=remoteLink )
|
||||
testRemoteNet( remote=remoteServer, link=remoteLink)
|
||||
testMininetCluster( remote=remoteServer, link=remoteLink)
|
||||
testRemoteSwitches( remote=remoteServer, link=remoteLink)
|
||||
signalTest( remote=remoteServer )
|
||||
# testRemoteTopo()
|
||||
# testRemoteNet()
|
||||
# testMininetCluster()
|
||||
# testRemoteSwitches()
|
||||
signalTest()
|
||||
|
||||
@@ -17,7 +17,6 @@ def clusterSanity():
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
clusterSanity()
|
||||
|
||||
+6
-13
@@ -27,23 +27,17 @@ class ClusterCLI( CLI ):
|
||||
def do_plot( self, _line ):
|
||||
"Plot topology colored by node placement"
|
||||
# Import networkx if needed
|
||||
global nx, plt, graphviz_layout
|
||||
global nx, plt
|
||||
if not nx:
|
||||
try:
|
||||
# pylint: disable=import-error,no-member
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# pylint: disable=import-error
|
||||
import networkx
|
||||
nx = networkx # satisfy pylint
|
||||
from matplotlib import pyplot
|
||||
plt = pyplot # satisfy pylint
|
||||
plt = pyplot # satisfiy pylint
|
||||
import pygraphviz
|
||||
assert pygraphviz # silence pyflakes
|
||||
# Networkx moved this around
|
||||
if hasattr( nx, 'graphviz_layout' ):
|
||||
graphviz_layout = nx.graphviz_layout
|
||||
else:
|
||||
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
|
||||
# pylint: enable=import-error,no-member
|
||||
# pylint: enable=import-error
|
||||
except ImportError:
|
||||
error( 'plot requires networkx, matplotlib and pygraphviz - '
|
||||
'please install them and try again\n' )
|
||||
@@ -51,8 +45,7 @@ class ClusterCLI( CLI ):
|
||||
# Make a networkx Graph
|
||||
g = nx.Graph()
|
||||
mn = self.mn
|
||||
servers = getattr( mn, 'servers', [ 'localhost' ] )
|
||||
hosts, switches = mn.hosts, mn.switches
|
||||
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
|
||||
nodes = hosts + switches
|
||||
g.add_nodes_from( nodes )
|
||||
links = [ ( link.intf1.node, link.intf2.node )
|
||||
@@ -62,7 +55,7 @@ class ClusterCLI( CLI ):
|
||||
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
|
||||
color = dict( zip( servers, self.colorsFor( servers ) ) )
|
||||
# Plot it!
|
||||
pos = graphviz_layout( g )
|
||||
pos = nx.graphviz_layout( g )
|
||||
opts = { 'ax': None, 'font_weight': 'bold',
|
||||
'width': 2, 'edge_color': 'darkblue' }
|
||||
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
|
||||
|
||||
from mininet.examples.cluster import ( MininetCluster, SwitchBinPlacer,
|
||||
RemoteLink )
|
||||
# ^ Could also use: RemoteSSHLink, RemoteGRELink
|
||||
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.examples.clustercli import ClusterCLI as CLI
|
||||
@@ -13,13 +11,12 @@ def demo():
|
||||
"Simple Demo of Cluster Mode"
|
||||
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
net = MininetCluster( topo=topo, servers=servers, link=RemoteLink,
|
||||
net = MininetCluster( topo=topo, servers=servers,
|
||||
placement=SwitchBinPlacer )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
demo()
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
|
||||
|
||||
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
def perf(Link):
|
||||
"Test connectivity nand performance over Link"
|
||||
net = Mininet( host=RemoteHost, link=Link, waitConnected=True )
|
||||
h1 = net.addHost( 'h1')
|
||||
h2 = net.addHost( 'h2', server='ubuntu2' )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
net.pingAll()
|
||||
net.iperf()
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
perf( RemoteSSHLink )
|
||||
perf( RemoteGRELink )
|
||||
@@ -27,7 +27,6 @@ Bob Lantz, April 2010
|
||||
|
||||
import re
|
||||
|
||||
# pylint: disable=import-error
|
||||
from Tkinter import Frame, Button, Label, Text, Scrollbar, Canvas, Wm, READABLE
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
@@ -35,9 +34,6 @@ from mininet.topolib import TreeNet
|
||||
from mininet.term import makeTerms, cleanUpScreens
|
||||
from mininet.util import quietRun
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
|
||||
|
||||
class Console( Frame ):
|
||||
"A simple console on a host."
|
||||
|
||||
@@ -323,7 +319,7 @@ class ConsoleApp( Frame ):
|
||||
if not m:
|
||||
return
|
||||
val, units = float( m.group( 1 ) ), m.group( 2 )
|
||||
# convert to Gbps
|
||||
#convert to Gbps
|
||||
if units[0] == 'M':
|
||||
val *= 10 ** -3
|
||||
elif units[0] == 'K':
|
||||
|
||||
@@ -17,7 +17,7 @@ setLogLevel( 'info' )
|
||||
# Ignore the warning message that the remote isn't (yet) running
|
||||
c0 = Controller( 'c0', port=6633 )
|
||||
c1 = Controller( 'c1', port=6634 )
|
||||
c2 = RemoteController( 'c2', ip='127.0.0.1', port=6633 )
|
||||
c2 = RemoteController( 'c2', ip='127.0.0.1' )
|
||||
|
||||
cmap = { 's1': c0, 's2': c1, 's3': c2 }
|
||||
|
||||
@@ -26,9 +26,8 @@ class MultiSwitch( OVSSwitch ):
|
||||
def start( self, controllers ):
|
||||
return OVSSwitch.start( self, [ cmap[ self.name ] ] )
|
||||
|
||||
|
||||
topo = TreeTopo( depth=2, fanout=2 )
|
||||
net = Mininet( topo=topo, switch=MultiSwitch, build=False, waitConnected=True )
|
||||
net = Mininet( topo=topo, switch=MultiSwitch, build=False )
|
||||
for c in [ c0, c1 ]:
|
||||
net.addController(c)
|
||||
net.build()
|
||||
|
||||
+12
-15
@@ -11,54 +11,51 @@ Note that one could also create a custom switch class and pass it into
|
||||
the Mininet() constructor.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller, OVSSwitch
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
def multiControllerNet():
|
||||
"Create a network from semi-scratch with multiple controllers."
|
||||
|
||||
net = Mininet( controller=Controller, switch=OVSSwitch,
|
||||
waitConnected=True )
|
||||
net = Mininet( controller=Controller, switch=OVSSwitch )
|
||||
|
||||
info( "*** Creating (reference) controllers\n" )
|
||||
print "*** Creating (reference) controllers"
|
||||
c1 = net.addController( 'c1', port=6633 )
|
||||
c2 = net.addController( 'c2', port=6634 )
|
||||
|
||||
info( "*** Creating switches\n" )
|
||||
print "*** Creating switches"
|
||||
s1 = net.addSwitch( 's1' )
|
||||
s2 = net.addSwitch( 's2' )
|
||||
|
||||
info( "*** Creating hosts\n" )
|
||||
hosts1 = [ net.addHost( 'h%d' % n ) for n in ( 3, 4 ) ]
|
||||
hosts2 = [ net.addHost( 'h%d' % n ) for n in ( 5, 6 ) ]
|
||||
print "*** Creating hosts"
|
||||
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
|
||||
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
|
||||
|
||||
info( "*** Creating links\n" )
|
||||
print "*** Creating links"
|
||||
for h in hosts1:
|
||||
net.addLink( s1, h )
|
||||
for h in hosts2:
|
||||
net.addLink( s2, h )
|
||||
net.addLink( s1, s2 )
|
||||
|
||||
info( "*** Starting network\n" )
|
||||
print "*** Starting network"
|
||||
net.build()
|
||||
c1.start()
|
||||
c2.start()
|
||||
s1.start( [ c1 ] )
|
||||
s2.start( [ c2 ] )
|
||||
|
||||
info( "*** Testing network\n" )
|
||||
print "*** Testing network"
|
||||
net.pingAll()
|
||||
|
||||
info( "*** Running CLI\n" )
|
||||
print "*** Running CLI"
|
||||
CLI( net )
|
||||
|
||||
info( "*** Stopping network\n" )
|
||||
print "*** Stopping network"
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' ) # for CLI output
|
||||
multiControllerNet()
|
||||
|
||||
+7
-10
@@ -49,7 +49,7 @@ class MininetFacade( object ):
|
||||
args: unnamed networks passed as arguments
|
||||
kwargs: named networks passed as arguments"""
|
||||
self.net = net
|
||||
self.nets = [ net ] + list( args ) + list( kwargs.values() )
|
||||
self.nets = [ net ] + list( args ) + kwargs.values()
|
||||
self.nameToNet = kwargs
|
||||
self.nameToNet['net'] = net
|
||||
|
||||
@@ -59,14 +59,13 @@ class MininetFacade( object ):
|
||||
|
||||
def __getitem__( self, key ):
|
||||
"returns primary/named networks or node from any net"
|
||||
# search kwargs for net named key
|
||||
#search kwargs for net named key
|
||||
if key in self.nameToNet:
|
||||
return self.nameToNet[ key ]
|
||||
# search each net for node named key
|
||||
#search each net for node named key
|
||||
for net in self.nets:
|
||||
if key in net:
|
||||
return net[ key ]
|
||||
return None
|
||||
|
||||
def __iter__( self ):
|
||||
"Iterate through all nodes in all Mininet objects"
|
||||
@@ -101,10 +100,10 @@ class MininetFacade( object ):
|
||||
|
||||
class ControlNetwork( Topo ):
|
||||
"Control Network Topology"
|
||||
# pylint: disable=arguments-differ
|
||||
def build( self, n, dataController=DataController, **_kwargs ):
|
||||
def __init__( self, n, dataController=DataController, **kwargs ):
|
||||
"""n: number of data network controller nodes
|
||||
dataController: class for data network controllers"""
|
||||
Topo.__init__( self, **kwargs )
|
||||
# Connect everything to a single switch
|
||||
cs0 = self.addSwitch( 'cs0' )
|
||||
# Add hosts which will serve as data network controllers
|
||||
@@ -125,8 +124,7 @@ def run():
|
||||
|
||||
info( '* Creating Control Network\n' )
|
||||
ctopo = ControlNetwork( n=4, dataController=DataController )
|
||||
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24',
|
||||
controller=None, waitConnected=True )
|
||||
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
|
||||
info( '* Adding Control Network Controller\n')
|
||||
cnet.addController( 'cc0', controller=Controller )
|
||||
info( '* Starting Control Network\n')
|
||||
@@ -136,8 +134,7 @@ def run():
|
||||
topo = TreeTopo( depth=2, fanout=2 )
|
||||
# UserSwitch so we can easily test failover
|
||||
sw = partial( UserSwitch, opts='--inactivity-probe=1 --max-backoff=1' )
|
||||
net = Mininet( topo=topo, switch=sw, controller=None,
|
||||
waitConnected=True )
|
||||
net = Mininet( topo=topo, switch=sw, controller=None )
|
||||
info( '* Adding Controllers to Data Network\n' )
|
||||
for host in cnet.hosts:
|
||||
if isinstance(host, Controller):
|
||||
|
||||
+20
-54
@@ -2,40 +2,16 @@
|
||||
|
||||
"""
|
||||
cpu.py: test iperf bandwidth for varying cpu limits
|
||||
|
||||
Since we are limiting the hosts (only), we should expect the iperf
|
||||
processes to be affected, as well as any system processing which is
|
||||
billed to the hosts.
|
||||
|
||||
We reserve >50% of cycles for system processing; we assume that
|
||||
this is enough for it not to affect results. Hosts are limited to
|
||||
40% of total cycles, which we assume is enough to make them CPU
|
||||
bound.
|
||||
|
||||
As CPU performance increases over time, we may have to reduce the
|
||||
overall CPU allocation so that the host processing is still CPU bound.
|
||||
This is perhaps an argument for specifying performance in a more
|
||||
system-independent manner.
|
||||
|
||||
It would also be nice to have a better handle on limiting packet
|
||||
processing cycles. It's not entirely clear to me how those are
|
||||
billed to user or system processes if we are using OVS with a kernel
|
||||
datapath. With a user datapath, they are easier to account for, but
|
||||
overall performance is usually lower.
|
||||
|
||||
Although the iperf client uses more CPU and should be CPU bound (?),
|
||||
we measure the received data at the server since the client transmit
|
||||
rate includes buffering.
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, waitListening, decode
|
||||
from mininet.util import custom, waitListening
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.clean import cleanup
|
||||
|
||||
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
|
||||
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
cpu: cpu limit as fraction of overall CPU time"""
|
||||
|
||||
@@ -44,36 +20,27 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
results = {}
|
||||
|
||||
for sched in 'rt', 'cfs':
|
||||
info( '*** Testing with', sched, 'bandwidth limiting\n' )
|
||||
print '*** Testing with', sched, 'bandwidth limiting'
|
||||
for cpu in cpuLimits:
|
||||
# cpu is the cpu fraction for all hosts, so we divide
|
||||
# it across two hosts
|
||||
host = custom( CPULimitedHost, sched=sched,
|
||||
period_us=period_us,
|
||||
cpu=.5*cpu )
|
||||
cpu=cpu )
|
||||
try:
|
||||
net = Mininet( topo=topo, host=host, waitConnected=True )
|
||||
net = Mininet( topo=topo, host=host )
|
||||
# pylint: disable=bare-except
|
||||
except: # noqa
|
||||
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
|
||||
cleanup()
|
||||
except:
|
||||
info( '*** Skipping host %s\n' % sched )
|
||||
break
|
||||
net.start()
|
||||
net.pingAll()
|
||||
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
|
||||
client, server = hosts[ 0 ], hosts[ -1 ]
|
||||
info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' %
|
||||
( 100.0 * cpu ) )
|
||||
# We measure at the server because it doesn't include
|
||||
# the client's buffer fill rate
|
||||
popen = server.popen( 'iperf -yc -s -p 5001' )
|
||||
server.cmd( 'iperf -s -p 5001 &' )
|
||||
waitListening( client, server, 5001 )
|
||||
# ignore empty result from waitListening/telnet
|
||||
popen.stdout.readline()
|
||||
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
|
||||
result = decode( popen.stdout.readline() ).split( ',' )
|
||||
result = client.cmd( 'iperf -yc -t %s -c %s' % (
|
||||
seconds, server.IP() ) ).split( ',' )
|
||||
bps = float( result[ -1 ] )
|
||||
popen.terminate()
|
||||
server.cmdPrint( 'kill %iperf' )
|
||||
net.stop()
|
||||
updated = results.get( sched, [] )
|
||||
updated += [ ( cpu, bps ) ]
|
||||
@@ -85,23 +52,22 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
def dump( results ):
|
||||
"Dump results"
|
||||
|
||||
fmt = '%s\t%s\t%s\n'
|
||||
fmt = '%s\t%s\t%s'
|
||||
|
||||
info( '\n' )
|
||||
info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) )
|
||||
print
|
||||
print fmt % ( 'sched', 'cpu', 'client MB/s' )
|
||||
print
|
||||
|
||||
for sched in sorted( results.keys() ):
|
||||
entries = results[ sched ]
|
||||
for cpu, bps in entries:
|
||||
pct = '%d%%' % ( cpu * 100 )
|
||||
mbps = '%.2e' % bps
|
||||
info( fmt % ( sched, pct, mbps ) )
|
||||
pct = '%.2f%%' % ( cpu * 100 )
|
||||
mbps = bps / 1e6
|
||||
print fmt % ( sched, pct, mbps )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# These are the limits for the hosts/iperfs - the
|
||||
# rest is for system processes
|
||||
limits = [ .5, .4, .3, .2, .1 ]
|
||||
limits = [ .45, .4, .3, .2, .1 ]
|
||||
out = bwtest( limits )
|
||||
dump( out )
|
||||
|
||||
@@ -14,7 +14,7 @@ def emptyNet():
|
||||
|
||||
"Create an empty network and add nodes to it."
|
||||
|
||||
net = Mininet( controller=Controller, waitConnected=True )
|
||||
net = Mininet( controller=Controller )
|
||||
|
||||
info( '*** Adding controller\n' )
|
||||
net.addController( 'c0' )
|
||||
@@ -39,7 +39,6 @@ def emptyNet():
|
||||
info( '*** Stopping network' )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
emptyNet()
|
||||
|
||||
+3
-8
@@ -8,8 +8,6 @@ hardware interface) to a network after the network is created.
|
||||
import re
|
||||
import sys
|
||||
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel, info, error
|
||||
from mininet.net import Mininet
|
||||
@@ -17,20 +15,17 @@ from mininet.link import Intf
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import quietRun
|
||||
|
||||
|
||||
def checkIntf( intf ):
|
||||
"Make sure intf exists and is not configured."
|
||||
config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True )
|
||||
if not config:
|
||||
if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
|
||||
error( 'Error:', intf, 'does not exist!\n' )
|
||||
exit( 1 )
|
||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', config )
|
||||
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
|
||||
if ips:
|
||||
error( 'Error:', intf, 'has an IP address,'
|
||||
'and is probably in use!\n' )
|
||||
exit( 1 )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
|
||||
@@ -42,7 +37,7 @@ if __name__ == '__main__':
|
||||
checkIntf( intfName )
|
||||
|
||||
info( '*** Creating network\n' )
|
||||
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ), waitConnected=True )
|
||||
net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) )
|
||||
|
||||
switch = net.switches[ 0 ]
|
||||
info( '*** Adding hardware interface', intfName, 'to switch',
|
||||
|
||||
@@ -13,7 +13,7 @@ from mininet.link import TCLink
|
||||
|
||||
def intfOptions():
|
||||
"run various traffic control commands on a single interface"
|
||||
net = Mininet( autoStaticArp=True, waitConnected=True )
|
||||
net = Mininet( autoStaticArp=True )
|
||||
net.addController( 'c0' )
|
||||
h1 = net.addHost( 'h1' )
|
||||
h2 = net.addHost( 'h2' )
|
||||
@@ -25,10 +25,10 @@ def intfOptions():
|
||||
# flush out latency from reactive forwarding delay
|
||||
net.pingAll()
|
||||
|
||||
info( '\n*** Configuring one intf with bandwidth of 10 Mb\n' )
|
||||
link1.intf1.config( bw=10 )
|
||||
info( '\n*** Configuring one intf with bandwidth of 5 Mb\n' )
|
||||
link1.intf1.config( bw=5 )
|
||||
info( '\n*** Running iperf to test\n' )
|
||||
net.iperf( seconds=10 )
|
||||
net.iperf()
|
||||
|
||||
info( '\n*** Configuring one intf with loss of 50%\n' )
|
||||
link1.intf1.config( loss=50 )
|
||||
@@ -43,7 +43,6 @@ def intfOptions():
|
||||
info( '\n*** Done testing\n' )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
intfOptions()
|
||||
|
||||
+2
-3
@@ -34,7 +34,7 @@ def limit( bw=10, cpu=.1 ):
|
||||
'Skipping this test\n' )
|
||||
continue
|
||||
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
|
||||
net = Mininet( topo=myTopo, intf=intf, host=host, waitConnected=True )
|
||||
net = Mininet( topo=myTopo, intf=intf, host=host )
|
||||
net.start()
|
||||
testLinkLimit( net, bw=bw )
|
||||
net.runCpuLimitTest( cpu=cpu )
|
||||
@@ -43,7 +43,7 @@ def limit( bw=10, cpu=.1 ):
|
||||
def verySimpleLimit( bw=150 ):
|
||||
"Absurdly simple limiting test"
|
||||
intf = custom( TCIntf, bw=bw )
|
||||
net = Mininet( intf=intf, waitConnected=True )
|
||||
net = Mininet( intf=intf )
|
||||
h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' )
|
||||
net.addLink( h1, h2 )
|
||||
net.start()
|
||||
@@ -55,7 +55,6 @@ def verySimpleLimit( bw=150 ):
|
||||
h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
limit()
|
||||
|
||||
+31
-32
@@ -23,26 +23,25 @@ of switches, this example demonstrates:
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
from functools import partial
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
|
||||
from mininet.topo import Topo
|
||||
from mininet.log import lg, info
|
||||
from mininet.log import lg
|
||||
from mininet.util import irange, quietRun
|
||||
from mininet.link import TCLink
|
||||
from functools import partial
|
||||
|
||||
import sys
|
||||
flush = sys.stdout.flush
|
||||
|
||||
|
||||
class LinearTestTopo( Topo ):
|
||||
"Topology for a string of N hosts and N-1 switches."
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def build( self, N, **params ):
|
||||
def __init__( self, N, **params ):
|
||||
|
||||
# Initialize topology
|
||||
Topo.__init__( self, **params )
|
||||
|
||||
# Create switches and hosts
|
||||
hosts = [ self.addHost( 'h%s' % h )
|
||||
for h in irange( 1, N ) ]
|
||||
@@ -83,45 +82,45 @@ def linearBandwidthTest( lengths ):
|
||||
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
|
||||
assert 'reno' in output
|
||||
|
||||
for datapath in switches:
|
||||
info( "*** testing", datapath, "datapath\n" )
|
||||
for datapath in switches.keys():
|
||||
print "*** testing", datapath, "datapath"
|
||||
Switch = switches[ datapath ]
|
||||
results[ datapath ] = []
|
||||
link = partial( TCLink, delay='30ms', bw=100 )
|
||||
link = partial( TCLink, delay='1ms' )
|
||||
net = Mininet( topo=topo, switch=Switch,
|
||||
controller=Controller, link=link,
|
||||
waitConnected=True )
|
||||
controller=Controller, waitConnected=True,
|
||||
link=link )
|
||||
net.start()
|
||||
info( "*** testing basic connectivity\n" )
|
||||
print "*** testing basic connectivity"
|
||||
for n in lengths:
|
||||
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
|
||||
info( "*** testing bandwidth\n" )
|
||||
print "*** testing bandwidth"
|
||||
for n in lengths:
|
||||
src, dst = net.hosts[ 0 ], net.hosts[ n ]
|
||||
# Try to prime the pump to reduce PACKET_INs during test
|
||||
# since the reference controller is reactive
|
||||
src.cmd( 'telnet', dst.IP(), '5001' )
|
||||
info( "testing", src.name, "<->", dst.name, '\n' )
|
||||
# serverbw = received; _clientbw = buffered
|
||||
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=5 )
|
||||
info( serverbw, '\n' )
|
||||
print "testing", src.name, "<->", dst.name,
|
||||
bandwidth = net.iperf( [ src, dst ], seconds=10 )
|
||||
print bandwidth
|
||||
flush()
|
||||
results[ datapath ] += [ ( n, serverbw ) ]
|
||||
results[ datapath ] += [ ( n, bandwidth ) ]
|
||||
net.stop()
|
||||
|
||||
for datapath in switches:
|
||||
info( "\n*** Linear network results for", datapath, "datapath:\n" )
|
||||
for datapath in switches.keys():
|
||||
print
|
||||
print "*** Linear network results for", datapath, "datapath:"
|
||||
print
|
||||
result = results[ datapath ]
|
||||
info( "SwitchCount\tiperf Results\n" )
|
||||
for switchCount, serverbw in result:
|
||||
info( switchCount, '\t\t' )
|
||||
info( serverbw, '\n' )
|
||||
info( '\n')
|
||||
info( '\n' )
|
||||
|
||||
print "SwitchCount\tiperf Results"
|
||||
for switchCount, bandwidth in result:
|
||||
print switchCount, '\t\t',
|
||||
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
|
||||
print
|
||||
print
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info' )
|
||||
sizes = [ 1, 2, 3, 4 ]
|
||||
info( "*** Running linearBandwidthTest", sizes, '\n' )
|
||||
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
|
||||
print "*** Running linearBandwidthTest", sizes
|
||||
linearBandwidthTest( sizes )
|
||||
|
||||
@@ -27,18 +27,15 @@ Additional routes may be added to the router or hosts by
|
||||
executing 'ip route' or 'route' commands on the router or hosts.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
|
||||
|
||||
class LinuxRouter( Node ):
|
||||
"A Node with IP forwarding enabled."
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, **params ):
|
||||
super( LinuxRouter, self).config( **params )
|
||||
# Enable forwarding on the router
|
||||
@@ -52,13 +49,12 @@ class LinuxRouter( Node ):
|
||||
class NetworkTopo( Topo ):
|
||||
"A LinuxRouter connecting three IP subnets"
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def build( self, **_opts ):
|
||||
|
||||
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
|
||||
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
|
||||
|
||||
s1, s2, s3 = [ self.addSwitch( s ) for s in ( 's1', 's2', 's3' ) ]
|
||||
s1, s2, s3 = [ self.addSwitch( s ) for s in 's1', 's2', 's3' ]
|
||||
|
||||
self.addLink( s1, router, intfName2='r0-eth1',
|
||||
params2={ 'ip' : defaultIP } ) # for clarity
|
||||
@@ -81,15 +77,13 @@ class NetworkTopo( Topo ):
|
||||
def run():
|
||||
"Test linux router"
|
||||
topo = NetworkTopo()
|
||||
net = Mininet( topo=topo,
|
||||
waitConnected=True ) # controller is used by s1-s3
|
||||
net = Mininet( topo=topo ) # controller is used by s1-s3
|
||||
net.start()
|
||||
info( '*** Routing Table on Router:\n' )
|
||||
info( net[ 'r0' ].cmd( 'route' ) )
|
||||
print net[ 'r0' ].cmd( 'route' )
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
run()
|
||||
|
||||
+117
-137
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
MiniEdit: a simple network editor for Mininet
|
||||
@@ -13,72 +13,57 @@ Controller icon from http://semlabs.co.uk/
|
||||
OpenFlow icon from https://www.opennetworking.org/
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
# Miniedit needs some work in order to pass pylint...
|
||||
# pylint: disable=line-too-long,too-many-branches
|
||||
# pylint: disable=too-many-statements,attribute-defined-outside-init
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
MINIEDIT_VERSION = '2.2.0.1'
|
||||
|
||||
from distutils.version import StrictVersion
|
||||
from functools import partial
|
||||
from optparse import OptionParser
|
||||
# from Tkinter import *
|
||||
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton,
|
||||
Menu, Toplevel, Button, BitmapImage, PhotoImage, Canvas,
|
||||
Scrollbar, Wm, TclError, StringVar, IntVar,
|
||||
E, W, EW, NW, Y, VERTICAL, SOLID, CENTER,
|
||||
RIGHT, LEFT, BOTH, TRUE, FALSE )
|
||||
from ttk import Notebook
|
||||
from tkMessageBox import showerror
|
||||
from subprocess import call
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
import tkFont
|
||||
import tkFileDialog
|
||||
import tkSimpleDialog
|
||||
import re
|
||||
import json
|
||||
from distutils.version import StrictVersion
|
||||
import os
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
from mininet.log import info, debug, warn, setLogLevel
|
||||
if 'PYTHONPATH' in os.environ:
|
||||
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||
|
||||
# someday: from ttk import *
|
||||
|
||||
from mininet.log import info, setLogLevel
|
||||
from mininet.net import Mininet, VERSION
|
||||
from mininet.util import (netParse, ipAdd, quietRun,
|
||||
buildTopo, custom, customClass )
|
||||
from mininet.util import netParse, ipAdd, quietRun
|
||||
from mininet.util import buildTopo
|
||||
from mininet.util import custom, customClass
|
||||
from mininet.term import makeTerm, cleanUpScreens
|
||||
from mininet.node import (Controller, RemoteController, NOX, OVSController,
|
||||
CPULimitedHost, Host, Node,
|
||||
OVSSwitch, UserSwitch, IVSSwitch )
|
||||
from mininet.node import Controller, RemoteController, NOX, OVSController
|
||||
from mininet.node import CPULimitedHost, Host, Node
|
||||
from mininet.node import OVSSwitch, UserSwitch
|
||||
from mininet.link import TCLink, Intf, Link
|
||||
from mininet.cli import CLI
|
||||
from mininet.moduledeps import moduleDeps
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
|
||||
# pylint: disable=import-error
|
||||
if sys.version_info[0] == 2:
|
||||
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
|
||||
Checkbutton, Menu, Toplevel, Button, BitmapImage,
|
||||
PhotoImage, Canvas, Scrollbar, Wm, TclError,
|
||||
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
|
||||
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
|
||||
from ttk import Notebook
|
||||
from tkMessageBox import showerror
|
||||
import tkFont
|
||||
import tkFileDialog
|
||||
import tkSimpleDialog
|
||||
else:
|
||||
from tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
|
||||
Checkbutton, Menu, Toplevel, Button, BitmapImage,
|
||||
PhotoImage, Canvas, Scrollbar, Wm, TclError,
|
||||
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
|
||||
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
|
||||
from tkinter.ttk import Notebook
|
||||
from tkinter.messagebox import showerror
|
||||
from tkinter import font as tkFont
|
||||
from tkinter import simpledialog as tkSimpleDialog
|
||||
from tkinter import filedialog as tkFileDialog
|
||||
# someday: from ttk import *
|
||||
# pylint: enable=import-error
|
||||
|
||||
|
||||
# Miniedit still needs work in order to pass pylint...
|
||||
# pylint: disable=line-too-long,too-many-branches
|
||||
# pylint: disable=too-many-statements,attribute-defined-outside-init
|
||||
# pylint: disable=missing-docstring,too-many-ancestors
|
||||
# pylint: disable=too-many-nested-blocks,too-many-arguments
|
||||
|
||||
|
||||
MINIEDIT_VERSION = '2.2.0.1'
|
||||
|
||||
if 'PYTHONPATH' in os.environ:
|
||||
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
|
||||
|
||||
info( 'MiniEdit running against Mininet '+VERSION, '\n' )
|
||||
print 'MiniEdit running against Mininet '+VERSION
|
||||
MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
|
||||
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
|
||||
from mininet.node import IVSSwitch
|
||||
|
||||
TOPODEF = 'none'
|
||||
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
||||
@@ -138,7 +123,6 @@ class LegacyRouter( Node ):
|
||||
def __init__( self, name, inNamespace=True, **params ):
|
||||
Node.__init__( self, name, inNamespace, **params )
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, **_params ):
|
||||
if self.intfs:
|
||||
self.setParam( _params, 'setIP', ip='0.0.0.0' )
|
||||
@@ -395,14 +379,14 @@ class PrefsDialog(tkSimpleDialog.Dialog):
|
||||
@staticmethod
|
||||
def getOvsVersion():
|
||||
"Return OVS version"
|
||||
outp = quietRun("ovs-vsctl --version")
|
||||
r = r'ovs-vsctl \(Open vSwitch\) (.*)'
|
||||
outp = quietRun("ovs-vsctl show")
|
||||
r = r'ovs_version: "(.*)"'
|
||||
m = re.search(r, outp)
|
||||
if m is None:
|
||||
warn( 'Version check failed' )
|
||||
print 'Version check failed'
|
||||
return None
|
||||
else:
|
||||
info( 'Open vSwitch version is '+m.group(1), '\n' )
|
||||
print 'Open vSwitch version is '+m.group(1)
|
||||
return m.group(1)
|
||||
|
||||
|
||||
@@ -771,7 +755,7 @@ class SwitchDialog(CustomDialog):
|
||||
def apply(self):
|
||||
externalInterfaces = []
|
||||
for row in range(self.tableFrame.rows):
|
||||
# debug( 'Interface is ' + self.tableFrame.get(row, 0), '\n' )
|
||||
#print 'Interface is ' + self.tableFrame.get(row, 0)
|
||||
if len(self.tableFrame.get(row, 0)) > 0:
|
||||
externalInterfaces.append(self.tableFrame.get(row, 0))
|
||||
|
||||
@@ -819,8 +803,8 @@ class VerticalScrolledTable(LabelFrame):
|
||||
* This frame only allows vertical scrolling
|
||||
|
||||
"""
|
||||
def __init__(self, parent, rows=2, columns=2, title=None, **kw):
|
||||
LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, **kw)
|
||||
def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
|
||||
LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
|
||||
|
||||
# create a canvas object and a vertical scrollbar for scrolling it
|
||||
vscrollbar = Scrollbar(self, orient=VERTICAL)
|
||||
@@ -856,6 +840,8 @@ class VerticalScrolledTable(LabelFrame):
|
||||
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
|
||||
canvas.bind('<Configure>', _configure_canvas)
|
||||
|
||||
return
|
||||
|
||||
class TableFrame(Frame):
|
||||
def __init__(self, parent, rows=2, columns=2):
|
||||
|
||||
@@ -880,14 +866,14 @@ class TableFrame(Frame):
|
||||
return widget.get()
|
||||
|
||||
def addRow( self, value=None, readonly=False ):
|
||||
# debug( "Adding row " + str(self.rows +1), '\n' )
|
||||
#print "Adding row " + str(self.rows +1)
|
||||
current_row = []
|
||||
for column in range(self.columns):
|
||||
label = Entry(self, borderwidth=0)
|
||||
label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
|
||||
if value is not None:
|
||||
label.insert(0, value[column])
|
||||
if readonly:
|
||||
if readonly == True:
|
||||
label.configure(state='readonly')
|
||||
current_row.append(label)
|
||||
self._widgets.append(current_row)
|
||||
@@ -1400,11 +1386,11 @@ class MiniEdit( Frame ):
|
||||
|
||||
def addNode( self, node, nodeNum, x, y, name=None):
|
||||
"Add a new node to our canvas."
|
||||
if node == 'Switch':
|
||||
if 'Switch' == node:
|
||||
self.switchCount += 1
|
||||
if node == 'Host':
|
||||
if 'Host' == node:
|
||||
self.hostCount += 1
|
||||
if node == 'Controller':
|
||||
if 'Controller' == node:
|
||||
self.controllerCount += 1
|
||||
if name is None:
|
||||
name = self.nodePrefixes[ node ] + nodeNum
|
||||
@@ -1421,17 +1407,14 @@ class MiniEdit( Frame ):
|
||||
|
||||
def convertJsonUnicode(self, text):
|
||||
"Some part of Mininet don't like Unicode"
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
return text
|
||||
if isinstance(text, dict):
|
||||
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
|
||||
if isinstance(text, list):
|
||||
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.iteritems()}
|
||||
elif isinstance(text, list):
|
||||
return [self.convertJsonUnicode(element) for element in text]
|
||||
if isinstance(text, unicode): # pylint: disable=undefined-variable
|
||||
elif isinstance(text, unicode):
|
||||
return text.encode('utf-8')
|
||||
return text
|
||||
else:
|
||||
return text
|
||||
|
||||
def loadTopology( self ):
|
||||
"Load command."
|
||||
@@ -1442,7 +1425,7 @@ class MiniEdit( Frame ):
|
||||
('All Files','*'),
|
||||
]
|
||||
f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
|
||||
if f is None:
|
||||
if f == None:
|
||||
return
|
||||
self.newTopology()
|
||||
loadedTopology = self.convertJsonUnicode(json.load(f))
|
||||
@@ -1603,11 +1586,10 @@ class MiniEdit( Frame ):
|
||||
for widget in self.widgetToItem:
|
||||
if name == widget[ 'text' ]:
|
||||
return widget
|
||||
return None
|
||||
|
||||
def newTopology( self ):
|
||||
"New command."
|
||||
for widget in self.widgetToItem:
|
||||
for widget in self.widgetToItem.keys():
|
||||
self.deleteItem( self.widgetToItem[ widget ] )
|
||||
self.hostCount = 0
|
||||
self.switchCount = 0
|
||||
@@ -1687,7 +1669,7 @@ class MiniEdit( Frame ):
|
||||
f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
|
||||
# pylint: disable=broad-except
|
||||
except Exception as er:
|
||||
warn( er, '\n' )
|
||||
print er
|
||||
# pylint: enable=broad-except
|
||||
finally:
|
||||
f.close()
|
||||
@@ -1701,7 +1683,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
|
||||
if len(fileName ) > 0:
|
||||
# debug( "Now saving under %s\n" % fileName )
|
||||
#print "Now saving under %s" % fileName
|
||||
f = open(fileName, 'wb')
|
||||
|
||||
f.write("#!/usr/bin/python\n")
|
||||
@@ -1728,7 +1710,7 @@ class MiniEdit( Frame ):
|
||||
if controllerType == 'inband':
|
||||
inBandCtrl = True
|
||||
|
||||
if inBandCtrl:
|
||||
if inBandCtrl == True:
|
||||
f.write("\n")
|
||||
f.write("class InbandController( RemoteController ):\n")
|
||||
f.write("\n")
|
||||
@@ -1853,7 +1835,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
# Save Links
|
||||
f.write(" info( '*** Add links\\n')\n")
|
||||
for key,linkDetail in self.links.items():
|
||||
for key,linkDetail in self.links.iteritems():
|
||||
tags = self.canvas.gettags(key)
|
||||
if 'data' in tags:
|
||||
optsExist = False
|
||||
@@ -2125,7 +2107,7 @@ class MiniEdit( Frame ):
|
||||
c = self.canvas
|
||||
x, y = c.canvasx( event.x ), c.canvasy( event.y )
|
||||
name = self.nodePrefixes[ node ]
|
||||
if node == 'Switch':
|
||||
if 'Switch' == node:
|
||||
self.switchCount += 1
|
||||
name = self.nodePrefixes[ node ] + str( self.switchCount )
|
||||
self.switchOpts[name] = {}
|
||||
@@ -2133,14 +2115,14 @@ class MiniEdit( Frame ):
|
||||
self.switchOpts[name]['hostname']=name
|
||||
self.switchOpts[name]['switchType']='default'
|
||||
self.switchOpts[name]['controllers']=[]
|
||||
if node == 'LegacyRouter':
|
||||
if 'LegacyRouter' == node:
|
||||
self.switchCount += 1
|
||||
name = self.nodePrefixes[ node ] + str( self.switchCount )
|
||||
self.switchOpts[name] = {}
|
||||
self.switchOpts[name]['nodeNum']=self.switchCount
|
||||
self.switchOpts[name]['hostname']=name
|
||||
self.switchOpts[name]['switchType']='legacyRouter'
|
||||
if node == 'LegacySwitch':
|
||||
if 'LegacySwitch' == node:
|
||||
self.switchCount += 1
|
||||
name = self.nodePrefixes[ node ] + str( self.switchCount )
|
||||
self.switchOpts[name] = {}
|
||||
@@ -2148,13 +2130,13 @@ class MiniEdit( Frame ):
|
||||
self.switchOpts[name]['hostname']=name
|
||||
self.switchOpts[name]['switchType']='legacySwitch'
|
||||
self.switchOpts[name]['controllers']=[]
|
||||
if node == 'Host':
|
||||
if 'Host' == node:
|
||||
self.hostCount += 1
|
||||
name = self.nodePrefixes[ node ] + str( self.hostCount )
|
||||
self.hostOpts[name] = {'sched':'host'}
|
||||
self.hostOpts[name]['nodeNum']=self.hostCount
|
||||
self.hostOpts[name]['hostname']=name
|
||||
if node == 'Controller':
|
||||
if 'Controller' == node:
|
||||
name = self.nodePrefixes[ node ] + str( self.controllerCount )
|
||||
ctrlr = { 'controllerType': 'ref',
|
||||
'hostname': name,
|
||||
@@ -2172,15 +2154,15 @@ class MiniEdit( Frame ):
|
||||
self.itemToWidget[ item ] = icon
|
||||
self.selectItem( item )
|
||||
icon.links = {}
|
||||
if node == 'Switch':
|
||||
if 'Switch' == node:
|
||||
icon.bind('<Button-3>', self.do_switchPopup )
|
||||
if node == 'LegacyRouter':
|
||||
if 'LegacyRouter' == node:
|
||||
icon.bind('<Button-3>', self.do_legacyRouterPopup )
|
||||
if node == 'LegacySwitch':
|
||||
if 'LegacySwitch' == node:
|
||||
icon.bind('<Button-3>', self.do_legacySwitchPopup )
|
||||
if node == 'Host':
|
||||
if 'Host' == node:
|
||||
icon.bind('<Button-3>', self.do_hostPopup )
|
||||
if node == 'Controller':
|
||||
if 'Controller' == node:
|
||||
icon.bind('<Button-3>', self.do_controllerPopup )
|
||||
|
||||
def clickController( self, event ):
|
||||
@@ -2250,7 +2232,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
def clickNode( self, event ):
|
||||
"Node click handler."
|
||||
if self.active == 'NetLink':
|
||||
if self.active is 'NetLink':
|
||||
self.startLink( event )
|
||||
else:
|
||||
self.selectNode( event )
|
||||
@@ -2258,14 +2240,14 @@ class MiniEdit( Frame ):
|
||||
|
||||
def dragNode( self, event ):
|
||||
"Node drag handler."
|
||||
if self.active == 'NetLink':
|
||||
if self.active is 'NetLink':
|
||||
self.dragNetLink( event )
|
||||
else:
|
||||
self.dragNodeAround( event )
|
||||
|
||||
def releaseNode( self, event ):
|
||||
"Node release handler."
|
||||
if self.active == 'NetLink':
|
||||
if self.active is 'NetLink':
|
||||
self.finishLink( event )
|
||||
|
||||
# Specific node handlers
|
||||
@@ -2377,8 +2359,6 @@ class MiniEdit( Frame ):
|
||||
# For now, don't allow hosts to be directly linked
|
||||
stags = self.canvas.gettags( self.widgetToItem[ source ] )
|
||||
dtags = self.canvas.gettags( target )
|
||||
# TODO: Make this less confusing
|
||||
# pylint: disable=too-many-boolean-expressions
|
||||
if (('Host' in stags and 'Host' in dtags) or
|
||||
('Controller' in dtags and 'LegacyRouter' in stags) or
|
||||
('Controller' in stags and 'LegacyRouter' in dtags) or
|
||||
@@ -2509,7 +2489,7 @@ class MiniEdit( Frame ):
|
||||
if len(hostBox.result['privateDirectory']) > 0:
|
||||
newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
|
||||
self.hostOpts[name] = newHostOpts
|
||||
info( 'New host details for ' + name + ' = ' + str(newHostOpts), '\n' )
|
||||
print 'New host details for ' + name + ' = ' + str(newHostOpts)
|
||||
|
||||
def switchDetails( self, _ignore=None ):
|
||||
if ( self.selection is None or
|
||||
@@ -2547,7 +2527,7 @@ class MiniEdit( Frame ):
|
||||
newSwitchOpts['sflow'] = switchBox.result['sflow']
|
||||
newSwitchOpts['netflow'] = switchBox.result['netflow']
|
||||
self.switchOpts[name] = newSwitchOpts
|
||||
info( 'New switch details for ' + name + ' = ' + str(newSwitchOpts), '\n' )
|
||||
print 'New switch details for ' + name + ' = ' + str(newSwitchOpts)
|
||||
|
||||
def linkUp( self ):
|
||||
if ( self.selection is None or
|
||||
@@ -2586,12 +2566,12 @@ class MiniEdit( Frame ):
|
||||
linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
|
||||
if linkBox.result is not None:
|
||||
linkDetail['linkOpts'] = linkBox.result
|
||||
info( 'New link details = ' + str(linkBox.result), '\n' )
|
||||
print 'New link details = ' + str(linkBox.result)
|
||||
|
||||
def prefDetails( self ):
|
||||
prefDefaults = self.appPrefs
|
||||
prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
|
||||
info( 'New Prefs = ' + str(prefBox.result), '\n' )
|
||||
print 'New Prefs = ' + str(prefBox.result)
|
||||
if prefBox.result:
|
||||
self.appPrefs = prefBox.result
|
||||
|
||||
@@ -2610,14 +2590,14 @@ class MiniEdit( Frame ):
|
||||
|
||||
ctrlrBox = ControllerDialog(self, title='Controller Details', ctrlrDefaults=self.controllers[name])
|
||||
if ctrlrBox.result:
|
||||
# debug( 'Controller is ' + ctrlrBox.result[0], '\n' )
|
||||
#print 'Controller is ' + ctrlrBox.result[0]
|
||||
if len(ctrlrBox.result['hostname']) > 0:
|
||||
name = ctrlrBox.result['hostname']
|
||||
widget[ 'text' ] = name
|
||||
else:
|
||||
ctrlrBox.result['hostname'] = name
|
||||
self.controllers[name] = ctrlrBox.result
|
||||
info( 'New controller details for ' + name + ' = ' + str(self.controllers[name]), '\n' )
|
||||
print 'New controller details for ' + name + ' = ' + str(self.controllers[name])
|
||||
# Find references to controller and change name
|
||||
if oldName != name:
|
||||
for widget in self.widgetToItem:
|
||||
@@ -2662,7 +2642,7 @@ class MiniEdit( Frame ):
|
||||
linkopts = {}
|
||||
source.links[ dest ] = self.link
|
||||
dest.links[ source ] = self.link
|
||||
self.links[ self.link ] = {'type':linktype,
|
||||
self.links[ self.link ] = {'type' :linktype,
|
||||
'src':source,
|
||||
'dest':dest,
|
||||
'linkOpts':linkopts}
|
||||
@@ -2718,15 +2698,15 @@ class MiniEdit( Frame ):
|
||||
|
||||
def buildNodes( self, net):
|
||||
# Make nodes
|
||||
info( "Getting Hosts and Switches.\n" )
|
||||
print "Getting Hosts and Switches."
|
||||
for widget in self.widgetToItem:
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
# debug( name+' has '+str(tags), '\n' )
|
||||
#print name+' has '+str(tags)
|
||||
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
# debug( str(opts), '\n' )
|
||||
#print str(opts)
|
||||
|
||||
# Create the correct switch class
|
||||
switchClass = customOvs
|
||||
@@ -2792,7 +2772,7 @@ class MiniEdit( Frame ):
|
||||
newSwitch = net.addHost( name , cls=LegacyRouter)
|
||||
elif 'Host' in tags:
|
||||
opts = self.hostOpts[name]
|
||||
# debug( str(opts), '\n' )
|
||||
#print str(opts)
|
||||
ip = None
|
||||
defaultRoute = None
|
||||
if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
|
||||
@@ -2817,7 +2797,7 @@ class MiniEdit( Frame ):
|
||||
privateDirs=opts['privateDirectory'] )
|
||||
else:
|
||||
hostCls=Host
|
||||
debug( hostCls, '\n' )
|
||||
print hostCls
|
||||
newHost = net.addHost( name,
|
||||
cls=hostCls,
|
||||
ip=ip,
|
||||
@@ -2837,7 +2817,7 @@ class MiniEdit( Frame ):
|
||||
Intf( extInterface, node=newHost )
|
||||
if 'vlanInterfaces' in opts:
|
||||
if len(opts['vlanInterfaces']) > 0:
|
||||
info( 'Checking that OS is VLAN prepared\n' )
|
||||
print 'Checking that OS is VLAN prepared'
|
||||
self.pathCheck('vconfig', moduleName='vlan package')
|
||||
moduleDeps( add='8021q' )
|
||||
elif 'Controller' in tags:
|
||||
@@ -2854,7 +2834,7 @@ class MiniEdit( Frame ):
|
||||
controllerPort = opts['remotePort']
|
||||
|
||||
# Make controller
|
||||
info( 'Getting controller selection:'+controllerType, '\n' )
|
||||
print 'Getting controller selection:'+controllerType
|
||||
if controllerType == 'remote':
|
||||
net.addController(name=name,
|
||||
controller=RemoteController,
|
||||
@@ -2894,8 +2874,8 @@ class MiniEdit( Frame ):
|
||||
|
||||
def buildLinks( self, net):
|
||||
# Make links
|
||||
info( "Getting Links.\n" )
|
||||
for key,link in self.links.items():
|
||||
print "Getting Links."
|
||||
for key,link in self.links.iteritems():
|
||||
tags = self.canvas.gettags(key)
|
||||
if 'data' in tags:
|
||||
src=link['src']
|
||||
@@ -2906,14 +2886,14 @@ class MiniEdit( Frame ):
|
||||
if linkopts:
|
||||
net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
|
||||
else:
|
||||
# debug( str(srcNode) )
|
||||
# debug( str(dstNode), '\n' )
|
||||
#print str(srcNode)
|
||||
#print str(dstNode)
|
||||
net.addLink(srcNode, dstNode)
|
||||
self.canvas.itemconfig(key, dash=())
|
||||
|
||||
|
||||
def build( self ):
|
||||
"Build network based on our topology."
|
||||
print "Build network based on our topology."
|
||||
|
||||
dpctl = None
|
||||
if len(self.appPrefs['dpctl']) > 0:
|
||||
@@ -2944,7 +2924,7 @@ class MiniEdit( Frame ):
|
||||
# Attach vlan interfaces
|
||||
if 'vlanInterfaces' in opts:
|
||||
for vlanInterface in opts['vlanInterfaces']:
|
||||
info( 'adding vlan interface '+vlanInterface[1], '\n' )
|
||||
print 'adding vlan interface '+vlanInterface[1]
|
||||
newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
|
||||
# Run User Defined Start Command
|
||||
if 'startCommand' in opts:
|
||||
@@ -2970,7 +2950,7 @@ class MiniEdit( Frame ):
|
||||
opts = self.switchOpts[name]
|
||||
if 'netflow' in opts:
|
||||
if opts['netflow'] == '1':
|
||||
info( name+' has Netflow enabled\n' )
|
||||
print name+' has Netflow enabled'
|
||||
nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
|
||||
nflowEnabled=True
|
||||
if nflowEnabled:
|
||||
@@ -2979,13 +2959,13 @@ class MiniEdit( Frame ):
|
||||
nflowCmd = nflowCmd + ' add_id_to_interface=true'
|
||||
else:
|
||||
nflowCmd = nflowCmd + ' add_id_to_interface=false'
|
||||
info( 'cmd = '+nflowCmd+nflowSwitches, '\n' )
|
||||
print 'cmd = '+nflowCmd+nflowSwitches
|
||||
call(nflowCmd+nflowSwitches, shell=True)
|
||||
|
||||
else:
|
||||
info( 'No switches with Netflow\n' )
|
||||
print 'No switches with Netflow'
|
||||
else:
|
||||
info( 'No NetFlow targets specified.\n' )
|
||||
print 'No NetFlow targets specified.'
|
||||
|
||||
# Configure sFlow
|
||||
sflowValues = self.appPrefs['sflow']
|
||||
@@ -3000,23 +2980,23 @@ class MiniEdit( Frame ):
|
||||
opts = self.switchOpts[name]
|
||||
if 'sflow' in opts:
|
||||
if opts['sflow'] == '1':
|
||||
info( name+' has sflow enabled\n' )
|
||||
print name+' has sflow enabled'
|
||||
sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
|
||||
sflowEnabled=True
|
||||
if sflowEnabled:
|
||||
sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
|
||||
info( 'cmd = '+sflowCmd+sflowSwitches, '\n' )
|
||||
print 'cmd = '+sflowCmd+sflowSwitches
|
||||
call(sflowCmd+sflowSwitches, shell=True)
|
||||
|
||||
else:
|
||||
info( 'No switches with sflow\n' )
|
||||
print 'No switches with sflow'
|
||||
else:
|
||||
info( 'No sFlow targets specified.\n' )
|
||||
print 'No sFlow targets specified.'
|
||||
|
||||
## NOTE: MAKE SURE THIS IS LAST THING CALLED
|
||||
# Start the CLI if enabled
|
||||
if self.appPrefs['startCLI'] == '1':
|
||||
info( "\n\n NOTE: PLEASE REMEMBER TO EXIT THE CLI BEFORE YOU PRESS THE STOP BUTTON. Not exiting will prevent MiniEdit from quitting and will prevent you from starting the network again during this session.\n\n")
|
||||
info( "\n\n NOTE: PLEASE REMEMBER TO EXIT THE CLI BEFORE YOU PRESS THE STOP BUTTON. Not exiting will prevent MiniEdit from quitting and will prevent you from starting the network again during this sessoin.\n\n")
|
||||
CLI(self.net)
|
||||
|
||||
def start( self ):
|
||||
@@ -3230,15 +3210,14 @@ class MiniEdit( Frame ):
|
||||
"Parse custom file and add params before parsing cmd-line options."
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
with open( fileName, 'r' ) as f:
|
||||
exec( f.read() ) # pylint: disable=exec-used
|
||||
for name, val in customs.items():
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
def importTopo( self ):
|
||||
info( 'topo='+self.options.topo, '\n' )
|
||||
print 'topo='+self.options.topo
|
||||
if self.options.topo == 'none':
|
||||
return
|
||||
self.newTopology()
|
||||
@@ -3252,7 +3231,7 @@ class MiniEdit( Frame ):
|
||||
currentY = 100
|
||||
|
||||
# Add Controllers
|
||||
info( 'controllers:'+str(len(importNet.controllers)), '\n' )
|
||||
print 'controllers:'+str(len(importNet.controllers))
|
||||
for controller in importNet.controllers:
|
||||
name = controller.name
|
||||
x = self.controllerCount*100+100
|
||||
@@ -3272,7 +3251,7 @@ class MiniEdit( Frame ):
|
||||
currentY = currentY + rowIncrement
|
||||
|
||||
# Add switches
|
||||
info( 'switches:'+str(len(importNet.switches)), '\n' )
|
||||
print 'switches:'+str(len(importNet.switches))
|
||||
columnCount = 0
|
||||
for switch in importNet.switches:
|
||||
name = switch.name
|
||||
@@ -3313,7 +3292,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
currentY = currentY + rowIncrement
|
||||
# Add hosts
|
||||
info( 'hosts:'+str(len(importNet.hosts)), '\n' )
|
||||
print 'hosts:'+str(len(importNet.hosts))
|
||||
columnCount = 0
|
||||
for host in importNet.hosts:
|
||||
name = host.name
|
||||
@@ -3333,10 +3312,10 @@ class MiniEdit( Frame ):
|
||||
else:
|
||||
columnCount =columnCount+1
|
||||
|
||||
info( 'links:'+str(len(topo.links())), '\n' )
|
||||
print 'links:'+str(len(topo.links()))
|
||||
#[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
|
||||
for link in topo.links():
|
||||
info( str(link), '\n' )
|
||||
print str(link)
|
||||
srcNode = link[0]
|
||||
src = self.findWidgetByName(srcNode)
|
||||
sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
|
||||
@@ -3346,7 +3325,7 @@ class MiniEdit( Frame ):
|
||||
dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
|
||||
|
||||
params = topo.linkInfo( srcNode, destNode )
|
||||
info( 'Link Parameters='+str(params), '\n' )
|
||||
print 'Link Parameters='+str(params)
|
||||
|
||||
self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
|
||||
fill='blue', tag='link' )
|
||||
@@ -3598,7 +3577,8 @@ def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
app = MiniEdit()
|
||||
app.parseArgs()
|
||||
### import topology if specified ###
|
||||
app.parseArgs()
|
||||
app.importTopo()
|
||||
|
||||
app.mainloop()
|
||||
|
||||
+13
-17
@@ -19,12 +19,12 @@ to-do:
|
||||
- think about clearing last hop - why doesn't that work?
|
||||
"""
|
||||
|
||||
from random import randint
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import OVSSwitch
|
||||
from mininet.topo import LinearTopo
|
||||
from mininet.log import info, output, warn, setLogLevel
|
||||
from mininet.log import output, warn
|
||||
|
||||
from random import randint
|
||||
|
||||
|
||||
class MobilitySwitch( OVSSwitch ):
|
||||
@@ -37,7 +37,6 @@ class MobilitySwitch( OVSSwitch ):
|
||||
del self.intfs[ port ]
|
||||
del self.nameToIntf[ intf.name ]
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def addIntf( self, intf, rename=False, **kwargs ):
|
||||
"Add (and reparent) an interface"
|
||||
OVSSwitch.addIntf( self, intf, **kwargs )
|
||||
@@ -106,33 +105,30 @@ def moveHost( host, oldSwitch, newSwitch, newPort=None ):
|
||||
|
||||
def mobilityTest():
|
||||
"A simple test of mobility"
|
||||
info( '* Simple mobility test\n' )
|
||||
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch,
|
||||
waitConnected=True )
|
||||
info( '* Starting network:\n' )
|
||||
print '* Simple mobility test'
|
||||
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
|
||||
print '* Starting network:'
|
||||
net.start()
|
||||
printConnections( net.switches )
|
||||
info( '* Testing network\n' )
|
||||
print '* Testing network'
|
||||
net.pingAll()
|
||||
info( '* Identifying switch interface for h1\n' )
|
||||
print '* Identifying switch interface for h1'
|
||||
h1, old = net.get( 'h1', 's1' )
|
||||
for s in 2, 3, 1:
|
||||
new = net[ 's%d' % s ]
|
||||
port = randint( 10, 20 )
|
||||
info( '* Moving', h1, 'from', old, 'to', new, 'port', port, '\n' )
|
||||
print '* Moving', h1, 'from', old, 'to', new, 'port', port
|
||||
hintf, sintf = moveHost( h1, old, new, newPort=port )
|
||||
info( '*', hintf, 'is now connected to', sintf, '\n' )
|
||||
info( '* Clearing out old flows\n' )
|
||||
print '*', hintf, 'is now connected to', sintf
|
||||
print '* Clearing out old flows'
|
||||
for sw in net.switches:
|
||||
sw.dpctl( 'del-flows' )
|
||||
info( '* New network:\n' )
|
||||
print '* New network:'
|
||||
printConnections( net.switches )
|
||||
info( '* Testing connectivity:\n' )
|
||||
print '* Testing connectivity:'
|
||||
net.pingAll()
|
||||
old = new
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
mobilityTest()
|
||||
|
||||
@@ -13,7 +13,7 @@ from mininet.topo import Topo
|
||||
def runMultiLink():
|
||||
"Create and run multiple link network"
|
||||
topo = simpleMultiLinkTopo( n=2 )
|
||||
net = Mininet( topo=topo, waitConnected=True )
|
||||
net = Mininet( topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
@@ -21,8 +21,9 @@ def runMultiLink():
|
||||
class simpleMultiLinkTopo( Topo ):
|
||||
"Simple topology with multiple links"
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def build( self, n, **_kwargs ):
|
||||
def __init__( self, n, **kwargs ):
|
||||
Topo.__init__( self, **kwargs )
|
||||
|
||||
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
|
||||
s1 = self.addSwitch( 's1' )
|
||||
|
||||
@@ -30,7 +31,6 @@ class simpleMultiLinkTopo( Topo ):
|
||||
self.addLink( s1, h1 )
|
||||
self.addLink( s1, h2 )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
runMultiLink()
|
||||
|
||||
Executable
+275
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Multiple ovsdb OVS!!
|
||||
|
||||
We scale up by creating multiple ovsdb instances,
|
||||
each of which is shared by several OVS switches
|
||||
|
||||
The shell may also be shared among switch instances,
|
||||
which causes switch.cmd() and switch.popen() to be
|
||||
delegated to the ovsdb instance.
|
||||
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node, OVSSwitch
|
||||
from mininet.node import OVSBridge
|
||||
from mininet.link import Link, OVSIntf
|
||||
from mininet.topo import LinearTopo, SingleSwitchTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
from mininet.clean import Cleanup, sh
|
||||
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
|
||||
class OVSDB( Node ):
|
||||
"Namespace for an OVSDB instance"
|
||||
|
||||
privateDirs = [ '/etc/openvswitch',
|
||||
'/var/run/openvswitch',
|
||||
'/var/log/openvswitch' ]
|
||||
|
||||
# Control network
|
||||
ipBase = '172.123.123.0/24'
|
||||
cnet = None
|
||||
nat = None
|
||||
|
||||
@classmethod
|
||||
def startControlNet( cls ):
|
||||
"Start control net if necessary and return it"
|
||||
cnet = cls.cnet
|
||||
if not cnet:
|
||||
info( '### Starting control network\n' )
|
||||
cnet = Mininet( ipBase=cls.ipBase )
|
||||
cswitch = cnet.addSwitch( 'ovsbr0', cls=OVSBridge )
|
||||
# Add NAT - note this can conflict with data network NAT
|
||||
info( '### Adding NAT for control and data networks'
|
||||
' (use --nat flush=0 for data network)\n' )
|
||||
cls.cnet = cnet
|
||||
cls.nat = cnet.addNAT( 'ovsdbnat0')
|
||||
cnet.start()
|
||||
info( '### Control network started\n' )
|
||||
return cnet
|
||||
|
||||
def stopControlNet( self ):
|
||||
info( '\n### Stopping control network\n' )
|
||||
cls = self.__class__
|
||||
cls.cnet.stop()
|
||||
info( '### Control network stopped\n' )
|
||||
|
||||
def addSwitch( self, switch ):
|
||||
"Add a switch to our namespace"
|
||||
# Attach first switch to cswitch!
|
||||
self.switches.append( switch )
|
||||
|
||||
def delSwitch( self, switch ):
|
||||
"Delete a switch from our namespace, and terminate if none left"
|
||||
self.switches.remove( switch )
|
||||
if not self.switches:
|
||||
self.stopOVS()
|
||||
|
||||
ovsdbCount = 0
|
||||
|
||||
def startOVS( self ):
|
||||
"Start new OVS instance"
|
||||
self.cmd( 'ovsdb-tool create /etc/openvswitch/conf.db' )
|
||||
self.cmd( 'ovsdb-server /etc/openvswitch/conf.db'
|
||||
' -vfile:emer -vfile:err -vfile:info'
|
||||
' --remote=punix:/var/run/openvswitch/db.sock '
|
||||
' --log-file=/var/log/openvswitch/ovsdb-server.log'
|
||||
' --pidfile=/var/run/openvswitch/ovsdb-server-mn.pid'
|
||||
' --no-chdir'
|
||||
' --detach' )
|
||||
|
||||
self.cmd( 'ovs-vswitchd unix:/var/run/openvswitch/db.sock'
|
||||
' -vfile:emer -vfile:err -vfile:info'
|
||||
' --mlockall --log-file=/var/log/openvswitch/ovs-vswitchd.log'
|
||||
' --pidfile=/var/run/openvswitch/ovs-vswitchd-mn.pid'
|
||||
' --no-chdir'
|
||||
' --detach' )
|
||||
|
||||
def stopOVS( self ):
|
||||
self.cmd( 'kill',
|
||||
'`cat /var/run/openvswitch/ovs-vswitchd-mn.pid`',
|
||||
'`cat /var/run/openvswitch/ovsdb-server-mn.pid`' )
|
||||
self.cmd( 'wait' )
|
||||
self.__class__.ovsdbCount -= 1
|
||||
if self.__class__.ovsdbCount <= 0:
|
||||
self.stopControlNet()
|
||||
|
||||
@classmethod
|
||||
def cleanUpOVS( cls ):
|
||||
"Clean up leftover ovsdb-server/ovs-vswitchd processes"
|
||||
info( '*** Shutting down extra ovsdb-server/ovs-vswitchd processes\n' )
|
||||
sh( 'pkill -f mn.pid' )
|
||||
|
||||
def self( self, *args, **kwargs ):
|
||||
"A fake constructor that sets params and returns self"
|
||||
self.params = kwargs
|
||||
return self
|
||||
|
||||
def __init__( self, **kwargs ):
|
||||
cls = self.__class__
|
||||
cls.ovsdbCount += 1
|
||||
cnet = self.startControlNet()
|
||||
# Create a new ovsdb namespace
|
||||
self.switches = []
|
||||
name = 'ovsdb%d' % cls.ovsdbCount
|
||||
kwargs.update( inNamespace=True )
|
||||
kwargs.setdefault( 'privateDirs', self.privateDirs )
|
||||
super( OVSDB, self ).__init__( name, **kwargs )
|
||||
ovsdb = cnet.addHost( name, cls=self.self, **kwargs )
|
||||
link = cnet.addLink( ovsdb, cnet.switches[ 0 ] )
|
||||
cnet.switches[ 0 ].attach( link.intf2 )
|
||||
ovsdb.configDefault()
|
||||
ovsdb.setDefaultRoute( 'via %s' % self.nat.intfs[ 0 ].IP() )
|
||||
ovsdb.startOVS()
|
||||
|
||||
|
||||
# Install cleanup callback
|
||||
Cleanup.addCleanupCallback( OVSDB.cleanUpOVS )
|
||||
|
||||
|
||||
class OVSSwitchNS( OVSSwitch ):
|
||||
"OVS Switch in shared OVSNS namespace"
|
||||
|
||||
isSetup = False
|
||||
|
||||
@classmethod
|
||||
def batchStartup( cls, switches ):
|
||||
result = []
|
||||
for ovsdb, switchGroup in groupby( switches, attrgetter( 'ovsdb') ):
|
||||
switchGroup = list( switchGroup )
|
||||
info( '(%s)' % ovsdb )
|
||||
result += OVSSwitch.batchStartup( switchGroup, run=ovsdb.cmd )
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def batchShutdown( cls, switches ):
|
||||
result = []
|
||||
for ovsdb, switchGroup in groupby( switches, attrgetter( 'ovsdb') ):
|
||||
switchGroup = list( switchGroup )
|
||||
info( '(%s)' % ovsdb )
|
||||
for switch in switches:
|
||||
if switch.pid == ovsdb.pid:
|
||||
switch.pid = None
|
||||
switch.shell = None
|
||||
result += OVSSwitch.batchShutdown( switchGroup, run=ovsdb.cmd )
|
||||
for switch in switchGroup:
|
||||
switch.ovsdbFree()
|
||||
return result
|
||||
|
||||
# OVSDB allocation
|
||||
groupSize = 64
|
||||
switchCount = 0
|
||||
lastOvsdb = None
|
||||
|
||||
@classmethod
|
||||
def ovsdbAlloc( cls, switch ):
|
||||
"Allocate (possibly new) OVSDB instance for switch"
|
||||
if cls.switchCount % switch.groupSize == 0:
|
||||
cls.lastOvsdb = OVSDB()
|
||||
cls.switchCount += 1
|
||||
cls.lastOvsdb.addSwitch( switch )
|
||||
return cls.lastOvsdb
|
||||
|
||||
def ovsdbFree( self ):
|
||||
"Deallocate OVSDB instance for switch"
|
||||
self.ovsdb.delSwitch( self )
|
||||
|
||||
def startShell( self, *args, **kwargs ):
|
||||
"Start shell in shared OVSDB namespace"
|
||||
ovsdb = self.ovsdbAlloc( self )
|
||||
kwargs.update( mnopts='-da %d ' % ovsdb.pid )
|
||||
self.ns = [ 'net' ]
|
||||
self.ovsdb = ovsdb
|
||||
self._waiting = False
|
||||
if self.privateShell:
|
||||
super( OVSSwitchNS, self ).startShell( *args, **kwargs )
|
||||
else:
|
||||
# Delegate methods and initialize local vars
|
||||
attrs = ( 'cmd', 'cmdPrint', 'sendCmd', 'waitOutput',
|
||||
'monitor', 'write', 'read',
|
||||
'pid', 'shell', 'stdout',)
|
||||
for attr in attrs:
|
||||
setattr( self, attr, getattr( ovsdb, attr ) )
|
||||
self.defaultIntf().updateIP()
|
||||
|
||||
@property
|
||||
def waiting( self ):
|
||||
"Optionally delegated to ovsdb"
|
||||
return self._waiting if self.privateShell else self.ovsdb.waiting
|
||||
|
||||
@waiting.setter
|
||||
def waiting( self, value ):
|
||||
"Optionally delegated to ovsdb (read only!)"
|
||||
if self.privateShell:
|
||||
_waiting = value
|
||||
|
||||
def start( self, controllers ):
|
||||
"Update controller IP addresses if necessary"
|
||||
for controller in controllers:
|
||||
if controller.IP() == '127.0.0.1' and not controller.intfs:
|
||||
controller.intfs[ 0 ] = self.ovsdb.nat.intfs[ 0 ]
|
||||
super( OVSSwitchNS, self ).start( controllers )
|
||||
|
||||
def stop( self, *args, **kwargs ):
|
||||
"Stop and free OVSDB namespace if necessary"
|
||||
self.ovsdbFree()
|
||||
|
||||
def terminate( self, *args, **kwargs ):
|
||||
if self.privateShell:
|
||||
super( OVSSwitchNS, self ).terminate( *args, **kwargs )
|
||||
else:
|
||||
self.pid = None
|
||||
self.shell= None
|
||||
|
||||
def defaultIntf( self ):
|
||||
return self.ovsdb.defaultIntf()
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"""n: number of OVS instances per OVSDB
|
||||
shell: run private shell/bash process? (False)
|
||||
If shell is shared/not private, cmd() and popen() are
|
||||
delegated to the OVSDB instance, which is different than
|
||||
regular OVSSwitch semantics!!"""
|
||||
self.groupSize = kwargs.pop( 'n', self.groupSize )
|
||||
self.privateShell = kwargs.pop( 'shell', False )
|
||||
super( OVSSwitchNS, self ).__init__( *args, **kwargs )
|
||||
|
||||
|
||||
class OVSLinkNS( Link ):
|
||||
"OVSLink that supports OVSSwitchNS"
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"See Link.__init__() for options"
|
||||
self.isPatchLink = False
|
||||
if ( isinstance( node1, OVSSwitch ) and
|
||||
isinstance( node2, OVSSwitch ) and
|
||||
getattr( node1, 'ovsdb', None ) ==
|
||||
getattr( node2, 'ovsdb', None ) ):
|
||||
self.isPatchLink = True
|
||||
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
switches = { 'ovsns': OVSSwitchNS, 'ovsm': OVSSwitchNS }
|
||||
|
||||
links = { 'ovs': OVSLinkNS }
|
||||
|
||||
def test():
|
||||
"Test OVSNS switch"
|
||||
setLogLevel( 'info' )
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
net = Mininet( topo=topo, switch=OVSSwitchNS )
|
||||
# Add connectivity to controller which is on LAN or in root NS
|
||||
# net.addNAT().configDefault()
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
@@ -8,14 +8,13 @@ multiple hosts and monitor their output interactively for a period=
|
||||
of time.
|
||||
"""
|
||||
|
||||
from select import poll, POLLIN
|
||||
from time import time
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import info, setLogLevel
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
from select import poll, POLLIN
|
||||
from time import time
|
||||
|
||||
def chunks( l, n ):
|
||||
"Divide list l into chunks of size n - thanks Stackoverflow"
|
||||
@@ -35,8 +34,8 @@ def startpings( host, targetips ):
|
||||
' done; '
|
||||
'done &' )
|
||||
|
||||
info( '*** Host %s (%s) will be pinging ips: %s\n' %
|
||||
( host.name, host.IP(), targetips ) )
|
||||
print ( '*** Host %s (%s) will be pinging ips: %s' %
|
||||
( host.name, host.IP(), targetips ) )
|
||||
|
||||
host.cmd( cmd )
|
||||
|
||||
@@ -45,7 +44,7 @@ def multiping( netsize, chunksize, seconds):
|
||||
|
||||
# Create network and identify subnets
|
||||
topo = SingleSwitchTopo( netsize )
|
||||
net = Mininet( topo=topo, waitConnected=True )
|
||||
net = Mininet( topo=topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
subnets = chunks( hosts, chunksize )
|
||||
@@ -59,7 +58,7 @@ def multiping( netsize, chunksize, seconds):
|
||||
# Start pings
|
||||
for subnet in subnets:
|
||||
ips = [ host.IP() for host in subnet ]
|
||||
# adding bogus to generate packet loss
|
||||
#adding bogus to generate packet loss
|
||||
ips.append( '10.0.0.200' )
|
||||
for host in subnet:
|
||||
startpings( host, ips )
|
||||
@@ -70,7 +69,7 @@ def multiping( netsize, chunksize, seconds):
|
||||
readable = poller.poll(1000)
|
||||
for fd, _mask in readable:
|
||||
node = Node.outToNode[ fd ]
|
||||
info( '%s:' % node.name, node.monitor().strip(), '\n' )
|
||||
print '%s:' % node.name, node.monitor().strip()
|
||||
|
||||
# Stop pings
|
||||
for host in hosts:
|
||||
|
||||
+9
-12
@@ -5,22 +5,19 @@ 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
|
||||
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import info, setLogLevel
|
||||
from mininet.util import decode
|
||||
|
||||
|
||||
def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
"Monitor set of files and return [(host, line)...]"
|
||||
devnull = open( '/dev/null', 'w' )
|
||||
tails, fdToFile, fdToHost = {}, {}, {}
|
||||
for h, outfile in outfiles.items():
|
||||
for h, outfile in outfiles.iteritems():
|
||||
tail = Popen( [ 'tail', '-f', outfile ],
|
||||
stdout=PIPE, stderr=devnull )
|
||||
fd = tail.stdout.fileno()
|
||||
@@ -41,7 +38,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
host = fdToHost[ fd ]
|
||||
# Wait for a line of output
|
||||
line = f.readline().strip()
|
||||
yield host, decode( line )
|
||||
yield host, line
|
||||
else:
|
||||
# If we timed out, return nothing
|
||||
yield None, ''
|
||||
@@ -53,10 +50,10 @@ def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
def monitorTest( N=3, seconds=3 ):
|
||||
"Run pings and monitor multiple hosts"
|
||||
topo = SingleSwitchTopo( N )
|
||||
net = Mininet( topo, waitConnected=True )
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
info( "Starting test...\n" )
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
outfiles, errfiles = {}, {}
|
||||
for h in hosts:
|
||||
@@ -70,10 +67,10 @@ def monitorTest( N=3, seconds=3 ):
|
||||
'>', outfiles[ h ],
|
||||
'2>', errfiles[ h ],
|
||||
'&' )
|
||||
info( "Monitoring output for", seconds, "seconds\n" )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
|
||||
if h:
|
||||
info( '%s: %s\n' % ( h.name, line ) )
|
||||
print '%s: %s' % ( h.name, line )
|
||||
for h in hosts:
|
||||
h.cmd('kill %ping')
|
||||
net.stop()
|
||||
|
||||
@@ -17,14 +17,12 @@ def ifconfigTest( net ):
|
||||
for host in hosts:
|
||||
info( host.cmd( 'ifconfig' ) )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info' )
|
||||
info( "*** Initializing Mininet and kernel modules\n" )
|
||||
OVSKernelSwitch.setup()
|
||||
info( "*** Creating network\n" )
|
||||
network = Mininet( TreeTopo( depth=2, fanout=2), switch=OVSKernelSwitch,
|
||||
waitConnected=True )
|
||||
network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
|
||||
info( "*** Starting network\n" )
|
||||
network.start()
|
||||
info( "*** Running ping test\n" )
|
||||
|
||||
+97
-8
@@ -2,22 +2,111 @@
|
||||
|
||||
"""
|
||||
Example to create a Mininet topology and connect it to the internet via NAT
|
||||
through eth0 on the host.
|
||||
|
||||
Glen Gibb, February 2011
|
||||
|
||||
(slight modifications by BL, 5/13)
|
||||
"""
|
||||
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, info
|
||||
from mininet.log import lg
|
||||
from mininet.node import Node
|
||||
from mininet.topolib import TreeNet
|
||||
|
||||
#################################
|
||||
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
|
||||
"""Start NAT/forwarding between Mininet and external network
|
||||
root: node to access iptables from
|
||||
inetIntf: interface for internet access
|
||||
subnet: Mininet subnet (default 10.0/8)="""
|
||||
|
||||
# Identify the interface connecting to the mininet network
|
||||
localIntf = root.defaultIntf()
|
||||
|
||||
# Flush any currently active rules
|
||||
root.cmd( 'iptables -F' )
|
||||
root.cmd( 'iptables -t nat -F' )
|
||||
|
||||
# Create default entries for unmatched traffic
|
||||
root.cmd( 'iptables -P INPUT ACCEPT' )
|
||||
root.cmd( 'iptables -P OUTPUT ACCEPT' )
|
||||
root.cmd( 'iptables -P FORWARD DROP' )
|
||||
|
||||
# Configure NAT
|
||||
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
|
||||
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
|
||||
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
|
||||
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
|
||||
|
||||
# Instruct the kernel to perform forwarding
|
||||
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
||||
|
||||
def stopNAT( root ):
|
||||
"""Stop NAT/forwarding between Mininet and external network"""
|
||||
# Flush any currently active rules
|
||||
root.cmd( 'iptables -F' )
|
||||
root.cmd( 'iptables -t nat -F' )
|
||||
|
||||
# Instruct the kernel to stop forwarding
|
||||
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
def fixNetworkManager( root, intf ):
|
||||
"""Prevent network-manager from messing with our interface,
|
||||
by specifying manual configuration in /etc/network/interfaces
|
||||
root: a node in the root namespace (for running commands)
|
||||
intf: interface name"""
|
||||
cfile = '/etc/network/interfaces'
|
||||
line = '\niface %s inet manual\n' % intf
|
||||
config = open( cfile ).read()
|
||||
if line not in config:
|
||||
print '*** Adding', line.strip(), 'to', cfile
|
||||
with open( cfile, 'a' ) as f:
|
||||
f.write( line )
|
||||
# Probably need to restart network-manager to be safe -
|
||||
# hopefully this won't disconnect you
|
||||
root.cmd( 'service network-manager restart' )
|
||||
|
||||
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
|
||||
"""Connect the network to the internet
|
||||
switch: switch to connect to root namespace
|
||||
rootip: address for interface in root namespace
|
||||
subnet: Mininet subnet"""
|
||||
switch = network.get( switch )
|
||||
prefixLen = subnet.split( '/' )[ 1 ]
|
||||
|
||||
# Create a node in root namespace
|
||||
root = Node( 'root', inNamespace=False )
|
||||
|
||||
# Prevent network-manager from interfering with our interface
|
||||
fixNetworkManager( root, 'root-eth0' )
|
||||
|
||||
# Create link between root NS and switch
|
||||
link = network.addLink( root, switch )
|
||||
link.intf1.setIP( rootip, prefixLen )
|
||||
|
||||
# Start network that now includes link to root namespace
|
||||
network.start()
|
||||
|
||||
# Start NAT and establish forwarding
|
||||
startNAT( root )
|
||||
|
||||
# Establish routes from end hosts
|
||||
for host in network.hosts:
|
||||
host.cmd( 'ip route flush root 0/0' )
|
||||
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
|
||||
host.cmd( 'route add default gw', rootip )
|
||||
|
||||
return root
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4, waitConnected=True )
|
||||
# Add NAT connectivity
|
||||
net.addNAT().configDefault()
|
||||
net.start()
|
||||
info( "*** Hosts are running and should have internet connectivity\n" )
|
||||
info( "*** Type 'exit' or control-D to shut down network\n" )
|
||||
net = TreeNet( depth=1, fanout=4 )
|
||||
# Configure and start NATted connectivity
|
||||
rootnode = connectToInternet( net )
|
||||
print "*** Hosts are running and should have internet connectivity"
|
||||
print "*** Type 'exit' or control-D to shut down network"
|
||||
CLI( net )
|
||||
# Shut down NAT
|
||||
stopNAT( rootnode )
|
||||
net.stop()
|
||||
|
||||
+4
-4
@@ -27,8 +27,9 @@ from mininet.util import irange
|
||||
|
||||
class InternetTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
# pylint: disable=arguments-differ
|
||||
def build(self, n=2, **_kwargs ):
|
||||
def __init__(self, n=2, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
|
||||
# set up inet switch
|
||||
inetSwitch = self.addSwitch('s0')
|
||||
# add inet host
|
||||
@@ -58,12 +59,11 @@ class InternetTopo(Topo):
|
||||
def run():
|
||||
"Create network and run the CLI"
|
||||
topo = InternetTopo()
|
||||
net = Mininet(topo=topo, waitConnected=True )
|
||||
net = Mininet(topo=topo)
|
||||
net.start()
|
||||
CLI(net)
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel('info')
|
||||
run()
|
||||
|
||||
@@ -6,7 +6,6 @@ Validate that the port numbers match to the interface name,
|
||||
and that the ovs ports match the mininet ports.
|
||||
"""
|
||||
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Controller
|
||||
from mininet.log import setLogLevel, info, warn
|
||||
@@ -28,7 +27,7 @@ def testPortNumbering():
|
||||
mid-level API) and check that implicit and
|
||||
explicit port numbering works as expected."""
|
||||
|
||||
net = Mininet( controller=Controller, waitConnected=True )
|
||||
net = Mininet( controller=Controller )
|
||||
|
||||
info( '*** Adding controller\n' )
|
||||
net.addController( 'c0' )
|
||||
@@ -66,16 +65,15 @@ def testPortNumbering():
|
||||
'is actually on port', s1.ports[intfs], '... ' )
|
||||
if validatePort( s1, intfs ):
|
||||
info( 'Validated.\n' )
|
||||
info( '\n' )
|
||||
print '\n'
|
||||
|
||||
# test the network with pingall
|
||||
net.pingAll()
|
||||
info( '\n' )
|
||||
print '\n'
|
||||
|
||||
info( '*** Stopping network\n' )
|
||||
info( '*** Stopping network' )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
testPortNumbering()
|
||||
|
||||
+8
-6
@@ -6,14 +6,17 @@ pmonitor()
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.util import pmonitor
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import custom, pmonitor
|
||||
|
||||
def monitorhosts( hosts=5 ):
|
||||
def monitorhosts( hosts=5, sched='cfs' ):
|
||||
"Start a bunch of pings and monitor them using popen"
|
||||
mytopo = SingleSwitchTopo( hosts )
|
||||
net = Mininet( topo=mytopo, waitConnected=True )
|
||||
cpu = .5 / hosts
|
||||
myhost = custom( CPULimitedHost, cpu=cpu, sched=sched )
|
||||
net = Mininet( topo=mytopo, host=myhost )
|
||||
net.start()
|
||||
# Start a bunch of pings
|
||||
popens = {}
|
||||
@@ -24,11 +27,10 @@ def monitorhosts( hosts=5 ):
|
||||
# Monitor them and print output
|
||||
for host, line in pmonitor( popens ):
|
||||
if host:
|
||||
info( "<%s>: %s" % ( host.name, line ) )
|
||||
print "<%s>: %s" % ( host.name, line.strip() )
|
||||
# Done
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
monitorhosts( hosts=5 )
|
||||
|
||||
+6
-11
@@ -2,37 +2,32 @@
|
||||
|
||||
"Monitor multiple hosts using popen()/pmonitor()"
|
||||
|
||||
from time import time
|
||||
from signal import SIGINT
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.util import pmonitor
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from time import time
|
||||
from signal import SIGINT
|
||||
|
||||
def pmonitorTest( N=3, seconds=10 ):
|
||||
"Run pings and monitor multiple hosts using pmonitor"
|
||||
topo = SingleSwitchTopo( N )
|
||||
net = Mininet( topo, waitConnected=True )
|
||||
net = Mininet( topo )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
info( "Starting test...\n" )
|
||||
print "Starting test..."
|
||||
server = hosts[ 0 ]
|
||||
popens = {}
|
||||
for h in hosts:
|
||||
popens[ h ] = h.popen('ping', server.IP() )
|
||||
info( "Monitoring output for", seconds, "seconds\n" )
|
||||
print "Monitoring output for", seconds, "seconds"
|
||||
endTime = time() + seconds
|
||||
for h, line in pmonitor( popens, timeoutms=500 ):
|
||||
if h:
|
||||
info( '<%s>: %s' % ( h.name, line ) )
|
||||
print '<%s>: %s' % ( h.name, line ),
|
||||
if time() >= endTime:
|
||||
for p in popens.values():
|
||||
p.send_signal( SIGINT )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
pmonitorTest()
|
||||
|
||||
@@ -8,14 +8,13 @@ but it exposes the configuration details and allows customization.
|
||||
For most tasks, the higher-level API will be preferable.
|
||||
"""
|
||||
|
||||
from time import sleep
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node
|
||||
from mininet.link import Link
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.util import quietRun
|
||||
|
||||
from time import sleep
|
||||
|
||||
def scratchNet( cname='controller', cargs='-v ptcp:' ):
|
||||
"Create network from scratch using Open vSwitch."
|
||||
@@ -41,7 +40,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
|
||||
switch.cmd( 'ovs-vsctl del-br dp0' )
|
||||
switch.cmd( 'ovs-vsctl add-br dp0' )
|
||||
for intf in switch.intfs.values():
|
||||
switch.cmd( 'ovs-vsctl add-port dp0 %s\n' % intf )
|
||||
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
|
||||
|
||||
# Note: controller and switch are in root namespace, and we
|
||||
# can connect via loopback interface
|
||||
@@ -62,7 +61,6 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
|
||||
switch.deleteIntfs()
|
||||
info( '\n' )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
info( '*** Scratch network demo (kernel datapath)\n' )
|
||||
|
||||
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
info( '*** Starting controller and user datapath\n' )
|
||||
controller.cmd( cname + ' ' + cargs + '&' )
|
||||
switch.cmd( 'ifconfig lo 127.0.0.1' )
|
||||
intfs = str( sintf1 ), str( sintf2 )
|
||||
intfs = [ str( i ) for i in sintf1, sintf2 ]
|
||||
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
|
||||
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
|
||||
|
||||
@@ -66,7 +66,6 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
|
||||
switch.deleteIntfs()
|
||||
info( '\n' )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
info( '*** Scratch network demo (user datapath)\n' )
|
||||
|
||||
+13
-26
@@ -9,53 +9,40 @@ iperf will hang indefinitely if the TCP handshake fails
|
||||
to complete.
|
||||
"""
|
||||
|
||||
from sys import argv
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.link import TCLink
|
||||
from mininet.util import dumpNodeConnections
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
|
||||
# It would be nice if we didn't have to do this:
|
||||
# pylint: disable=arguments-differ
|
||||
|
||||
class SingleSwitchTopo( Topo ):
|
||||
class SingleSwitchTopo(Topo):
|
||||
"Single switch connected to n hosts."
|
||||
def build( self, n=2, lossy=True ):
|
||||
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)
|
||||
if lossy:
|
||||
# 10 Mbps, 5ms delay, 10% packet loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
else:
|
||||
# 10 Mbps, 5ms delay, no packet loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=0, use_htb=True)
|
||||
# 10 Mbps, 5ms delay, 10% loss
|
||||
self.addLink(host, switch,
|
||||
bw=10, delay='5ms', loss=10, use_htb=True)
|
||||
|
||||
|
||||
def perfTest( lossy=True ):
|
||||
def perfTest():
|
||||
"Create network and run simple performance test"
|
||||
topo = SingleSwitchTopo( n=4, lossy=lossy )
|
||||
topo = SingleSwitchTopo( n=4 )
|
||||
net = Mininet( topo=topo,
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
autoStaticArp=True )
|
||||
net.start()
|
||||
info( "Dumping host connections\n" )
|
||||
print "Dumping host connections"
|
||||
dumpNodeConnections(net.hosts)
|
||||
info( "Testing bandwidth between h1 and h4\n" )
|
||||
print "Testing bandwidth between h1 and h4"
|
||||
h1, h4 = net.getNodeByName('h1', 'h4')
|
||||
net.iperf( ( h1, h4 ), l4Type='UDP' )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# Prevent test_simpleperf from failing due to packet loss
|
||||
perfTest( lossy=( 'testmode' not in argv ) )
|
||||
setLogLevel('info')
|
||||
perfTest()
|
||||
|
||||
+9
-9
@@ -20,16 +20,15 @@ import sys
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import lg, info
|
||||
from mininet.log import lg
|
||||
from mininet.node import Node
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import waitListening
|
||||
|
||||
|
||||
def TreeNet( depth=1, fanout=2, **kwargs ):
|
||||
"Convenience function for creating tree networks."
|
||||
topo = TreeTopo( depth, fanout )
|
||||
return Mininet( topo, waitConnected=True, **kwargs )
|
||||
return Mininet( topo, **kwargs )
|
||||
|
||||
def connectToRootNS( network, switch, ip, routes ):
|
||||
"""Connect hosts to root namespace via switch. Starts network.
|
||||
@@ -47,7 +46,6 @@ def connectToRootNS( network, switch, ip, routes ):
|
||||
for route in routes:
|
||||
root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) )
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
|
||||
ip='10.123.123.1/32', routes=None, switch=None ):
|
||||
"""Start a network, connect it to root ns, and run sshd on all hosts.
|
||||
@@ -61,20 +59,22 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
|
||||
connectToRootNS( network, switch, ip, routes )
|
||||
for host in network.hosts:
|
||||
host.cmd( cmd + ' ' + opts + '&' )
|
||||
info( "*** Waiting for ssh daemons to start\n" )
|
||||
print "*** Waiting for ssh daemons to start"
|
||||
for server in network.hosts:
|
||||
waitListening( server=server, port=22, timeout=5 )
|
||||
|
||||
info( "\n*** Hosts are running sshd at the following addresses:\n" )
|
||||
print
|
||||
print "*** Hosts are running sshd at the following addresses:"
|
||||
print
|
||||
for host in network.hosts:
|
||||
info( host.name, host.IP(), '\n' )
|
||||
info( "\n*** Type 'exit' or control-D to shut down network\n" )
|
||||
print host.name, host.IP()
|
||||
print
|
||||
print "*** Type 'exit' or control-D to shut down network"
|
||||
CLI( network )
|
||||
for host in network.hosts:
|
||||
host.cmd( 'kill %' + cmd )
|
||||
network.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
lg.setLogLevel( 'info')
|
||||
net = TreeNet( depth=1, fanout=4 )
|
||||
|
||||
@@ -32,8 +32,7 @@ def runTests( testDir, verbosity=1 ):
|
||||
# discover all tests in testDir
|
||||
testSuite = unittest.defaultTestLoader.discover( testDir )
|
||||
# run tests
|
||||
success = MininetTestRunner( verbosity=verbosity ).run( testSuite ).wasSuccessful()
|
||||
sys.exit( 0 if success else 1 )
|
||||
MininetTestRunner( verbosity=verbosity ).run( testSuite )
|
||||
|
||||
if __name__ == '__main__':
|
||||
# get the directory containing example tests
|
||||
|
||||
@@ -5,9 +5,8 @@ Tests for baresshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from mininet.clean import cleanup, sh
|
||||
from sys import stdout
|
||||
|
||||
class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
@@ -15,9 +14,7 @@ class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
def connected( self ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
|
||||
'-o StrictHostKeyChecking=no '
|
||||
'-i /tmp/ssh/test_rsa exit' )
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
@@ -25,7 +22,6 @@ class testBareSSHD( unittest.TestCase ):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def setUp( self ):
|
||||
# verify that sshd is not running
|
||||
self.assertFalse( self.connected() )
|
||||
@@ -59,7 +55,7 @@ class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
def tearDown( self ):
|
||||
# kill the ssh process
|
||||
sh( "ps aux | grep ssh |grep Banner| awk '{ print $2 }' | xargs kill" )
|
||||
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
|
||||
cleanup()
|
||||
# remove public key pair
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
|
||||
@@ -5,7 +5,7 @@ Tests for bind.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testBind( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ A simple sanity check test for cluster edition
|
||||
'''
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class clusterSanityCheck( unittest.TestCase ):
|
||||
|
||||
@@ -14,8 +14,6 @@ class clusterSanityCheck( unittest.TestCase ):
|
||||
def testClusterPingAll( self ):
|
||||
p = pexpect.spawn( 'python -m mininet.examples.clusterSanity' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'py net.waitConnected()' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect ( '(\d+)% dropped' )
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
|
||||
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testControllers( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@ Test for controlnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
|
||||
from sys import stdout
|
||||
import pexpect
|
||||
|
||||
class testControlNet( unittest.TestCase ):
|
||||
|
||||
@@ -15,7 +13,7 @@ class testControlNet( unittest.TestCase ):
|
||||
|
||||
def testPingall( self ):
|
||||
"Simple pingall test that verifies 0% packet drop in data network"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.controlnet', logfile=stdout)
|
||||
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'pingall' )
|
||||
p.expect ( '(\d+)% dropped' )
|
||||
@@ -28,9 +26,9 @@ class testControlNet( unittest.TestCase ):
|
||||
def testFailover( self ):
|
||||
"Kill controllers and verify that switch, s1, fails over properly"
|
||||
count = 1
|
||||
p = pexpect.spawn( 'python -m mininet.examples.controlnet', logfile=stdout )
|
||||
p = pexpect.spawn( 'python -m mininet.examples.controlnet' )
|
||||
p.expect( self.prompt )
|
||||
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log', logfile=stdout )
|
||||
lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' )
|
||||
lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' )
|
||||
ip = int( lp.match.group( 1 ) )
|
||||
self.assertEqual( count, ip )
|
||||
|
||||
+12
-13
@@ -5,17 +5,18 @@ Test for cpu.py
|
||||
|
||||
results format:
|
||||
|
||||
sched cpu received bits/sec
|
||||
cfs 50% 8.14e+09
|
||||
cfs 40% 6.48e+09
|
||||
cfs 30% 4.56e+09
|
||||
cfs 20% 2.84e+09
|
||||
cfs 10% 1.29e+09
|
||||
sched cpu client MB/s
|
||||
|
||||
cfs 45.00% 13254.669841
|
||||
cfs 40.00% 11822.441399
|
||||
cfs 30.00% 5112.963009
|
||||
cfs 20.00% 3449.090009
|
||||
cfs 10.00% 2271.741564
|
||||
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testCPU( unittest.TestCase ):
|
||||
@@ -25,13 +26,13 @@ class testCPU( unittest.TestCase ):
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testCPU( self ):
|
||||
"Verify that CPU utilization is monotonically decreasing for each scheduler"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.cpu', timeout=300 )
|
||||
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
|
||||
# matches each line from results( shown above )
|
||||
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)',
|
||||
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
|
||||
pexpect.EOF ]
|
||||
scheds = []
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
index = p.expect( opts, timeout=600 )
|
||||
if index == 0:
|
||||
sched = p.match.group( 1 )
|
||||
cpu = float( p.match.group( 2 ) )
|
||||
@@ -39,9 +40,7 @@ class testCPU( unittest.TestCase ):
|
||||
if sched not in scheds:
|
||||
scheds.append( sched )
|
||||
else:
|
||||
self.assertTrue( bw < previous_bw,
|
||||
"%e should be less than %e\n" %
|
||||
( bw, previous_bw ) )
|
||||
self.assertTrue( bw < previous_bw )
|
||||
previous_bw = bw
|
||||
else:
|
||||
break
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for emptynet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testEmptyNet( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Test for hwintf.py
|
||||
import unittest
|
||||
import re
|
||||
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import Node
|
||||
@@ -57,8 +57,8 @@ class testHwintf( unittest.TestCase ):
|
||||
p.wait()
|
||||
|
||||
def tearDown( self ):
|
||||
self.h3.stop( deleteIntfs=True )
|
||||
self.n0.stop( deleteIntfs=True )
|
||||
self.h3.terminate()
|
||||
self.n0.terminate()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for intfOptions.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testIntfOptions( unittest.TestCase ):
|
||||
@@ -13,31 +13,27 @@ class testIntfOptions( unittest.TestCase ):
|
||||
def testIntfOptions( self ):
|
||||
"verify that intf.config is correctly limiting traffic"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
|
||||
tolerance = .25 # plus or minus 25% for cloud CI tests
|
||||
tolerance = .8
|
||||
opts = [ "Results: \['([\d\.]+) .bits/sec",
|
||||
"Results: \['10M', '([\d\.]+) .bits/sec",
|
||||
"h(\d+)->h(\d+): (\d)/(\d),"
|
||||
"rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
|
||||
"h(\d+)->h(\d+): (\d)/(\d), rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
|
||||
pexpect.EOF ]
|
||||
while True:
|
||||
index = p.expect( opts, timeout=600 )
|
||||
if index == 0:
|
||||
BW = 10
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertGreaterEqual( bw, BW * ( 1 - tolerance ) )
|
||||
self.assertLessEqual( bw, BW * ( 1 + tolerance ) )
|
||||
self.assertGreaterEqual( bw, float( 5 * tolerance ) )
|
||||
self.assertLessEqual( bw, float( 5 + 5 * ( 1 - tolerance ) ) )
|
||||
elif index == 1:
|
||||
BW = 10
|
||||
measuredBw = float( p.match.group( 1 ) )
|
||||
loss = ( measuredBw / BW ) * 100
|
||||
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ),
|
||||
'loss of %d%% << 50%%' % loss )
|
||||
self.assertLessEqual( loss, 50 * ( 1 + tolerance ),
|
||||
'loss of %d%% >> 50%%' % loss )
|
||||
self.assertGreaterEqual( loss, 50 * tolerance )
|
||||
self.assertLessEqual( loss, 50 + 50 * ( 1 - tolerance ) )
|
||||
elif index == 2:
|
||||
delay = float( p.match.group( 6 ) )
|
||||
self.assertGreaterEqual( delay, 15 * ( 1 - tolerance ) )
|
||||
self.assertLessEqual( delay, 15 * ( 1 + tolerance ) )
|
||||
self.assertGreaterEqual( delay, 15 * tolerance )
|
||||
self.assertLessEqual( delay, 15 + 15 * ( 1 - tolerance ) )
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for limit.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testLimit( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testLinearBandwidth( unittest.TestCase ):
|
||||
@@ -34,8 +34,8 @@ class testLinearBandwidth( unittest.TestCase ):
|
||||
bw *= 10 ** 9
|
||||
# check that we have a previous result to compare to
|
||||
if n != 1:
|
||||
info = ( 'bw: %.2e bits/s across %d switches, '
|
||||
'previous: %.2e bits/s across %d switches' %
|
||||
info = ( 'bw: %d bits/s across %d switches, '
|
||||
'previous: %d bits/s across %d switches' %
|
||||
( bw, n, previous_bw, previous_n ) )
|
||||
self.assertTrue( bw < previous_bw, info )
|
||||
previous_bw, previous_n = bw, n
|
||||
|
||||
Executable → Regular
+1
-1
@@ -5,7 +5,7 @@ Test for linuxrouter.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testLinuxRouter( unittest.TestCase ):
|
||||
|
||||
@@ -6,7 +6,7 @@ validates mininet interfaces against systems interfaces
|
||||
'''
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testMultiLink( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for multiping.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from collections import defaultdict
|
||||
|
||||
class testMultiPing( unittest.TestCase ):
|
||||
@@ -31,20 +31,18 @@ class testMultiPing( unittest.TestCase ):
|
||||
target = p.match.group(3)
|
||||
received = int( p.match.group(4) )
|
||||
if target == '10.0.0.200':
|
||||
self.assertEqual( received, 0, p.match.group(0) + '\n' +
|
||||
target + ' received %d != 0 packets' % received )
|
||||
self.assertEqual( received, 0 )
|
||||
else:
|
||||
self.assertEqual( received, 1, p.match.group(0) + '\n' +
|
||||
target + ' received %d != 1 packets' % received )
|
||||
self.assertEqual( received, 1 )
|
||||
try:
|
||||
pings[ name ].remove( target )
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
self.assertTrue( len( pings ) > 0, 'too few pings' )
|
||||
self.assertTrue( len( pings ) > 0 )
|
||||
for t in pings.values():
|
||||
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
|
||||
self.assertEqual( len( t ), 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for multipoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testMultiPoll( unittest.TestCase ):
|
||||
|
||||
@@ -16,7 +16,7 @@ class testMultiPoll( unittest.TestCase ):
|
||||
"(h\d+): \d+ bytes from",
|
||||
"Monitoring output for (\d+) seconds",
|
||||
pexpect.EOF ]
|
||||
pings, seconds = {}, -1
|
||||
pings = {}
|
||||
while True:
|
||||
index = p.expect( opts )
|
||||
if index == 0:
|
||||
@@ -32,8 +32,7 @@ class testMultiPoll( unittest.TestCase ):
|
||||
self.assertTrue( len( pings ) > 0 )
|
||||
# make sure we have received at least one ping per second
|
||||
for count in pings.values():
|
||||
self.assertTrue( count >= seconds,
|
||||
'%d pings < %d seconds' % ( count, seconds ) )
|
||||
self.assertTrue( count >= seconds )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for multitest.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testMultiTest( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for nat.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
destIP = '8.8.8.8' # Google DNS
|
||||
|
||||
Executable → Regular
+1
-1
@@ -5,7 +5,7 @@ Test for natnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testNATNet( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for numberedports.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from collections import defaultdict
|
||||
from mininet.node import OVSSwitch
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testPopen( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for scratchnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
|
||||
class testScratchNet( unittest.TestCase ):
|
||||
|
||||
@@ -14,9 +14,8 @@ class testScratchNet( unittest.TestCase ):
|
||||
def pingTest( self, name ):
|
||||
"Verify that no ping packets were dropped"
|
||||
p = pexpect.spawn( 'python -m %s' % name )
|
||||
index = p.expect( self.opts, timeout=120 )
|
||||
index = p.expect( self.opts )
|
||||
self.assertEqual( index, 0 )
|
||||
p.wait()
|
||||
|
||||
def testPingKernel( self ):
|
||||
self.pingTest( 'mininet.examples.scratchnet' )
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for simpleperf.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
@@ -16,15 +16,15 @@ class testSimplePerf( unittest.TestCase ):
|
||||
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
|
||||
def testE2E( self ):
|
||||
"Run the example and verify iperf results"
|
||||
# 10 Mb/s, plus or minus 20% tolerance
|
||||
BW = 10
|
||||
TOLERANCE = .2
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
|
||||
TOLERANCE = .8
|
||||
expectedBw = BW * TOLERANCE
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
|
||||
# check iperf results
|
||||
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
|
||||
measuredBw = float( p.match.group( 1 ) )
|
||||
lowerBound = BW * ( 1 - TOLERANCE )
|
||||
upperBound = BW + ( 1 + TOLERANCE )
|
||||
lowerBound = expectedBw * TOLERANCE
|
||||
upperBound = expectedBw + expectedBw * ( 1 - TOLERANCE )
|
||||
self.assertGreaterEqual( measuredBw, lowerBound )
|
||||
self.assertLessEqual( measuredBw, upperBound )
|
||||
p.wait()
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for sshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
from mininet.clean import sh
|
||||
|
||||
class testSSHD( unittest.TestCase ):
|
||||
@@ -16,12 +16,11 @@ class testSSHD( unittest.TestCase ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
# Note: this test will fail if "Welcome" is not in the sshd banner
|
||||
# and '#'' or '$'' are not in the prompt
|
||||
ssh = 'ssh -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa ' + ip
|
||||
p = pexpect.spawn( ssh, timeout=5 )
|
||||
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
print( p.match.group(0) )
|
||||
print p.match.group(0)
|
||||
p.sendline( 'yes' )
|
||||
elif index == 1:
|
||||
return False
|
||||
@@ -58,3 +57,4 @@ class testSSHD( unittest.TestCase ):
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for tree1024.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testTree1024( unittest.TestCase ):
|
||||
@@ -17,15 +17,13 @@ class testTree1024( unittest.TestCase ):
|
||||
"Run the example and do a simple ping test from h1 to h1024"
|
||||
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
|
||||
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
|
||||
p.sendline( 'h1 ping -c 20 h1024' )
|
||||
p.sendline( 'h1 ping -c 1 h1024' )
|
||||
p.expect ( '(\d+)% packet loss' )
|
||||
packetLossPercent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
percent = int( p.match.group( 1 ) ) if p.match else -1
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
# Tolerate slow startup on some systems - we should revisit this
|
||||
# and determine the root cause.
|
||||
self.assertLess( packetLossPercent, 60 )
|
||||
self.assertEqual( percent, 0 )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for treeping64.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
|
||||
class testTreePing64( unittest.TestCase ):
|
||||
|
||||
Executable → Regular
+1
-1
@@ -5,7 +5,7 @@ Test for vlanhost.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from mininet.util import pexpect
|
||||
import pexpect
|
||||
import sys
|
||||
from mininet.util import quietRun
|
||||
|
||||
|
||||
@@ -11,10 +11,8 @@ from mininet.cli import CLI
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import OVSSwitch
|
||||
from mininet.topolib import TreeNet
|
||||
from mininet.examples.treeping64 import HostV4
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
network = TreeNet( depth=2, fanout=32, host=HostV4,
|
||||
switch=OVSSwitch, waitConnected=True)
|
||||
network = TreeNet( depth=2, fanout=32, switch=OVSSwitch )
|
||||
network.run( CLI, network )
|
||||
|
||||
+11
-23
@@ -2,42 +2,30 @@
|
||||
|
||||
"Create a 64-node tree network, and test connectivity using ping."
|
||||
|
||||
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch, Host
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
|
||||
from mininet.topolib import TreeNet
|
||||
|
||||
|
||||
class HostV4( Host ):
|
||||
"Try to IPv6 and its awful neighbor discovery"
|
||||
def __init__( self, *args, **kwargs ):
|
||||
super( HostV4, self ).__init__( *args, **kwargs )
|
||||
cfgs = [ 'all.disable_ipv6=1', 'default.disable_ipv6=1',
|
||||
'default.autoconf=0', 'lo.autoconf=0' ]
|
||||
for cfg in cfgs:
|
||||
self.cmd( 'sysctl -w net.ipv6.conf.' + cfg )
|
||||
|
||||
|
||||
def treePing64():
|
||||
"Run ping test on 64-node tree networks."
|
||||
|
||||
results = {}
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
switches = { # 'reference kernel': KernelSwitch,
|
||||
'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
for name in switches:
|
||||
info( "*** Testing", name, "datapath\n" )
|
||||
print "*** Testing", name, "datapath"
|
||||
switch = switches[ name ]
|
||||
network = TreeNet( depth=2, fanout=8, switch=switch,
|
||||
waitConnected=True )
|
||||
network = TreeNet( depth=2, fanout=8, switch=switch )
|
||||
result = network.run( network.pingAll )
|
||||
results[ name ] = result
|
||||
|
||||
info( "\n*** Tree network ping results:\n" )
|
||||
print
|
||||
print "*** Tree network ping results:"
|
||||
for name in switches:
|
||||
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
|
||||
info( '\n' )
|
||||
|
||||
print "%s: %d%% packet loss" % ( name, results[ name ] )
|
||||
print
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
|
||||
@@ -24,18 +24,14 @@ Usage (example uses VLAN ID=1000):
|
||||
|
||||
"""
|
||||
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
|
||||
from mininet.node import Host
|
||||
from mininet.topo import Topo
|
||||
from mininet.util import quietRun
|
||||
from mininet.log import error
|
||||
|
||||
|
||||
class VLANHost( Host ):
|
||||
"Host connected to VLAN interface"
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, vlan=100, **params ):
|
||||
"""Configure VLANHost according to (optional) parameters:
|
||||
vlan: VLAN ID for default interface"""
|
||||
@@ -58,7 +54,6 @@ class VLANHost( Host ):
|
||||
|
||||
return r
|
||||
|
||||
|
||||
hosts = { 'vlan': VLANHost }
|
||||
|
||||
|
||||
@@ -70,7 +65,7 @@ def exampleAllHosts( vlan ):
|
||||
|
||||
# Start a basic network using our VLANHost
|
||||
topo = SingleSwitchTopo( k=2 )
|
||||
net = Mininet( host=host, topo=topo, waitConnected=True )
|
||||
net = Mininet( host=host, topo=topo )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
@@ -101,12 +96,11 @@ class VLANStarTopo( Topo ):
|
||||
def exampleCustomTags():
|
||||
"""Simple example that exercises VLANStarTopo"""
|
||||
|
||||
net = Mininet( topo=VLANStarTopo(), waitConnected=True )
|
||||
net = Mininet( topo=VLANStarTopo() )
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
+6
-7
@@ -16,13 +16,12 @@ import time
|
||||
|
||||
from mininet.log import info
|
||||
from mininet.term import cleanUpScreens
|
||||
from mininet.util import decode
|
||||
|
||||
|
||||
def sh( cmd ):
|
||||
"Print a command and send it to the shell"
|
||||
info( cmd + '\n' )
|
||||
result = Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
return decode( result )
|
||||
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
|
||||
def killprocs( pattern ):
|
||||
"Reliably terminate processes matching a pattern (including args)"
|
||||
@@ -51,9 +50,8 @@ class Cleanup( object ):
|
||||
|
||||
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
|
||||
"pings/noxes\n" )
|
||||
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
|
||||
'lt-nox_core ovs-openflowd ovs-controller'
|
||||
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
|
||||
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
|
||||
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
|
||||
# Note: real zombie processes can't actually be killed, since they
|
||||
# are already (un)dead. Then again,
|
||||
# you can't connect to them either, so they're mostly harmless.
|
||||
@@ -77,6 +75,7 @@ class Cleanup( object ):
|
||||
for dp in dps:
|
||||
if dp:
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
|
||||
info( "*** Removing OVS datapaths\n" )
|
||||
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
|
||||
if dps:
|
||||
@@ -93,7 +92,7 @@ class Cleanup( object ):
|
||||
).splitlines()
|
||||
# Delete blocks of links
|
||||
n = 1000 # chunk size
|
||||
for i in range( 0, len( links ), n ):
|
||||
for i in xrange( 0, len( links ), n ):
|
||||
cmd = ';'.join( 'ip link del %s' % link
|
||||
for link in links[ i : i + n ] )
|
||||
sh( '( %s ) 2> /dev/null' % cmd )
|
||||
|
||||
+14
-33
@@ -29,8 +29,6 @@ from subprocess import call
|
||||
from cmd import Cmd
|
||||
from os import isatty
|
||||
from select import poll, POLLIN
|
||||
import select
|
||||
import errno
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
@@ -46,8 +44,7 @@ class CLI( Cmd ):
|
||||
|
||||
prompt = 'mininet> '
|
||||
|
||||
def __init__( self, mininet, stdin=sys.stdin, script=None,
|
||||
**kwargs ):
|
||||
def __init__( self, mininet, stdin=sys.stdin, script=None ):
|
||||
"""Start and run interactive or batch mode CLI
|
||||
mininet: Mininet network object
|
||||
stdin: standard input for CLI
|
||||
@@ -56,10 +53,11 @@ class CLI( Cmd ):
|
||||
# Local variable bindings for py command
|
||||
self.locals = { 'net': mininet }
|
||||
# Attempt to handle input
|
||||
self.stdin = stdin
|
||||
self.inPoller = poll()
|
||||
self.inPoller.register( stdin )
|
||||
self.inputFile = script
|
||||
Cmd.__init__( self, stdin=stdin, **kwargs )
|
||||
Cmd.__init__( self )
|
||||
info( '*** Starting CLI:\n' )
|
||||
|
||||
if self.inputFile:
|
||||
@@ -79,16 +77,13 @@ class CLI( Cmd ):
|
||||
return
|
||||
cls.readlineInited = True
|
||||
try:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from readline import ( read_history_file, write_history_file,
|
||||
set_history_length )
|
||||
from readline import read_history_file, write_history_file
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
history_path = os.path.expanduser( '~/.mininet_history' )
|
||||
if os.path.isfile( history_path ):
|
||||
read_history_file( history_path )
|
||||
set_history_length( 1000 )
|
||||
atexit.register( lambda: write_history_file( history_path ) )
|
||||
|
||||
def run( self ):
|
||||
@@ -142,10 +137,10 @@ class CLI( Cmd ):
|
||||
' mininet> xterm h2\n\n'
|
||||
)
|
||||
|
||||
def do_help( self, line ): # pylint: disable=arguments-differ
|
||||
def do_help( self, line ):
|
||||
"Describe available CLI commands."
|
||||
Cmd.do_help( self, line )
|
||||
if line == '':
|
||||
if line is '':
|
||||
output( self.helpStr )
|
||||
|
||||
def do_nodes( self, _line ):
|
||||
@@ -174,7 +169,6 @@ class CLI( Cmd ):
|
||||
"""Evaluate a Python expression.
|
||||
Node names may be used, e.g.: py h1.cmd('ls')"""
|
||||
try:
|
||||
# pylint: disable=eval-used
|
||||
result = eval( line, globals(), self.getLocals() )
|
||||
if not result:
|
||||
return
|
||||
@@ -182,7 +176,7 @@ class CLI( Cmd ):
|
||||
output( result + '\n' )
|
||||
else:
|
||||
output( repr( result ) + '\n' )
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# We are in fact using the exec() pseudo-function
|
||||
@@ -193,7 +187,7 @@ class CLI( Cmd ):
|
||||
Node names may be used, e.g.: px print h1.cmd('ls')"""
|
||||
try:
|
||||
exec( line, globals(), self.getLocals() )
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
output( str( e ) + '\n' )
|
||||
|
||||
# pylint: enable=broad-except,exec-used
|
||||
@@ -377,7 +371,7 @@ class CLI( Cmd ):
|
||||
def do_links( self, _line ):
|
||||
"Report on links"
|
||||
for link in self.mn.links:
|
||||
output( link, link.status(), '\n' )
|
||||
print link, link.status()
|
||||
|
||||
def do_switch( self, line ):
|
||||
"Starts or stops a switch"
|
||||
@@ -401,22 +395,17 @@ class CLI( Cmd ):
|
||||
error( 'invalid command: '
|
||||
'switch <switch name> {start, stop}\n' )
|
||||
|
||||
def do_wait( self, _line ):
|
||||
"Wait until all switches have connected to a controller"
|
||||
self.mn.waitConnected()
|
||||
|
||||
def default( self, line ):
|
||||
"""Called on an input line when the command prefix is not recognized.
|
||||
Overridden to run shell commands when a node is the first
|
||||
CLI argument. Past the first CLI argument, node names are
|
||||
automatically replaced with corresponding IP addrs."""
|
||||
Overridden to run shell commands when a node is the first CLI argument.
|
||||
Past the first CLI argument, node names are automatically replaced with
|
||||
corresponding IP addrs."""
|
||||
|
||||
first, args, line = self.parseline( line )
|
||||
|
||||
if first in self.mn:
|
||||
if not args:
|
||||
error( '*** Please enter a command for node: %s <cmd>\n'
|
||||
% first )
|
||||
print "*** Enter a command for node: %s <cmd>" % first
|
||||
return
|
||||
node = self.mn[ first ]
|
||||
rest = args.split( ' ' )
|
||||
@@ -450,7 +439,7 @@ class CLI( Cmd ):
|
||||
# XXX BL: this doesn't quite do what we want.
|
||||
if False and self.inputFile:
|
||||
key = self.inputFile.read( 1 )
|
||||
if key != '':
|
||||
if key is not '':
|
||||
node.write( key )
|
||||
else:
|
||||
self.inputFile = None
|
||||
@@ -467,13 +456,6 @@ class CLI( Cmd ):
|
||||
# it's possible to interrupt ourselves after we've
|
||||
# read data but before it has been printed.
|
||||
node.sendInt()
|
||||
except select.error as e:
|
||||
# pylint: disable=unpacking-non-sequence
|
||||
# pylint: disable=unbalanced-tuple-unpacking
|
||||
errno_, errmsg = e.args
|
||||
if errno_ != errno.EINTR:
|
||||
error( "select.error: %s, %s" % (errno_, errmsg) )
|
||||
node.sendInt()
|
||||
|
||||
def precmd( self, line ):
|
||||
"allow for comments in the cli"
|
||||
@@ -490,4 +472,3 @@ def isReadable( poller ):
|
||||
mask = fdmask[ 1 ]
|
||||
if mask & POLLIN:
|
||||
return True
|
||||
return False
|
||||
|
||||
+50
-99
@@ -24,14 +24,10 @@ TCIntf: interface with bandwidth limiting and delay via tc
|
||||
Link: basic link class for creating veth pairs
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from mininet.log import info, error, debug
|
||||
from mininet.util import makeIntfPair
|
||||
|
||||
# Make pylint happy:
|
||||
# pylint: disable=too-many-arguments
|
||||
|
||||
import mininet.node
|
||||
import re
|
||||
|
||||
class Intf( object ):
|
||||
|
||||
@@ -53,14 +49,12 @@ class Intf( object ):
|
||||
# This saves an ifconfig command per node
|
||||
if self.name == 'lo':
|
||||
self.ip = '127.0.0.1'
|
||||
self.prefixLen = 8
|
||||
# Add to node (and move ourselves if necessary )
|
||||
if node:
|
||||
moveIntfFn = params.pop( 'moveIntfFn', None )
|
||||
if moveIntfFn:
|
||||
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
|
||||
else:
|
||||
node.addIntf( self, port=port )
|
||||
moveIntfFn = params.pop( 'moveIntfFn', None )
|
||||
if moveIntfFn:
|
||||
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
|
||||
else:
|
||||
node.addIntf( self, port=port )
|
||||
# Save params for future reference
|
||||
self.params = params
|
||||
self.config( **params )
|
||||
@@ -151,9 +145,6 @@ class Intf( object ):
|
||||
|
||||
def rename( self, newname ):
|
||||
"Rename interface"
|
||||
if self.node and self.name in self.node.nameToIntf:
|
||||
# rename intf in node's nameToIntf
|
||||
self.node.nameToIntf[newname] = self.node.nameToIntf.pop(self.name)
|
||||
self.ifconfig( 'down' )
|
||||
result = self.cmd( 'ip link set', self.name, 'name', newname )
|
||||
self.name = newname
|
||||
@@ -172,10 +163,10 @@ class Intf( object ):
|
||||
method: config method name
|
||||
param: arg=value (ignore if value=None)
|
||||
value may also be list or dict"""
|
||||
name, value = list( param.items() )[ 0 ]
|
||||
name, value = param.items()[ 0 ]
|
||||
f = getattr( self, method, None )
|
||||
if not f or value is None:
|
||||
return None
|
||||
return
|
||||
if isinstance( value, list ):
|
||||
result = f( *value )
|
||||
elif isinstance( value, dict ):
|
||||
@@ -210,8 +201,6 @@ class Intf( object ):
|
||||
# if self.node.inNamespace:
|
||||
# Link may have been dumped into root NS
|
||||
# quietRun( 'ip link del ' + self.name )
|
||||
self.node.delIntf( self )
|
||||
self.link = None
|
||||
|
||||
def status( self ):
|
||||
"Return intf status as a string"
|
||||
@@ -261,7 +250,7 @@ class TCIntf( Intf ):
|
||||
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
|
||||
elif use_tbf:
|
||||
if latency_ms is None:
|
||||
latency_ms = 15.0 * 8 / bw
|
||||
latency_ms = 15 * 8 / bw
|
||||
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
|
||||
'rate %fMbit burst 15000 latency %fms' %
|
||||
( bw, latency_ms ) ]
|
||||
@@ -293,14 +282,18 @@ class TCIntf( Intf ):
|
||||
loss=None, max_queue_size=None ):
|
||||
"Internal method: return tc commands for delay and loss"
|
||||
cmds = []
|
||||
if loss and ( loss < 0 or loss > 100 ):
|
||||
if delay and delay < 0:
|
||||
error( 'Negative delay', delay, '\n' )
|
||||
elif 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 %.5f ' % loss if (loss is not None and loss > 0) 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:
|
||||
@@ -316,52 +309,27 @@ class TCIntf( Intf ):
|
||||
debug(" *** executing command: %s\n" % c)
|
||||
return self.cmd( c )
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, bw=None, delay=None, jitter=None, loss=None,
|
||||
gro=False, txo=True, rxo=True,
|
||||
speedup=0, use_hfsc=False, use_tbf=False,
|
||||
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.
|
||||
bw: bandwidth in b/s (e.g. '10m')
|
||||
delay: transmit delay (e.g. '1ms' )
|
||||
jitter: jitter (e.g. '1ms')
|
||||
loss: loss (e.g. '1%' )
|
||||
gro: enable GRO (False)
|
||||
txo: enable transmit checksum offload (True)
|
||||
rxo: enable receive checksum offload (True)
|
||||
speedup: experimental switch-side bw option
|
||||
use_hfsc: use HFSC scheduling
|
||||
use_tbf: use TBF scheduling
|
||||
latency_ms: TBF latency parameter
|
||||
enable_ecn: enable ECN (False)
|
||||
enable_red: enable RED (False)
|
||||
max_queue_size: queue limit parameter for netem"""
|
||||
|
||||
# Support old names for parameters
|
||||
gro = not params.pop( 'disable_gro', not gro )
|
||||
"Configure the port and set its properties."
|
||||
|
||||
result = Intf.config( self, **params)
|
||||
|
||||
def on( isOn ):
|
||||
"Helper method: bool -> 'on'/'off'"
|
||||
return 'on' if isOn else 'off'
|
||||
|
||||
# Set offload parameters with ethool
|
||||
self.cmd( 'ethtool -K', self,
|
||||
'gro', on( gro ),
|
||||
'tx', on( txo ),
|
||||
'rx', on( rxo ) )
|
||||
# Disable GRO
|
||||
if disable_gro:
|
||||
self.cmd( 'ethtool -K %s gro off' % self )
|
||||
|
||||
# Optimization: return if nothing else to configure
|
||||
# Question: what happens if we want to reset things?
|
||||
if ( bw is None and not delay and not loss
|
||||
and max_queue_size is None ):
|
||||
return None
|
||||
return
|
||||
|
||||
# Clear existing configuration
|
||||
tcoutput = self.tc( '%s qdisc show dev %s' )
|
||||
if "priomap" not in tcoutput and "noqueue" not in tcoutput:
|
||||
if "priomap" not in tcoutput:
|
||||
cmds = [ '%s qdisc del dev %s root' ]
|
||||
else:
|
||||
cmds = []
|
||||
@@ -385,7 +353,7 @@ class TCIntf( Intf ):
|
||||
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
|
||||
( [ '%s delay' % delay ] if delay is not None else [] ) +
|
||||
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
|
||||
( ['%.5f%% loss' % loss ] if loss is not None else [] ) +
|
||||
( ['%d%% loss' % loss ] if loss is not None else [] ) +
|
||||
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
|
||||
if enable_red else [] ) )
|
||||
info( '(' + ' '.join( stuff ) + ') ' )
|
||||
@@ -413,7 +381,7 @@ class Link( object ):
|
||||
def __init__( self, node1, node2, port1=None, port2=None,
|
||||
intfName1=None, intfName2=None, addr1=None, addr2=None,
|
||||
intf=Intf, cls1=None, cls2=None, params1=None,
|
||||
params2=None, fast=True, **params ):
|
||||
params2=None, fast=True ):
|
||||
"""Create veth link to another node, making two new interfaces.
|
||||
node1: first node
|
||||
node2: second node
|
||||
@@ -423,15 +391,18 @@ class Link( object ):
|
||||
cls1, cls2: optional interface-specific constructors
|
||||
intfName1: node1 interface name (optional)
|
||||
intfName2: node2 interface name (optional)
|
||||
params1: parameters for interface 1 (optional)
|
||||
params2: parameters for interface 2 (optional)
|
||||
**params: additional parameters for both interfaces"""
|
||||
|
||||
params1: parameters for interface 1
|
||||
params2: parameters for interface 2"""
|
||||
# This is a bit awkward; it seems that having everything in
|
||||
# params is more orthogonal, but being able to specify
|
||||
# in-line arguments is more convenient! So we support both.
|
||||
params1 = dict( params1 ) if params1 else {}
|
||||
params2 = dict( params2 ) if params2 else {}
|
||||
if params1 is None:
|
||||
params1 = {}
|
||||
if params2 is None:
|
||||
params2 = {}
|
||||
# Allow passing in params1=params2
|
||||
if params2 is params1:
|
||||
params2 = dict( params1 )
|
||||
if port1 is not None:
|
||||
params1[ 'port' ] = port1
|
||||
if port2 is not None:
|
||||
@@ -445,10 +416,6 @@ class Link( object ):
|
||||
if not intfName2:
|
||||
intfName2 = self.intfName( node2, params2[ 'port' ] )
|
||||
|
||||
# Update with remaining parameter list
|
||||
params1.update( params )
|
||||
params2.update( params )
|
||||
|
||||
self.fast = fast
|
||||
if fast:
|
||||
params1.setdefault( 'moveIntfFn', self._ignore )
|
||||
@@ -470,7 +437,6 @@ class Link( object ):
|
||||
|
||||
# All we are is dust in the wind, and our two interfaces
|
||||
self.intf1, self.intf2 = intf1, intf2
|
||||
|
||||
# pylint: enable=too-many-branches
|
||||
|
||||
@staticmethod
|
||||
@@ -504,9 +470,9 @@ class Link( object ):
|
||||
def delete( self ):
|
||||
"Delete this link"
|
||||
self.intf1.delete()
|
||||
self.intf1 = None
|
||||
self.intf2.delete()
|
||||
self.intf2 = None
|
||||
# We only need to delete one side, though this doesn't seem to
|
||||
# cost us much and might help subclasses.
|
||||
# self.intf2.delete()
|
||||
|
||||
def stop( self ):
|
||||
"Override to stop and clean up link as needed"
|
||||
@@ -539,19 +505,13 @@ class OVSLink( Link ):
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"See Link.__init__() for options"
|
||||
try:
|
||||
OVSSwitch
|
||||
except NameError:
|
||||
# pylint: disable=import-outside-toplevel,cyclic-import
|
||||
from mininet.node import OVSSwitch
|
||||
self.isPatchLink = False
|
||||
if ( isinstance( node1, OVSSwitch ) and
|
||||
isinstance( node2, OVSSwitch ) ):
|
||||
if ( isinstance( node1, mininet.node.OVSSwitch ) and
|
||||
isinstance( node2, mininet.node.OVSSwitch ) ):
|
||||
self.isPatchLink = True
|
||||
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
# pylint: disable=arguments-differ, signature-differs
|
||||
def makeIntfPair( self, *args, **kwargs ):
|
||||
"Usually delegated to OVSSwitch"
|
||||
if self.isPatchLink:
|
||||
@@ -561,23 +521,14 @@ class OVSLink( Link ):
|
||||
|
||||
|
||||
class TCLink( Link ):
|
||||
"Link with TC interfaces"
|
||||
def __init__( self, *args, **kwargs):
|
||||
kwargs.setdefault( 'cls1', TCIntf )
|
||||
kwargs.setdefault( 'cls2', TCIntf )
|
||||
Link.__init__( self, *args, **kwargs)
|
||||
|
||||
|
||||
class TCULink( TCLink ):
|
||||
"""TCLink with default settings optimized for UserSwitch
|
||||
(txo=rxo=0/False). Unfortunately with recent Linux kernels,
|
||||
enabling TX and RX checksum offload on veth pairs doesn't work
|
||||
well with UserSwitch: either it gets terrible performance or
|
||||
TCP packets with bad checksums are generated, forwarded, and
|
||||
*dropped* due to having bad checksums! OVS and LinuxBridge seem
|
||||
to cope with this somehow, but it is likely to be an issue with
|
||||
many software Ethernet bridges."""
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
kwargs.update( txo=False, rxo=False )
|
||||
TCLink.__init__( self, *args, **kwargs )
|
||||
"Link with symmetric TC interfaces configured via opts"
|
||||
def __init__( self, node1, node2, port1=None, port2=None,
|
||||
intfName1=None, intfName2=None,
|
||||
addr1=None, addr2=None, **params ):
|
||||
Link.__init__( self, node1, node2, port1=port1, port2=port2,
|
||||
intfName1=intfName1, intfName2=intfName2,
|
||||
cls1=TCIntf,
|
||||
cls2=TCIntf,
|
||||
addr1=addr1, addr2=addr2,
|
||||
params1=params,
|
||||
params2=params )
|
||||
|
||||
+29
-25
@@ -4,7 +4,6 @@ import logging
|
||||
from logging import Logger
|
||||
import types
|
||||
|
||||
|
||||
# Create a new loglevel, 'CLI info', which enables a Mininet user to see only
|
||||
# the output of the commands they execute, plus any errors or warnings. This
|
||||
# level is in between info and warning. CLI info-level commands should not be
|
||||
@@ -15,14 +14,13 @@ LEVELS = { 'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'output': OUTPUT,
|
||||
'warning': logging.WARNING,
|
||||
'warn': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL }
|
||||
|
||||
# change this to logging.INFO to get printouts when running unit tests
|
||||
LOGLEVELDEFAULT = OUTPUT
|
||||
|
||||
# default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
LOGMSGFORMAT = '%(message)s'
|
||||
|
||||
|
||||
@@ -53,7 +51,7 @@ class StreamHandlerNoNewline( logging.StreamHandler ):
|
||||
self.flush()
|
||||
except ( KeyboardInterrupt, SystemExit ):
|
||||
raise
|
||||
except: # noqa pylint: disable=bare-except
|
||||
except:
|
||||
self.handleError( record )
|
||||
|
||||
|
||||
@@ -97,9 +95,9 @@ class MininetLogger( Logger, object ):
|
||||
|
||||
__metaclass__ = Singleton
|
||||
|
||||
def __init__( self, name="mininet" ):
|
||||
def __init__( self ):
|
||||
|
||||
Logger.__init__( self, name )
|
||||
Logger.__init__( self, "mininet" )
|
||||
|
||||
# create console handler
|
||||
ch = StreamHandlerNoNewline()
|
||||
@@ -107,22 +105,30 @@ class MininetLogger( Logger, object ):
|
||||
formatter = logging.Formatter( LOGMSGFORMAT )
|
||||
# add formatter to ch
|
||||
ch.setFormatter( formatter )
|
||||
# add ch to lg and initialize log level
|
||||
# add ch to lg
|
||||
self.addHandler( ch )
|
||||
self.ch = ch
|
||||
|
||||
self.setLogLevel()
|
||||
|
||||
def setLogLevel( self, levelname=None ):
|
||||
"""Setup loglevel.
|
||||
Convenience function to support lowercase names.
|
||||
levelName: level name from LEVELS"""
|
||||
if levelname and levelname not in LEVELS:
|
||||
print(LEVELS)
|
||||
raise Exception( 'setLogLevel: unknown levelname %s' % levelname )
|
||||
level = LEVELS.get( levelname, LOGLEVELDEFAULT )
|
||||
self.setLevel( level )
|
||||
self.ch.setLevel( level )
|
||||
level = LOGLEVELDEFAULT
|
||||
if levelname is not None:
|
||||
if levelname not in LEVELS:
|
||||
raise Exception( 'unknown levelname seen in setLogLevel' )
|
||||
else:
|
||||
level = LEVELS.get( levelname, level )
|
||||
|
||||
self.setLevel( level )
|
||||
self.handlers[ 0 ].setLevel( level )
|
||||
|
||||
# pylint: disable=method-hidden
|
||||
# "An attribute inherited from mininet.log hide this method" (sic)
|
||||
# Not sure why this is occurring - this function definitely gets called.
|
||||
|
||||
# See /usr/lib/python2.5/logging/__init__.py; modified from warning()
|
||||
def output( self, msg, *args, **kwargs ):
|
||||
"""Log 'msg % args' with severity 'OUTPUT'.
|
||||
|
||||
@@ -131,11 +137,14 @@ class MininetLogger( Logger, object ):
|
||||
|
||||
logger.warning("Houston, we have a %s", "cli output", exc_info=1)
|
||||
"""
|
||||
if getattr( self.manager, 'disabled', 0 ) >= OUTPUT:
|
||||
if self.manager.disable >= OUTPUT:
|
||||
return
|
||||
if self.isEnabledFor( OUTPUT ):
|
||||
self._log( OUTPUT, msg, args, kwargs )
|
||||
|
||||
# pylint: enable=method-hidden
|
||||
|
||||
lg = MininetLogger()
|
||||
|
||||
# Make things a bit more convenient by adding aliases
|
||||
# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
|
||||
@@ -151,7 +160,7 @@ def makeListCompatible( fn ):
|
||||
"Generated function. Closure-ish."
|
||||
if len( args ) == 1:
|
||||
return fn( *args )
|
||||
args = ' '.join( str( arg ) for arg in args )
|
||||
args = ' '.join( [ str( arg ) for arg in args ] )
|
||||
return fn( args )
|
||||
|
||||
# Fix newfn's name and docstring
|
||||
@@ -159,14 +168,9 @@ def makeListCompatible( fn ):
|
||||
setattr( newfn, '__doc__', fn.__doc__ )
|
||||
return newfn
|
||||
|
||||
info, output, warn, error, debug = (
|
||||
lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
|
||||
makeListCompatible( f ) for f in
|
||||
lg.info, lg.output, lg.warn, lg.error, lg.debug ]
|
||||
|
||||
# Initialize logger and logging functions
|
||||
|
||||
logging.setLoggerClass( MininetLogger )
|
||||
lg = logging.getLogger( "mininet" )
|
||||
_loggers = lg.info, lg.output, lg.warning, lg.error, lg.debug
|
||||
_loggers = tuple( makeListCompatible( logger ) for logger in _loggers )
|
||||
lg.info, lg.output, lg.warning, lg.error, lg.debug = _loggers
|
||||
info, output, warning, error, debug = _loggers
|
||||
warn = warning # alternate/old name
|
||||
setLogLevel = lg.setLogLevel
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
"Module dependency utility functions for Mininet."
|
||||
|
||||
from os import environ
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
|
||||
from mininet.util import quietRun, BaseString
|
||||
from mininet.util import quietRun
|
||||
from mininet.log import info, error, debug
|
||||
|
||||
from os import environ
|
||||
|
||||
def lsmod():
|
||||
"Return output of lsmod."
|
||||
@@ -21,7 +18,6 @@ def modprobe( mod ):
|
||||
mod: module string"""
|
||||
return quietRun( [ 'modprobe', mod ] )
|
||||
|
||||
|
||||
OF_KMOD = 'ofdatapath'
|
||||
OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
|
||||
TUN = 'tun'
|
||||
@@ -32,9 +28,9 @@ def moduleDeps( subtract=None, add=None ):
|
||||
add: string or list of module names to add, if not already loaded"""
|
||||
subtract = subtract if subtract is not None else []
|
||||
add = add if add is not None else []
|
||||
if isinstance( subtract, BaseString ):
|
||||
if isinstance( subtract, basestring ):
|
||||
subtract = [ subtract ]
|
||||
if isinstance( add, BaseString ):
|
||||
if isinstance( add, basestring ):
|
||||
add = [ add ]
|
||||
for mod in subtract:
|
||||
if mod in lsmod():
|
||||
|
||||
+19
-84
@@ -92,7 +92,6 @@ import select
|
||||
import signal
|
||||
import random
|
||||
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
from time import sleep
|
||||
from itertools import chain, groupby
|
||||
from math import ceil
|
||||
@@ -105,16 +104,15 @@ from mininet.nodelib import NAT
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
|
||||
macColonHex, ipStr, ipParse, netParse, ipAdd,
|
||||
waitListening, BaseString )
|
||||
waitListening )
|
||||
from mininet.term import cleanUpScreens, makeTerms
|
||||
|
||||
# Mininet version: should be consistent with README and LICENSE
|
||||
VERSION = "2.3.0b2"
|
||||
VERSION = "2.2.1"
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
|
||||
controller=DefaultController, link=Link, intf=Intf,
|
||||
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||
@@ -146,9 +144,7 @@ class Mininet( object ):
|
||||
self.intf = intf
|
||||
self.ipBase = ipBase
|
||||
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
|
||||
hostIP = ( 0xffffffff >> self.prefixLen ) & self.ipBaseNum
|
||||
# Start for address allocation
|
||||
self.nextIP = hostIP if hostIP > 0 else 1
|
||||
self.nextIP = 1 # start for address allocation
|
||||
self.inNamespace = inNamespace
|
||||
self.xterms = xterms
|
||||
self.cleanup = cleanup
|
||||
@@ -175,7 +171,7 @@ class Mininet( object ):
|
||||
if topo and build:
|
||||
self.build()
|
||||
|
||||
def waitConnected( self, timeout=5, delay=.5 ):
|
||||
def waitConnected( self, timeout=None, delay=.5 ):
|
||||
"""wait for each switch to connect to a controller,
|
||||
up to 5 seconds
|
||||
timeout: time to wait, or None to wait indefinitely
|
||||
@@ -192,7 +188,7 @@ class Mininet( object ):
|
||||
if not remaining:
|
||||
info( '\n' )
|
||||
return True
|
||||
if timeout is not None and time > timeout:
|
||||
if time > timeout and timeout is not None:
|
||||
break
|
||||
sleep( delay )
|
||||
time += delay
|
||||
@@ -230,24 +226,6 @@ class Mininet( object ):
|
||||
self.nameToNode[ name ] = h
|
||||
return h
|
||||
|
||||
def delNode( self, node, nodes=None):
|
||||
"""Delete node
|
||||
node: node to delete
|
||||
nodes: optional list to delete from (e.g. self.hosts)"""
|
||||
if nodes is None:
|
||||
nodes = ( self.hosts if node in self.hosts else
|
||||
( self.switches if node in self.switches else
|
||||
( self.controllers if node in self.controllers else
|
||||
[] ) ) )
|
||||
node.stop( deleteIntfs=True )
|
||||
node.terminate()
|
||||
nodes.remove( node )
|
||||
del self.nameToNode[ node.name ]
|
||||
|
||||
def delHost( self, host ):
|
||||
"Delete a host"
|
||||
self.delNode( host, nodes=self.hosts )
|
||||
|
||||
def addSwitch( self, name, cls=None, **params ):
|
||||
"""Add switch.
|
||||
name: name of switch to add
|
||||
@@ -266,10 +244,6 @@ class Mininet( object ):
|
||||
self.nameToNode[ name ] = sw
|
||||
return sw
|
||||
|
||||
def delSwitch( self, switch ):
|
||||
"Delete a switch"
|
||||
self.delNode( switch, nodes=self.switches )
|
||||
|
||||
def addController( self, name='c0', controller=None, **params ):
|
||||
"""Add controller.
|
||||
controller: Controller class"""
|
||||
@@ -291,12 +265,6 @@ class Mininet( object ):
|
||||
self.nameToNode[ name ] = controller_new
|
||||
return controller_new
|
||||
|
||||
def delController( self, controller ):
|
||||
"""Delete a controller
|
||||
Warning - does not reconfigure switches, so they
|
||||
may still attempt to connect to it!"""
|
||||
self.delNode( controller )
|
||||
|
||||
def addNAT( self, name='nat0', connect=True, inNamespace=False,
|
||||
**params):
|
||||
"""Add a NAT to the Mininet network
|
||||
@@ -313,7 +281,7 @@ class Mininet( object ):
|
||||
# Use first switch if not specified
|
||||
connect = self.switches[ 0 ]
|
||||
# Connect the nat to the switch
|
||||
self.addLink( nat, connect )
|
||||
self.addLink( nat, self.switches[ 0 ] )
|
||||
# Set the default route on hosts
|
||||
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
|
||||
for host in self.hosts:
|
||||
@@ -335,13 +303,9 @@ class Mininet( object ):
|
||||
|
||||
# Even more convenient syntax for node lookup and iteration
|
||||
def __getitem__( self, key ):
|
||||
"net[ name ] operator: Return node with given name"
|
||||
"""net [ name ] operator: Return node(s) with given name(s)"""
|
||||
return self.nameToNode[ key ]
|
||||
|
||||
def __delitem__( self, key ):
|
||||
"del net[ name ] operator - delete node with given name"
|
||||
self.delNode( self.nameToNode[ key ] )
|
||||
|
||||
def __iter__( self ):
|
||||
"return iterator over node names"
|
||||
for node in chain( self.hosts, self.switches, self.controllers ):
|
||||
@@ -385,8 +349,8 @@ class Mininet( object ):
|
||||
params: additional link params (optional)
|
||||
returns: link object"""
|
||||
# Accept node objects or names
|
||||
node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
|
||||
node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
|
||||
node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ]
|
||||
node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ]
|
||||
options = dict( params )
|
||||
# Port is optional
|
||||
if port1 is not None:
|
||||
@@ -403,30 +367,6 @@ class Mininet( object ):
|
||||
self.links.append( link )
|
||||
return link
|
||||
|
||||
def delLink( self, link ):
|
||||
"Remove a link from this network"
|
||||
link.delete()
|
||||
self.links.remove( link )
|
||||
|
||||
def linksBetween( self, node1, node2 ):
|
||||
"Return Links between node1 and node2"
|
||||
return [ link for link in self.links
|
||||
if ( node1, node2 ) in (
|
||||
( link.intf1.node, link.intf2.node ),
|
||||
( link.intf2.node, link.intf1.node ) ) ]
|
||||
|
||||
def delLinkBetween( self, node1, node2, index=0, allLinks=False ):
|
||||
"""Delete link(s) between node1 and node2
|
||||
index: index of link to delete if multiple links (0)
|
||||
allLinks: ignore index and delete all such links (False)
|
||||
returns: deleted link(s)"""
|
||||
links = self.linksBetween( node1, node2 )
|
||||
if not allLinks:
|
||||
links = [ links[ index ] ]
|
||||
for link in links:
|
||||
self.delLink( link )
|
||||
return links
|
||||
|
||||
def configHosts( self ):
|
||||
"Configure a set of hosts."
|
||||
for host in self.hosts:
|
||||
@@ -551,8 +491,7 @@ class Mininet( object ):
|
||||
switch.start( self.controllers )
|
||||
started = {}
|
||||
for swclass, switches in groupby(
|
||||
sorted( self.switches,
|
||||
key=lambda s: str( type( s ) ) ), type ):
|
||||
sorted( self.switches, key=type ), type ):
|
||||
switches = tuple( switches )
|
||||
if hasattr( swclass, 'batchStartup' ):
|
||||
success = swclass.batchStartup( switches )
|
||||
@@ -579,8 +518,7 @@ class Mininet( object ):
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
stopped = {}
|
||||
for swclass, switches in groupby(
|
||||
sorted( self.switches,
|
||||
key=lambda s: str( type( s ) ) ), type ):
|
||||
sorted( self.switches, key=type ), type ):
|
||||
switches = tuple( switches )
|
||||
if hasattr( swclass, 'batchShutdown' ):
|
||||
success = swclass.batchShutdown( switches )
|
||||
@@ -638,7 +576,7 @@ class Mininet( object ):
|
||||
# Check for downed link
|
||||
if 'connect: Network is unreachable' in pingOutput:
|
||||
return 1, 0
|
||||
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
|
||||
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' %
|
||||
@@ -700,7 +638,7 @@ class Mininet( object ):
|
||||
m = re.search( r, pingOutput )
|
||||
if m is not None:
|
||||
return errorTuple
|
||||
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
|
||||
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' %
|
||||
@@ -829,14 +767,8 @@ class Mininet( object ):
|
||||
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
|
||||
server.IP() + ' ' + bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
servout = ''
|
||||
# We want the last *b/sec from the iperf server output
|
||||
# for TCP, there are two of them because of waitListening
|
||||
count = 2 if l4Type == 'TCP' else 1
|
||||
while len( re.findall( '/sec', servout ) ) < count:
|
||||
servout += server.monitor( timeoutms=5000 )
|
||||
server.sendInt()
|
||||
servout += server.waitOutput()
|
||||
servout = server.waitOutput()
|
||||
debug( 'Server output: %s\n' % servout )
|
||||
result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
|
||||
if l4Type == 'UDP':
|
||||
@@ -850,6 +782,7 @@ class Mininet( object ):
|
||||
duration: test duration in seconds (integer)
|
||||
returns a single list of measured CPU fractions as floats.
|
||||
"""
|
||||
cores = int( quietRun( 'nproc' ) )
|
||||
pct = cpu * 100
|
||||
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
|
||||
hosts = self.hosts
|
||||
@@ -901,8 +834,10 @@ class Mininet( object ):
|
||||
elif dst not in self.nameToNode:
|
||||
error( 'dst not in network: %s\n' % dst )
|
||||
else:
|
||||
src = self.nameToNode[ src ]
|
||||
dst = self.nameToNode[ dst ]
|
||||
if isinstance( src, basestring ):
|
||||
src = self.nameToNode[ src ]
|
||||
if isinstance( dst, basestring ):
|
||||
dst = self.nameToNode[ dst ]
|
||||
connections = src.connectionsTo( dst )
|
||||
if len( connections ) == 0:
|
||||
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
|
||||
|
||||
+68
-146
@@ -57,22 +57,16 @@ import pty
|
||||
import re
|
||||
import signal
|
||||
import select
|
||||
from distutils.version import StrictVersion
|
||||
from re import findall
|
||||
from subprocess import Popen, PIPE
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
from time import sleep
|
||||
|
||||
from mininet.log import info, error, warn, debug
|
||||
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
|
||||
numCores, retry, mountCgroups, BaseString, decode,
|
||||
encode, getincrementaldecoder, Python3, which )
|
||||
numCores, retry, mountCgroups )
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, TUN
|
||||
from mininet.link import Link, Intf, TCIntf, OVSIntf
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
|
||||
from re import findall
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
class Node( object ):
|
||||
"""A virtual network node is simply a shell in a network namespace.
|
||||
@@ -93,19 +87,12 @@ class Node( object ):
|
||||
self.privateDirs = params.get( 'privateDirs', [] )
|
||||
self.inNamespace = params.get( 'inNamespace', inNamespace )
|
||||
|
||||
# Python 3 complains if we don't wait for shell exit
|
||||
self.waitExited = params.get( 'waitExited', Python3 )
|
||||
|
||||
# Stash configuration parameters for future reference
|
||||
self.params = params
|
||||
|
||||
# dict of port numbers to interfacse
|
||||
self.intfs = {}
|
||||
|
||||
# dict of interfaces to port numbers
|
||||
# todo: replace with Port objects, eventually ?
|
||||
self.ports = {}
|
||||
|
||||
self.intfs = {} # dict of port numbers to interfaces
|
||||
self.ports = {} # dict of interfaces to port numbers
|
||||
# replace with Port objects, eventually ?
|
||||
self.nameToIntf = {} # dict of interface names to Intfs
|
||||
|
||||
# Make pylint happy
|
||||
@@ -115,11 +102,7 @@ class Node( object ):
|
||||
self.waiting = False
|
||||
self.readbuf = ''
|
||||
|
||||
# Incremental decoder for buffered reading
|
||||
self.decoder = getincrementaldecoder()
|
||||
|
||||
# Start command interpreter shell
|
||||
self.master, self.slave = None, None # pylint
|
||||
self.startShell()
|
||||
self.mountPrivateDirs()
|
||||
|
||||
@@ -152,18 +135,14 @@ class Node( object ):
|
||||
# -s: pass $* to shell, and make process easy to find in ps
|
||||
# prompt is set to sentinel chr( 127 )
|
||||
cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
|
||||
'bash', '--norc', '--noediting',
|
||||
'-is', 'mininet:' + self.name ]
|
||||
|
||||
'bash', '--norc', '-is', 'mininet:' + self.name ]
|
||||
# Spawn a shell subprocess in a pseudo-tty, to disable buffering
|
||||
# in the subprocess and insulate it from signals (e.g. SIGINT)
|
||||
# received by the parent
|
||||
self.master, self.slave = pty.openpty()
|
||||
self.shell = self._popen( cmd, stdin=self.slave, stdout=self.slave,
|
||||
stderr=self.slave, close_fds=False )
|
||||
# XXX BL: This doesn't seem right, and we should also probably
|
||||
# close our files when we exit...
|
||||
self.stdin = os.fdopen( self.master, 'r' )
|
||||
master, slave = pty.openpty()
|
||||
self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
|
||||
close_fds=False )
|
||||
self.stdin = os.fdopen( master, 'rw' )
|
||||
self.stdout = self.stdin
|
||||
self.pid = self.shell.pid
|
||||
self.pollOut = select.poll()
|
||||
@@ -190,7 +169,7 @@ class Node( object ):
|
||||
def mountPrivateDirs( self ):
|
||||
"mount private directories"
|
||||
# Avoid expanding a string into a list of chars
|
||||
assert not isinstance( self.privateDirs, BaseString )
|
||||
assert not isinstance( self.privateDirs, basestring )
|
||||
for directory in self.privateDirs:
|
||||
if isinstance( directory, tuple ):
|
||||
# mount given private directory
|
||||
@@ -219,9 +198,7 @@ class Node( object ):
|
||||
params: parameters to Popen()"""
|
||||
# Leave this is as an instance method for now
|
||||
assert self
|
||||
popen = Popen( cmd, **params )
|
||||
debug( '_popen', cmd, popen.pid )
|
||||
return popen
|
||||
return Popen( cmd, **params )
|
||||
|
||||
def cleanup( self ):
|
||||
"Help python collect its garbage."
|
||||
@@ -230,34 +207,27 @@ class Node( object ):
|
||||
# for intfName in self.intfNames():
|
||||
# if self.name in intfName:
|
||||
# quietRun( 'ip link del ' + intfName )
|
||||
if self.shell:
|
||||
# Close ptys
|
||||
self.stdin.close()
|
||||
os.close(self.slave)
|
||||
if self.waitExited:
|
||||
debug( 'waiting for', self.pid, 'to terminate\n' )
|
||||
self.shell.wait()
|
||||
self.shell = None
|
||||
|
||||
# Subshell I/O, commands and control
|
||||
|
||||
def read( self, size=1024 ):
|
||||
"""Buffered read from node, potentially blocking.
|
||||
size: maximum number of characters to return"""
|
||||
def read( self, maxbytes=1024 ):
|
||||
"""Buffered read from node, non-blocking.
|
||||
maxbytes: maximum number of bytes to return"""
|
||||
count = len( self.readbuf )
|
||||
if count < size:
|
||||
data = os.read( self.stdout.fileno(), size - count )
|
||||
self.readbuf += self.decoder.decode( data )
|
||||
if size >= len( self.readbuf ):
|
||||
if count < maxbytes:
|
||||
data = os.read( self.stdout.fileno(), maxbytes - count )
|
||||
self.readbuf += data
|
||||
if maxbytes >= len( self.readbuf ):
|
||||
result = self.readbuf
|
||||
self.readbuf = ''
|
||||
else:
|
||||
result = self.readbuf[ :size ]
|
||||
self.readbuf = self.readbuf[ size: ]
|
||||
result = self.readbuf[ :maxbytes ]
|
||||
self.readbuf = self.readbuf[ maxbytes: ]
|
||||
return result
|
||||
|
||||
def readline( self ):
|
||||
"""Buffered readline from node, potentially blocking.
|
||||
"""Buffered readline from node, non-blocking.
|
||||
returns: line (minus newline) or None"""
|
||||
self.readbuf += self.read( 1024 )
|
||||
if '\n' not in self.readbuf:
|
||||
@@ -270,7 +240,7 @@ class Node( object ):
|
||||
def write( self, data ):
|
||||
"""Write data to node.
|
||||
data: string"""
|
||||
os.write( self.stdin.fileno(), encode( data ) )
|
||||
os.write( self.stdin.fileno(), data )
|
||||
|
||||
def terminate( self ):
|
||||
"Send kill signal to Node and clean up after it."
|
||||
@@ -289,11 +259,9 @@ class Node( object ):
|
||||
|
||||
def waitReadable( self, timeoutms=None ):
|
||||
"""Wait until node's output is readable.
|
||||
timeoutms: timeout in ms or None to wait indefinitely.
|
||||
returns: result of poll()"""
|
||||
timeoutms: timeout in ms or None to wait indefinitely."""
|
||||
if len( self.readbuf ) == 0:
|
||||
return self.pollOut.poll( timeoutms )
|
||||
return None
|
||||
self.pollOut.poll( timeoutms )
|
||||
|
||||
def sendCmd( self, *args, **kwargs ):
|
||||
"""Send a command, followed by a command to echo a sentinel,
|
||||
@@ -335,9 +303,7 @@ class Node( object ):
|
||||
Set self.waiting to False if command has completed.
|
||||
timeoutms: timeout in ms or None to wait indefinitely
|
||||
findPid: look for PID from mnexec -p"""
|
||||
ready = self.waitReadable( timeoutms )
|
||||
if not ready:
|
||||
return ''
|
||||
self.waitReadable( timeoutms )
|
||||
data = self.read( 1024 )
|
||||
pidre = r'\[\d+\] \d+\r\n'
|
||||
# Look for PID
|
||||
@@ -387,7 +353,6 @@ class Node( object ):
|
||||
return self.waitOutput( verbose )
|
||||
else:
|
||||
warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
|
||||
return None
|
||||
|
||||
def cmdPrint( self, *args):
|
||||
"""Call cmd and printing its output
|
||||
@@ -402,23 +367,23 @@ class Node( object ):
|
||||
'mncmd':
|
||||
[ 'mnexec', '-da', str( self.pid ) ] }
|
||||
defaults.update( kwargs )
|
||||
shell = defaults.pop( 'shell', False )
|
||||
if len( args ) == 1:
|
||||
if isinstance( args[ 0 ], list ):
|
||||
# popen([cmd, arg1, arg2...])
|
||||
cmd = args[ 0 ]
|
||||
elif isinstance( args[ 0 ], BaseString ):
|
||||
elif isinstance( args[ 0 ], basestring ):
|
||||
# popen("cmd arg1 arg2...")
|
||||
cmd = [ args[ 0 ] ] if shell else args[ 0 ].split()
|
||||
cmd = args[ 0 ].split()
|
||||
else:
|
||||
raise Exception( 'popen() requires a string or list' )
|
||||
elif len( args ) > 0:
|
||||
# popen( cmd, arg1, arg2... )
|
||||
cmd = list( args )
|
||||
if shell:
|
||||
cmd = [ os.environ[ 'SHELL' ], '-c' ] + [ ' '.join( cmd ) ]
|
||||
# Attach to our namespace using mnexec -a
|
||||
cmd = defaults.pop( 'mncmd' ) + cmd
|
||||
# Shell requires a string, not a list!
|
||||
if defaults.get( 'shell', False ):
|
||||
cmd = ' '.join( cmd )
|
||||
popen = self._popen( cmd, **defaults )
|
||||
return popen
|
||||
|
||||
@@ -430,7 +395,7 @@ class Node( object ):
|
||||
# Warning: this can fail with large numbers of fds!
|
||||
out, err = popen.communicate()
|
||||
exitcode = popen.wait()
|
||||
return decode( out ), decode( err ), exitcode
|
||||
return out, err, exitcode
|
||||
|
||||
# Interface management, configuration, and routing
|
||||
|
||||
@@ -463,15 +428,6 @@ class Node( object ):
|
||||
debug( 'moving', intf, 'into namespace for', self.name, '\n' )
|
||||
moveIntfFn( intf.name, self )
|
||||
|
||||
def delIntf( self, intf ):
|
||||
"""Remove interface from Node's known interfaces
|
||||
Note: to fully delete interface, call intf.delete() instead"""
|
||||
port = self.ports.get( intf )
|
||||
if port is not None:
|
||||
del self.intfs[ port ]
|
||||
del self.ports[ intf ]
|
||||
del self.nameToIntf[ intf.name ]
|
||||
|
||||
def defaultIntf( self ):
|
||||
"Return interface for lowest port"
|
||||
ports = self.intfs.keys()
|
||||
@@ -480,7 +436,6 @@ class Node( object ):
|
||||
else:
|
||||
warn( '*** defaultIntf: warning:', self.name,
|
||||
'has no interfaces\n' )
|
||||
return None
|
||||
|
||||
def intf( self, intf=None ):
|
||||
"""Return our interface object with given string name,
|
||||
@@ -493,7 +448,7 @@ class Node( object ):
|
||||
"""
|
||||
if not intf:
|
||||
return self.defaultIntf()
|
||||
elif isinstance( intf, BaseString):
|
||||
elif isinstance( intf, basestring):
|
||||
return self.nameToIntf[ intf ]
|
||||
else:
|
||||
return intf
|
||||
@@ -520,7 +475,7 @@ class Node( object ):
|
||||
# explicitly so that we won't get errors if we run before they
|
||||
# have been removed by the kernel. Unfortunately this is very slow,
|
||||
# at least with Linux kernels before 2.6.33
|
||||
for intf in list( self.intfs.values() ):
|
||||
for intf in self.intfs.values():
|
||||
# Protect against deleting hardware interfaces
|
||||
if ( self.name in intf.name ) or ( not checkName ):
|
||||
intf.delete()
|
||||
@@ -545,7 +500,7 @@ class Node( object ):
|
||||
"""Set the default route to go through intf.
|
||||
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
|
||||
# Note setParam won't call us if intf is none
|
||||
if isinstance( intf, BaseString ) and ' ' in intf:
|
||||
if isinstance( intf, basestring ) and ' ' in intf:
|
||||
params = intf
|
||||
else:
|
||||
params = 'dev %s' % intf
|
||||
@@ -592,12 +547,12 @@ class Node( object ):
|
||||
method: config method name
|
||||
param: arg=value (ignore if value=None)
|
||||
value may also be list or dict"""
|
||||
name, value = list( param.items() )[ 0 ]
|
||||
name, value = param.items()[ 0 ]
|
||||
if value is None:
|
||||
return None
|
||||
return
|
||||
f = getattr( self, method, None )
|
||||
if not f:
|
||||
return None
|
||||
return
|
||||
if isinstance( value, list ):
|
||||
result = f( *value )
|
||||
elif isinstance( value, dict ):
|
||||
@@ -641,7 +596,7 @@ class Node( object ):
|
||||
|
||||
def intfList( self ):
|
||||
"List of our interfaces sorted by port number"
|
||||
return [ self.intfs[ p ] for p in sorted( self.intfs.keys() ) ]
|
||||
return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
|
||||
|
||||
def intfNames( self ):
|
||||
"The names of our interfaces sorted by port number"
|
||||
@@ -665,12 +620,11 @@ class Node( object ):
|
||||
@classmethod
|
||||
def checkSetup( cls ):
|
||||
"Make sure our class and superclasses are set up"
|
||||
clas = cls
|
||||
while clas and not getattr( clas, 'isSetup', True ):
|
||||
clas.setup()
|
||||
clas.isSetup = True
|
||||
while cls and not getattr( cls, 'isSetup', True ):
|
||||
cls.setup()
|
||||
cls.isSetup = True
|
||||
# Make pylint happy
|
||||
clas = getattr( type( clas ), '__base__', None )
|
||||
cls = getattr( type( cls ), '__base__', None )
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
@@ -727,17 +681,15 @@ class CPULimitedHost( Host ):
|
||||
"Clean up our cgroup"
|
||||
# info( '*** deleting cgroup', self.cgroup, '\n' )
|
||||
_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
|
||||
# Sometimes cgdelete returns a resource busy error but still
|
||||
# deletes the group; next attempt will give "no such file"
|
||||
return exitcode == 0 or ( 'no such file' in _err.lower() )
|
||||
return exitcode == 0 # success condition
|
||||
|
||||
def popen( self, *args, **kwargs ):
|
||||
"""Return a Popen() object in node's namespace
|
||||
args: Popen() args, single list, or string
|
||||
kwargs: Popen() keyword args"""
|
||||
# Tell mnexec to execute command in our cgroup
|
||||
mncmd = kwargs.pop( 'mncmd', [ 'mnexec', '-g', self.name,
|
||||
'-da', str( self.pid ) ] )
|
||||
mncmd = [ 'mnexec', '-g', self.name,
|
||||
'-da', str( self.pid ) ]
|
||||
# if our cgroup is not given any cpu time,
|
||||
# we cannot assign the RR Scheduler.
|
||||
if self.sched == 'rt':
|
||||
@@ -751,7 +703,7 @@ class CPULimitedHost( Host ):
|
||||
def cleanup( self ):
|
||||
"Clean up Node, then clean up our cgroup"
|
||||
super( CPULimitedHost, self ).cleanup()
|
||||
retry( retries=3, delaySecs=.1, fn=self.cgroupDel )
|
||||
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
|
||||
|
||||
_rtGroupSched = False # internal class var: Is CONFIG_RT_GROUP_SCHED set?
|
||||
|
||||
@@ -851,7 +803,6 @@ class CPULimitedHost( Host ):
|
||||
errFail( 'cgclassify -g cpuset:/%s %s' % (
|
||||
self.name, self.pid ) )
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, cpu=-1, cores=None, **params ):
|
||||
"""cpu: desired overall system CPU fraction
|
||||
cores: (real) core(s) this host can run on
|
||||
@@ -914,7 +865,7 @@ class Switch( Node ):
|
||||
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
|
||||
if dpid:
|
||||
# Remove any colons and make sure it's a good hex number
|
||||
dpid = dpid.replace( ':', '' )
|
||||
dpid = dpid.translate( None, ':' )
|
||||
assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
|
||||
else:
|
||||
# Use hex of the first number in the switch name
|
||||
@@ -922,7 +873,6 @@ class Switch( Node ):
|
||||
if nums:
|
||||
dpid = hex( int( nums[ 0 ] ) )[ 2: ]
|
||||
else:
|
||||
self.terminate() # Python 3.6 crash workaround
|
||||
raise Exception( 'Unable to derive default datapath ID - '
|
||||
'please either specify a dpid or use a '
|
||||
'canonical switch name such as s23.' )
|
||||
@@ -944,7 +894,6 @@ class Switch( Node ):
|
||||
else:
|
||||
error( '*** Error: %s has execed and cannot accept commands' %
|
||||
self.name )
|
||||
return None
|
||||
|
||||
def connected( self ):
|
||||
"Is the switch connected to a controller? (override this method)"
|
||||
@@ -1074,7 +1023,7 @@ class OVSSwitch( Switch ):
|
||||
inband=False, protocols=None,
|
||||
reconnectms=1000, stp=False, batch=False, **params ):
|
||||
"""name: name for switch
|
||||
failMode: controller loss behavior (secure|standalone)
|
||||
failMode: controller loss behavior (secure|open)
|
||||
datapath: userspace or kernel mode (kernel|user)
|
||||
inband: use in-band control (False)
|
||||
protocols: use specific OpenFlow version(s) (e.g. OpenFlow13)
|
||||
@@ -1130,7 +1079,6 @@ class OVSSwitch( Switch ):
|
||||
if self.batch:
|
||||
cmd = ' '.join( str( arg ).strip() for arg in args )
|
||||
self.commands.append( cmd )
|
||||
return None
|
||||
else:
|
||||
return self.cmd( 'ovs-vsctl', *args, **kwargs )
|
||||
|
||||
@@ -1198,7 +1146,6 @@ class OVSSwitch( Switch ):
|
||||
opts += ' protocols=%s' % self.protocols
|
||||
if self.stp and self.failMode == 'standalone':
|
||||
opts += ' stp_enable=true'
|
||||
opts += ' other-config:dp-desc=%s' % self.name
|
||||
return opts
|
||||
|
||||
def start( self, controllers ):
|
||||
@@ -1268,7 +1215,7 @@ class OVSSwitch( Switch ):
|
||||
run( cmds, shell=True )
|
||||
# Reapply link config if necessary...
|
||||
for switch in switches:
|
||||
for intf in switch.intfs.values():
|
||||
for intf in switch.intfs.itervalues():
|
||||
if isinstance( intf, TCIntf ):
|
||||
intf.config( **intf.params )
|
||||
return switches
|
||||
@@ -1294,7 +1241,7 @@ class OVSSwitch( Switch ):
|
||||
pids = ' '.join( str( switch.pid ) for switch in switches )
|
||||
run( 'kill -HUP ' + pids )
|
||||
for switch in switches:
|
||||
switch.terminate()
|
||||
switch.shell = None
|
||||
return switches
|
||||
|
||||
|
||||
@@ -1318,7 +1265,7 @@ class OVSBridge( OVSSwitch ):
|
||||
"Are we forwarding yet?"
|
||||
if self.stp:
|
||||
status = self.dpctl( 'show' )
|
||||
return 'STP_FORWARD' in status and 'STP_LEARN' not in status
|
||||
return 'STP_FORWARD' in status and not 'STP_LEARN' in status
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1398,12 +1345,10 @@ class Controller( Node ):
|
||||
OpenFlow controller."""
|
||||
|
||||
def __init__( self, name, inNamespace=False, command='controller',
|
||||
cargs='ptcp:%d', cdir=None, ip="127.0.0.1",
|
||||
port=6653, protocol='tcp', verbose=False, **params ):
|
||||
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
|
||||
port=6633, protocol='tcp', **params ):
|
||||
self.command = command
|
||||
self.cargs = cargs
|
||||
if verbose:
|
||||
cargs = '-v ' + cargs
|
||||
self.cdir = cdir
|
||||
# Accept 'ip:port' syntax as shorthand
|
||||
if ':' in ip:
|
||||
@@ -1445,7 +1390,6 @@ class Controller( Node ):
|
||||
' 1>' + cout + ' 2>' + cout + ' &' )
|
||||
self.execed = False
|
||||
|
||||
# pylint: disable=arguments-differ,signature-differs
|
||||
def stop( self, *args, **kwargs ):
|
||||
"Stop controller."
|
||||
self.cmd( 'kill %' + self.command )
|
||||
@@ -1469,21 +1413,20 @@ class Controller( Node ):
|
||||
@classmethod
|
||||
def isAvailable( cls ):
|
||||
"Is controller available?"
|
||||
return which( 'controller' )
|
||||
return quietRun( 'which controller' )
|
||||
|
||||
|
||||
class OVSController( Controller ):
|
||||
"Open vSwitch controller"
|
||||
def __init__( self, name, **kwargs ):
|
||||
kwargs.setdefault( 'command', self.isAvailable() or
|
||||
'ovs-controller' )
|
||||
Controller.__init__( self, name, **kwargs )
|
||||
def __init__( self, name, command='ovs-controller', **kwargs ):
|
||||
if quietRun( 'which test-controller' ):
|
||||
command = 'test-controller'
|
||||
Controller.__init__( self, name, command=command, **kwargs )
|
||||
|
||||
@classmethod
|
||||
def isAvailable( cls ):
|
||||
return (which( 'ovs-controller' ) or
|
||||
which( 'test-controller' ) or
|
||||
which( 'ovs-testcontroller' ))
|
||||
return ( quietRun( 'which ovs-controller' ) or
|
||||
quietRun( 'which test-controller' ) )
|
||||
|
||||
class NOX( Controller ):
|
||||
"Controller to run a NOX application."
|
||||
@@ -1496,7 +1439,7 @@ class NOX( Controller ):
|
||||
warn( 'warning: no NOX modules specified; '
|
||||
'running packetdump only\n' )
|
||||
noxArgs = [ 'packetdump' ]
|
||||
elif not isinstance( noxArgs, ( list, tuple ) ):
|
||||
elif type( noxArgs ) not in ( list, tuple ):
|
||||
noxArgs = [ noxArgs ]
|
||||
|
||||
if 'NOX_CORE_DIR' not in os.environ:
|
||||
@@ -1522,7 +1465,7 @@ class Ryu( Controller ):
|
||||
warn( 'warning: no Ryu modules specified; '
|
||||
'running simple_switch only\n' )
|
||||
ryuArgs = [ ryuCoreDir + 'simple_switch.py' ]
|
||||
elif not isinstance( ryuArgs, ( list, tuple ) ):
|
||||
elif type( ryuArgs ) not in ( list, tuple ):
|
||||
ryuArgs = [ ryuArgs ]
|
||||
|
||||
Controller.__init__( self, name,
|
||||
@@ -1537,7 +1480,7 @@ class RemoteController( Controller ):
|
||||
"Controller running outside of Mininet's control."
|
||||
|
||||
def __init__( self, name, ip='127.0.0.1',
|
||||
port=None, **kwargs):
|
||||
port=6633, **kwargs):
|
||||
"""Init.
|
||||
name: name to give controller
|
||||
ip: the IP address where the remote controller is
|
||||
@@ -1549,37 +1492,17 @@ class RemoteController( Controller ):
|
||||
"Overridden to do nothing."
|
||||
return
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def stop( self ):
|
||||
"Overridden to do nothing."
|
||||
return
|
||||
|
||||
def checkListening( self ):
|
||||
"Warn if remote controller is not accessible"
|
||||
if self.port is not None:
|
||||
self.isListening( self.ip, self.port )
|
||||
else:
|
||||
for port in 6653, 6633:
|
||||
if self.isListening( self.ip, port ):
|
||||
self.port = port
|
||||
info( "Connecting to remote controller"
|
||||
" at %s:%d\n" % ( self.ip, self.port ))
|
||||
break
|
||||
|
||||
if self.port is None:
|
||||
self.port = 6653
|
||||
warn( "Setting remote controller"
|
||||
" to %s:%d\n" % ( self.ip, self.port ))
|
||||
|
||||
def isListening( self, ip, port ):
|
||||
"Check if a remote controller is listening at a specific ip and port"
|
||||
listening = self.cmd( "echo A | telnet -e A %s %d" % ( ip, port ) )
|
||||
listening = self.cmd( "echo A | telnet -e A %s %d" %
|
||||
( self.ip, self.port ) )
|
||||
if 'Connected' not in listening:
|
||||
warn( "Unable to contact the remote controller"
|
||||
" at %s:%d\n" % ( ip, port ) )
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
" at %s:%d\n" % ( self.ip, self.port ) )
|
||||
|
||||
|
||||
DefaultControllers = ( Controller, OVSController )
|
||||
@@ -1589,7 +1512,6 @@ def findController( controllers=DefaultControllers ):
|
||||
for controller in controllers:
|
||||
if controller.isAvailable():
|
||||
return controller
|
||||
return None
|
||||
|
||||
def DefaultController( name, controllers=DefaultControllers, **kwargs ):
|
||||
"Find a controller that is available and instantiate it"
|
||||
|
||||
+38
-34
@@ -9,6 +9,7 @@ from mininet.log import info, warn
|
||||
from mininet.moduledeps import pathCheck
|
||||
from mininet.util import quietRun
|
||||
|
||||
import re
|
||||
|
||||
class LinuxBridge( Switch ):
|
||||
"Linux Bridge (with optional spanning tree)"
|
||||
@@ -72,50 +73,29 @@ class LinuxBridge( Switch ):
|
||||
class NAT( Node ):
|
||||
"NAT: Provides connectivity to external network"
|
||||
|
||||
def __init__( self, name, subnet='10.0/8',
|
||||
def __init__( self, name, inetIntf=None, subnet='10.0/8',
|
||||
localIntf=None, flush=False, **params):
|
||||
"""Start NAT/forwarding between Mininet and external network
|
||||
inetIntf: interface for internet access
|
||||
subnet: Mininet subnet (default 10.0/8)
|
||||
flush: flush iptables before installing NAT rules"""
|
||||
super( NAT, self ).__init__( name, **params )
|
||||
|
||||
self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
|
||||
self.subnet = subnet
|
||||
self.localIntf = localIntf
|
||||
self.flush = flush
|
||||
self.forwardState = self.cmd( 'sysctl -n net.ipv4.ip_forward' ).strip()
|
||||
|
||||
def setManualConfig( self, intf ):
|
||||
"""Prevent network-manager/networkd from messing with our interface
|
||||
by specifying manual configuration in /etc/network/interfaces"""
|
||||
cfile = '/etc/network/interfaces'
|
||||
line = '\niface %s inet manual\n' % intf
|
||||
try:
|
||||
with open( cfile ) as f:
|
||||
config = f.read()
|
||||
except IOError:
|
||||
config = ''
|
||||
if ( line ) not in config:
|
||||
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
|
||||
with open( cfile, 'a' ) as f:
|
||||
f.write( line )
|
||||
# Probably need to restart network manager to be safe -
|
||||
# hopefully this won't disconnect you
|
||||
self.cmd( 'service network-manager restart || netplan apply' )
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, **params ):
|
||||
"""Configure the NAT and iptables"""
|
||||
super( NAT, self).config( **params )
|
||||
|
||||
if not self.localIntf:
|
||||
self.localIntf = self.defaultIntf()
|
||||
|
||||
self.setManualConfig( self.localIntf )
|
||||
|
||||
# Now we can configure manually without interference
|
||||
super( NAT, self).config( **params )
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
if self.flush:
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
self.cmd( 'iptables -F' )
|
||||
self.cmd( 'iptables -t nat -F' )
|
||||
# Create default entries for unmatched traffic
|
||||
@@ -129,14 +109,39 @@ class NAT( Node ):
|
||||
self.cmd( 'iptables -A FORWARD',
|
||||
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -A FORWARD',
|
||||
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
|
||||
'-i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -t nat -A POSTROUTING',
|
||||
'-s', self.subnet, "'!'", '-d', self.subnet,
|
||||
'-j MASQUERADE' )
|
||||
'-o', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
|
||||
|
||||
# Instruct the kernel to perform forwarding
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
||||
|
||||
# Prevent network-manager from messing with our interface
|
||||
# by specifying manual configuration in /etc/network/interfaces
|
||||
intf = self.localIntf
|
||||
cfile = '/etc/network/interfaces'
|
||||
line = '\niface %s inet manual\n' % intf
|
||||
config = open( cfile ).read()
|
||||
if ( line ) not in config:
|
||||
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
|
||||
with open( cfile, 'a' ) as f:
|
||||
f.write( line )
|
||||
# Probably need to restart network-manager to be safe -
|
||||
# hopefully this won't disconnect you
|
||||
self.cmd( 'service network-manager restart' )
|
||||
|
||||
def getGatewayIntf( self, fallback='eth0' ):
|
||||
"""Return gateway interface name
|
||||
fallback: default device to fall back to"""
|
||||
routes = self.cmd( 'ip route show' )
|
||||
match = re.search( r'default via \S+ dev (\S+)', routes )
|
||||
if match:
|
||||
return match.group( 1 )
|
||||
else:
|
||||
warn( 'There is no default route set.',
|
||||
'Using', fallback, 'as gateway interface...\n' )
|
||||
return fallback
|
||||
|
||||
def terminate( self ):
|
||||
"Stop NAT/forwarding between Mininet and external network"
|
||||
# Remote NAT rules
|
||||
@@ -145,10 +150,9 @@ class NAT( Node ):
|
||||
self.cmd( 'iptables -D FORWARD',
|
||||
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -D FORWARD',
|
||||
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
|
||||
'-i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -t nat -D POSTROUTING',
|
||||
'-s', self.subnet, '\'!\'', '-d', self.subnet,
|
||||
'-j MASQUERADE' )
|
||||
# Put the forwarding state back to what it was
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
|
||||
'-o', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
|
||||
# Instruct the kernel to stop forwarding
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
super( NAT, self ).terminate()
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
|
||||
}
|
||||
if term not in cmds:
|
||||
error( 'invalid terminal type: %s' % term )
|
||||
return None
|
||||
return
|
||||
display, tunnel = tunnelX11( node, display )
|
||||
if display is None:
|
||||
return []
|
||||
|
||||
@@ -21,10 +21,7 @@ def runTests( testDir, verbosity=1 ):
|
||||
# discover all tests in testDir
|
||||
testSuite = defaultTestLoader.discover( testDir )
|
||||
# run tests
|
||||
success = ( TextTestRunner( verbosity=verbosity )
|
||||
.run( testSuite ).wasSuccessful() )
|
||||
sys.exit( 0 if success else 1 )
|
||||
|
||||
TextTestRunner( verbosity=verbosity ).run( testSuite )
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
|
||||
@@ -45,7 +45,7 @@ class testOptionsTopoCommon( object ):
|
||||
@staticmethod
|
||||
def tearDown():
|
||||
"Clean up if necessary"
|
||||
if sys.exc_info() != ( None, None, None ):
|
||||
if sys.exc_info != ( None, None, None ):
|
||||
cleanup()
|
||||
|
||||
def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
|
||||
@@ -95,7 +95,7 @@ class testOptionsTopoCommon( object ):
|
||||
CPU_FRACTION = 0.1
|
||||
CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
|
||||
hopts = { 'cpu': CPU_FRACTION }
|
||||
# self.runOptionsTopoTest( N, hopts=hopts )
|
||||
#self.runOptionsTopoTest( N, hopts=hopts )
|
||||
|
||||
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
|
||||
host=CPULimitedHost, switch=self.switchClass,
|
||||
@@ -118,7 +118,7 @@ class testOptionsTopoCommon( object ):
|
||||
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
|
||||
self.switchClass ) )
|
||||
for pct in results:
|
||||
# divide cpu by 100 to convert from percentage to fraction
|
||||
#divide cpu by 100 to convert from percentage to fraction
|
||||
self.assertWithinTolerance( pct/100, CPU_FRACTION,
|
||||
CPU_TOLERANCE, msg )
|
||||
|
||||
@@ -263,7 +263,6 @@ class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
|
||||
longMessage = True
|
||||
switchClass = UserSwitch
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
|
||||
@@ -26,7 +26,7 @@ class testSingleSwitchCommon( object ):
|
||||
@staticmethod
|
||||
def tearDown():
|
||||
"Clean up if necessary"
|
||||
if sys.exc_info() != ( None, None, None ):
|
||||
if sys.exc_info != ( None, None, None ):
|
||||
cleanup()
|
||||
|
||||
def testMinimal( self ):
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Regression test for pty leak in Node()
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.clean import cleanup
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
|
||||
class TestPtyLeak( unittest.TestCase ):
|
||||
"Verify that there is no pty leakage"
|
||||
|
||||
@staticmethod
|
||||
def testPtyLeak():
|
||||
"Test for pty leakage"
|
||||
net = Mininet( SingleSwitchTopo() )
|
||||
net.start()
|
||||
host = net[ 'h1' ]
|
||||
for _ in range( 0, 10 ):
|
||||
oldptys = host.slave, host.master
|
||||
net.delHost( host )
|
||||
host = net.addHost( 'h1' )
|
||||
assert ( host.slave, host.master ) == oldptys
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
cleanup()
|
||||
@@ -24,16 +24,16 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
|
||||
"Clean up if necessary"
|
||||
# satisfy pylint
|
||||
assert self
|
||||
if sys.exc_info() != ( None, None, None ):
|
||||
if sys.exc_info != ( None, None, None ):
|
||||
cleanup()
|
||||
|
||||
def testDefaultDpid( self ):
|
||||
"""Verify that the default dpid is assigned using a valid provided
|
||||
canonical switchname if no dpid is passed in switch creation."""
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
switch = net.addSwitch( 's1' )
|
||||
switch = Mininet( Topo(),
|
||||
self.switchClass,
|
||||
Host, Controller ).addSwitch( 's1' )
|
||||
self.assertEqual( switch.defaultDpid(), switch.dpid )
|
||||
net.stop()
|
||||
|
||||
def dpidFrom( self, num ):
|
||||
"Compute default dpid from number"
|
||||
@@ -44,34 +44,31 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
|
||||
"""Verify that Switch dpid is the actual dpid assigned if dpid is
|
||||
passed in switch creation."""
|
||||
dpid = self.dpidFrom( 0xABCD )
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
switch = net.addSwitch( 's1', dpid=dpid )
|
||||
switch = Mininet( Topo(), self.switchClass,
|
||||
Host, Controller ).addSwitch(
|
||||
's1', dpid=dpid )
|
||||
self.assertEqual( switch.dpid, dpid )
|
||||
net.stop()
|
||||
|
||||
def testDefaultDpidAssignmentFailure( self ):
|
||||
"""Verify that Default dpid assignment raises an Exception if the
|
||||
name of the switch does not contin a digit. Also verify the
|
||||
exception message."""
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
with self.assertRaises( Exception ) as raises_cm:
|
||||
net.addSwitch( 'A' )
|
||||
self.assertTrue( 'Unable to derive '
|
||||
Mininet( Topo(), self.switchClass,
|
||||
Host, Controller ).addSwitch( 'A' )
|
||||
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
|
||||
'default datapath ID - please either specify a dpid '
|
||||
'or use a canonical switch name such as s23.'
|
||||
in str( raises_cm.exception ) )
|
||||
net.stop()
|
||||
'or use a canonical switch name such as s23.')
|
||||
|
||||
def testDefaultDpidLen( self ):
|
||||
"""Verify that Default dpid length is 16 characters consisting of
|
||||
16 - len(hex of first string of contiguous digits passed in switch
|
||||
name) 0's followed by hex of first string of contiguous digits passed
|
||||
in switch name."""
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
switch = net.addSwitch( 's123' )
|
||||
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
|
||||
net.stop()
|
||||
switch = Mininet( Topo(), self.switchClass,
|
||||
Host, Controller ).addSwitch( 's123' )
|
||||
|
||||
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
|
||||
|
||||
class OVSUser( OVSSwitch):
|
||||
"OVS User Switch convenience class"
|
||||
@@ -95,8 +92,6 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
|
||||
"Test dpid assignment of Userspace switch."
|
||||
switchClass = UserSwitch
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
cleanup()
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Package: mininet
|
||||
Test functions defined in mininet.util."""
|
||||
|
||||
import unittest
|
||||
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testQuietRun( unittest.TestCase ):
|
||||
"""Test quietRun that runs a command and returns its merged output from
|
||||
STDOUT and STDIN"""
|
||||
|
||||
@staticmethod
|
||||
def getEchoCmd( n ):
|
||||
"Return a command that will print n characters"
|
||||
return "echo -n " + "x" * n
|
||||
|
||||
def testEmpty( self ):
|
||||
"Run a command that prints nothing"
|
||||
output = quietRun(testQuietRun.getEchoCmd( 0 ) )
|
||||
self.assertEqual( 0, len( output ) )
|
||||
|
||||
def testOneRead( self ):
|
||||
"""Run a command whose output is entirely read on the first call if
|
||||
each call reads at most 1024 characters
|
||||
"""
|
||||
for n in [ 42, 1024 ]:
|
||||
output = quietRun( testQuietRun.getEchoCmd( n ) )
|
||||
self.assertEqual( n, len( output ) )
|
||||
|
||||
def testMultipleReads( self ):
|
||||
"Run a command whose output is not entirely read on the first read"
|
||||
for n in [ 1025, 4242 ]:
|
||||
output = quietRun(testQuietRun.getEchoCmd( n ) )
|
||||
self.assertEqual( n, len( output ) )
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -6,22 +6,18 @@ Tests for the Mininet Walkthrough
|
||||
TODO: missing xterm test
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from mininet.util import quietRun
|
||||
from distutils.version import StrictVersion
|
||||
from sys import stdout
|
||||
|
||||
from mininet.util import quietRun, pexpect
|
||||
from mininet.clean import cleanup
|
||||
|
||||
|
||||
def tsharkVersion():
|
||||
"Return tshark version"
|
||||
versionStr = quietRun( 'tshark -v' )
|
||||
versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
|
||||
return versionMatch[ 0 ]
|
||||
versionMatch = re.findall( r'TShark \d+.\d+.\d+', versionStr )[0]
|
||||
return versionMatch.split()[ 1 ]
|
||||
|
||||
# pylint doesn't understand pexpect.match, unfortunately!
|
||||
# pylint:disable=maybe-no-member
|
||||
@@ -31,11 +27,6 @@ class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
prompt = 'mininet>'
|
||||
|
||||
@staticmethod
|
||||
def setup():
|
||||
"Be paranoid and run cleanup() before each test"
|
||||
cleanup()
|
||||
|
||||
# PART 1
|
||||
def testHelp( self ):
|
||||
"Check the usage message"
|
||||
@@ -51,18 +42,15 @@ class testWalkthrough( unittest.TestCase ):
|
||||
tshark = pexpect.spawn( 'tshark -i lo -R of' )
|
||||
else:
|
||||
tshark = pexpect.spawn( 'tshark -i lo -Y openflow_v1' )
|
||||
tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback" ] )
|
||||
tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback'" ] )
|
||||
mn = pexpect.spawn( 'mn --test pingall' )
|
||||
mn.expect( '0% dropped' )
|
||||
tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
|
||||
tshark.sendintr()
|
||||
mn.expect( pexpect.EOF )
|
||||
tshark.expect( 'aptured' ) # 'xx packets captured'
|
||||
tshark.expect( pexpect.EOF )
|
||||
|
||||
def testBasic( self ):
|
||||
"Test basic CLI commands (help, nodes, net, dump)"
|
||||
p = pexpect.spawn( 'mn -w' )
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# help command
|
||||
p.sendline( 'help' )
|
||||
@@ -76,7 +64,7 @@ class testWalkthrough( unittest.TestCase ):
|
||||
p.expect( self.prompt )
|
||||
# net command
|
||||
p.sendline( 'net' )
|
||||
expected = list( nodes )
|
||||
expected = [ x for x in nodes ]
|
||||
while len( expected ) > 0:
|
||||
index = p.expect( expected )
|
||||
node = p.match.group( 0 )
|
||||
@@ -101,18 +89,15 @@ class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
def testHostCommands( self ):
|
||||
"Test ifconfig and ps on h1 and s1"
|
||||
p = pexpect.spawn( 'mn -w' )
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# Third pattern is a local interface beginning with 'eth' or 'en'
|
||||
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
|
||||
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
|
||||
self.prompt ]
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
# h1 ifconfig
|
||||
p.sendline( 'h1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index in (0, 3):
|
||||
if index == 0 or index == 3:
|
||||
ifcount += 1
|
||||
elif index == 1:
|
||||
self.fail( 's1 interface displayed in "h1 ifconfig"' )
|
||||
@@ -128,11 +113,11 @@ class testWalkthrough( unittest.TestCase ):
|
||||
index = p.expect( interfaces )
|
||||
if index == 0:
|
||||
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
||||
elif index in (1, 2, 3):
|
||||
elif index == 1 or index == 2 or index == 3:
|
||||
ifcount += 1
|
||||
else:
|
||||
break
|
||||
self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
|
||||
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
|
||||
# h1 ps
|
||||
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
|
||||
p.expect( self.prompt )
|
||||
@@ -141,19 +126,16 @@ class testWalkthrough( unittest.TestCase ):
|
||||
p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
|
||||
p.expect( self.prompt )
|
||||
s1Output = p.before
|
||||
# strip command from ps output and compute diffs
|
||||
h1Output = h1Output.split( '\n' )[ 1: ]
|
||||
s1Output = s1Output.split( '\n' )[ 1: ]
|
||||
diffs = set( h1Output ).difference( set( s1Output ) )
|
||||
# allow up to two diffs to account for daemons, etc.
|
||||
self.assertTrue( len( diffs ) <= 2,
|
||||
'h1 and s1 "ps" output differ too much: %s' % diffs )
|
||||
# strip command from ps output
|
||||
h1Output = h1Output.split( '\n', 1 )[ 1 ]
|
||||
s1Output = s1Output.split( '\n', 1 )[ 1 ]
|
||||
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
|
||||
def testConnectivity( self ):
|
||||
"Test ping and pingall"
|
||||
p = pexpect.spawn( 'mn -w' )
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 ping -c 1 h2' )
|
||||
p.expect( '1 packets transmitted, 1 received' )
|
||||
@@ -166,20 +148,9 @@ class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
def testSimpleHTTP( self ):
|
||||
"Start an HTTP server on h1 and wget from h2"
|
||||
if 'Python 2' in quietRun( 'python --version' ):
|
||||
httpserver = 'SimpleHTTPServer'
|
||||
else:
|
||||
httpserver = 'http.server'
|
||||
p = pexpect.spawn( 'mn -w', logfile=stdout )
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 python -m %s 80 >& /dev/null &' % httpserver )
|
||||
p.expect( self.prompt )
|
||||
# The walkthrough doesn't specify a delay here, and
|
||||
# we also don't read the output (also a possible problem),
|
||||
# but for now let's wait a number of seconds to make
|
||||
# it less likely to fail due to the race condition.
|
||||
p.sendline( 'px from mininet.util import waitListening;'
|
||||
'waitListening(h1, port=80, timeout=30)' )
|
||||
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( ' h2 wget -O - h1' )
|
||||
p.expect( '200 OK' )
|
||||
@@ -224,24 +195,22 @@ class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
def testLinkChange( self ):
|
||||
"Test TCLink bw and delay"
|
||||
p = pexpect.spawn( 'mn -w --link tc,bw=10,delay=10ms' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 route && ping -c1 h2' )
|
||||
p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
|
||||
# test bw
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'iperf' )
|
||||
p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
|
||||
bw = float( p.match.group( 1 ) )
|
||||
self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
|
||||
self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
|
||||
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
|
||||
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
|
||||
p.expect( self.prompt )
|
||||
# test delay
|
||||
p.sendline( 'h1 ping -c 4 h2' )
|
||||
p.expect( r'rtt min/avg/max/mdev = '
|
||||
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
|
||||
delay = float( p.match.group( 2 ) )
|
||||
self.assertTrue( delay >= 40, 'Delay < 40ms' )
|
||||
self.assertTrue( delay <= 50, 'Delay > 50ms' )
|
||||
self.assertTrue( delay > 40, 'Delay < 40ms' )
|
||||
self.assertTrue( delay < 45, 'Delay > 40ms' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
@@ -278,10 +247,8 @@ class testWalkthrough( unittest.TestCase ):
|
||||
p.expect( self.prompt )
|
||||
for i in range( 1, 3 ):
|
||||
p.sendline( 'h%d ifconfig' % i )
|
||||
p.expect( r'\s00:00:00:00:00:0%d\s' % i )
|
||||
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.expect( pexpect.EOF )
|
||||
|
||||
def testSwitches( self ):
|
||||
"Run iperf test using user and ovsk switches"
|
||||
@@ -304,14 +271,12 @@ class testWalkthrough( unittest.TestCase ):
|
||||
"Test running user switch in its own namespace"
|
||||
p = pexpect.spawn( 'mn --innamespace --switch user' )
|
||||
p.expect( self.prompt )
|
||||
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
|
||||
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
|
||||
self.prompt ]
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
p.sendline( 's1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
index = p.expect( interfaces )
|
||||
if index in (1, 3):
|
||||
if index == 1 or index == 3:
|
||||
ifcount += 1
|
||||
elif index == 0:
|
||||
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
||||
@@ -332,7 +297,7 @@ class testWalkthrough( unittest.TestCase ):
|
||||
# PART 3
|
||||
def testPythonInterpreter( self ):
|
||||
"Test py and px by checking IP for h1 and adding h3"
|
||||
p = pexpect.spawn( 'mn -w' )
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# test host IP
|
||||
p.sendline( 'py h1.IP()' )
|
||||
@@ -354,7 +319,7 @@ class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
def testLink( self ):
|
||||
"Test link CLI command using ping"
|
||||
p = pexpect.spawn( 'mn -w' )
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'link s1 h1 down' )
|
||||
p.expect( self.prompt )
|
||||
|
||||
+7
-9
@@ -13,9 +13,6 @@ setup for testing, and can even be emulated with the Mininet package.
|
||||
|
||||
from mininet.util import irange, natural, naturalSeq
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
|
||||
|
||||
class MultiGraph( object ):
|
||||
"Utility class to track nodes and edges - replaces networkx.MultiGraph"
|
||||
|
||||
@@ -37,7 +34,7 @@ class MultiGraph( object ):
|
||||
key: optional key
|
||||
attr_dict: optional attribute dict
|
||||
attrs: more attributes
|
||||
warning: updates attr_dict with attrs"""
|
||||
warning: udpates attr_dict with attrs"""
|
||||
attr_dict = {} if attr_dict is None else attr_dict
|
||||
attr_dict.update( attrs )
|
||||
self.node.setdefault( src, {} )
|
||||
@@ -59,13 +56,13 @@ class MultiGraph( object ):
|
||||
return self.node.items() if data else self.node.keys()
|
||||
|
||||
def edges_iter( self, data=False, keys=False ):
|
||||
"Iterator: return graph edges, optionally with data and keys"
|
||||
for src, entry in self.edge.items():
|
||||
for dst, entrykeys in entry.items():
|
||||
"Iterator: return graph edges"
|
||||
for src, entry in self.edge.iteritems():
|
||||
for dst, keys in entry.iteritems():
|
||||
if src > dst:
|
||||
# Skip duplicate edges
|
||||
continue
|
||||
for k, attrs in entrykeys.items():
|
||||
for k, attrs in keys.iteritems():
|
||||
if data:
|
||||
if keys:
|
||||
yield( src, dst, k, attrs )
|
||||
@@ -159,7 +156,8 @@ class Topo( object ):
|
||||
port1, port2 = self.addPort( node1, node2, port1, port2 )
|
||||
opts = dict( opts )
|
||||
opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
|
||||
return self.g.add_edge(node1, node2, key, opts )
|
||||
self.g.add_edge(node1, node2, key, opts )
|
||||
return key
|
||||
|
||||
def nodes( self, sort=True ):
|
||||
"Return nodes in graph"
|
||||
|
||||
+1
-2
@@ -67,8 +67,7 @@ class TorusTopo( Topo ):
|
||||
switch = switches[ i, j ] = self.addSwitch(
|
||||
's' + loc, dpid='%x' % dpid )
|
||||
for k in range( 0, n ):
|
||||
host = hosts[ i, j, k ] = self.addHost(
|
||||
genHostName( loc, k + 1 ) )
|
||||
host = hosts[ i, j, k ] = self.addHost( genHostName( loc, k + 1 ) )
|
||||
self.addLink( host, switch )
|
||||
# Connect switches
|
||||
for i in range( 0, x ):
|
||||
|
||||
+49
-129
@@ -1,79 +1,16 @@
|
||||
"Utility functions for Mininet."
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from functools import partial
|
||||
from os import O_NONBLOCK
|
||||
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
from select import poll, POLLIN, POLLHUP
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
from time import sleep
|
||||
|
||||
from mininet.log import output, info, error, warn, debug
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
|
||||
|
||||
# Python 2/3 compatibility
|
||||
|
||||
Python3 = sys.version_info[0] == 3
|
||||
BaseString = str if Python3 else getattr( str, '__base__' )
|
||||
Encoding = 'utf-8' if Python3 else None
|
||||
class NullCodec( object ):
|
||||
"Null codec for Python 2"
|
||||
@staticmethod
|
||||
def decode( buf ):
|
||||
"Null decode"
|
||||
return buf
|
||||
|
||||
@staticmethod
|
||||
def encode( buf ):
|
||||
"Null encode"
|
||||
return buf
|
||||
|
||||
|
||||
if Python3:
|
||||
def decode( buf ):
|
||||
"Decode buffer for Python 3"
|
||||
return buf.decode( Encoding )
|
||||
|
||||
def encode( buf ):
|
||||
"Encode buffer for Python 3"
|
||||
return buf.encode( Encoding )
|
||||
getincrementaldecoder = codecs.getincrementaldecoder( Encoding )
|
||||
else:
|
||||
decode, encode = NullCodec.decode, NullCodec.encode
|
||||
|
||||
def getincrementaldecoder():
|
||||
"Return null codec for Python 2"
|
||||
return NullCodec
|
||||
|
||||
try:
|
||||
# pylint: disable=import-error
|
||||
oldpexpect = None
|
||||
import pexpect as oldpexpect
|
||||
|
||||
# pylint: enable=import-error
|
||||
class Pexpect( object ):
|
||||
"Custom pexpect that is compatible with str"
|
||||
@staticmethod
|
||||
def spawn( *args, **kwargs):
|
||||
"pexpect.spawn that is compatible with str"
|
||||
if Python3 and 'encoding' not in kwargs:
|
||||
kwargs.update( encoding='utf-8' )
|
||||
return oldpexpect.spawn( *args, **kwargs )
|
||||
|
||||
def __getattr__( self, name ):
|
||||
return getattr( oldpexpect, name )
|
||||
pexpect = Pexpect()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from time import sleep
|
||||
from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
from select import poll, POLLIN, POLLHUP
|
||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
import re
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
# Command execution support
|
||||
|
||||
@@ -95,7 +32,7 @@ def oldQuietRun( *cmd ):
|
||||
cmd: list of command params"""
|
||||
if len( cmd ) == 1:
|
||||
cmd = cmd[ 0 ]
|
||||
if isinstance( cmd, BaseString ):
|
||||
if isinstance( cmd, str ):
|
||||
cmd = cmd.split( ' ' )
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
|
||||
# We can't use Popen.communicate() because it uses
|
||||
@@ -119,7 +56,7 @@ def oldQuietRun( *cmd ):
|
||||
# This is a bit complicated, but it enables us to
|
||||
# monitor command output as it is happening
|
||||
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
def errRun( *cmd, **kwargs ):
|
||||
"""Run a command and return stdout, stderr and return code
|
||||
cmd: string or list of command and args
|
||||
@@ -136,7 +73,7 @@ def errRun( *cmd, **kwargs ):
|
||||
if len( cmd ) == 1:
|
||||
cmd = cmd[ 0 ]
|
||||
# Allow passing in a list or a string
|
||||
if isinstance( cmd, BaseString ) and not shell:
|
||||
if isinstance( cmd, str ) and not shell:
|
||||
cmd = cmd.split( ' ' )
|
||||
cmd = [ str( arg ) for arg in cmd ]
|
||||
elif isinstance( cmd, list ) and shell:
|
||||
@@ -148,21 +85,18 @@ def errRun( *cmd, **kwargs ):
|
||||
out, err = '', ''
|
||||
poller = poll()
|
||||
poller.register( popen.stdout, POLLIN )
|
||||
fdToFile = { popen.stdout.fileno(): popen.stdout }
|
||||
fdToDecoder = { popen.stdout.fileno(): getincrementaldecoder() }
|
||||
fdtofile = { popen.stdout.fileno(): popen.stdout }
|
||||
outDone, errDone = False, True
|
||||
if popen.stderr:
|
||||
fdToFile[ popen.stderr.fileno() ] = popen.stderr
|
||||
fdToDecoder[ popen.stderr.fileno() ] = getincrementaldecoder()
|
||||
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 ]
|
||||
decoder = fdToDecoder[ fd ]
|
||||
if event & ( POLLIN | POLLHUP ):
|
||||
data = decoder.decode( f.read( 1024 ) )
|
||||
f = fdtofile[ fd ]
|
||||
if event & POLLIN:
|
||||
data = f.read( 1024 )
|
||||
if echo:
|
||||
output( data )
|
||||
if f == popen.stdout:
|
||||
@@ -173,7 +107,7 @@ def errRun( *cmd, **kwargs ):
|
||||
err += data
|
||||
if data == '':
|
||||
errDone = True
|
||||
else: # something unexpected
|
||||
else: # POLLHUP or something unexpected
|
||||
if f == popen.stdout:
|
||||
outDone = True
|
||||
elif f == popen.stderr:
|
||||
@@ -181,10 +115,6 @@ def errRun( *cmd, **kwargs ):
|
||||
poller.unregister( fd )
|
||||
|
||||
returncode = popen.wait()
|
||||
# Python 3 complains if we don't explicitly close these
|
||||
popen.stdout.close()
|
||||
if stderr == PIPE:
|
||||
popen.stderr.close()
|
||||
debug( out, err, returncode )
|
||||
return out, err, returncode
|
||||
# pylint: enable=too-many-branches
|
||||
@@ -201,26 +131,19 @@ def quietRun( cmd, **kwargs ):
|
||||
"Run a command and return merged stdout and stderr"
|
||||
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
|
||||
|
||||
def which(cmd, **kwargs ):
|
||||
"Run a command and return merged stdout and stderr"
|
||||
out, _, ret = errRun( ["which", cmd], stderr=STDOUT, **kwargs )
|
||||
return out.rstrip() if ret == 0 else None
|
||||
|
||||
# pylint: enable=maybe-no-member
|
||||
|
||||
def isShellBuiltin( cmd ):
|
||||
"Return True if cmd is a bash builtin."
|
||||
if isShellBuiltin.builtIns is None:
|
||||
isShellBuiltin.builtIns = set(quietRun( 'bash -c enable' ).split())
|
||||
isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
|
||||
space = cmd.find( ' ' )
|
||||
if space > 0:
|
||||
cmd = cmd[ :space]
|
||||
return cmd in isShellBuiltin.builtIns
|
||||
|
||||
|
||||
isShellBuiltin.builtIns = None
|
||||
|
||||
|
||||
# Interface management
|
||||
#
|
||||
# Interfaces are managed as strings which are simply the
|
||||
@@ -397,7 +320,7 @@ def ipParse( ip ):
|
||||
"Parse an IP address and return an unsigned int."
|
||||
args = [ int( arg ) for arg in ip.split( '.' ) ]
|
||||
while len(args) < 4:
|
||||
args.insert( len(args) - 1, 0 )
|
||||
args.append( 0 )
|
||||
return ipNum( *args )
|
||||
|
||||
def netParse( ipstr ):
|
||||
@@ -407,7 +330,7 @@ def netParse( ipstr ):
|
||||
if '/' in ipstr:
|
||||
ip, pf = ipstr.split( '/' )
|
||||
prefixLen = int( pf )
|
||||
# if no prefix is specified, set the prefix to 24
|
||||
#if no prefix is specified, set the prefix to 24
|
||||
else:
|
||||
ip = ipstr
|
||||
prefixLen = 24
|
||||
@@ -450,34 +373,30 @@ def pmonitor(popens, timeoutms=500, readline=True,
|
||||
terminates: when all EOFs received"""
|
||||
poller = poll()
|
||||
fdToHost = {}
|
||||
fdToDecoder = {}
|
||||
for host, popen in popens.items():
|
||||
for host, popen in popens.iteritems():
|
||||
fd = popen.stdout.fileno()
|
||||
fdToHost[ fd ] = host
|
||||
fdToDecoder[ fd ] = getincrementaldecoder()
|
||||
poller.register( fd, POLLIN )
|
||||
flags = fcntl( fd, F_GETFL )
|
||||
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
if not readline:
|
||||
# Use non-blocking reads
|
||||
flags = fcntl( fd, F_GETFL )
|
||||
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
|
||||
while popens:
|
||||
fds = poller.poll( timeoutms )
|
||||
if fds:
|
||||
for fd, event in fds:
|
||||
host = fdToHost[ fd ]
|
||||
decoder = fdToDecoder[ fd ]
|
||||
popen = popens[ host ]
|
||||
if event & ( POLLIN | POLLHUP ):
|
||||
while True:
|
||||
try:
|
||||
f = popen.stdout
|
||||
line = decoder.decode( f.readline() if readline
|
||||
else f.read( readmax ) )
|
||||
except IOError:
|
||||
line = ''
|
||||
if line == '':
|
||||
break
|
||||
yield host, line
|
||||
if event & POLLHUP:
|
||||
if event & POLLIN:
|
||||
if readline:
|
||||
# Attempt to read a line of output
|
||||
# This blocks until we receive a newline!
|
||||
line = popen.stdout.readline()
|
||||
else:
|
||||
line = popen.stdout.read( readmax )
|
||||
yield host, line
|
||||
# Check for EOF
|
||||
elif event & POLLHUP:
|
||||
poller.unregister( fd )
|
||||
del popens[ host ]
|
||||
else:
|
||||
@@ -486,19 +405,19 @@ def pmonitor(popens, timeoutms=500, readline=True,
|
||||
# Other stuff we use
|
||||
def sysctlTestAndSet( name, limit ):
|
||||
"Helper function to set sysctl limits"
|
||||
# convert non-directory names into directory names
|
||||
#convert non-directory names into directory names
|
||||
if '/' not in name:
|
||||
name = '/proc/sys/' + name.replace( '.', '/' )
|
||||
# read limit
|
||||
#read limit
|
||||
with open( name, 'r' ) as readFile:
|
||||
oldLimit = readFile.readline()
|
||||
if isinstance( limit, int ):
|
||||
# compare integer limits before overriding
|
||||
#compare integer limits before overriding
|
||||
if int( oldLimit ) < limit:
|
||||
with open( name, 'w' ) as writeFile:
|
||||
writeFile.write( "%d" % limit )
|
||||
else:
|
||||
# overwrite non-integer limits
|
||||
#overwrite non-integer limits
|
||||
with open( name, 'w' ) as writeFile:
|
||||
writeFile.write( limit )
|
||||
|
||||
@@ -515,21 +434,21 @@ def fixLimits():
|
||||
try:
|
||||
rlimitTestAndSet( RLIMIT_NPROC, 8192 )
|
||||
rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
|
||||
# Increase open file limit
|
||||
#Increase open file limit
|
||||
sysctlTestAndSet( 'fs.file-max', 10000 )
|
||||
# Increase network buffer space
|
||||
#Increase network buffer space
|
||||
sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
|
||||
sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
|
||||
sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
|
||||
sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
|
||||
sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
|
||||
# Increase arp cache size
|
||||
#Increase arp cache size
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
|
||||
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
|
||||
# Increase routing table size
|
||||
#Increase routing table size
|
||||
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
|
||||
# Increase number of PTYs for nodes
|
||||
#Increase number of PTYs for nodes
|
||||
sysctlTestAndSet( 'kernel.pty.max', 20000 )
|
||||
# pylint: disable=broad-except
|
||||
except Exception:
|
||||
@@ -540,7 +459,7 @@ def fixLimits():
|
||||
|
||||
def mountCgroups():
|
||||
"Make sure cgroups file system is mounted"
|
||||
mounts = quietRun( 'grep cgroup /proc/mounts' )
|
||||
mounts = quietRun( 'cat /proc/mounts' )
|
||||
cgdir = '/sys/fs/cgroup'
|
||||
csdir = cgdir + '/cpuset'
|
||||
if ('cgroup %s' % cgdir not in mounts and
|
||||
@@ -668,8 +587,9 @@ def ensureRoot():
|
||||
Probably we should only sudo when needed as per Big Switch's patch.
|
||||
"""
|
||||
if os.getuid() != 0:
|
||||
error( '*** Mininet must run as root.\n' )
|
||||
print "*** Mininet must run as root."
|
||||
exit( 1 )
|
||||
return
|
||||
|
||||
def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
|
||||
"""Wait until server is listening on port.
|
||||
@@ -679,7 +599,7 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
|
||||
if not runCmd( 'which telnet' ):
|
||||
raise Exception('Could not find telnet' )
|
||||
# pylint: disable=maybe-no-member
|
||||
serverIP = server if isinstance( server, BaseString ) else server.IP()
|
||||
serverIP = server if isinstance( server, basestring ) else server.IP()
|
||||
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
|
||||
time = 0
|
||||
result = runCmd( cmd )
|
||||
|
||||
@@ -130,16 +130,6 @@ int main(int argc, char *argv[])
|
||||
perror("unshare");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mark our whole hierarchy recursively as private, so that our
|
||||
* mounts do not propagate to other processes.
|
||||
*/
|
||||
|
||||
if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1) {
|
||||
perror("remount");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mount sysfs to pick up the new network namespace */
|
||||
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
|
||||
perror("mount");
|
||||
|
||||
+93
-201
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mininet install script for Ubuntu and Debian
|
||||
# Original author: Brandon Heller
|
||||
# Mininet install script for Ubuntu (and Debian Wheezy+)
|
||||
# Brandon Heller (brandonh@stanford.edu)
|
||||
|
||||
# Fail on error
|
||||
set -e
|
||||
@@ -13,7 +13,7 @@ set -o nounset
|
||||
MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )"
|
||||
|
||||
# Set up build directory, which by default is the working directory
|
||||
# unless the working directory is a subdirectory of mininet,
|
||||
# unless the working directory is a subdirectory of mininet,
|
||||
# in which case we use the directory containing mininet
|
||||
BUILD_DIR="$(pwd -P)"
|
||||
case $BUILD_DIR in
|
||||
@@ -36,38 +36,24 @@ if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi
|
||||
test -e /etc/debian_version && DIST="Debian"
|
||||
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
|
||||
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
|
||||
# Truly non-interactive apt-get installation
|
||||
install='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q install'
|
||||
remove='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q remove'
|
||||
install='sudo apt-get -y install'
|
||||
remove='sudo apt-get -y remove'
|
||||
pkginst='sudo dpkg -i'
|
||||
update='sudo apt-get'
|
||||
# Prereqs for this script
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install lsb-release
|
||||
fi
|
||||
fi
|
||||
test -e /etc/fedora-release && DIST="Fedora"
|
||||
test -e /etc/redhat-release && DIST="RedHatEnterpriseServer"
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
install='sudo yum -y install'
|
||||
remove='sudo yum -y erase'
|
||||
pkginst='sudo rpm -ivh'
|
||||
update='sudo yum'
|
||||
# Prereqs for this script
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install redhat-lsb-core
|
||||
fi
|
||||
fi
|
||||
test -e /etc/SuSE-release && DIST="SUSE Linux"
|
||||
if [ "$DIST" = "SUSE Linux" ]; then
|
||||
install='sudo zypper --non-interactive install '
|
||||
remove='sudo zypper --non-interactive remove '
|
||||
pkginst='sudo rpm -ivh'
|
||||
# Prereqs for this script
|
||||
if ! which lsb_release &> /dev/null; then
|
||||
$install openSUSE-release
|
||||
fi
|
||||
fi
|
||||
if which lsb_release &> /dev/null; then
|
||||
DIST=`lsb_release -is`
|
||||
RELEASE=`lsb_release -rs`
|
||||
@@ -80,12 +66,8 @@ echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
|
||||
KERNEL_NAME=`uname -r`
|
||||
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
|
||||
|
||||
# Treat Raspbian as Debian
|
||||
[ "$DIST" = 'Raspbian' ] && DIST='Debian'
|
||||
|
||||
DISTS='Ubuntu|Debian|Fedora|RedHatEnterpriseServer|SUSE LINUX'
|
||||
if ! echo $DIST | egrep "$DISTS" >/dev/null; then
|
||||
echo "Install.sh currently only supports $DISTS."
|
||||
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora'; then
|
||||
echo "Install.sh currently only supports Ubuntu, Debian and Fedora."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -102,26 +84,6 @@ function version_ge {
|
||||
[ "$1" == "$latest" ]
|
||||
}
|
||||
|
||||
# Attempt to detect Python version
|
||||
PYTHON=${PYTHON:-python}
|
||||
PRINTVERSION='import sys; print(sys.version_info)'
|
||||
PYTHON_VERSION=unknown
|
||||
for python in $PYTHON python2 python3; do
|
||||
if $python -c "$PRINTVERSION" |& grep 'major=2'; then
|
||||
PYTHON=$python; PYTHON_VERSION=2; PYPKG=python
|
||||
break
|
||||
elif $python -c "$PRINTVERSION" |& grep 'major=3'; then
|
||||
PYTHON=$python; PYTHON_VERSION=3; PYPKG=python3
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$PYTHON_VERSION" == unknown ]; then
|
||||
echo "Can't find a working python command ('$PYTHON' doesn't work.)"
|
||||
echo "You may wish to export PYTHON or install a working 'python'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Detected Python (${PYTHON}) version ${PYTHON_VERSION}"
|
||||
|
||||
# Kernel Deb pkg to be removed:
|
||||
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
|
||||
@@ -139,7 +101,7 @@ OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
|
||||
|
||||
function kernel {
|
||||
echo "Install Mininet-compatible kernel if necessary"
|
||||
$update update
|
||||
sudo apt-get update
|
||||
if ! $install linux-image-$KERNEL_NAME; then
|
||||
echo "Could not install linux-image-$KERNEL_NAME"
|
||||
echo "Skipping - assuming installed kernel is OK."
|
||||
@@ -161,50 +123,25 @@ function kernel_clean {
|
||||
# Install Mininet deps
|
||||
function mn_deps {
|
||||
echo "Installing Mininet dependencies"
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install gcc make socat psmisc xterm openssh-clients iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man pyflakes pylint python-pep8 python-pexpect
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install gcc make socat psmisc xterm openssh iperf \
|
||||
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
|
||||
ethtool help2man python-pyflakes python3-pylint \
|
||||
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
|
||||
else # Debian/Ubuntu
|
||||
pf=pyflakes
|
||||
# Starting around 20.04, installing pyflakes instead of pyflakes3
|
||||
# causes Python 2 to be installed, which is exactly NOT what we want.
|
||||
if [ `expr $RELEASE '>=' 20.04` = "1" ]; then
|
||||
pf=pyflakes3
|
||||
fi
|
||||
$install gcc make socat psmisc xterm ssh iperf telnet \
|
||||
ethtool help2man $pf pylint pep8 \
|
||||
net-tools \
|
||||
${PYPKG}-pexpect ${PYPKG}-tk
|
||||
# Install pip
|
||||
$install ${PYPKG}-pip || $install ${PYPKG}-pip-whl
|
||||
if ! ${PYTHON} -m pip -V; then
|
||||
if [ $PYTHON_VERSION == 2 ]; then
|
||||
wget https://bootstrap.pypa.io/2.6/get-pip.py
|
||||
else
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
fi
|
||||
sudo ${PYTHON} get-pip.py
|
||||
rm get-pip.py
|
||||
fi
|
||||
$install iproute2 || $install iproute
|
||||
$install cgroup-tools || $install cgroup-bin
|
||||
ethtool help2man pyflakes pylint python-pep8
|
||||
else
|
||||
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
|
||||
python-setuptools cgroup-bin ethtool help2man \
|
||||
pyflakes pylint pep8
|
||||
fi
|
||||
|
||||
echo "Installing Mininet core"
|
||||
pushd $MININET_DIR/mininet
|
||||
sudo PYTHON=${PYTHON} make install
|
||||
sudo make install
|
||||
popd
|
||||
}
|
||||
|
||||
# Install Mininet documentation dependencies
|
||||
function mn_doc {
|
||||
echo "Installing Mininet documentation dependencies"
|
||||
# Install Mininet developer dependencies
|
||||
function mn_dev {
|
||||
echo "Installing Mininet developer dependencies"
|
||||
$install doxygen doxypy texlive-fonts-recommended
|
||||
if ! $install doxygen-latex; then
|
||||
echo "doxygen-latex not needed"
|
||||
@@ -219,16 +156,12 @@ function of {
|
||||
echo "Installing OpenFlow reference implementation..."
|
||||
cd $BUILD_DIR
|
||||
$install autoconf automake libtool make gcc
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install git pkgconfig glibc-devel
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install git pkgconfig glibc-devel
|
||||
else
|
||||
$install git-core autotools-dev pkg-config libc6-dev
|
||||
fi
|
||||
# was: git clone git://openflowswitch.org/openflow.git
|
||||
# Use our own fork on github for now:
|
||||
git clone git://github.com/mininet/openflow
|
||||
git clone git://openflowswitch.org/openflow.git
|
||||
cd $BUILD_DIR/openflow
|
||||
|
||||
# Patch controller to handle more than 16 switches
|
||||
@@ -245,14 +178,10 @@ function of {
|
||||
function of13 {
|
||||
echo "Installing OpenFlow 1.3 soft switch implementation..."
|
||||
cd $BUILD_DIR/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev \
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
|
||||
unzip libpcre3-dev flex bison libboost-dev
|
||||
if [ "$DIST" = "Ubuntu" ] && version_le $RELEASE 16.04; then
|
||||
$install libxerces-c2-dev
|
||||
else
|
||||
$install libxerces-c-dev
|
||||
fi
|
||||
|
||||
if [ ! -d "ofsoftswitch13" ]; then
|
||||
git clone https://github.com/CPqD/ofsoftswitch13.git
|
||||
if [[ -n "$OF13_SWITCH_REV" ]]; then
|
||||
@@ -263,17 +192,24 @@ function of13 {
|
||||
fi
|
||||
|
||||
# Install netbee
|
||||
if [ ! -d "netbee" ]; then
|
||||
git clone https://github.com/netgroup-polito/netbee.git
|
||||
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
|
||||
NBEESRC="nbeesrc-feb-24-2015"
|
||||
NBEEDIR="netbee"
|
||||
else
|
||||
NBEESRC="nbeesrc-jan-10-2013"
|
||||
NBEEDIR="nbeesrc-jan-10-2013"
|
||||
fi
|
||||
cd netbee/src
|
||||
|
||||
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
|
||||
wget -nc ${NBEEURL}${NBEESRC}.zip
|
||||
unzip ${NBEESRC}.zip
|
||||
cd ${NBEEDIR}/src
|
||||
cmake .
|
||||
make
|
||||
|
||||
cd $BUILD_DIR
|
||||
sudo cp netbee/bin/libn*.so /usr/local/lib
|
||||
cd $BUILD_DIR/
|
||||
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
|
||||
sudo ldconfig
|
||||
sudo cp -R netbee/include/ /usr/
|
||||
sudo cp -R ${NBEEDIR}/include/ /usr/
|
||||
|
||||
# Resume the install:
|
||||
cd $BUILD_DIR/ofsoftswitch13
|
||||
@@ -288,10 +224,8 @@ function of13 {
|
||||
function install_wireshark {
|
||||
if ! which wireshark; then
|
||||
echo "Installing Wireshark"
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install wireshark wireshark-gnome
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install wireshark
|
||||
else
|
||||
$install wireshark tshark
|
||||
fi
|
||||
@@ -363,36 +297,30 @@ function ubuntuOvs {
|
||||
fi
|
||||
|
||||
# Remove any old packages
|
||||
|
||||
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-pki openvswitch-switch \
|
||||
openvswitch-controller || true
|
||||
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
|
||||
openvswitch-pki openvswitch-switch
|
||||
|
||||
# Get build deps
|
||||
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
|
||||
pkg-config bzip2 openssl ${PYPKG}-all procps ${PYPKG}-qt4 \
|
||||
${PYPKG}-zopeinterface ${PYPKG}-twisted-conch dkms dh-python dh-autoreconf \
|
||||
uuid-runtime
|
||||
pkg-config bzip2 openssl python-all procps python-qt4 \
|
||||
python-zopeinterface python-twisted-conch dkms
|
||||
|
||||
# Build OVS
|
||||
parallel=`grep processor /proc/cpuinfo | wc -l`
|
||||
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
|
||||
DEB_BUILD_OPTIONS='parallel=$parallel nocheck' fakeroot debian/rules binary
|
||||
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
|
||||
cd ..
|
||||
for pkg in common datapath-dkms pki switch; do
|
||||
pkg=openvswitch-${pkg}_$OVS_RELEASE*.deb
|
||||
echo "Installing $pkg"
|
||||
$pkginst $pkg
|
||||
done
|
||||
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb 2>/dev/null; then
|
||||
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
|
||||
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
|
||||
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
|
||||
echo "Ignoring error installing openvswitch-controller"
|
||||
fi
|
||||
|
||||
/sbin/modinfo openvswitch
|
||||
modinfo openvswitch
|
||||
sudo ovs-vsctl show
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop 2>/dev/null; then
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
@@ -406,7 +334,7 @@ function ubuntuOvs {
|
||||
function ovs {
|
||||
echo "Installing Open vSwitch..."
|
||||
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" == "Fedora" ]; then
|
||||
$install openvswitch openvswitch-controller
|
||||
return
|
||||
fi
|
||||
@@ -425,34 +353,23 @@ function ovs {
|
||||
fi
|
||||
|
||||
$install openvswitch-switch
|
||||
OVSC=""
|
||||
if $install openvswitch-controller; then
|
||||
OVSC="openvswitch-controller"
|
||||
else
|
||||
echo "Attempting to install openvswitch-testcontroller"
|
||||
if $install openvswitch-testcontroller; then
|
||||
OVSC="openvswitch-testcontroller"
|
||||
else
|
||||
echo "Failed - skipping openvswitch-testcontroller"
|
||||
fi
|
||||
fi
|
||||
if [ "$OVSC" ]; then
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service $OVSC stop 2>/dev/null; then
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/$OVSC ]; then
|
||||
sudo update-rc.d $OVSC disable
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
else
|
||||
echo "Attempting to install openvswitch-testcontroller"
|
||||
if ! $install openvswitch-testcontroller; then
|
||||
echo "Failed - skipping openvswitch-testcontroller"
|
||||
fi
|
||||
fi
|
||||
# This service seems to hang on 20.04
|
||||
if systemctl list-units | \
|
||||
grep status netplan-ovs-cleanup.service>&/dev/null; then
|
||||
echo 'TimeoutSec=10' | sudo EDITOR='tee -a' \
|
||||
sudo systemctl edit --full netplan-ovs-cleanup.service
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function remove_ovs {
|
||||
@@ -482,19 +399,12 @@ function ivs {
|
||||
IVS_SRC=$BUILD_DIR/ivs
|
||||
|
||||
# Install dependencies
|
||||
$install gcc make
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
$install git pkgconfig libnl3-devel libcap-devel openssl-devel
|
||||
else
|
||||
$install git-core pkg-config libnl-3-dev libnl-route-3-dev \
|
||||
libnl-genl-3-dev
|
||||
fi
|
||||
$install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
|
||||
|
||||
# Install IVS from source
|
||||
cd $BUILD_DIR
|
||||
git clone git://github.com/floodlight/ivs $IVS_SRC
|
||||
git clone git://github.com/floodlight/ivs $IVS_SRC --recursive
|
||||
cd $IVS_SRC
|
||||
git submodule update --init
|
||||
make
|
||||
sudo make install
|
||||
}
|
||||
@@ -505,20 +415,27 @@ function ryu {
|
||||
|
||||
# install Ryu dependencies"
|
||||
$install autoconf automake g++ libtool python make
|
||||
if [ "$DIST" = "Ubuntu" -o "$DIST" = "Debian" ]; then
|
||||
$install gcc ${PYPKG}-pip ${PYPKG}-dev libffi-dev libssl-dev \
|
||||
libxml2-dev libxslt1-dev zlib1g-dev
|
||||
if [ "$DIST" = "Ubuntu" ]; then
|
||||
$install libxml2 libxslt-dev python-pip python-dev
|
||||
sudo pip install gevent
|
||||
elif [ "$DIST" = "Debian" ]; then
|
||||
$install libxml2 libxslt-dev python-pip python-dev
|
||||
sudo pip install gevent
|
||||
fi
|
||||
|
||||
# if needed, update python-six
|
||||
SIX_VER=`pip show six | grep Version | awk '{print $2}'`
|
||||
if version_ge 1.7.0 $SIX_VER; then
|
||||
echo "Installing python-six version 1.7.0..."
|
||||
sudo pip install -I six==1.7.0
|
||||
fi
|
||||
# fetch RYU
|
||||
cd $BUILD_DIR/
|
||||
git clone git://github.com/osrg/ryu.git ryu
|
||||
cd ryu
|
||||
|
||||
# install ryu
|
||||
sudo pip install -r tools/pip-requires -r tools/optional-requires \
|
||||
-r tools/test-requires
|
||||
sudo python setup.py install
|
||||
sudo python ./setup.py install
|
||||
|
||||
# Add symbolic link to /usr/bin
|
||||
sudo ln -s ./bin/ryu-manager /usr/local/bin/ryu-manager
|
||||
@@ -529,17 +446,17 @@ function nox {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
|
||||
# Install NOX deps:
|
||||
$install autoconf automake g++ libtool python ${PYPKG}-twisted \
|
||||
$install autoconf automake g++ libtool python python-twisted \
|
||||
swig libssl-dev make
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install ${PYPKG}-dev libboost-dev
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
# Install NOX optional deps:
|
||||
$install libsqlite3-dev ${PYPKG}-simplejson
|
||||
$install libsqlite3-dev python-simplejson
|
||||
|
||||
# Fetch NOX destiny
|
||||
cd $BUILD_DIR/
|
||||
@@ -577,12 +494,12 @@ function nox13 {
|
||||
echo "Installing NOX w/tutorial files..."
|
||||
|
||||
# Install NOX deps:
|
||||
$install autoconf automake g++ libtool python ${PYPKG}-twisted \
|
||||
$install autoconf automake g++ libtool python python-twisted \
|
||||
swig libssl-dev make
|
||||
if [ "$DIST" = "Debian" ]; then
|
||||
$install libboost1.35-dev
|
||||
elif [ "$DIST" = "Ubuntu" ]; then
|
||||
$install ${PYPKG}-dev libboost-dev
|
||||
$install python-dev libboost-dev
|
||||
$install libboost-filesystem-dev
|
||||
$install libboost-test-dev
|
||||
fi
|
||||
@@ -618,8 +535,7 @@ function oftest {
|
||||
echo "Installing oftest..."
|
||||
|
||||
# Install deps:
|
||||
$install tcpdump
|
||||
$install ${PYPKG}-scapy || sudo $PYTHON -m pip install scapy
|
||||
$install tcpdump python-scapy
|
||||
|
||||
# Install oftest:
|
||||
cd $BUILD_DIR/
|
||||
@@ -630,22 +546,17 @@ function oftest {
|
||||
function cbench {
|
||||
echo "Installing cbench..."
|
||||
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install net-snmp-devel libpcap-devel libconfig-devel
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install net-snmp-devel libpcap-devel libconfig-devel
|
||||
else
|
||||
$install libsnmp-dev libpcap-dev libconfig-dev
|
||||
fi
|
||||
cd $BUILD_DIR/
|
||||
# was: git clone git://gitosis.stanford.edu/oflops.git
|
||||
# Use our own fork on github for now:
|
||||
git clone git://github.com/mininet/oflops
|
||||
git clone git://gitosis.stanford.edu/oflops.git
|
||||
cd oflops
|
||||
sh boot.sh || true # possible error in autoreconf, so run twice
|
||||
sh boot.sh
|
||||
./configure --with-openflow-src-dir=$BUILD_DIR/openflow
|
||||
make liboflops_test.la
|
||||
make
|
||||
sudo make install || true # make install fails; force past this
|
||||
}
|
||||
@@ -677,10 +588,9 @@ net.ipv6.conf.default.disable_ipv6 = 1
|
||||
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
fi
|
||||
# Since the above doesn't disable neighbor discovery, also do this:
|
||||
# disable via boot line, and also restore eth0 naming for VM use
|
||||
if ! grep 'ipv6.disable' /etc/default/grub; then
|
||||
sudo sed -i -e \
|
||||
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 net.ifnames=0 /' \
|
||||
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 /' \
|
||||
/etc/default/grub
|
||||
sudo update-grub
|
||||
fi
|
||||
@@ -706,7 +616,7 @@ net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
$install ntp
|
||||
|
||||
# Install vconfig for VLAN example
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
if [ "$DIST" = "Fedora" ]; then
|
||||
$install vconfig
|
||||
else
|
||||
$install vlan
|
||||
@@ -758,8 +668,8 @@ function all {
|
||||
echo "Installing all packages except for -eix (doxypy, ivs, nox-classic)..."
|
||||
kernel
|
||||
mn_deps
|
||||
# Skip mn_doc (doxypy/texlive/fonts/etc.) because it's huge
|
||||
# mn_doc
|
||||
# Skip mn_dev (doxypy/texlive/fonts/etc.) because it's huge
|
||||
# mn_dev
|
||||
of
|
||||
install_wireshark
|
||||
ovs
|
||||
@@ -787,31 +697,13 @@ function vm_clean {
|
||||
rm -f ~/.ssh/id_rsa* ~/.ssh/known_hosts
|
||||
sudo rm -f ~/.ssh/authorized_keys*
|
||||
|
||||
# Remove SSH keys and regenerate on boot
|
||||
echo 'Removing SSH keys from /etc/ssh/'
|
||||
sudo rm -f /etc/ssh/*key*
|
||||
if [ ! -e /etc/rc.local ]; then
|
||||
echo '#!/usr/bin/bash' | sudo tee /etc/rc.local
|
||||
sudo chmod +x /etc/rc.local
|
||||
fi
|
||||
if ! grep mininet /etc/rc.local >& /dev/null; then
|
||||
sudo sed -i -e "s/exit 0//" /etc/rc.local || true
|
||||
echo '
|
||||
# mininet: regenerate ssh keys if we deleted them
|
||||
if ! stat -t /etc/ssh/*key* >/dev/null 2>&1; then
|
||||
/usr/sbin/dpkg-reconfigure openssh-server
|
||||
fi
|
||||
exit 0
|
||||
' | sudo tee -a /etc/rc.local > /dev/null
|
||||
sudo chmod +x /etc/rc.local
|
||||
fi
|
||||
# Remove Mininet files
|
||||
#sudo rm -f /lib/modules/python2.5/site-packages/mininet*
|
||||
#sudo rm -f /usr/bin/mnexec
|
||||
|
||||
# Clear optional dev script for SSH keychain load on boot
|
||||
rm -f ~/.bash_profile
|
||||
|
||||
# Remove leftover install script if any
|
||||
rm -f install-mininet-vm.sh
|
||||
|
||||
# Clear git changes
|
||||
git config --global user.name "None"
|
||||
git config --global user.email "None"
|
||||
@@ -819,7 +711,7 @@ exit 0
|
||||
# Note: you can shrink the .vmdk in vmware using
|
||||
# vmware-vdiskmanager -k *.vmdk
|
||||
echo "Zeroing out disk blocks for efficient compaction..."
|
||||
time sudo dd if=/dev/zero of=/tmp/zero bs=1M || true
|
||||
time sudo dd if=/dev/zero of=/tmp/zero bs=1M
|
||||
sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero
|
||||
|
||||
}
|
||||
@@ -838,7 +730,7 @@ function usage {
|
||||
printf -- ' -b: install controller (B)enchmark (oflops)\n' >&2
|
||||
printf -- ' -c: (C)lean up after kernel install\n' >&2
|
||||
printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2
|
||||
printf -- ' -e: install Mininet documentation/LaT(e)X dependencies\n' >&2
|
||||
printf -- ' -e: install Mininet d(E)veloper dependencies\n' >&2
|
||||
printf -- ' -f: install Open(F)low\n' >&2
|
||||
printf -- ' -h: print this (H)elp message\n' >&2
|
||||
printf -- ' -i: install (I)ndigo Virtual Switch\n' >&2
|
||||
@@ -872,7 +764,7 @@ else
|
||||
b) cbench;;
|
||||
c) kernel_clean;;
|
||||
d) vm_clean;;
|
||||
e) mn_doc;;
|
||||
e) mn_dev;;
|
||||
f) case $OF_VERSION in
|
||||
1.0) of;;
|
||||
1.3) of13;;
|
||||
|
||||
+1
-2
@@ -40,7 +40,6 @@ Bob Lantz, rlantz@cs.stanford.edu
|
||||
1/24/2010
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import re, sys
|
||||
|
||||
def fixUnderscoreTriplet( match ):
|
||||
@@ -196,4 +195,4 @@ def convertFromPep8( program ):
|
||||
return program
|
||||
|
||||
if __name__ == '__main__':
|
||||
print( convertFromPep8( sys.stdin.read() ) )
|
||||
print convertFromPep8( sys.stdin.read() )
|
||||
+5
-10
@@ -1,19 +1,14 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from subprocess import check_output as co
|
||||
from sys import exit, version_info
|
||||
|
||||
def run(*args, **kwargs):
|
||||
"Run co and decode for python3"
|
||||
result = co(*args, **kwargs)
|
||||
return result.decode() if version_info[ 0 ] >= 3 else result
|
||||
from sys import exit
|
||||
|
||||
# Actually run bin/mn rather than importing via python path
|
||||
version = 'Mininet ' + run( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
|
||||
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
|
||||
version = version.strip()
|
||||
|
||||
# Find all Mininet path references
|
||||
lines = run( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
|
||||
lines = co( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
|
||||
|
||||
error = False
|
||||
|
||||
@@ -21,8 +16,8 @@ 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 ) )
|
||||
print "%s: incorrect version '%s' (should be '%s')" % (
|
||||
fname, fversion, version )
|
||||
error = True
|
||||
|
||||
if error:
|
||||
|
||||
+87
-149
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2.7
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
build.py: build a Mininet VM
|
||||
@@ -31,7 +31,6 @@ from os import stat, path
|
||||
from stat import ST_MODE, ST_SIZE
|
||||
from os.path import abspath
|
||||
from sys import exit, stdout, argv, modules
|
||||
import sys
|
||||
import re
|
||||
from glob import glob
|
||||
from subprocess import check_output, call, Popen
|
||||
@@ -40,13 +39,11 @@ from time import time, strftime, localtime
|
||||
import argparse
|
||||
from distutils.spawn import find_executable
|
||||
import inspect
|
||||
from traceback import print_exc
|
||||
|
||||
|
||||
pexpect = None # For code check - imported dynamically
|
||||
|
||||
# boot can be slooooow!!!! need to debug/optimize somehow
|
||||
TIMEOUT = 600
|
||||
TIMEOUT=600
|
||||
|
||||
# Some configuration options
|
||||
# Possibly change this to use the parsed arguments instead!
|
||||
@@ -57,46 +54,48 @@ NoKVM = False # Don't use kvm and use emulation instead
|
||||
Branch = None # Branch to update and check out before testing
|
||||
Zip = False # Archive .ovf and .vmdk into a .zip file
|
||||
Forward = [] # VM port forwarding options (-redir)
|
||||
Chown = '' # Build directory owner
|
||||
|
||||
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
|
||||
|
||||
Prompt = '\$ ' # Shell prompt that pexpect will wait for
|
||||
|
||||
|
||||
# URLs for Ubunto .iso images
|
||||
|
||||
def serverURL( version, arch ):
|
||||
"Return .iso URL for Ubuntu version and arch"
|
||||
server = 'http://cdimage.ubuntu.com/ubuntu/releases/%s/release/'
|
||||
iso = 'ubuntu-%s-server-%s.iso'
|
||||
return (server + iso ) % ( version, version, arch )
|
||||
|
||||
def legacyURL( version, arch ):
|
||||
"Return .iso URL for Ubuntu version"
|
||||
server = ( 'http://cdimage.ubuntu.com/ubuntu-legacy-server/'
|
||||
'releases/%s/release/' )
|
||||
iso = 'ubuntu-%s-legacy-server-%s.iso'
|
||||
return (server + iso ) % ( version, version, arch )
|
||||
|
||||
isoURLs = {
|
||||
'precise32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
|
||||
'ubuntu-12.04.5-server-i386.iso',
|
||||
'precise64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
|
||||
'ubuntu-12.04.5-server-amd64.iso',
|
||||
'quantal32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
|
||||
'ubuntu-12.10-server-i386.iso',
|
||||
'quantal64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
|
||||
'ubuntu-12.10-server-amd64.iso',
|
||||
'raring32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
|
||||
'ubuntu-13.04-server-i386.iso',
|
||||
'raring64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
|
||||
'ubuntu-13.04-server-amd64.iso',
|
||||
'saucy32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
|
||||
'ubuntu-13.10-server-i386.iso',
|
||||
'saucy64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
|
||||
'ubuntu-13.10-server-amd64.iso',
|
||||
'trusty32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
|
||||
'ubuntu-14.04.4-server-i386.iso',
|
||||
'ubuntu-14.04-server-i386.iso',
|
||||
'trusty64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
|
||||
'ubuntu-14.04.4-server-amd64.iso',
|
||||
'xenial32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
|
||||
'ubuntu-16.04.6-server-i386.iso',
|
||||
'xenial64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
|
||||
'ubuntu-16.04.7-server-amd64.iso',
|
||||
'bionic64server': serverURL( '18.04.5', 'amd64' ),
|
||||
'focal64server': legacyURL( '20.04.1', 'amd64' ),
|
||||
'ubuntu-14.04-server-amd64.iso',
|
||||
'utopic32server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
|
||||
'ubuntu-14.10-server-i386.iso',
|
||||
'utopic64server':
|
||||
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
|
||||
'ubuntu-14.10-server-amd64.iso',
|
||||
}
|
||||
|
||||
|
||||
@@ -130,11 +129,11 @@ def log( *args, **kwargs ):
|
||||
msg = ' '.join( str( arg ) for arg in args )
|
||||
output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg )
|
||||
if cr:
|
||||
print( output )
|
||||
print output
|
||||
else:
|
||||
print( output, )
|
||||
print output,
|
||||
# Optionally mirror to LogFile
|
||||
if LogFile:
|
||||
if type( LogFile ) is file:
|
||||
if cr:
|
||||
output += '\n'
|
||||
LogFile.write( output )
|
||||
@@ -143,7 +142,7 @@ def log( *args, **kwargs ):
|
||||
|
||||
def run( cmd, **kwargs ):
|
||||
"Convenient interface to check_output"
|
||||
log( '+', cmd )
|
||||
log( '-', cmd )
|
||||
cmd = cmd.split()
|
||||
arg0 = cmd[ 0 ]
|
||||
if not find_executable( arg0 ):
|
||||
@@ -165,8 +164,8 @@ def depend():
|
||||
log( '* Installing package dependencies' )
|
||||
run( 'sudo apt-get -qy update' )
|
||||
run( 'sudo apt-get -qy install'
|
||||
' kvmtool cloud-utils genisoimage qemu-kvm qemu-utils'
|
||||
' e2fsprogs curl'
|
||||
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
|
||||
' e2fsprogs dnsmasq curl'
|
||||
' python-setuptools mtools zip' )
|
||||
run( 'sudo easy_install pexpect' )
|
||||
|
||||
@@ -191,9 +190,9 @@ def findiso( flavor ):
|
||||
url = isoURLs[ flavor ]
|
||||
name = path.basename( url )
|
||||
iso = path.join( VMImageDir, name )
|
||||
if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0o777 != 0o444 ):
|
||||
if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0777 != 0444 ):
|
||||
log( '* Retrieving', url )
|
||||
run( 'curl -L -C - -o %s %s' % ( iso, url ) )
|
||||
run( 'curl -C - -o %s %s' % ( iso, url ) )
|
||||
# Make sure the file header/type is something reasonable like
|
||||
# 'ISO' or 'x86 boot sector', and not random html or text
|
||||
result = run( 'file ' + iso )
|
||||
@@ -202,7 +201,7 @@ def findiso( flavor ):
|
||||
raise Exception( 'findiso: could not download iso from ' + url )
|
||||
# Write-protect iso, signaling it is complete
|
||||
log( '* Write-protecting iso', iso)
|
||||
os.chmod( iso, 0o444 )
|
||||
os.chmod( iso, 0444 )
|
||||
log( '* Using iso', iso )
|
||||
return iso
|
||||
|
||||
@@ -213,15 +212,14 @@ def attachNBD( cow, flags='' ):
|
||||
# qemu-nbd requires an absolute path
|
||||
cow = abspath( cow )
|
||||
log( '* Checking for unused /dev/nbdX device ' )
|
||||
for i in range ( 1, 63 ):
|
||||
entry = 'nbd%d' % i
|
||||
nbd = '/dev/' + entry
|
||||
for i in range ( 0, 63 ):
|
||||
nbd = '/dev/nbd%d' % i
|
||||
# Check whether someone's already messing with that device
|
||||
if call( [ 'pgrep', '-f', entry ] ) == 0:
|
||||
if call( [ 'pgrep', '-f', nbd ] ) == 0:
|
||||
continue
|
||||
srun( 'modprobe nbd max-part=64' )
|
||||
srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
|
||||
print()
|
||||
print
|
||||
return nbd
|
||||
raise Exception( "Error: could not find unused /dev/nbdX device" )
|
||||
|
||||
@@ -235,26 +233,14 @@ def extractKernel( image, flavor, imageDir=VMImageDir ):
|
||||
"Extract kernel and initrd from base image"
|
||||
kernel = path.join( imageDir, flavor + '-vmlinuz' )
|
||||
initrd = path.join( imageDir, flavor + '-initrd' )
|
||||
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444:
|
||||
# If kernel is there, then initrd should also be there
|
||||
return kernel, initrd
|
||||
log( '* Extracting kernel to', kernel )
|
||||
nbd = attachNBD( image, flags='-r' )
|
||||
try:
|
||||
print( srun( 'partx ' + nbd ) )
|
||||
except:
|
||||
log( 'Warning - partx failed with error' )
|
||||
# Guess that kernel is in partition 1/boot/vmlinuz*generic
|
||||
# ...but look for other Linux partitions just in case
|
||||
print srun( 'partx ' + nbd )
|
||||
# Assume kernel is in partition 1/boot/vmlinuz*generic for now
|
||||
part = nbd + 'p1'
|
||||
partitions = srun( 'fdisk -l ' + nbd )
|
||||
for line in partitions.split( '\n' ):
|
||||
line = line.strip()
|
||||
if line.endswith( 'Linux' ):
|
||||
part = line.split()[ 0 ]
|
||||
break
|
||||
partnum = int( part.split( 'p' )[ -1 ] )
|
||||
if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0o777 ) == 0o444:
|
||||
# If kernel is there, then initrd should also be there
|
||||
detachNBD( nbd )
|
||||
return kernel, initrd, partnum
|
||||
mnt = mkdtemp()
|
||||
srun( 'mount -o ro,noload %s %s' % ( part, mnt ) )
|
||||
kernsrc = glob( '%s/boot/vmlinuz*generic' % mnt )[ 0 ]
|
||||
@@ -266,7 +252,7 @@ def extractKernel( image, flavor, imageDir=VMImageDir ):
|
||||
srun( 'umount ' + mnt )
|
||||
run( 'rmdir ' + mnt )
|
||||
detachNBD( nbd )
|
||||
return kernel, initrd, partnum
|
||||
return kernel, initrd
|
||||
|
||||
|
||||
def findBaseImage( flavor, size='8G' ):
|
||||
@@ -274,11 +260,10 @@ def findBaseImage( flavor, size='8G' ):
|
||||
image = path.join( VMImageDir, flavor + '-base.qcow2' )
|
||||
if path.exists( image ):
|
||||
# Detect race condition with multiple builds
|
||||
perms = stat( image )[ ST_MODE ] & 0o777
|
||||
if perms != 0o444:
|
||||
raise Exception( 'Error - base image %s is writable.' % image +
|
||||
' Are multiple builds running? if not,'
|
||||
' remove %s and try again.' % image )
|
||||
perms = stat( image )[ ST_MODE ] & 0777
|
||||
if perms != 0444:
|
||||
raise Exception( 'Error - %s is writable ' % image +
|
||||
'; are multiple builds running?' )
|
||||
else:
|
||||
# We create VMImageDir here since we are called first
|
||||
run( 'mkdir -p %s' % VMImageDir )
|
||||
@@ -288,11 +273,10 @@ def findBaseImage( flavor, size='8G' ):
|
||||
installUbuntu( iso, image )
|
||||
# Write-protect image, also signaling it is complete
|
||||
log( '* Write-protecting image', image)
|
||||
os.chmod( image, 0o444 )
|
||||
kernel, initrd, partnum = extractKernel( image, flavor )
|
||||
log( '* Using base image', image, 'and kernel', kernel,
|
||||
'and partition #', partnum )
|
||||
return image, kernel, initrd, partnum
|
||||
os.chmod( image, 0444 )
|
||||
kernel, initrd = extractKernel( image, flavor )
|
||||
log( '* Using base image', image, 'and kernel', kernel )
|
||||
return image, kernel, initrd
|
||||
|
||||
|
||||
# Kickstart and Preseed files for Ubuntu/Debian installer
|
||||
@@ -405,28 +389,12 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
|
||||
# Mount iso so we can use its kernel
|
||||
mnt = mkdtemp()
|
||||
srun( 'mount %s %s' % ( iso, mnt ) )
|
||||
for kdir in 'install', 'casper':
|
||||
kernel = path.join( mnt, kdir, 'vmlinuz' )
|
||||
if not path.exists( kernel ):
|
||||
kernel = ''
|
||||
for initrd in 'initrd.gz', 'initrd':
|
||||
initrd = path.join( mnt, kdir, initrd )
|
||||
if path.exists( initrd ):
|
||||
break
|
||||
else:
|
||||
initrd = ''
|
||||
if kernel and initrd:
|
||||
break
|
||||
if not kernel or not initrd:
|
||||
raise Exception( 'unable to locate kernel and initrd in iso image' )
|
||||
kernel = path.join( mnt, 'install/vmlinuz' )
|
||||
initrd = path.join( mnt, 'install/initrd.gz' )
|
||||
if NoKVM:
|
||||
accel = 'tcg'
|
||||
else:
|
||||
accel = 'kvm'
|
||||
try:
|
||||
run( 'kvm-ok' )
|
||||
except:
|
||||
raise Exception( 'kvm-ok failed; try using --nokvm' )
|
||||
cmd = [ 'sudo', kvm,
|
||||
'-machine', 'accel=%s' % accel,
|
||||
'-nographic',
|
||||
@@ -442,7 +410,6 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
|
||||
'-append',
|
||||
' ks=floppy:/' + kickstart +
|
||||
' preseed/file=floppy://' + preseed +
|
||||
' net.ifnames=0' +
|
||||
' console=ttyS0' ]
|
||||
ubuntuStart = time()
|
||||
log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image )
|
||||
@@ -459,8 +426,6 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
|
||||
logfile.close()
|
||||
elapsed = time() - ubuntuStart
|
||||
# Unmount iso and clean up
|
||||
### DEBUGGING
|
||||
srun( 'ls -l ' + mnt )
|
||||
srun( 'umount ' + mnt )
|
||||
run( 'rmdir ' + mnt )
|
||||
if vm.returncode != 0:
|
||||
@@ -470,13 +435,12 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
|
||||
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
|
||||
|
||||
|
||||
def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1, partnum=1 ):
|
||||
def boot( cow, kernel, initrd, logfile, memory=1024 ):
|
||||
"""Boot qemu/kvm with a COW disk and local/user data store
|
||||
cow: COW disk path
|
||||
kernel: kernel path
|
||||
logfile: log file for pexpect object
|
||||
memory: memory size in MB
|
||||
cpuCores: number of CPU cores to use
|
||||
returns: pexpect object to qemu process"""
|
||||
# pexpect might not be installed until after depend() is called
|
||||
global pexpect
|
||||
@@ -502,13 +466,9 @@ def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1, partnum=1 ):
|
||||
'-kernel', kernel,
|
||||
'-initrd', initrd,
|
||||
'-drive file=%s,if=virtio' % cow,
|
||||
'-append "root=/dev/vda%d init=/sbin/init'
|
||||
' net.ifnames=0 console=ttyS0" ' % partnum ]
|
||||
log( cmd )
|
||||
'-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
|
||||
if Forward:
|
||||
cmd += sum( [ [ '-redir', f ] for f in Forward ], [] )
|
||||
if cpuCores > 1:
|
||||
cmd += [ '-smp cores=%s' % cpuCores ]
|
||||
cmd = ' '.join( cmd )
|
||||
log( '* BOOTING VM FROM', cow )
|
||||
log( cmd )
|
||||
@@ -620,9 +580,6 @@ def useTest( vm, prompt=Prompt ):
|
||||
log( '* Restoring logging to stdout' )
|
||||
vm.logfile = stdout
|
||||
|
||||
# A convenient alias for use - 'run'; we might want to allow
|
||||
# 'run' to take a parameter
|
||||
runTest = useTest
|
||||
|
||||
def checkOutBranch( vm, branch, prompt=Prompt ):
|
||||
# This is a bit subtle; it will check out an existing branch (e.g. master)
|
||||
@@ -630,16 +587,13 @@ def checkOutBranch( vm, branch, prompt=Prompt ):
|
||||
# The branch will be rebased to its parent on origin.
|
||||
# This probably doesn't matter since we're running on a COW disk
|
||||
# anyway.
|
||||
vm.sendline( 'cd ~/mininet; git fetch origin ' + branch +
|
||||
'; git checkout ' + branch +
|
||||
'; git pull --rebase origin ' + branch )
|
||||
vm.sendline( 'cd ~/mininet; git fetch --all; git checkout '
|
||||
+ branch + '; git pull --rebase origin ' + branch )
|
||||
vm.expect( prompt )
|
||||
# Use install.sh since we may need to identify python version?
|
||||
vm.sendline( 'util/install.sh -n' )
|
||||
vm.sendline( 'sudo -n make install' )
|
||||
|
||||
|
||||
def interact( vm, tests, pre='', post='', prompt=Prompt,
|
||||
clean=True):
|
||||
def interact( vm, tests, pre='', post='', prompt=Prompt ):
|
||||
"Interact with vm, which is a pexpect object"
|
||||
login( vm )
|
||||
log( '* Waiting for login...' )
|
||||
@@ -662,11 +616,8 @@ def interact( vm, tests, pre='', post='', prompt=Prompt,
|
||||
vm.expect ( 'password for mininet: ' )
|
||||
vm.sendline( 'mininet' )
|
||||
log( '* Waiting for script to complete... ' )
|
||||
# Long timeout since we may be on cloud CI
|
||||
# 30min for kvm, 1.5hr for emulation
|
||||
# TODO: detect installation errors
|
||||
timeout = 5200 if NoKVM else 1800
|
||||
vm.expect( 'Done preparing Mininet', timeout=timeout )
|
||||
# Gigantic timeout for now ;-(
|
||||
vm.expect( 'Done preparing Mininet', timeout=3600 )
|
||||
log( '* Completed successfully' )
|
||||
vm.expect( prompt )
|
||||
version = getMininetVersion( vm )
|
||||
@@ -681,10 +632,6 @@ def interact( vm, tests, pre='', post='', prompt=Prompt,
|
||||
vm.sendline( "sudo sed -i -e 's/^GRUB_TERMINAL=serial/#GRUB_TERMINAL=serial/' "
|
||||
"/etc/default/grub; sudo update-grub" )
|
||||
vm.expect( prompt )
|
||||
if clean:
|
||||
log( '* Cleaning vm' )
|
||||
vm.sendline( '~/mininet/util/install.sh -d' )
|
||||
vm.expect( prompt )
|
||||
log( '* Shutting down' )
|
||||
vm.sendline( 'sync; sudo shutdown -h now' )
|
||||
log( '* Waiting for EOF/shutdown' )
|
||||
@@ -842,7 +789,7 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
|
||||
post: command line to run in VM after tests
|
||||
prompt: shell prompt (default '$ ')
|
||||
memory: memory size in MB"""
|
||||
global LogFile, Zip, Chown
|
||||
global LogFile, Zip
|
||||
start = time()
|
||||
lstart = localtime()
|
||||
date = strftime( '%y%m%d-%H-%M-%S', lstart)
|
||||
@@ -851,16 +798,14 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
|
||||
if Branch:
|
||||
dir = 'mn-%s-%s-%s' % ( Branch, flavor, date )
|
||||
try:
|
||||
os.mkdir( dir)
|
||||
os.mkdir( dir )
|
||||
except:
|
||||
raise Exception( "Failed to create build directory %s" % dir )
|
||||
if Chown:
|
||||
run( 'chown %s %s' % ( Chown, dir ) )
|
||||
os.chdir( dir )
|
||||
LogFile = open( 'build.log', 'w' )
|
||||
log( '* Logging to', abspath( LogFile.name ) )
|
||||
log( '* Created working directory', dir )
|
||||
image, kernel, initrd, partnum = findBaseImage( flavor )
|
||||
image, kernel, initrd = findBaseImage( flavor )
|
||||
basename = 'mininet-' + flavor
|
||||
volume = basename + '.qcow2'
|
||||
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, volume ) )
|
||||
@@ -870,7 +815,7 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
|
||||
else:
|
||||
logfile = open( flavor + '.log', 'w+' )
|
||||
log( '* Logging results to', abspath( logfile.name ) )
|
||||
vm = boot( volume, kernel, initrd, logfile, memory=memory, partnum=partnum )
|
||||
vm = boot( volume, kernel, initrd, logfile, memory=memory )
|
||||
version = interact( vm, tests=tests, pre=pre, post=post )
|
||||
size = qcow2size( volume )
|
||||
arch = archFor( flavor )
|
||||
@@ -929,22 +874,21 @@ def runTests( vm, tests=None, pre='', post='', prompt=Prompt, uninstallNtpd=Fals
|
||||
|
||||
def getMininetVersion( vm ):
|
||||
"Run mn to find Mininet version in VM"
|
||||
vm.sendline( '(cd ~/mininet; PYTHONPATH=. bin/mn --version)' )
|
||||
vm.sendline( '~/mininet/bin/mn --version' )
|
||||
# Eat command line echo, then read output line
|
||||
vm.readline()
|
||||
version = vm.readline().strip()
|
||||
return version
|
||||
|
||||
|
||||
def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
|
||||
def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
|
||||
runFunction=None, **runArgs ):
|
||||
"""Boot and test VM
|
||||
tests: list of tests to run
|
||||
pre: command line to run in VM before tests
|
||||
post: command line to run in VM after tests
|
||||
prompt: shell prompt (default '$ ')
|
||||
memory: VM memory size in MB
|
||||
cpuCores: number of CPU cores to use"""
|
||||
memory: VM memory size in MB"""
|
||||
bootTestStart = time()
|
||||
basename = path.basename( image )
|
||||
image = abspath( image )
|
||||
@@ -954,7 +898,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
|
||||
log( '* Creating COW disk', cow )
|
||||
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) )
|
||||
log( '* Extracting kernel and initrd' )
|
||||
kernel, initrd, part = extractKernel( image, flavor=basename, imageDir=tmpdir )
|
||||
kernel, initrd = extractKernel( image, flavor=basename, imageDir=tmpdir )
|
||||
if LogToConsole:
|
||||
logfile = stdout
|
||||
else:
|
||||
@@ -962,7 +906,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
|
||||
suffix='.testlog', delete=False )
|
||||
log( '* Logging VM output to', logfile.name )
|
||||
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
|
||||
memory=memory, cpuCores=cpuCores, part=part )
|
||||
memory=memory )
|
||||
login( vm )
|
||||
log( '* Waiting for prompt after login' )
|
||||
vm.expect( prompt )
|
||||
@@ -984,7 +928,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
|
||||
|
||||
def buildFlavorString():
|
||||
"Return string listing valid build flavors"
|
||||
return 'valid build flavors: %s' % ' '.join( sorted( isoURLs ) )
|
||||
return 'valid build flavors: ( %s )' % ' '.join( sorted( isoURLs ) )
|
||||
|
||||
|
||||
def testDict():
|
||||
@@ -1000,16 +944,15 @@ def testDict():
|
||||
|
||||
def testString():
|
||||
"Return string listing valid tests"
|
||||
tests = [ '%s <%s>' % ( name, func.__doc__ )
|
||||
for name, func in testDict().items() ]
|
||||
return 'valid tests: %s' % ', '.join( tests )
|
||||
return 'valid tests: ( %s )' % ' '.join( testDict().keys() )
|
||||
|
||||
|
||||
def parseArgs():
|
||||
"Parse command line arguments and run"
|
||||
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward, Chown
|
||||
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward
|
||||
parser = argparse.ArgumentParser( description='Mininet VM build script',
|
||||
epilog='' )
|
||||
epilog=buildFlavorString() + ' ' +
|
||||
testString() )
|
||||
parser.add_argument( '-v', '--verbose', action='store_true',
|
||||
help='send VM output to console rather than log file' )
|
||||
parser.add_argument( '-d', '--depend', action='store_true',
|
||||
@@ -1029,7 +972,7 @@ def parseArgs():
|
||||
help='Boot and test an existing VM image' )
|
||||
parser.add_argument( '-t', '--test', metavar='test', default=[],
|
||||
action='append',
|
||||
help='specify a test to run; ' + testString() )
|
||||
help='specify a test to run' )
|
||||
parser.add_argument( '-w', '--timeout', metavar='timeout', type=int,
|
||||
default=0, help='set expect timeout' )
|
||||
parser.add_argument( '-r', '--run', metavar='cmd', default='',
|
||||
@@ -1039,20 +982,18 @@ def parseArgs():
|
||||
parser.add_argument( '-b', '--branch', metavar='branch',
|
||||
help='branch to install and/or check out and test' )
|
||||
parser.add_argument( 'flavor', nargs='*',
|
||||
help='VM flavor(s) to build; ' + buildFlavorString() )
|
||||
help='VM flavor(s) to build (e.g. raring32server)' )
|
||||
parser.add_argument( '-z', '--zip', action='store_true',
|
||||
help='archive .ovf and .vmdk into .zip file' )
|
||||
parser.add_argument( '-o', '--out',
|
||||
help='output file for test image (vmdk)' )
|
||||
parser.add_argument( '-f', '--forward', default=[], action='append',
|
||||
help='forward VM ports to local server, e.g. tcp:5555::22' )
|
||||
parser.add_argument( '-u', '--chown', metavar='user',
|
||||
help='specify an owner for build directory' )
|
||||
args = parser.parse_args()
|
||||
if args.depend:
|
||||
depend()
|
||||
if args.list:
|
||||
print( buildFlavorString() )
|
||||
print buildFlavorString()
|
||||
if args.clean:
|
||||
cleanup()
|
||||
if args.verbose:
|
||||
@@ -1069,19 +1010,16 @@ def parseArgs():
|
||||
Forward = args.forward
|
||||
if not args.test and not args.run and not args.post:
|
||||
args.test = [ 'sanity', 'core' ]
|
||||
if args.chown:
|
||||
Chown = args.chown
|
||||
for flavor in args.flavor:
|
||||
if flavor not in isoURLs:
|
||||
print( "Unknown build flavor:", flavor )
|
||||
print( buildFlavorString() )
|
||||
print "Unknown build flavor:", flavor
|
||||
print buildFlavorString()
|
||||
break
|
||||
try:
|
||||
build( flavor, tests=args.test, pre=args.run, post=args.post,
|
||||
memory=args.memory )
|
||||
except Exception as e:
|
||||
log( '* BUILD FAILED with exception: ', e )
|
||||
print_exc( e )
|
||||
exit( 1 )
|
||||
for image in args.image:
|
||||
bootAndRun( image, runFunction=runTests, tests=args.test, pre=args.run,
|
||||
|
||||
@@ -15,7 +15,13 @@ sudo sed -i -e 's/splash//' /etc/default/grub
|
||||
sudo sed -i -e 's/quiet/text/' /etc/default/grub
|
||||
sudo update-grub
|
||||
# Update from official archive
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get update
|
||||
# 12.10 and earlier
|
||||
#sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
|
||||
# /etc/apt/sources.list
|
||||
# 13.04 and later
|
||||
#sudo sed -i -e 's/\/archive.ubuntu.com/\/mirrors.kernel.org/' \
|
||||
# /etc/apt/sources.list
|
||||
# Clean up vmware easy install junk if present
|
||||
if [ -e /etc/issue.backup ]; then
|
||||
sudo mv /etc/issue.backup /etc/issue
|
||||
@@ -24,24 +30,20 @@ if [ -e /etc/rc.local.backup ]; then
|
||||
sudo mv /etc/rc.local.backup /etc/rc.local
|
||||
fi
|
||||
# Fetch Mininet
|
||||
sudo apt-get -y -qq install git-core openssh-server
|
||||
sudo apt-get -y install git-core openssh-server
|
||||
git clone git://github.com/mininet/mininet
|
||||
# Optionally check out branch
|
||||
if [ "$1" != "" ]; then
|
||||
pushd mininet
|
||||
git fetch origin $1
|
||||
git checkout $1
|
||||
popd
|
||||
pushd mininet
|
||||
#git checkout -b $1 $1
|
||||
# TODO branch will in detached HEAD state if it is not master
|
||||
git checkout $1
|
||||
popd
|
||||
fi
|
||||
# Install Mininet for Python2 and Python3
|
||||
APT="sudo apt-get -y -qq"
|
||||
$APT install python3
|
||||
$APT install python2 || $APT install python
|
||||
python --version || $APT install python-is-python3
|
||||
time PYTHON=python2 mininet/util/install.sh -n
|
||||
time PYTHON=python3 mininet/util/install.sh
|
||||
# Install Mininet
|
||||
time mininet/util/install.sh
|
||||
# Finalize VM
|
||||
time mininet/util/install.sh -tcd
|
||||
time mininet/util/install.sh -tc
|
||||
# Ignoring this since NOX classic is deprecated
|
||||
#if ! grep NOX_CORE_DIR .bashrc; then
|
||||
# echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc
|
||||
|
||||
Reference in New Issue
Block a user