Compare commits

..

10 Commits

Author SHA1 Message Date
Bob Lantz 7ce07884f4 Install cleanup callback on load. 2015-06-12 13:06:51 -07:00
Bob Lantz be53ac31d6 Fix CLI for switches, and rename cleanup (in superclass!!) 2015-06-11 00:17:19 -07:00
Bob Lantz 50bfbd0566 Add cleanup callback 2015-06-11 00:17:19 -07:00
Bob Lantz a4d8049661 Rename pid files to make them more distinctive and to facilitate pkill
If there are leftover ovsdb/vswitchd processes, you can kill them with
sudo pkill -9 -f mn.pid
2015-06-11 00:17:19 -07:00
Bob Lantz da4c53b3c9 Add OVSLinkNSh and share shell by default
Note: this changes the semantics somewhat, but it drastically reduces
the number of bash processes that we start up.
2015-06-11 00:17:19 -07:00
Bob Lantz 1b96ff3539 Use 172.123.123.0/24 for control network so we can ping data network 2015-06-11 00:17:19 -07:00
Bob Lantz 93d04123c6 Rename group option to n (number of OVS instances per ovsdb) 2015-06-11 00:17:19 -07:00
Bob Lantz 22655cf9fd We can now use --nat flush=0 2015-06-11 00:17:19 -07:00
Bob Lantz fe82d2c04a Minor changes + allow --switch ovsm,group=16 2015-06-11 00:17:18 -07:00
Bob Lantz 0534932bf3 First version of OVS in shared namespaces 2015-06-11 00:17:18 -07:00
100 changed files with 1543 additions and 2290 deletions
-19
View File
@@ -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:
-22
View File
@@ -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
-51
View File
@@ -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
+6 -14
View File
@@ -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
View File
@@ -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
+27 -66
View File
@@ -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
---
+2 -2
View File
@@ -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
+10 -18
View File
@@ -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." \
+19 -25
View File
@@ -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
+96 -134
View File
@@ -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" +
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
-1
View File
@@ -17,7 +17,6 @@ def clusterSanity():
CLI( net )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
clusterSanity()
+6 -13
View File
@@ -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 -5
View File
@@ -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()
-24
View File
@@ -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 )
+1 -5
View File
@@ -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':
+2 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 )
+1 -2
View File
@@ -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
View File
@@ -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',
+4 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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 )
+3 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+4 -4
View File
@@ -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()
+275
View File
@@ -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 -9
View File
@@ -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
View File
@@ -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()
+1 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+4 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+2 -4
View File
@@ -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' )
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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 )
+1 -2
View File
@@ -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
+3 -7
View File
@@ -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' )
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for bind.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testBind( unittest.TestCase ):
+1 -3
View File
@@ -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
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testControllers( unittest.TestCase ):
+4 -6
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -5,7 +5,7 @@ Test for emptynet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testEmptyNet( unittest.TestCase ):
+3 -3
View File
@@ -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' )
+9 -13
View File
@@ -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
+1 -1
View File
@@ -5,7 +5,7 @@ Test for limit.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testLimit( unittest.TestCase ):
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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 ):
+1 -1
View File
@@ -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
View File
@@ -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()
+3 -4
View File
@@ -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()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multitest.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testMultiTest( unittest.TestCase ):
+1 -1
View File
@@ -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
View File
@@ -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 ):
+1 -1
View File
@@ -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
+1 -1
View File
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testPopen( unittest.TestCase ):
+2 -3
View File
@@ -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' )
+6 -6
View File
@@ -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()
+4 -4
View File
@@ -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()
+4 -6
View File
@@ -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()
+1 -1
View File
@@ -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
View File
@@ -5,7 +5,7 @@ Test for vlanhost.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
from mininet.util import quietRun
+1 -3
View File
@@ -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
View File
@@ -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' )
+2 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 []
+1 -4
View File
@@ -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' )
+3 -4
View File
@@ -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()
+1 -1
View File
@@ -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 ):
-32
View File
@@ -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()
+14 -19
View File
@@ -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()
-40
View File
@@ -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()
+30 -65
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 )
-10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+16 -14
View File
@@ -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