Compare commits

..

1 Commits

Author SHA1 Message Date
Brian O'Connor 993b73c1be Adding --upgrade to fetch the latest from GitHub. 2014-10-31 23:04:15 -07:00
66 changed files with 1763 additions and 2339 deletions
+5 -9
View File
@@ -41,19 +41,16 @@ 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).
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
locally-disabled
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=colorized
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
# Include message's id in output
# Include message's id in outpu
include-ids=yes
# Put messages in a separate file for each module / package specified on the
@@ -194,7 +191,7 @@ additional-builtins=
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp,build
defining-attr-methods=__init__,__new__,setUp
# checks for sign of poor/misdesign:
@@ -267,8 +264,7 @@ int-import-graph=
max-line-length=80
# Maximum number of lines in a module
# XXX 1500 -> 4000 for miniedit.py
max-module-lines=4000
max-module-lines=1500
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
+1 -12
View File
@@ -7,43 +7,32 @@ or send a pull request.
Contributors include:
Mininet Core Team (and alumni)
Mininet Core Team
Bob Lantz
Brandon Heller
Nikhil Handigol
Vimal Jeyakumar
Brian O'Connor
Cody Burkard
Additional Mininet Contributors
Tomasz Buchert
Gustavo Pantuza Coelho Pinto
Fernando Cappi
Ryan Cox
Shaun Crampton
David Erickson
Glen Gibb
Andrew Ferguson
Eder Leao Fernandes
Gregory Gee
Jon Hall
Vitaly Ivanov
Rich Lane
Zi Shen Lim
Murphy McCauley
José Pedro Oliveira
James Page
Rich Lane
Rémy Léone
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Andreas Wundsam
Isaku Yamahata
Baohua Yang
Thanks also to everyone who has submitted issues and pull
requests on github, and to our friendly mininet-discuss
+11 -17
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.2.0
Mininet 2.1.0+
---
The supported installation methods for Mininet are 1) using a
@@ -53,27 +53,19 @@ like to contribute an installation script, we would welcome it!)
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
version of Mininet, use:
cd mininet
git tag
git clone git://github.com/mininet/mininet
git checkout -b 2.1.0 2.1.0
and then
git checkout <release tag>
where <release tag> is the release you want to check out.
If you are running Ubuntu, Debian, or Fedora, you may be able to use
our handy `install.sh` script, which is in `mininet/util`.
If you are running Ubuntu, you may be able to use our handy
`install.sh` script, which is in `mininet/util`.
*WARNING: USE AT YOUR OWN RISK!*
`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.
such as `mininet`, `openflow`, `oftest`, `pox`, etc..
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!
@@ -161,8 +153,10 @@ like to contribute an installation script, we would welcome it!)
* A Linux kernel compiled with network namespace support enabled
* An compatible software switch such as Open vSwitch or
the Linux bridge.
* An OpenFlow implementation (either the reference user or kernel
space implementations, or Open vSwitch.) Appropriate kernel
modules (e.g. tun and ofdatapath for the reference kernel
implementation) must be loaded.
* Python, `bash`, `ping`, `iperf`, etc.
+2 -2
View File
@@ -1,6 +1,6 @@
Mininet 2.2.0 License
Mininet 2.1.0+ License
Copyright (c) 2013-2014 Open Networking Laboratory
Copyright (c) 2013 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+4 -10
View File
@@ -6,7 +6,7 @@ BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
P8IGN = E251,E201,E302,E202
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
DOCDIRS = doc/html doc/latex
@@ -24,8 +24,7 @@ codecheck: $(PYSRC)
util/versioncheck.py
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
# Exclude miniedit from pep8 checking for now
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
errcheck: $(PYSRC)
-echo "Running check for errors only"
@@ -37,11 +36,6 @@ test: $(MININET) $(TEST)
mininet/test/test_nets.py
mininet/test/test_hifi.py
slowtest: $(MININET)
-echo "Running slower tests (walkthrough, examples)"
mininet/test/test_walkthrough.py -v
mininet/examples/test/runner.py -v
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
@@ -51,7 +45,7 @@ install: $(MNEXEC) $(MANPAGES)
python setup.py install
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py develop
@@ -64,7 +58,7 @@ mn.1: $(MN)
mnexec.1: mnexec
help2man -N -n "execution utility for Mininet." \
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
.PHONY: doc
+24 -44
View File
@@ -3,7 +3,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
*The best way to emulate almost any network on your laptop!*
Mininet 2.2.0
Version 2.1.0+
### What is Mininet?
@@ -66,47 +66,33 @@ Mininet includes:
`mn -c`
### New features in this release
### New features in 2.1.0+
This release provides a number of bug fixes as well as
Mininet 2.1.0+ provides a number of bug fixes as well as
several new features, including:
* Improved OpenFlow 1.3 support
* Convenient access to `Mininet()` as a dict of nodes
* X11 tunneling (wireshark in Mininet hosts, finally!)
* Accurate reflection of the `Mininet()` object in the CLI
* Automatically detecting and adjusting resource limits
* Automatic cleanup on failure of the `mn` command
* Preliminary support for running OVS in user space mode
* Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch
* support for installing the OpenFlow 1.3 versions of the reference
user switch and NOX from CPqD
* The ability to import modules from `mininet.examples`
- `mn --switch ovs,protocols=openflow13` starts OVS in 1.3 mode
- `install.sh -w` installs a 1.3-compatible Wireshark dissector using
Loxigen
- `install.sh -y` installs the Ryu 1.3-compatible controller
We have provided several new examples (which can easily be
imported to provide useful functionality) including:
* A new `nodelib.py` node library, and new `Node` types including
`LinuxBridge`, `OVSBridge`, `LinuxRouter` (see `examples/`)
and `NAT`
* A `--nat` option which connects a Mininet network to your LAN using NAT
(For this to work correctly, Mininet's `--ipbase` subnet should not
overlap with any external or internet IP addresses you wish to use)
* An improved MiniEdit GUI (`examples/miniedit.py`) - thanks to
Gregory Gee
* Support for multiple `--custom` arguments to `mn`
* Experimental cluster support - consult the
[documentation](http://docs.mininet.org) for details -
as well as `examples/cluster.py` and an experimental `--cluster`
option for topologies built with the default `Host` and `OVSSwitch`
classes:
`mn --cluster localhost,server1,server2`
* Modeling separate control and data networks: `mininet.examples.controlnet`
* Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat`
* Creating per-host custom directories using bind mounts: `mininet.examples.bind`
Note that examples contain experimental features which might
"graduate" into mainline Mininet in the future, but they should
not be considered a stable part of the Mininet API!
A number of bugs have also been fixed, most notably multiple link
support in `Topo()`. See github issues and the release notes on
the Mininet wiki for additional information.
### Installation
See `INSTALL` for installation instructions and details.
@@ -127,27 +113,21 @@ Mininet mailing list, `mininet-discuss` at:
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
### Join Us
### Contributing
Mininet is an open source project and is currently hosted
at <https://github.com/mininet>. You are encouraged to download
the code, examine it, modify it, and submit bug reports, bug fixes,
feature requests, new features and other issues and pull requests.
Thanks to everyone who has contributed code to the Mininet project
(see CONTRIBUTORS for more info!) It is because of everyone's
hard work that Mininet continues to grow and improve.
### Enjoy Mininet
Thanks to everyone who has contributed to the project
(see CONTRIBUTORS for more info!)
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
The Mininet Core Team:
### Credits
The Mininet 2.1.0+ Team:
* Bob Lantz
* Brian O'Connor
* Cody Burkard
Thanks again to all of the Mininet contributors, particularly Gregory
Gee for his work on MiniEdit.
+45 -93
View File
@@ -25,16 +25,16 @@ 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,
DefaultController,
UserSwitch, OVSSwitch, OVSBridge,
RYU, NOX, RemoteController, DefaultController,
UserSwitch, OVSSwitch,
OVSLegacyKernelSwitch, IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import customConstructor, splitArgs
from mininet.util import custom, customConstructor
from mininet.util import buildTopo
from mininet.util import updateMininet
from functools import partial
@@ -42,7 +42,7 @@ from functools import partial
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
RemoteOVSSwitch, RemoteLink,
SwitchBinPlacer, RandomPlacer )
from mininet.examples.clustercli import ClusterCLI
from mininet.examples.clustercli import DemoCLI as ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
@@ -55,29 +55,27 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'tree': TreeTopo,
'torus': TorusTopo }
SWITCHDEF = 'default'
SWITCHDEF = 'ovsk'
SWITCHES = { 'user': UserSwitch,
'ovs': OVSSwitch,
'ovsbr' : OVSBridge,
'ovs': OVSSwitch,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch,
'lxbr': LinuxBridge,
'default': OVSSwitch }
'lxbr': LinuxBridge }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': partial( CPULimitedHost, sched='rt' ),
'cfs': partial( CPULimitedHost, sched='cfs' ) }
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'default'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'default': DefaultController,
'ryu': RYU,
'default': DefaultController, # Note: replaced below
'none': lambda name: None }
LINKDEF = 'default'
@@ -119,7 +117,6 @@ def version( *_args ):
print "%s" % VERSION
exit()
class MininetRunner( object ):
"Build, setup, and run Mininet."
@@ -133,29 +130,6 @@ class MininetRunner( object ):
self.setup()
self.begin()
def custom( self, _option, _opt_str, value, _parser ):
"""Parse custom file and add params.
option: option e.g. --custom
opt_str: option string e.g. --custom
value: the value the follows the option
parser: option parser instance"""
files = []
if os.path.isfile( value ):
# Accept any single file (including those with commas)
files.append( value )
else:
# Accept a comma-separated list of filenames
files += value.split(',')
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
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' ):
@@ -169,23 +143,26 @@ class MininetRunner( object ):
# Add or modify global variable or class
globals()[ name ] = value
def setNat( self, _option, opt_str, value, parser ):
"Set NAT option(s)"
assert self # satisfy pylint
parser.values.nat = True
# first arg, first char != '-'
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-':
value = parser.rargs.pop( 0 )
_, args, kwargs = splitArgs( opt_str + ',' + value )
parser.values.nat_args = args
parser.values.nat_kwargs = kwargs
def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options."
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
parser.values.nat_args = []
parser.values.nat_kwargs = {}
raise Exception( 'could not find custom file: %s' % fileName )
def parseArgs( self ):
"""Parse command-line args and return options object.
returns: opts parse options dict"""
if '--custom' in sys.argv:
index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1:
filename = sys.argv[ index + 1 ]
self.parseCustomFile( filename )
else:
raise Exception( 'Custom file name not found' )
desc = ( "The %prog utility creates Mininet network from the\n"
"command line. It can create parametrized topologies,\n"
@@ -203,12 +180,9 @@ class MininetRunner( object ):
opts.add_option( '--clean', '-c', action='store_true',
default=False, help='clean and exit' )
opts.add_option( '--custom', action='callback',
callback=self.custom,
type='string',
help='read custom classes or params from .py file(s)'
)
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py' +
'file' )
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
@@ -237,16 +211,13 @@ class MininetRunner( object ):
opts.add_option( '--pin', action='store_true',
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
opts.add_option( '--nat', action='callback', callback=self.setNat,
help="adds a NAT to the topology that"
" connects Mininet hosts to the physical network."
" Warning: This may route any traffic on the machine"
" that uses Mininet's"
" IP subnet into the Mininet network."
" If you need to change"
" 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( '--nat', action='store_true',
default=False, help="adds a NAT to the topology "
"that connects Mininet to the physical network" )
opts.add_option( '--version', action='callback', callback=version )
opts.add_option( '--upgrade', action='callback', callback=updateMininet,
callback_kwargs={ 'version':VERSION },
help='check for Mininet updates' )
opts.add_option( '--cluster', type='string', default=None,
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
@@ -274,35 +245,17 @@ class MininetRunner( object ):
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# Maybe we'll reorganize this someday...
# pylint: disable=too-many-branches,too-many-statements
def begin( self ):
"Create and run mininet."
global CLI
if self.options.clean:
cleanup()
exit()
start = time.time()
if self.options.controller == 'default':
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
if CONTROLLERS[ 'default' ] is None:
if self.options.switch == 'default':
info( '*** No default OpenFlow controller found '
'for default switch!\n' )
info( '*** Falling back to OVS Bridge\n' )
self.options.switch = 'ovsbr'
self.options.controller = 'none'
elif self.options.switch in ( 'ovsbr', 'lxbr' ):
self.options.controller = 'none'
else:
raise Exception( "Could not find a default controller "
"for switch %s" %
self.options.switch )
topo = buildTopo( TOPOS, self.options.topo )
switch = customConstructor( SWITCHES, self.options.switch )
host = customConstructor( HOSTS, self.options.host )
@@ -328,11 +281,11 @@ class MininetRunner( object ):
print "Please specify --innamespace OR --cluster"
exit()
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' )
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
CLI = ClusterCLI
Net = partial( MininetCluster, servers=cluster.split( ',' ),
placement=PLACEMENT[ self.options.placement ] )
@@ -345,13 +298,12 @@ class MininetRunner( object ):
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
if self.options.ensure_value( 'nat', False ):
nat = mn.addNAT( *self.options.nat_args,
**self.options.nat_kwargs )
if self.options.nat:
nat = mn.addNAT()
nat.configDefault()
if self.options.pre:
cli( mn, script=self.options.pre )
CLI( mn, script=self.options.pre )
test = self.options.test
test = ALTSPELLING.get( test, test )
@@ -366,13 +318,13 @@ class MininetRunner( object ):
mn.ping()
mn.iperf()
elif test == 'cli':
cli( mn )
CLI( mn )
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
if self.options.post:
cli( mn, script=self.options.post )
CLI( mn, script=self.options.post )
mn.stop()
+3 -43
View File
@@ -11,31 +11,6 @@ Mininet's Python API.
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
#### bind.py:
This example shows how you can create private directories for each
node in a Mininet topology.
#### cluster.py:
This example contains all of the code for experimental cluster
edition. Remote classes and MininetCluster can be imported from
here to create a topology with nodes on remote machines.
#### clusterSanity.py:
This example runs cluster edition locally as a sanity check to test
basic functionality.
#### clustercli.py:
This example contains a CLI for experimental cluster edition.
#### clusterdemo.py:
This example is a basic demo of cluster edition on 3 servers with
a tree topology of depth 3 and fanout 3.
#### consoles.py:
This example creates a grid of console windows, one for each node,
@@ -72,11 +47,6 @@ topology object) and adding nodes to it.
This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
#### intfoptions.py:
This example reconfigures a TCIntf during runtime with different
traffic control commands to test bandwidth, loss, and delay.
#### limit.py:
This example shows how to use link and CPU limits.
@@ -86,7 +56,7 @@ This example shows how to use link and CPU limits.
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
#### linuxrouter.py:
### linuxrouter.py:
This example shows how to create and configure a router in Mininet
that uses Linux IP forwarding.
@@ -95,16 +65,11 @@ that uses Linux IP forwarding.
This example demonstrates creating a network via a graphical editor.
#### mobility.py:
#### mobility.py
This example demonstrates detaching an interface from one switch and
attaching it another as a basic way to move a host around a network.
#### multiLink.py:
This example demonstrates the creation of multiple links between
nodes using a custom Topology class.
#### multiping.py:
This example demonstrates one method for
@@ -124,12 +89,7 @@ This example shows how to connect a Mininet network to the Internet
using NAT. It also answers the eternal question "why can't I ping
`google.com`?"
#### natnet.py:
This example demonstrates how to create a network using a NAT node
to connect hosts to the internet.
#### numberedports.py:
#### numberedports.py
This example verifies the mininet ofport numbers match up to the ovs port numbers.
It also verifies that the port numbers match up to the interface numbers
+14 -9
View File
@@ -22,7 +22,7 @@ to temporary private directories. To do this, simply create a list of
directories to be made private. A tmpfs will then be mounted on them.
You may use both temporary and persistent directories at the same
time. In the following privateDirs string, each host will have a
time. In the following privateDirs string, each host will have a
persistent directory in the root filesystem at
"/tmp/(hostname)/var/run" mounted on "/var/run". Each host will also
have a temporary private directory mounted on "/var/log".
@@ -35,10 +35,10 @@ on '/var/mn'
"""
from mininet.net import Mininet
from mininet.node import Host
from mininet.node import Host, HostWithPrivateDirs
from mininet.cli import CLI
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel, info, debug
from functools import partial
@@ -48,16 +48,19 @@ from functools import partial
def testHostWithPrivateDirs():
"Test bind mounts"
topo = SingleSwitchTopo( 10 )
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/var/run', '/tmp/%(name)s/var/run' ),
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/var/run', '/tmp/%(name)s/var/run' ),
'/var/mn' ]
host = partial( Host,
host = partial( HostWithPrivateDirs,
privateDirs=privateDirs )
net = Mininet( topo=topo, host=host )
net.start()
directories = [ directory[ 0 ] if isinstance( directory, tuple )
else directory for directory in privateDirs ]
info( 'Private Directories:', directories, '\n' )
directories = []
for directory in privateDirs:
directories.append( directory[ 0 ]
if isinstance( directory, tuple )
else directory )
info( 'Private Directories:', directories, '\n' )
CLI( net )
net.stop()
@@ -65,3 +68,5 @@ if __name__ == '__main__':
setLogLevel( 'info' )
testHostWithPrivateDirs()
info( 'Done.\n')
+81 -107
View File
@@ -83,11 +83,11 @@ from mininet.util import quietRun, makeIntfPair, errRun, retry
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from signal import signal, SIGINT, SIG_IGN
from signal import signal, SIGINT, SIGHUP, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
import os
from random import randrange
import sys
from sys import exit
import re
from distutils.version import StrictVersion
@@ -112,65 +112,68 @@ class RemoteMixin( object ):
'-o', 'BatchMode=yes',
'-o', 'ForwardAgent=yes', '-tt' ]
def __init__( self, name, server='localhost', user=None, serverIP=None,
controlPath=False, splitInit=False, **kwargs):
def __init__( self, name, server=None, user=None, serverIP=None,
controlPath='/tmp/mn-%r@%h:%p', splitInit=False, **kwargs):
"""Instantiate a remote node
name: name of remote node
server: remote server (optional)
user: user on remote server (optional)
controlPath: specify shared ssh control path (optional)
controlPath: ssh control path template (optional)
splitInit: split initialization?
**kwargs: see Node()"""
# We connect to servers by IP address
self.server = server if server else 'localhost'
self.serverIP = ( serverIP if serverIP
else self.findServerIP( self.server ) )
if server == 'localhost':
server = None
self.server = server
self.serverIP = serverIP if serverIP else self.findServerIP( server )
self.user = user if user else self.findUser()
if controlPath is True:
# Set a default control path for shared SSH connections
controlPath = '/tmp/mn-%r@%h:%p'
self.controlPath = controlPath
self.splitInit = splitInit
if self.user and self.server != 'localhost':
if self.user and self.server:
self.dest = '%s@%s' % ( self.user, self.serverIP )
else:
self.dest = None
self.controlPath = controlPath
self.sshcmd = []
if self.dest:
self.sshcmd = [ 'sudo', '-E', '-u', self.user ] + self.sshbase
if self.controlPath:
self.sshcmd += [ '-o', 'ControlPath=' + self.controlPath,
'-o', 'ControlMaster=auto',
'-o', 'ControlPersist=' + '1' ]
'-o', 'ControlMaster=auto' ]
self.sshcmd = self.sshcmd + [ self.dest ]
self.isRemote = True
else:
self.dest = None
self.sshcmd = []
self.isRemote = False
# Satisfy pylint
self.shell, self.pid = None, None
self.splitInit = splitInit
super( RemoteMixin, self ).__init__( name, **kwargs )
@staticmethod
def findUser():
"Try to return logged-in (usually non-root) user"
return (
try:
# If we're running sudo
os.environ.get( 'SUDO_USER', False ) or
# 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' ) )
return os.environ[ 'SUDO_USER' ]
except:
try:
# Logged-in user (if we have a tty)
return quietRun( 'who am i' ).split()[ 0 ]
except:
# Give up and return effective user
return quietRun( 'whoami' )
# Determine IP address of local host
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
@classmethod
def findServerIP( cls, server ):
def findServerIP( cls, server, intf='eth0' ):
"Return our server's IP address"
# First, check for an IP address
ipmatch = cls._ipMatchRegex.findall( server )
if ipmatch:
return ipmatch[ 0 ]
# Otherwise, look up remote server
output = quietRun( 'getent ahostsv4 %s' % server )
# Check for this server
if not server:
output = quietRun( 'ifconfig %s' % intf )
# Otherwise, handle remote server
else:
# First, check for an IP address
if server:
ipmatch = cls._ipMatchRegex.findall( server )
if ipmatch:
return ipmatch[ 0 ]
# Otherwise, look up remote server
output = quietRun( 'getent ahostsv4 %s' % server )
ips = cls._ipMatchRegex.findall( output )
ip = ips[ 0 ] if ips else None
return ip
@@ -178,16 +181,15 @@ class RemoteMixin( object ):
# Command support via shell process in namespace
def startShell( self, *args, **kwargs ):
"Start a shell process for running commands"
if self.isRemote:
if self.dest:
kwargs.update( mnopts='-c' )
super( RemoteMixin, self ).startShell( *args, **kwargs )
# Optional split initialization
self.sendCmd( 'echo $$' )
if not self.splitInit:
self.finishInit()
if self.splitInit:
self.sendCmd( 'echo $$' )
else:
self.pid = int( self.cmd( 'echo $$' ) )
def finishInit( self ):
"Wait for split initialization to complete"
self.pid = int( self.waitOutput() )
def rpopen( self, *cmd, **opts ):
@@ -229,7 +231,7 @@ class RemoteMixin( object ):
returns: Popen() object"""
if type( cmd ) is str:
cmd = cmd.split()
if self.isRemote:
if self.dest:
if sudo:
cmd = [ 'sudo', '-E' ] + cmd
if tt:
@@ -254,18 +256,9 @@ class RemoteMixin( object ):
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
return super( RemoteMixin,
self).addIntf( *args,
moveIntfFn=RemoteLink.moveIntf,
**kwargs )
return super( RemoteMixin, self).addIntf( *args,
moveIntfFn=RemoteLink.moveIntf, **kwargs )
def cleanup( self ):
"Help python collect its garbage."
# Intfs may end up in root NS
for intfName in self.intfNames():
if self.name in intfName:
self.rcmd( 'ip link del ' + intfName )
self.shell = None
class RemoteNode( RemoteMixin, Node ):
"A node on a remote server"
@@ -279,23 +272,20 @@ class RemoteHost( RemoteNode ):
class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
"Remote instance of Open vSwitch"
OVSVersions = {}
def isOldOVS( self ):
"Is remote switch using an old OVS version?"
cls = type( self )
if self.server not in cls.OVSVersions:
# pylint: disable=not-callable
vers = self.cmd( 'ovs-vsctl --version' )
# pylint: enable=not-callable
cls.OVSVersions[ self.server ] = re.findall(
r'\d+\.\d+', vers )[ 0 ]
cls.OVSVersions[ self.server ] = re.findall( '\d+\.\d+', vers )[ 0 ]
return ( StrictVersion( cls.OVSVersions[ self.server ] ) <
StrictVersion( '1.10' ) )
StrictVersion( '1.10' ) )
class RemoteLink( Link ):
"A RemoteLink is a link between nodes which may be on different servers"
def __init__( self, node1, node2, **kwargs ):
@@ -307,7 +297,6 @@ class RemoteLink( Link ):
self.tunnel = None
kwargs.setdefault( 'params1', {} )
kwargs.setdefault( 'params2', {} )
self.cmd = None # satisfy pylint
Link.__init__( self, node1, node2, **kwargs )
def stop( self ):
@@ -323,18 +312,17 @@ class RemoteLink( Link ):
(override this method [and possibly delete()]
to change link type)"""
node1, node2 = self.node1, self.node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == 'localhost' and server2 == 'localhost':
server1 = getattr( node1, 'server', None )
server2 = getattr( node2, 'server', None )
if not server1 and not server2:
# Local link
return makeIntfPair( intfname1, intfname2, addr1, addr2 )
elif server1 == server2:
# Remote link on same remote server
return makeIntfPair( intfname1, intfname2, addr1, addr2,
runCmd=node1.rcmd )
run=node1.rcmd )
# Otherwise, make a tunnel
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
addr1, addr2 )
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
return self.tunnel
@staticmethod
@@ -348,23 +336,16 @@ class RemoteLink( Link ):
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
node.rcmd( cmd )
links = node.cmd( 'ip link show' )
if not ' %s:' % intf in links:
if not ( ' %s:' % intf ) in links:
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
' 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 != '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( 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
@@ -388,21 +369,21 @@ class RemoteLink( Link ):
if ch != '@':
error( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
'command was:', cmd, '\n' )
tunnel.terminate()
tunnel.wait()
error( ch + tunnel.stdout.read() )
error( tunnel.stderr.read() )
sys.exit( 1 )
exit( 1 )
# 3. Move interfaces if necessary
for node in node1, node2:
if node.inNamespace:
retry( 3, .01, RemoteLink.moveIntf, 'tap9', node )
# 4. Rename tap interfaces to desired names
for node, intf, addr in ( ( node1, intfname1, addr1 ),
( node2, intfname2, addr2 ) ):
( node2, intfname2, addr2 ) ):
if not addr:
node.cmd( 'ip link set tap9 name', intf )
else:
@@ -429,9 +410,9 @@ class RemoteLink( Link ):
class Placer( object ):
"Node placement algorithm for MininetCluster"
def __init__( self, servers=None, nodes=None, hosts=None,
switches=None, controllers=None, links=None ):
switches=None, controllers=None, links=None ):
"""Initialize placement object
servers: list of servers
nodes: list of all nodes
@@ -450,9 +431,8 @@ class Placer( object ):
def place( self, node ):
"Return server for a given node"
assert self, node # satisfy pylint
# Default placement: run locally
return 'localhost'
return None
class RandomPlacer( Placer ):
@@ -460,7 +440,6 @@ class RandomPlacer( Placer ):
def place( self, nodename ):
"""Random placement function
nodename: node name"""
assert nodename # please pylint
# This may be slow with lots of servers
return self.servers[ randrange( 0, len( self.servers ) ) ]
@@ -469,7 +448,7 @@ class RoundRobinPlacer( Placer ):
"""Round-robin placement
Note this will usually result in cross-server links between
hosts and switches"""
def __init__( self, *args, **kwargs ):
Placer.__init__( self, *args, **kwargs )
self.next = 0
@@ -477,7 +456,6 @@ class RoundRobinPlacer( Placer ):
def place( self, nodename ):
"""Round-robin placement function
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 )
@@ -496,7 +474,7 @@ class SwitchBinPlacer( Placer ):
self.sset = frozenset( self.switches )
self.cset = frozenset( self.controllers )
# Server and switch placement indices
self.placement = self.calculatePlacement()
self.placement = self.calculatePlacement()
@staticmethod
def bin( nodes, servers ):
@@ -563,7 +541,7 @@ class HostSwitchBinPlacer( Placer ):
scount = len( self.servers )
self.hbin = max( int( len( self.hosts ) / scount ), 1 )
self.sbin = max( int( len( self.switches ) / scount ), 1 )
self.cbin = max( int( len( self.controllers ) / scount ), 1 )
self.cbin = max( int( len( self.controllers ) / scount ) , 1 )
info( 'scount:', scount )
info( 'bins:', self.hbin, self.sbin, self.cbin, '\n' )
self.servdict = dict( enumerate( self.servers ) )
@@ -571,7 +549,7 @@ class HostSwitchBinPlacer( Placer ):
self.sset = frozenset( self.switches )
self.cset = frozenset( self.controllers )
self.hind, self.sind, self.cind = 0, 0, 0
def place( self, nodename ):
"""Simple placement algorithm:
place nodes into evenly sized bins"""
@@ -591,6 +569,7 @@ class HostSwitchBinPlacer( Placer ):
return server
# The MininetCluster class is not strictly necessary.
# However, it has several purposes:
# 1. To set up ssh connection sharing/multiplexing
@@ -617,8 +596,8 @@ class MininetCluster( Mininet ):
'link': RemoteLink,
'precheck': True }
params.update( kwargs )
servers = params.pop( 'servers', [ 'localhost' ] )
servers = [ s if s else 'localhost' for s in servers ]
servers = params.pop( 'servers', [ None ] )
servers = [ s if s != 'localhost' else None for s in servers ]
self.servers = servers
self.serverIP = params.pop( 'serverIP', {} )
if not self.serverIP:
@@ -636,7 +615,6 @@ class MininetCluster( Mininet ):
def popen( self, cmd ):
"Popen() for server connections"
assert self # please pylint
old = signal( SIGINT, SIG_IGN )
conn = Popen( cmd, stdin=PIPE, stdout=PIPE, close_fds=True )
signal( SIGINT, old )
@@ -654,13 +632,13 @@ class MininetCluster( Mininet ):
for server in self.servers:
ip = self.serverIP[ server ]
if not server or server == 'localhost':
continue
continue
info( server, '' )
dest = '%s@%s' % ( self.user, ip )
cmd = [ 'sudo', '-E', '-u', self.user ]
cmd += self.sshcmd + [ '-n', dest, 'sudo true' ]
debug( ' '.join( cmd ), '\n' )
_out, _err, code = errRun( cmd )
out, err, code = errRun( cmd )
if code != 0:
error( '\nstartConnection: server connection check failed '
'to %s using command:\n%s\n'
@@ -668,19 +646,18 @@ class MininetCluster( Mininet ):
result |= code
if result:
error( '*** Server precheck failed.\n'
'*** Make sure that the above ssh command works'
' correctly.\n'
'*** Make sure that the above ssh command works correctly.\n'
'*** You may also need to run mn -c on all nodes, and/or\n'
'*** use sudo -E.\n' )
sys.exit( 1 )
exit( 1 )
info( '\n' )
def modifiedaddHost( self, *args, **kwargs ):
"Slightly modify addHost"
assert self # please pylint
kwargs[ 'splitInit' ] = True
return Mininet.addHost( *args, **kwargs )
def placeNodes( self ):
"""Place nodes on servers (if they don't have a server), and
start shell processes"""
@@ -694,10 +671,7 @@ class MininetCluster( Mininet ):
switches=self.topo.switches(),
links=self.topo.links() )
for node in nodes:
config = self.topo.nodeInfo( node )
# keep local server name consistent accross nodes
if 'server' in config.keys() and config[ 'server' ] is None:
config[ 'server' ] = 'localhost'
config = self.topo.node_info[ node ]
server = config.setdefault( 'server', placer.place( node ) )
if server:
config.setdefault( 'serverIP', self.serverIP[ server ] )
@@ -715,7 +689,7 @@ class MininetCluster( Mininet ):
if ( isinstance( controller, Controller)
and controller.IP() == '127.0.0.1'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
Intf( 'eth0', node=controller ).updateIP()
return controller
def buildFromTopo( self, *args, **kwargs ):
@@ -806,7 +780,7 @@ def testRemoteTopo():
"Test remote Node classes using Mininet()/Topo() API"
topo = LinearTopo( 2 )
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
link=RemoteLink, controller=ClusterController )
link=RemoteLink, controller=ClusterController )
net.start()
net.pingAll()
net.stop()
+4 -4
View File
@@ -5,18 +5,18 @@ A sanity check for cluster edition
'''
from mininet.examples.cluster import MininetCluster
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
from mininet.log import info, setLogLevel
from mininet.examples.clustercli import DemoCLI as CLI
from mininet.topo import SingleSwitchTopo
def clusterSanity():
"Sanity check for cluster mode"
topo = SingleSwitchTopo()
net = MininetCluster( topo=topo )
net = MininetCluster( topo=topo )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
if __name__ == '__main__':
setLogLevel( 'info' )
clusterSanity()
+17 -22
View File
@@ -5,38 +5,34 @@
from mininet.cli import CLI
from mininet.log import output, error
# pylint: disable=global-statement
nx, graphviz_layout, plt = None, None, None # Will be imported on demand
class ClusterCLI( CLI ):
class DemoCLI( CLI ):
"CLI with additional commands for Cluster Edition demo"
@staticmethod
def colorsFor( seq ):
"Return a list of background colors for a sequence"
colors = [ 'red', 'lightgreen', 'cyan', 'yellow', 'orange',
'magenta', 'pink', 'grey', 'brown',
'white' ]
'magenta', 'pink', 'grey', 'brown',
'white' ]
slen, clen = len( seq ), len( colors )
reps = max( 1, slen / clen )
colors = colors * reps
colors = colors[ 0 : slen ]
return colors
def do_plot( self, _line ):
def do_plot( self, line ):
"Plot topology colored by node placement"
# Import networkx if needed
global nx, plt
if not nx:
try:
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
plt = pyplot # satisfiy pylint
import networkx as nx
import matplotlib.pyplot as plt
import pygraphviz
assert pygraphviz # silence pyflakes
except ImportError:
except:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
return
@@ -44,6 +40,7 @@ class ClusterCLI( CLI ):
g = nx.Graph()
mn = self.mn
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
hlen, slen = len( hosts ), len( switches )
nodes = hosts + switches
g.add_nodes_from( nodes )
links = [ ( link.intf1.node, link.intf2.node )
@@ -55,14 +52,11 @@ class ClusterCLI( CLI ):
# Plot it!
pos = nx.graphviz_layout( g )
opts = { 'ax': None, 'font_weight': 'bold',
'width': 2, 'edge_color': 'darkblue' }
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
for h in hosts ]
scolors = [ color[ getattr( s, 'server', 'localhost' ) ]
for s in switches ]
nx.draw_networkx( g, pos=pos, nodelist=hosts, node_size=800,
label='host', node_color=hcolors, node_shape='s',
**opts )
'width': 2, 'edge_color': 'darkblue' }
hcolors = [ color[ h.server ] for h in hosts ]
scolors = [ color[ s.server ] for s in switches ]
nx.draw_networkx( g, pos=pos, nodelist=hosts, node_size=800, label='host',
node_color=hcolors, node_shape='s', **opts )
nx.draw_networkx( g, pos=pos, nodelist=switches, node_size=1000,
node_color=scolors, node_shape='o', **opts )
# Get rid of axes, add title, and show
@@ -74,7 +68,7 @@ class ClusterCLI( CLI ):
plt.title( 'Node Placement', fontweight='bold' )
plt.show()
def do_status( self, _line ):
def do_status( self, line ):
"Report on node shell status"
nodes = self.mn.hosts + self.mn.switches
for node in nodes:
@@ -88,7 +82,8 @@ class ClusterCLI( CLI ):
else:
output( 'All nodes are still running.\n' )
def do_placement( self, _line ):
def do_placement( self, line ):
"Describe node placement"
mn = self.mn
nodes = mn.hosts + mn.switches + mn.controllers
+2 -1
View File
@@ -5,7 +5,7 @@
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
from mininet.examples.clustercli import DemoCLI as CLI
def demo():
"Simple Demo of Cluster Mode"
@@ -20,3 +20,4 @@ def demo():
if __name__ == '__main__':
setLogLevel( 'info' )
demo()
+1 -1
View File
@@ -327,7 +327,7 @@ class ConsoleApp( Frame ):
elif units[0] == 'b':
val *= 10 ** -9
self.updates += 1
self.bw += val
self.bw += val
if self.updates >= self.hostCount:
self.graph.addBar( self.bw )
self.bw = 0
+2 -2
View File
@@ -35,7 +35,7 @@ class DataController( Controller ):
class MininetFacade( object ):
"""Mininet object facade that allows a single CLI to
talk to one or more networks"""
def __init__( self, net, *args, **kwargs ):
"""Create MininetFacade object.
net: Primary Mininet object
@@ -114,7 +114,7 @@ class ControlNetwork( Topo ):
def run():
"Create control and data networks, and invoke the CLI"
info( '* Creating Control Network\n' )
ctopo = ControlNetwork( n=4, dataController=DataController )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
+2 -2
View File
@@ -8,8 +8,9 @@ from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, waitListening
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel, output, info
from time import sleep
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
"""Example/test of link and CPU bandwidth limits
@@ -27,7 +28,6 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
cpu=cpu )
try:
net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except
except:
info( '*** Skipping host %s\n' % sched )
break
+1 -2
View File
@@ -5,8 +5,7 @@ This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
"""
import re
import sys
import re, sys
from mininet.cli import CLI
from mininet.log import setLogLevel, info, error
@@ -19,9 +19,9 @@ def intfOptions():
h2 = net.addHost( 'h2' )
s1 = net.addSwitch( 's1' )
link1 = net.addLink( h1, s1, cls=TCLink )
net.addLink( h2, s1 )
link2 = net.addLink( h2, s1 )
net.start()
# flush out latency from reactive forwarding delay
net.pingAll()
@@ -34,12 +34,12 @@ def intfOptions():
link1.intf1.config( loss=50 )
info( '\n' )
net.iperf( ( h1, h2 ), l4Type='UDP' )
info( '\n*** Configuring one intf with delay of 15ms\n' )
link1.intf1.config( delay='15ms' )
info( '\n*** Run a ping to confirm delay\n' )
net.pingPairFull()
info( '\n*** Done testing\n' )
net.stop()
+2 -4
View File
@@ -27,11 +27,9 @@ def limit( bw=10, cpu=.1 ):
info( '*** Testing with', sched, 'bandwidth limiting\n' )
if sched == 'rt':
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s'
% release )
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
info( '*** RT Scheduler is not enabled in your kernel. '
'Skipping this test\n' )
info( '*** RT Scheduler is not enabled in your kernel. Skipping this test\n' )
continue
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
net = Mininet( topo=myTopo, intf=intf, host=host )
+2 -6
View File
@@ -27,7 +27,7 @@ from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
from mininet.log import lg
from mininet.util import irange, quietRun
from mininet.util import irange
from mininet.link import TCLink
from functools import partial
@@ -78,10 +78,6 @@ def linearBandwidthTest( lengths ):
topo = LinearTestTopo( hostCount )
# Select TCP Reno
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath in switches.keys():
print "*** testing", datapath, "datapath"
Switch = switches[ datapath ]
@@ -101,7 +97,7 @@ def linearBandwidthTest( lengths ):
# since the reference controller is reactive
src.cmd( 'telnet', dst.IP(), '5001' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ], seconds=10 )
bandwidth = net.iperf( [ src, dst ] )
print bandwidth
flush()
results[ datapath ] += [ ( n, bandwidth ) ]
+11 -17
View File
@@ -2,7 +2,7 @@
"""
linuxrouter.py: Example network with Linux IP router
This example converts a Node into a router using IP forwarding
already built into Linux.
@@ -16,7 +16,7 @@ The topology contains a router with three IP subnets:
- h2 (IP: 172.16.0.100)
- h3 (IP: 10.0.0.100)
Routing entries can be added to the routing tables of the
Routing entries can be added to the routing tables of the
hosts or router using the "ip route add" or "route add" command.
See the man pages for more details.
@@ -27,6 +27,7 @@ from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
from mininet.util import irange
class LinuxRouter( Node ):
"A Node with IP forwarding enabled."
@@ -44,25 +45,18 @@ class LinuxRouter( Node ):
class NetworkTopo( Topo ):
"A simple topology of a router with three subnets (one host in each)."
def build( self, **_opts ):
def build( self, n=2, h=1, **opts ):
router = self.addNode( 'r0', cls=LinuxRouter, ip='192.168.1.1/24' )
h1 = self.addHost( 'h1', ip='192.168.1.100/24',
defaultRoute='via 192.168.1.1' )
h2 = self.addHost( 'h2', ip='172.16.0.100/12',
defaultRoute='via 172.16.0.1' )
h3 = self.addHost( 'h3', ip='10.0.0.100/8',
defaultRoute='via 10.0.0.1' )
self.addLink( h1, router, intfName2='r0-eth1',
params2={ 'ip' : '192.168.1.1/24' } )
self.addLink( h2, router, intfName2='r0-eth2',
params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( h3, router, intfName2='r0-eth3',
params2={ 'ip' : '10.0.0.1/8' } )
h1 = self.addHost( 'h1', ip='192.168.1.100/24', defaultRoute='via 192.168.1.1' )
h2 = self.addHost( 'h2', ip='172.16.0.100/12', defaultRoute='via 172.16.0.1' )
h3 = self.addHost( 'h3', ip='10.0.0.100/8', defaultRoute='via 10.0.0.1' )
self.addLink( h1, router, intfName2='r0-eth1', params2={ 'ip' : '192.168.1.1/24' } )
self.addLink( h2, router, intfName2='r0-eth2', params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( h3, router, intfName2='r0-eth3', params2={ 'ip' : '10.0.0.1/8' } )
def run():
"Test linux router"
topo = NetworkTopo()
net = Mininet( topo=topo, controller=None ) # no controller needed
net = Mininet( topo=topo, controller=None ) # no controller needed
net.start()
info( '*** Routing Table on Router\n' )
print net[ 'r0' ].cmd( 'route' )
+905 -910
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -22,9 +22,11 @@ to-do:
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.util import quietRun
from mininet.log import output, warn
from random import randint
from re import findall
class MobilitySwitch( OVSSwitch ):
@@ -59,7 +61,7 @@ class MobilitySwitch( OVSSwitch ):
def validatePort( self, intf ):
"Validate intf's OF port number"
ofport = int( self.cmd( 'ovs-vsctl get Interface', intf,
'ofport' ) )
'ofport' ) )
if ofport != self.ports[ intf ]:
warn( 'WARNING: ofport for', intf, 'is actually', ofport,
'\n' )
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/python
"""
This is a simple example that demonstrates multiple links
between nodes.
"""
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.topo import Topo
def runMultiLink():
"Create and run multiple link network"
topo = simpleMultiLinkTopo( n=2 )
net = Mininet( topo=topo )
net.start()
CLI( net )
net.stop()
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
def __init__( self, n, **kwargs ):
Topo.__init__( self, **kwargs )
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
s1 = self.addSwitch( 's1' )
for _ in range( n ):
self.addLink( s1, h1 )
self.addLink( s1, h2 )
if __name__ == '__main__':
setLogLevel( 'info' )
runMultiLink()
+1 -1
View File
@@ -22,7 +22,7 @@ def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
subnet: Mininet subnet (default 10.0/8)="""
# Identify the interface connecting to the mininet network
localIntf = root.defaultIntf()
localIntf = root.defaultIntf()
# Flush any currently active rules
root.cmd( 'iptables -F' )
+6 -5
View File
@@ -14,7 +14,7 @@ natnet.py: Example network with NATs
| |
s1 s2
| |
h1 h2
h1 h2
"""
@@ -27,7 +27,7 @@ from mininet.util import irange
class InternetTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
def __init__(self, n=2, h=1, **opts):
Topo.__init__(self, **opts)
# set up inet switch
@@ -44,15 +44,15 @@ class InternetTopo(Topo):
localSubnet = '192.168.%d.0/24' % i
natParams = { 'ip' : '%s/24' % localIP }
# add NAT to topology
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
inetIntf=inetIntf, localIntf=localIntf)
switch = self.addSwitch('s%d' % i)
# connect NAT to inet and local switches
self.addLink(nat, inetSwitch, intfName1=inetIntf)
self.addLink(nat, switch, intfName1=localIntf, params1=natParams)
# add host and connect to local switch
host = self.addHost('h%d' % i,
ip='192.168.%d.100/24' % i,
host = self.addHost('h%d' % i,
ip='192.168.%d.100/24' % i,
defaultRoute='via %s' % localIP)
self.addLink(host, switch)
@@ -67,3 +67,4 @@ def run():
if __name__ == '__main__':
setLogLevel('info')
run()
+14 -17
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python
"""
Create a network with 5 hosts, numbered 1-4 and 9.
Create a network with 5 hosts, numbered 1-4 and 9.
Validate that the port numbers match to the interface name,
and that the ovs ports match the mininet ports.
"""
@@ -9,23 +9,21 @@ 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
from mininet.node import Node
def validatePort( switch, intf ):
"Validate intf's OF port number"
ofport = int( switch.cmd( 'ovs-vsctl get Interface', intf,
'ofport' ) )
'ofport' ) )
if ofport != switch.ports[ intf ]:
warn( 'WARNING: ofport for', intf, 'is actually', ofport, '\n' )
return 0
else:
return 1
def testPortNumbering():
def net():
"""Test port numbering:
Create a network with 5 hosts (using Mininet's
mid-level API) and check that implicit and
explicit port numbering works as expected."""
"Create a network with 5 hosts."
net = Mininet( controller=Controller )
@@ -48,25 +46,23 @@ def testPortNumbering():
net.addLink( h2, s1 )
net.addLink( h3, s1 )
net.addLink( h4, s1 )
# specify a different port to connect host 5 to on the switch.
net.addLink( h5, s1, port1=1, port2= 9)
net.addLink( h5, s1, port1 = 1, port2 = 9 ) # specify a different port to connect host 5 to on the switch.
root = Node( 'root', inNamespace=False )
info( '*** Starting network\n' )
net.start()
# print the interfaces and their port numbers
info( '\n*** printing and validating the ports '
'running on each interface\n' )
info( '\n*** printing and validating the ports running on each interface\n' )
for intfs in s1.intfList():
if not intfs.name == "lo":
info( intfs, ': ', s1.ports[intfs],
'\n' )
info( 'Validating that', intfs,
'is actually on port', s1.ports[intfs], '... ' )
info( intfs, ': ', s1.ports[intfs],
'\n' )
info ( 'Validating that', intfs, 'is actually on port', s1.ports[intfs], '... ' )
if validatePort( s1, intfs ):
info( 'Validated.\n' )
print '\n'
# test the network with pingall
net.pingAll()
print '\n'
@@ -76,4 +72,5 @@ def testPortNumbering():
if __name__ == '__main__':
setLogLevel( 'info' )
testPortNumbering()
net()
+3 -3
View File
@@ -33,14 +33,14 @@ def perfTest():
"Create network and run simple performance test"
topo = SingleSwitchTopo( n=4 )
net = Mininet( topo=topo,
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
net.start()
print "Dumping host connections"
dumpNodeConnections(net.hosts)
print "Testing bandwidth between h1 and h4"
h1, h4 = net.getNodeByName('h1', 'h4')
net.iperf( ( h1, h4 ), l4Type='UDP' )
results = net.iperf( ( h1, h4 ), l4Type='UDP' )
net.stop()
if __name__ == '__main__':
+2 -2
View File
@@ -81,6 +81,6 @@ if __name__ == '__main__':
net = TreeNet( depth=1, fanout=4 )
# get sshd args from the command line or use default args
# useDNS=no -u0 to avoid reverse DNS lookup timeout
argvopts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
'-D -o UseDNS=no -u0' )
sshd( net, opts=argvopts )
sshd( net, opts=opts )
+1 -1
View File
@@ -36,7 +36,7 @@ class testBareSSHD( unittest.TestCase ):
'-o StrictModes=no' )
p = pexpect.spawn( cmd )
runOpts = [ 'You may now ssh into h1 at 10.0.0.1',
'after 5 seconds, h1 is not listening on port 22',
'after 5 seconds, h1 is not listening on port 22',
pexpect.EOF, pexpect.TIMEOUT ]
while True:
index = p.expect( runOpts )
+3 -3
View File
@@ -6,7 +6,7 @@ Test for cpu.py
results format:
sched cpu client MB/s
cfs 45.00% 13254.669841
cfs 40.00% 11822.441399
cfs 30.00% 5112.963009
@@ -28,13 +28,13 @@ class testCPU( unittest.TestCase ):
"Verify that CPU utilization is monotonically decreasing for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
# matches each line from results( shown above )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
sched = p.match.group( 1 )
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
bw = float( p.match.group( 3 ) )
if sched not in scheds:
+1 -1
View File
@@ -19,7 +19,7 @@ class testEmptyNet( unittest.TestCase ):
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# iperf test
p.sendline( 'iperf' )
@@ -12,7 +12,7 @@ class testIntfOptions( unittest.TestCase ):
def testIntfOptions( self ):
"verify that intf.config is correctly limiting traffic"
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
p = pexpect.spawn( 'python -m mininet.examples.intfOptions ' )
tolerance = .8
opts = [ "Results: \['([\d\.]+) .bits/sec",
"Results: \['10M', '([\d\.]+) .bits/sec",
+2 -2
View File
@@ -14,8 +14,8 @@ class testLimit( unittest.TestCase ):
def testLimit( self ):
"Verify that CPU limits are within a 2% tolerance of limit for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.limit' )
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
pexpect.EOF ]
count = 0
bw = 0
+2 -2
View File
@@ -15,8 +15,8 @@ class testLinearBandwidth( unittest.TestCase ):
"Verify that bandwidth is monotonically decreasing as # of hops increases"
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
count = 0
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env python
'''
Test for multiple links between nodes
validates mininet interfaces against systems interfaces
'''
import unittest
import pexpect
class testMultiLink( unittest.TestCase ):
prompt = 'mininet>'
def testMultiLink(self):
p = pexpect.spawn( 'python -m mininet.examples.multilink' )
p.expect( self.prompt )
p.sendline( 'intfs' )
p.expect( 's(\d): lo' )
intfsOutput = p.before
# parse interfaces from mininet intfs, and store them in a list
hostToIntfs = intfsOutput.split( '\r\n' )[ 1:3 ]
intfList = []
for hostToIntf in hostToIntfs:
intfList += [ intf for intf in
hostToIntf.split()[1].split(',') ]
# get interfaces from system by running ifconfig on every host
sysIntfList = []
opts = [ 'h(\d)-eth(\d)', self.prompt ]
p.expect( self.prompt )
p.sendline( 'h1 ifconfig' )
while True:
p.expect( opts )
if p.after == self.prompt:
break
sysIntfList.append( p.after )
p.sendline( 'h2 ifconfig' )
while True:
p.expect( opts )
if p.after == self.prompt:
break
sysIntfList.append( p.after )
failMsg = ( 'The systems interfaces and mininet interfaces\n'
'are not the same' )
self.assertEqual( sysIntfList, intfList, msg=failMsg )
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -11,7 +11,7 @@ from collections import defaultdict
class testMultiPing( unittest.TestCase ):
def testMultiPing( self ):
"""Verify that each target is pinged at least once, and
"""Verify that each target is pinged at least once, and
that pings to 'real' targets are successful and unknown targets fail"""
p = pexpect.spawn( 'python -m mininet.examples.multiping' )
opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)",
+1 -1
View File
@@ -14,7 +14,7 @@ class testNAT( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
'Destination IP is not reachable' )
def testNAT( self ):
"Attempt to ping an IP on the Internet and verify 0% packet loss"
+3 -3
View File
@@ -15,8 +15,8 @@ class testNumberedports( unittest.TestCase ):
def testConsistency( self ):
"""verify consistency between mininet and ovs ports"""
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
'Validating that s1-eth\d is actually on port \d ... WARNING',
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
'Validating that s1-eth\d is actually on port \d ... WARNING',
pexpect.EOF ]
correct_ports = True
count = 0
@@ -34,7 +34,7 @@ class testNumberedports( unittest.TestCase ):
def testNumbering( self ):
"""verify that all of the port numbers are printed correctly and consistent with their interface"""
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
opts = [ 's1-eth(\d+) : (\d+)',
opts = [ 's1-eth(\d+) : (\d+)',
pexpect.EOF ]
count_intfs = 0
while True:
+2 -2
View File
@@ -14,7 +14,7 @@ class testSSHD( unittest.TestCase ):
def connected( self, ip ):
"Log into ssh server, check banner, then exit"
# Note: this test will fail if "Welcome" is not in the sshd banner
# Note: this test will fail if "Welcome" is not in the sshd banner
# and '#'' or '$'' are not in the prompt
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
while True:
@@ -26,7 +26,7 @@ class testSSHD( unittest.TestCase ):
return False
elif index == 2:
p.sendline( 'exit' )
p.wait()
p.wait()
return True
else:
return False
+1 -1
View File
@@ -47,4 +47,4 @@ class testVLANHost( unittest.TestCase ):
self.assertEqual( i, 0 ) # check vlan intf is present
if __name__ == '__main__':
unittest.main()
unittest.main()
+12 -11
View File
@@ -30,18 +30,17 @@ from mininet.util import quietRun
from mininet.log import error
class VLANHost( Host ):
"Host connected to VLAN interface"
def config( self, vlan=100, **params ):
def config( self, vlan=100, **params ):
"""Configure VLANHost according to (optional) parameters:
vlan: VLAN ID for default interface"""
r = super( VLANHost, self ).config( **params )
r = super( Host, self ).config( **params )
intf = self.defaultIntf()
# remove IP from default, "physical" interface
self.cmd( 'ifconfig %s inet 0' % intf )
# create VLAN interface
# create VLAN interface
self.cmd( 'vconfig add %s %d' % ( intf, vlan ) )
# assign the host's IP to the VLAN interface
self.cmd( 'ifconfig %s.%d inet %s' % ( intf, vlan, params['ip'] ) )
@@ -70,8 +69,6 @@ def exampleAllHosts( vlan ):
CLI( net )
net.stop()
# pylint: disable=arguments-differ
class VLANStarTopo( Topo ):
"""Example topology that uses host in multiple VLANs
@@ -93,7 +90,7 @@ class VLANStarTopo( Topo ):
self.addLink( h, s1 )
def exampleCustomTags():
def exampleCustomTags( vlan ):
"""Simple example that exercises VLANStarTopo"""
net = Mininet( topo=VLANStarTopo() )
@@ -113,12 +110,16 @@ if __name__ == '__main__':
setLogLevel( 'info' )
if not quietRun( 'which vconfig' ):
error( "Cannot find command 'vconfig'\nThe package",
error( "Cannot find command 'vconfig'\nThe packge",
"'vlan' is required in Ubuntu or Debian,",
"or 'vconfig' in Fedora\n" )
exit()
try:
vlan = int( sys.argv[ 1 ] )
except Exception:
vlan = None
if len( sys.argv ) >= 2:
exampleAllHosts( vlan=int( sys.argv[ 1 ] ) )
if vlan:
exampleAllHosts( vlan )
else:
exampleCustomTags()
exampleCustomTags( vlan )
+9 -11
View File
@@ -10,14 +10,12 @@ It may also get rid of 'false positives', but hopefully
nothing irreplaceable!
"""
from subprocess import ( Popen, PIPE, check_output as co,
CalledProcessError )
from subprocess import Popen, PIPE, check_output as co
import time
from mininet.log import info
from mininet.term import cleanUpScreens
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
@@ -29,12 +27,12 @@ def killprocs( pattern ):
# Make sure they are gone
while True:
try:
pids = co( [ 'pgrep', '-f', pattern ] )
except CalledProcessError:
pids = co( 'pgrep -f %s' % pattern )
except:
pids = ''
if pids:
sh( 'pkill -9 -f %s' % pattern )
time.sleep( .5 )
sh( 'pkill -f 9 mininet:' )
sleep( .5 )
else:
break
@@ -51,7 +49,7 @@ def cleanup():
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
time.sleep(1)
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
# And kill off sudo mnexec
@@ -73,7 +71,7 @@ def cleanup():
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps:
sh( "ovs-vsctl " + " -- ".join( "--if-exists del-br " + dp
for dp in dps if dp ) )
for dp in dps if dp ) )
# And in case the above didn't work...
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
for dp in dps:
@@ -89,9 +87,9 @@ def cleanup():
info( "*** Killing stale mininet node processes\n" )
killprocs( 'mininet:' )
info( "*** Shutting down stale tunnels\n" )
info ( "*** Shutting down stale tunnels\n" )
killprocs( 'Tunnel=Ethernet' )
killprocs( '.ssh/mn')
sh( 'rm -f ~/.ssh/mn/*' )
info( "*** Cleanup complete.\n" )
+14 -31
View File
@@ -36,8 +36,8 @@ import atexit
from mininet.log import info, output, error
from mininet.term import makeTerms, runX11
from mininet.util import ( quietRun, dumpNodeConnections,
dumpPorts )
from mininet.util import ( quietRun, isShellBuiltin, dumpNodeConnections,
dumpPorts )
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
@@ -93,6 +93,11 @@ class CLI( Cmd ):
self.locals.update( self.mn )
return self.locals
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
' <node> command {args}\n'
@@ -123,7 +128,7 @@ class CLI( Cmd ):
nodes = ' '.join( sorted( self.mn ) )
output( 'available nodes are: \n%s\n' % nodes )
def do_ports( self, _line ):
def do_ports( self, line ):
"display ports and interfaces for each switch"
dumpPorts( self.mn.switches )
@@ -134,11 +139,10 @@ class CLI( Cmd ):
def do_sh( self, line ):
"""Run an external shell command
Usage: sh [cmd args]"""
assert self # satisfy pylint and allow override
call( line, shell=True )
# do_py() and do_px() need to catch any exception during eval()/exec()
# pylint: disable=broad-except
# pylint: disable-msg=W0703
def do_py( self, line ):
"""Evaluate a Python expression.
@@ -155,7 +159,7 @@ class CLI( Cmd ):
output( str( e ) + '\n' )
# We are in fact using the exec() pseudo-function
# pylint: disable=exec-used
# pylint: disable-msg=W0122
def do_px( self, line ):
"""Execute a Python statement.
@@ -165,7 +169,7 @@ class CLI( Cmd ):
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable=broad-except,exec-used
# pylint: enable-msg=W0703,W0122
def do_pingall( self, line ):
"Ping between all hosts."
@@ -280,7 +284,6 @@ class CLI( Cmd ):
def do_exit( self, _line ):
"Exit"
assert self # satisfy pylint and allow override
return 'exited by user command'
def do_quit( self, line ):
@@ -343,33 +346,11 @@ class CLI( Cmd ):
elapsed = time.time() - start
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
def do_links( self, _line ):
def do_links( self, line ):
"Report on links"
for link in self.mn.links:
print link, link.status()
def do_switch( self, line ):
"Starts or stops a switch"
args = line.split()
if len(args) != 2:
error( 'invalid number of args: switch <switch name>'
'{start, stop}\n' )
return
sw = args[ 0 ]
command = args[ 1 ]
if sw not in self.mn or self.mn.get( sw ) not in self.mn.switches:
error( 'invalid switch: %s\n' % args[ 1 ] )
else:
sw = args[ 0 ]
command = args[ 1 ]
if command == 'start':
self.mn.get( sw ).start( self.mn.controllers )
elif command == 'stop':
self.mn.get( sw ).stop( deleteIntfs=False )
else:
error( 'invalid command: '
'switch <switch name> {start, stop}\n' )
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.
@@ -396,6 +377,8 @@ class CLI( Cmd ):
else:
error( '*** Unknown command: %s\n' % line )
# pylint: enable-msg=R0201
def waitForNode( self, node ):
"Wait for a node to finish, and print its output."
# Pollers
+10 -19
View File
@@ -33,7 +33,7 @@ class Intf( object ):
"Basic interface object that can configure itself."
def __init__( self, name, node=None, port=None, link=None,
mac=None, **params ):
mac=None, srcNode=None, **params ):
"""name: interface name (e.g. h1-eth0)
node: owning node (where this intf most likely lives)
link: parent link if we're part of a link
@@ -43,7 +43,7 @@ class Intf( object ):
self.link = link
self.mac = mac
self.ip, self.prefixLen = None, None
# if interface is lo, we know the ip is 127.0.0.1.
# This saves an ifconfig command per node
if self.name == 'lo':
@@ -70,9 +70,6 @@ class Intf( object ):
self.ip, self.prefixLen = ipstr.split( '/' )
return self.ifconfig( ipstr, 'up' )
else:
if prefixLen is None:
raise Exception( 'No prefix length set for IP address %s'
% ( ipstr, ) )
self.ip, self.prefixLen = ipstr, prefixLen
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
@@ -91,8 +88,7 @@ class Intf( object ):
"Return updated IP address based on ifconfig"
# use pexec instead of node.cmd so that we dont read
# backgrounded output from the cli.
ifconfig, _err, _exitCode = self.node.pexec(
'ifconfig %s' % self.name )
ifconfig, _err, _exitCode = self.node.pexec( 'ifconfig %s' % self.name )
ips = self._ipMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
return self.ip
@@ -162,9 +158,9 @@ class Intf( object ):
f = getattr( self, method, None )
if not f or value is None:
return
if isinstance( value, list ):
if type( value ) is list:
result = f( *value )
elif isinstance( value, dict ):
elif type( value ) is dict:
result = f( **value )
else:
result = f( value )
@@ -198,7 +194,7 @@ class Intf( object ):
def status( self ):
"Return intf status as a string"
links, _err, _result = self.node.pexec( 'ip link show' )
links, err_, result_ = self.node.pexec( 'ip link show' )
if self.name in links:
return "OK"
else:
@@ -334,9 +330,8 @@ class TCIntf( Intf ):
# Delay/jitter/loss/max_queue_size using netem
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter,
loss=loss,
max_queue_size=max_queue_size,
parent=parent )
loss=loss, max_queue_size=max_queue_size,
parent=parent )
cmds += delaycmds
# Ugly but functional: display configuration info
@@ -420,21 +415,17 @@ class Link( object ):
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
def intfName( self, node, n ):
def intfName( _self, node, n ):
"Construct a canonical interface name node-ethN for interface n."
# Leave this as an instance method for now
assert self
return node.name + '-eth' + repr( n )
@classmethod
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None ):
def makeIntfPair( _cls, intfname1, intfname2, addr1=None, addr2=None ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
# Leave this as a class method for now
assert cls
return makeIntfPair( intfname1, intfname2, addr1, addr2 )
def delete( self ):
+3 -3
View File
@@ -124,8 +124,8 @@ class MininetLogger( Logger, object ):
self.setLevel( level )
self.handlers[ 0 ].setLevel( level )
# pylint: disable=method-hidden
# "An attribute inherited from mininet.log hide this method" (sic)
# pylint: disable-msg=E0202
# "An attribute inherited from mininet.log hide this method"
# Not sure why this is occurring - this function definitely gets called.
# See /usr/lib/python2.5/logging/__init__.py; modified from warning()
@@ -142,7 +142,7 @@ class MininetLogger( Logger, object ):
if self.isEnabledFor( OUTPUT ):
self._log( OUTPUT, msg, args, kwargs )
# pylint: enable=method-hidden
# pylint: enable-msg=E0202
lg = MininetLogger()
+2 -2
View File
@@ -28,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 type( subtract ) is str:
subtract = [ subtract ]
if isinstance( add, basestring ):
if type( add ) is str:
add = [ add ]
for mod in subtract:
if mod in lsmod():
+54 -81
View File
@@ -91,15 +91,14 @@ import re
import select
import signal
import random
import copy
from time import sleep
from itertools import chain, groupby
from math import ceil
from mininet.cli import CLI
from mininet.log import info, error, debug, output, warn
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
Controller )
from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller
from mininet.nodelib import NAT
from mininet.link import Link, Intf
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
@@ -107,7 +106,7 @@ from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.2.0"
VERSION = "2.1.0+"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -170,6 +169,7 @@ class Mininet( object ):
if topo and build:
self.build()
def waitConnected( self, timeout=None, delay=.5 ):
"""wait for each switch to connect to a controller,
up to 5 seconds
@@ -253,35 +253,25 @@ class Mininet( object ):
if isinstance( name, Controller ):
controller_new = name
# Pylint thinks controller is a str()
# pylint: disable=maybe-no-member
# pylint: disable=E1103
name = controller_new.name
# pylint: enable=maybe-no-member
# pylint: enable=E1103
else:
controller_new = controller( name, **params )
# Add new controller to net
if controller_new: # allow controller-less setups
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
return controller_new
def addNAT( self, name='nat0', connect=True, inNamespace=False,
**params):
"""Add a NAT to the Mininet network
name: name of NAT node
connect: switch to connect to | True (s1) | None
inNamespace: create in a network namespace
params: other NAT node params, notably:
ip: used as default gateway address"""
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
def addNAT( self, name='nat0', connect=True, inNamespace=False, **params ):
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
subnet=self.ipBase, **params )
# find first switch and create link
if connect:
if not isinstance( connect, Node ):
# Use first switch if not specified
connect = self.switches[ 0 ]
# Connect the nat to the switch
# connect the nat to the first switch
self.addLink( nat, self.switches[ 0 ] )
# Set the default route on hosts
# set the default route on hosts
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
for host in self.hosts:
if host.inNamespace:
@@ -331,36 +321,25 @@ class Mininet( object ):
"return (key,value) tuple list for every node in net"
return zip( self.keys(), self.values() )
@staticmethod
def randMac():
"Return a random, non-multicast MAC address"
return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
0x020000000000 )
def addLink( self, node1, node2, port1=None, port2=None,
cls=None, **params ):
""""Add a link from node1 to node2
node1: source node (or name)
node2: dest node (or name)
port1: source port (optional)
port2: dest port (optional)
cls: link class (optional)
params: additional link params (optional)
node1: source node
node2: dest node
port1: source port
port2: dest port
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 ]
options = dict( params )
# Port is optional
if port1 is not None:
options.setdefault( 'port1', port1 )
if port2 is not None:
options.setdefault( 'port2', port2 )
# Set default MAC - this should probably be in Link
options.setdefault( 'addr1', self.randMac() )
options.setdefault( 'addr2', self.randMac() )
cls = self.link if cls is None else cls
link = cls( node1, node2, **options )
mac1 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 )
mac2 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 )
defaults = { 'port1': port1,
'port2': port2,
'addr1': mac1,
'addr2': mac2,
'intf': self.intf }
defaults.update( params )
if not cls:
cls = self.link
link = cls( node1, node2, **defaults )
self.links.append( link )
return link
@@ -398,7 +377,7 @@ class Mininet( object ):
# Add a default controller
info( '*** Adding controller\n' )
classes = self.controller
if not isinstance( classes, list ):
if type( classes ) is not list:
classes = [ classes ]
for i, cls in enumerate( classes ):
# Allow Controller objects because nobody understands currying
@@ -418,10 +397,12 @@ class Mininet( object ):
info( switchName + ' ' )
info( '\n*** Adding links:\n' )
for srcName, dstName, params in topo.links(
sort=True, withInfo=True ):
self.addLink( **params )
info( '(%s, %s) ' % ( srcName, dstName ) )
for srcName, dstName in topo.links(sort=True):
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
params = topo.linkInfo( srcName, dstName )
srcPort, dstPort = topo.port( srcName, dstName )
self.addLink( src, dst, srcPort, dstPort, **params )
info( '(%s, %s) ' % ( src.name, dst.name ) )
info( '\n' )
@@ -474,9 +455,7 @@ class Mininet( object ):
self.build()
info( '*** Starting controller\n' )
for controller in self.controllers:
info( controller.name + ' ')
controller.start()
info( '\n' )
info( '*** Starting %s switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ')
@@ -496,8 +475,7 @@ class Mininet( object ):
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
info( '*** Stopping %i switches\n' % len( self.switches ) )
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
for swclass, switches in groupby( sorted( self.switches, key=type ), type ):
if hasattr( swclass, 'batchShutdown' ):
swclass.batchShutdown( switches )
for switch in self.switches:
@@ -532,13 +510,13 @@ class Mininet( object ):
if hosts is None:
hosts = self.hosts
poller = select.poll()
h1 = hosts[ 0 ] # so we can call class method fdToNode
Node = hosts[ 0 ] # so we can call class method fdToNode
for host in hosts:
poller.register( host.stdout )
while True:
ready = poller.poll( timeoutms )
for fd, event in ready:
host = h1.fdToNode( fd )
host = Node.fdToNode( fd )
if event & select.POLLIN:
line = host.readline()
if line is not None:
@@ -585,8 +563,7 @@ class Mininet( object ):
if timeout:
opts = '-W %s' % timeout
if dest.intfs:
result = node.cmd( 'ping -c1 %s %s' %
(opts, dest.IP()) )
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
sent, received = self._parsePing( result )
else:
sent, received = 0, 0
@@ -711,23 +688,21 @@ class Mininet( object ):
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
seconds=5):
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses first and last hosts
hosts: list of hosts; if None, uses opposite hosts
l4Type: string, one of [ TCP, UDP ]
udpBw: bandwidth target for UDP test
fmt: iperf format argument if any
seconds: iperf time to transmit
returns: two-element array of [ server, client ] speeds
returns: results two-element array of [ server, client ] speeds
note: send() is buffered, so client rate can be much higher than
the actual transmission rate; on an unloaded system, server
rate should be much closer to the actual receive rate"""
if not quietRun( 'which telnet' ):
error( 'Cannot find telnet in $PATH - required for iperf test' )
return
hosts = hosts or [ self.hosts[ 0 ], self.hosts[ -1 ] ]
assert len( hosts ) == 2
if not hosts:
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else:
assert len( hosts ) == 2
client, server = hosts
output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
output( "%s and %s\n" % ( client.name, server.name ) )
@@ -739,8 +714,8 @@ class Mininet( object ):
bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if fmt:
iperfArgs += '-f %s ' % fmt
if format:
iperfArgs += '-f %s ' %format
server.sendCmd( iperfArgs + '-s', printPid=True )
servout = ''
while server.lastPid is None:
@@ -750,8 +725,8 @@ class Mininet( object ):
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
info( 'Waiting for iperf to start up...' )
sleep(.5)
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
server.IP() + ' ' + bwArgs )
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs )
debug( 'Client output: %s\n' % cliout )
server.sendInt()
servout += server.waitOutput()
@@ -765,7 +740,7 @@ class Mininet( object ):
def runCpuLimitTest( self, cpu, duration=5 ):
"""run CPU limit test with 'while true' processes.
cpu: desired CPU fraction of each host
duration: test duration in seconds (integer)
duration: test duration in seconds
returns a single list of measured CPU fractions as floats.
"""
cores = int( quietRun( 'nproc' ) )
@@ -786,14 +761,12 @@ class Mininet( object ):
# get the initial cpu time for each host
for host in hosts:
outputs[ host ] = []
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
host, 'r' ) as f:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r' ) as f:
time[ host ] = float( f.read() )
for _ in range( duration ):
for _ in range( 5 ):
sleep( 1 )
for host in hosts:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
host, 'r' ) as f:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r' ) as f:
readTime = float( f.read() )
outputs[ host ].append( ( ( readTime - time[ host ] )
/ 1000000000 ) / cores * 100 )
@@ -820,9 +793,9 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
if isinstance( src, basestring ):
if type( src ) is str:
src = self.nameToNode[ src ]
if isinstance( dst, basestring ):
if type( dst ) is str:
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
+122 -181
View File
@@ -11,13 +11,16 @@ Host: a virtual host. By default, a host is simply a shell; commands
may be sent using Cmd (which waits for output), or using sendCmd(),
which returns immediately, allowing subsequent monitoring using
monitor(). Examples of how to run experiments using this
functionality are provided in the examples/ directory. By default,
hosts share the root file system, but they may also specify private
directories.
functionality are provided in the examples/ directory.
CPULimitedHost: a virtual host whose CPU bandwidth is limited by
RT or CFS bandwidth limiting.
HostWithPrivateDirs: a virtual host that has user-specified private
directories. These may be temporary directories stored as a tmpfs,
or persistent directories that are mounted from another directory in
the root filesystem.
Switch: superclass for switch nodes.
UserSwitch: a switch using the user-space switch from the OpenFlow
@@ -51,7 +54,7 @@ import pty
import re
import signal
import select
from subprocess import Popen, PIPE
from subprocess import Popen, PIPE, STDOUT
from operator import or_
from time import sleep
@@ -72,14 +75,12 @@ class Node( object ):
def __init__( self, name, inNamespace=True, **params ):
"""name: name of node
inNamespace: in network namespace?
privateDirs: list of private directory strings or tuples
params: Node parameters (see config() for details)"""
# Make sure class actually works
self.checkSetup()
self.name = params.get( 'name', name )
self.privateDirs = params.get( 'privateDirs', [] )
self.inNamespace = params.get( 'inNamespace', inNamespace )
# Stash configuration parameters for future reference
@@ -99,7 +100,6 @@ class Node( object ):
# Start command interpreter shell
self.startShell()
self.mountPrivateDirs()
# File descriptor to node mapping support
# Class variables and methods
@@ -129,7 +129,7 @@ class Node( object ):
# bash -m: enable job control, i: force interactive
# -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 ),
cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
'bash', '--norc', '-mis', '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)
@@ -161,36 +161,10 @@ class Node( object ):
self.cmd( 'stty -echo' )
self.cmd( 'set +m' )
def mountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
privateDir = directory[ 1 ] % self.__dict__
mountPoint = directory[ 0 ]
self.cmd( 'mkdir -p %s' % privateDir )
self.cmd( 'mkdir -p %s' % mountPoint )
self.cmd( 'mount --bind %s %s' %
( privateDir, mountPoint ) )
else:
# mount temporary filesystem on directory
self.cmd( 'mkdir -p %s' % directory )
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
def unmountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
self.cmd( 'umount ', directory[ 0 ] )
else:
self.cmd( 'umount ', directory )
def _popen( self, cmd, **params ):
"""Internal method: spawn and return a process
cmd: command to run (list)
params: parameters to Popen()"""
# Leave this is as an instance method for now
assert self
return Popen( cmd, **params )
def cleanup( self ):
@@ -236,17 +210,13 @@ class Node( object ):
def terminate( self ):
"Send kill signal to Node and clean up after it."
self.unmountPrivateDirs()
if self.shell:
if self.shell.poll() is None:
os.killpg( self.shell.pid, signal.SIGHUP )
self.cleanup()
def stop( self, deleteIntfs=False ):
"""Stop node.
deleteIntfs: delete interfaces? (False)"""
if deleteIntfs:
self.deleteIntfs()
def stop( self ):
"Stop node."
self.terminate()
def waitReadable( self, timeoutms=None ):
@@ -263,7 +233,7 @@ class Node( object ):
assert not self.waiting
printPid = kwargs.get( 'printPid', True )
# Allow sendCmd( [ list ] )
if len( args ) == 1 and isinstance( args[ 0 ], list ):
if len( args ) == 1 and type( args[ 0 ] ) is list:
cmd = args[ 0 ]
# Allow sendCmd( cmd, arg1, arg2... )
elif len( args ) > 0:
@@ -329,7 +299,7 @@ class Node( object ):
log = info if verbose else debug
output = ''
while self.waiting:
data = self.monitor( findPid=findPid )
data = self.monitor()
output += data
log( data )
return output
@@ -357,10 +327,10 @@ class Node( object ):
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
if len( args ) == 1:
if isinstance( args[ 0 ], list ):
if type( args[ 0 ] ) is list:
# popen([cmd, arg1, arg2...])
cmd = args[ 0 ]
elif isinstance( args[ 0 ], basestring ):
elif type( args[ 0 ] ) is str:
# popen("cmd arg1 arg2...")
cmd = args[ 0 ].split()
else:
@@ -380,7 +350,7 @@ class Node( object ):
"""Execute a command using popen
returns: out, err, exitcode"""
popen = self.popen( *args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
**kwargs )
**kwargs )
# Warning: this can fail with large numbers of fds!
out, err = popen.communicate()
exitcode = popen.wait()
@@ -426,7 +396,7 @@ class Node( object ):
warn( '*** defaultIntf: warning:', self.name,
'has no interfaces\n' )
def intf( self, intf=None ):
def intf( self, intf='' ):
"""Return our interface object with given string name,
default intf if name is falsy (None, empty string, etc).
or the input intf arg.
@@ -437,7 +407,7 @@ class Node( object ):
"""
if not intf:
return self.defaultIntf()
elif isinstance( intf, basestring):
elif type( intf ) is str:
return self.nameToIntf[ intf ]
else:
return intf
@@ -489,12 +459,12 @@ 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 type( intf ) is str and ' ' in intf:
params = intf
else:
params = 'dev %s' % intf
# Do this in one line in case we're messing with the root namespace
self.cmd( 'ip route del default; ip route add default', params )
self.cmd( 'ip route del default' )
return self.cmd( 'ip route add default', params )
# Convenience and configuration methods
@@ -539,14 +509,12 @@ class Node( object ):
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
if value is None:
return
f = getattr( self, method, None )
if not f:
if not f or value is None:
return
if isinstance( value, list ):
if type( value ) is list:
result = f( *value )
elif isinstance( value, dict ):
elif type( value ) is dict:
result = f( **value )
else:
result = f( value )
@@ -622,10 +590,12 @@ class Node( object ):
"Make sure our class dependencies are available"
pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
class Host( Node ):
"A host is simply a Node"
pass
class CPULimitedHost( Host ):
"CPU limited host"
@@ -647,9 +617,13 @@ class CPULimitedHost( Host ):
# still does better with larger period values.
self.period_us = kwargs.get( 'period_us', 100000 )
self.sched = sched
if sched == 'rt':
self.checkRtGroupSched()
self.rtprio = 20
if self.sched == 'rt':
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
error( '\n*** error: please enable RT_GROUP_SCHED in your kernel\n' )
exit( 1 )
self.rtprio = 20
def cgroupSet( self, param, value, resource='cpu' ):
"Set a cgroup parameter and return its value"
@@ -681,14 +655,8 @@ class CPULimitedHost( Host ):
# Tell mnexec to execute command in our cgroup
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':
if int( self.cgroupGet( 'rt_runtime_us', 'cpu' ) ) <= 0:
mncmd += [ '-r', str( self.rtprio ) ]
else:
debug( '*** error: not enough cpu time available for %s.' %
self.name, 'Using cfs scheduler for subprocess\n' )
mncmd += [ '-r', str( self.rtprio ) ]
return Host.popen( self, *args, mncmd=mncmd, **kwargs )
def cleanup( self ):
@@ -696,21 +664,6 @@ class CPULimitedHost( Host ):
super( CPULimitedHost, self ).cleanup()
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
_rtGroupSched = False # internal class var: Is CONFIG_RT_GROUP_SCHED set?
@classmethod
def checkRtGroupSched( cls ):
"Check (Ubuntu,Debian) kernel config for CONFIG_RT_GROUP_SCHED for RT"
if not cls._rtGroupSched:
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' %
release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
error( '\n*** error: please enable RT_GROUP_SCHED '
'in your kernel\n' )
exit( 1 )
cls._rtGroupSched = True
def chrt( self ):
"Set RT scheduling priority"
quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
@@ -728,7 +681,7 @@ class CPULimitedHost( Host ):
quota = int( self.period_us * f )
return pstr, qstr, self.period_us, quota
def cfsInfo( self, f ):
def cfsInfo( self, f):
"Internal method: return parameters for CFS bandwidth"
pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
# CFS uses wall clock time for period and CPU time for quota.
@@ -738,9 +691,6 @@ class CPULimitedHost( Host ):
debug( '(cfsInfo: increasing default period) ' )
quota = 1000
period = int( quota / f / numCores() )
# Reset to unlimited on negative quota
if quota < 0:
quota = -1
return pstr, qstr, period, quota
# BL comment:
@@ -752,36 +702,35 @@ class CPULimitedHost( Host ):
# to CPU seconds per second, essentially assuming that
# all CPUs are the same.
def setCPUFrac( self, f, sched=None ):
def setCPUFrac( self, f=-1, sched=None):
"""Set overall CPU fraction for this host
f: CPU bandwidth limit (positive fraction, or -1 for cfs unlimited)
f: CPU bandwidth limit (fraction)
sched: 'rt' or 'cfs'
Note 'cfs' requires CONFIG_CFS_BANDWIDTH,
and 'rt' requires CONFIG_RT_GROUP_SCHED"""
Note 'cfs' requires CONFIG_CFS_BANDWIDTH"""
if not f:
return
if not sched:
sched = self.sched
if sched == 'rt':
if not f or f < 0:
raise Exception( 'Please set a positive CPU fraction'
' for sched=rt\n' )
pstr, qstr, period, quota = self.rtInfo( f )
elif sched == 'cfs':
pstr, qstr, period, quota = self.cfsInfo( f )
else:
return
if quota < 0:
# Reset to unlimited
quota = -1
# Set cgroup's period and quota
setPeriod = self.cgroupSet( pstr, period )
setQuota = self.cgroupSet( qstr, quota )
self.cgroupSet( pstr, period )
self.cgroupSet( qstr, quota )
if sched == 'rt':
# Set RT priority if necessary
sched = self.chrt()
info( '(%s %d/%dus) ' % ( sched, setQuota, setPeriod ) )
self.chrt()
info( '(%s %d/%dus) ' % ( sched, quota, period ) )
def setCPUs( self, cores, mems=0 ):
"Specify (real) cores that our cgroup can run on"
if not cores:
return
if isinstance( cores, list ):
if type( cores ) is list:
cores = ','.join( [ str( c ) for c in cores ] )
self.cgroupSet( resource='cpuset', param='cpus',
value=cores )
@@ -794,7 +743,7 @@ class CPULimitedHost( Host ):
errFail( 'cgclassify -g cpuset:/%s %s' % (
self.name, self.pid ) )
def config( self, cpu=-1, cores=None, **params ):
def config( self, cpu=None, cores=None, **params ):
"""cpu: desired overall system CPU fraction
cores: (real) core(s) this host can run on
params: parameters for Node.config()"""
@@ -813,6 +762,33 @@ class CPULimitedHost( Host ):
mountCgroups()
cls.inited = True
class HostWithPrivateDirs( Host ):
"Host with private directories"
def __init__( self, name, *args, **kwargs ):
"privateDirs: list of private directory strings or tuples"
self.name = name
self.privateDirs = kwargs.pop( 'privateDirs', [] )
Host.__init__( self, name, *args, **kwargs )
self.mountPrivateDirs()
def mountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
privateDir = directory[ 1 ] % self.__dict__
mountPoint = directory[ 0 ]
self.cmd( 'mkdir -p %s' % privateDir )
self.cmd( 'mkdir -p %s' % mountPoint )
self.cmd( 'mount --bind %s %s' %
( privateDir, mountPoint ) )
else:
# mount temporary filesystem on directory
self.cmd( 'mkdir -p %s' % directory )
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
# Some important things to note:
#
@@ -888,11 +864,7 @@ class Switch( Node ):
def connected( self ):
"Is the switch connected to a controller? (override this method)"
# Assume that we are connected by default to whatever we need to
# be connected to. This should be overridden by any OpenFlow
# switch, but not by a standalone bridge.
debug( 'Assuming', repr( self ), 'is connected to a controller\n' )
return True
return False and self # satisfy pylint
def __repr__( self ):
"More informative string representation"
@@ -901,7 +873,6 @@ class Switch( Node ):
return '<%s %s: %s pid=%s> ' % (
self.__class__.__name__, self.name, intfs, self.pid )
class UserSwitch( Switch ):
"User-space switch."
@@ -949,13 +920,13 @@ class UserSwitch( Switch ):
over tc queuing disciplines. To resolve the conflict,
we re-create the user switch's configuration, but as a
leaf of the TCIntf-created configuration."""
if isinstance( intf, TCIntf ):
ifspeed = 10000000000 # 10 Gbps
if type( intf ) is TCIntf:
ifspeed = 10000000000 # 10 Gbps
minspeed = ifspeed * 0.001
res = intf.config( **intf.params )
if res is None: # link may not have TC parameters
if res is None: # link may not have TC parameters
return
# Re-add qdisc, root, and default classes user switch created, but
@@ -988,17 +959,17 @@ class UserSwitch( Switch ):
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
if "no-slicing" not in self.dpopts:
# Only TCReapply if slicing is enable
sleep(1) # Allow ofdatapath to start before re-arranging qdisc's
sleep(1) # Allow ofdatapath to start before re-arranging qdisc's
for intf in self.intfList():
if not intf.IP():
self.TCReapply( intf )
def stop( self, deleteIntfs=True ):
"""Stop OpenFlow reference user datapath.
deleteIntfs: delete interfaces? (True)"""
def stop( self ):
"Stop OpenFlow reference user datapath."
self.cmd( 'kill %ofdatapath' )
self.cmd( 'kill %ofprotocol' )
super( UserSwitch, self ).stop( deleteIntfs )
self.deleteIntfs()
class OVSLegacyKernelSwitch( Switch ):
"""Open VSwitch legacy kernel-space switch using ovs-openflowd.
@@ -1043,19 +1014,18 @@ class OVSLegacyKernelSwitch( Switch ):
' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False
def stop( self, deleteIntfs=True ):
"""Terminate kernel datapath."
deleteIntfs: delete interfaces? (True)"""
def stop( self ):
"Terminate kernel datapath."
quietRun( 'ovs-dpctl del-dp ' + self.dp )
self.cmd( 'kill %ovs-openflowd' )
super( OVSLegacyKernelSwitch, self ).stop( deleteIntfs )
self.deleteIntfs()
class OVSSwitch( Switch ):
"Open vSwitch switch. Depends on ovs-vsctl."
def __init__( self, name, failMode='secure', datapath='kernel',
inband=False, protocols=None, **params ):
inband=False, protocols=None, **params ):
"""Init.
name: name for switch
failMode: controller loss behavior (secure|open)
@@ -1086,14 +1056,13 @@ class OVSSwitch( Switch ):
'You may wish to try '
'"service openvswitch-switch start".\n' )
exit( 1 )
version = quietRun( 'ovs-vsctl --version' )
cls.OVSVersion = findall( r'\d+\.\d+', version )[ 0 ]
info = quietRun( 'ovs-vsctl --version' )
cls.OVSVersion = findall( '\d+\.\d+', info )[ 0 ]
@classmethod
def isOldOVS( cls ):
"Is OVS ersion < 1.10?"
return ( StrictVersion( cls.OVSVersion ) <
StrictVersion( '1.10' ) )
StrictVersion( '1.10' ) )
@classmethod
def batchShutdown( cls, switches ):
@@ -1111,7 +1080,7 @@ class OVSSwitch( Switch ):
"""Unfortunately OVS and Mininet are fighting
over tc queuing disciplines. As a quick hack/
workaround, we clear OVS's and reapply our own."""
if isinstance( intf, TCIntf ):
if type( intf ) is TCIntf:
intf.config( **intf.params )
def attach( self, intf ):
@@ -1128,7 +1097,7 @@ class OVSSwitch( Switch ):
"Return ovsdb UUIDs for our controllers"
uuids = []
controllers = self.cmd( 'ovs-vsctl -- get Bridge', self,
'Controller' ).strip()
'Controller' ).strip()
if controllers.startswith( '[' ) and controllers.endswith( ']' ):
controllers = controllers[ 1 : -1 ]
uuids = [ c.strip() for c in controllers.split( ',' ) ]
@@ -1137,7 +1106,7 @@ class OVSSwitch( Switch ):
def connected( self ):
"Are we connected to at least one of our controllers?"
results = [ 'true' in self.cmd( 'ovs-vsctl -- get Controller',
uuid, 'is_connected' )
uuid, 'is_connected' )
for uuid in self.controllerUUIDs() ]
return reduce( or_, results, False )
@@ -1148,15 +1117,15 @@ class OVSSwitch( Switch ):
'OVS kernel switch does not work in a namespace' )
# Annoyingly, --if-exists option seems not to work
self.cmd( 'ovs-vsctl del-br', self )
int( self.dpid, 16 ) # DPID must be a hex string
int( self.dpid, 16 ) # DPID must be a hex string
# Interfaces and controllers
intfs = ' '.join( '-- add-port %s %s ' % ( self, intf ) +
'-- set Interface %s ' % intf +
'ofport_request=%s ' % self.ports[ intf ]
for intf in self.intfList()
if self.ports[ intf ] and not intf.IP() )
for intf in self.intfList()
if self.ports[ intf ] and not intf.IP() )
clist = ' '.join( '%s:%s:%d' % ( c.protocol, c.IP(), c.port )
for c in controllers )
for c in controllers )
if self.listenPort:
clist += ' ptcp:%s' % self.listenPort
# Construct big ovs-vsctl command for new versions of OVS
@@ -1196,33 +1165,21 @@ class OVSSwitch( Switch ):
for intf in self.intfList():
self.TCReapply( intf )
def stop( self, deleteIntfs=True ):
"""Terminate OVS switch.
deleteIntfs: delete interfaces? (True)"""
def stop( self ):
"Terminate OVS switch."
self.cmd( 'ovs-vsctl del-br', self )
if self.datapath == 'user':
self.cmd( 'ip link del', self )
super( OVSSwitch, self ).stop( deleteIntfs )
self.deleteIntfs()
OVSKernelSwitch = OVSSwitch
class OVSBridge( OVSSwitch ):
"OVSBridge is an OVSSwitch in standalone/bridge mode"
class IVSSwitch(Switch):
"""IVS virtual switch"""
def __init__( self, args, **kwargs ):
kwargs.update( failMode='standalone' )
OVSSwitch.__init__( self, args, **kwargs )
def start( self, controllers ):
OVSSwitch.start( self, controllers=[] )
class IVSSwitch( Switch ):
"Indigo Virtual Switch"
def __init__( self, name, verbose=False, **kwargs ):
def __init__( self, name, verbose=True, **kwargs ):
Switch.__init__( self, name, **kwargs )
self.verbose = verbose
@@ -1265,12 +1222,11 @@ class IVSSwitch( Switch ):
self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
def stop( self, deleteIntfs=True ):
"""Terminate IVS switch.
deleteIntfs: delete interfaces? (True)"""
def stop( self ):
"Terminate IVS switch."
self.cmd( 'kill %ivs' )
self.cmd( 'wait' )
super( IVSSwitch, self ).stop( deleteIntfs )
self.deleteIntfs()
def attach( self, intf ):
"Connect a data port"
@@ -1334,11 +1290,11 @@ class Controller( Node ):
' 1>' + cout + ' 2>' + cout + ' &' )
self.execed = False
def stop( self, *args, **kwargs ):
def stop( self ):
"Stop controller."
self.cmd( 'kill %' + self.command )
self.cmd( 'wait %' + self.command )
super( Controller, self ).stop( *args, **kwargs )
self.terminate()
def IP( self, intf=None ):
"Return IP address of the Controller"
@@ -1353,24 +1309,19 @@ class Controller( Node ):
return '<%s %s: %s:%s pid=%s> ' % (
self.__class__.__name__, self.name,
self.IP(), self.port, self.pid )
@classmethod
def isAvailable( cls ):
"Is controller available?"
def isAvailable( self ):
return quietRun( 'which controller' )
class OVSController( Controller ):
"Open vSwitch controller"
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 ( quietRun( 'which ovs-controller' ) or
quietRun( 'which test-controller' ) )
def isAvailable( self ):
return quietRun( 'which ovs-controller' ) or quietRun( 'which test-controller' )
class NOX( Controller ):
"Controller to run a NOX application."
@@ -1413,11 +1364,11 @@ class RYU( Controller ):
ryuArgs = [ ryuArgs ]
Controller.__init__( self, name,
command='ryu-manager',
cargs='--ofp-tcp-listen-port %s ' +
' '.join( ryuArgs ),
cdir=ryuCoreDir,
**kwargs )
command='ryu-manager',
cargs='--ofp-tcp-listen-port %s ' +
' '.join( ryuArgs ),
cdir=ryuCoreDir,
**kwargs )
class RemoteController( Controller ):
"Controller running outside of Mininet's control."
@@ -1447,18 +1398,8 @@ class RemoteController( Controller ):
warn( "Unable to contact the remote controller"
" at %s:%d\n" % ( self.ip, self.port ) )
DefaultControllers = ( Controller, OVSController )
def findController( controllers=DefaultControllers ):
"Return first available controller from list, if any"
for controller in controllers:
def DefaultController( name, order=[ Controller, OVSController ], **kwargs ):
"find any controller that is available and run it"
for controller in order:
if controller.isAvailable():
return controller
def DefaultController( name, controllers=DefaultControllers, **kwargs ):
"Find a controller that is available and instantiate it"
controller = findController( controllers )
if not controller:
raise Exception( 'Could not find a default OpenFlow controller' )
return controller( name, **kwargs )
return controller( name, **kwargs )
+18 -51
View File
@@ -5,10 +5,7 @@ This contains additional Node types which you may find to be useful.
"""
from mininet.node import Node, Switch
from mininet.log import info, warn
from mininet.moduledeps import pathCheck
import re
from mininet.log import setLogLevel, info
class LinuxBridge( Switch ):
"Linux Bridge (with optional spanning tree)"
@@ -32,9 +29,8 @@ class LinuxBridge( Switch ):
return 'forwarding' in self.cmd( 'brctl showstp', self )
else:
return True
def start( self, _controllers ):
"Start Linux bridge"
def start( self, controllers ):
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
self.cmd( 'brctl addbr', self )
@@ -46,43 +42,29 @@ class LinuxBridge( Switch ):
self.cmd( 'brctl addif', self, i )
self.cmd( 'ifconfig', self, 'up' )
def stop( self, deleteIntfs=True ):
"""Stop Linux bridge
deleteIntfs: delete interfaces? (True)"""
def stop( self ):
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
super( LinuxBridge, self ).stop( deleteIntfs )
def dpctl( self, *args ):
"Run brctl command"
return self.cmd( 'brctl', *args )
@classmethod
def setup( cls ):
"Make sure our class dependencies are available"
pathCheck( 'brctl', moduleName='bridge-utils' )
class NAT( Node ):
"NAT: Provides connectivity to external network"
"""NAT: Provides connectivity to external network"""
def __init__( self, name, inetIntf=None, subnet='10.0/8',
localIntf=None, **params):
"""Start NAT/forwarding between Mininet and external network
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
def __init__( self, name, inetIntf='eth0', subnet='10.0/8', localIntf=None, **params):
super( NAT, self ).__init__( name, **params )
self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
"""Start NAT/forwarding between Mininet and external network
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
self.inetIntf = inetIntf
self.subnet = subnet
self.localIntf = localIntf
def config( self, **params ):
"""Configure the NAT and iptables"""
super( NAT, self).config( **params )
"""Configure the NAT and iptables"""
if not self.localIntf:
self.localIntf = self.defaultIntf()
self.localIntf = self.defaultIntf()
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
@@ -97,14 +79,10 @@ class NAT( Node ):
self.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
self.cmd( 'iptables -I FORWARD',
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -A FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -A FORWARD',
'-i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING',
'-o', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
self.cmd( 'iptables -I FORWARD -i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -A FORWARD -i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -A FORWARD -i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING -o ', self.inetIntf, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
@@ -116,25 +94,13 @@ class NAT( Node ):
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if ( line ) not in config:
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
info( '*** 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
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"""
# Flush any currently active rules
@@ -146,3 +112,4 @@ class NAT( Node ):
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
super( NAT, self ).terminate()
+2 -2
View File
@@ -32,7 +32,7 @@ def tunnelX11( node, display=None):
port = 6000 + int( float( screen ) )
connection = r'TCP\:%s\:%s' % ( host, port )
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm', display=None ):
@@ -41,7 +41,7 @@ def makeTerm( node, title='Node', term='xterm', display=None ):
title: base title
term: 'xterm' or 'gterm'
returns: two Popen objects, tunnel and terminal"""
title = '"%s: %s"' % ( title, node.name )
title += ': ' + node.name
if not node.inNamespace:
title += ' (root)'
cmds = {
+6 -6
View File
@@ -6,7 +6,7 @@ Run all mininet core tests
-quick : skip tests that take more than ~30 seconds
"""
from unittest import defaultTestLoader, TextTestRunner
import unittest
import os
import sys
from mininet.util import ensureRoot
@@ -19,13 +19,13 @@ def runTests( testDir, verbosity=1 ):
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = defaultTestLoader.discover( testDir )
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
TextTestRunner( verbosity=verbosity ).run( testSuite )
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
setLogLevel( 'warning' )
# get the directory containing example tests
thisdir = os.path.dirname( os.path.realpath( __file__ ) )
vlevel = 2 if '-v' in sys.argv else 1
runTests( testDir=thisdir, verbosity=vlevel )
testDir = os.path.dirname( os.path.realpath( __file__ ) )
verbosity = 2 if '-v' in sys.argv else 1
runTests( testDir, verbosity )
+14 -26
View File
@@ -4,7 +4,6 @@
Test creation and pings for topologies with link and/or CPU options."""
import unittest
import sys
from functools import partial
from mininet.net import Mininet
@@ -14,7 +13,6 @@ from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Number of hosts for each test
N = 2
@@ -40,13 +38,7 @@ class testOptionsTopoCommon( object ):
"""Verify ability to create networks with host and link options
(common code)."""
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
cleanup()
switchClass = None # overridden in subclasses
def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
"Generic topology-with-options test runner."
@@ -87,7 +79,7 @@ class testOptionsTopoCommon( object ):
upperBound, lowerBound ) )
msg += info
self.assertGreaterEqual( float( measured ), lowerBound, msg=msg )
self.assertGreaterEqual( float( measured ),lowerBound, msg=msg )
self.assertLessEqual( float( measured ), upperBound, msg=msg )
def testCPULimits( self ):
@@ -104,8 +96,7 @@ class testOptionsTopoCommon( object ):
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop()
hostUsage = '\n'.join( 'h%s: %s' %
( n + 1,
results[ (n - 1) * 5 : (n * 5) - 1 ] )
( n + 1, results[ ( n - 1 ) * 5: ( n * 5 ) - 1 ] )
for n in range( N ) )
hoptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in hopts.items() )
@@ -115,8 +106,7 @@ class testOptionsTopoCommon( object ):
'hopts = %s\n'
'host = CPULimitedHost\n'
'Switch = %s\n'
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
self.switchClass ) )
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr, self.switchClass ) )
for pct in results:
#divide cpu by 100 to convert from percentage to fraction
self.assertWithinTolerance( pct/100, CPU_FRACTION,
@@ -125,8 +115,7 @@ class testOptionsTopoCommon( object ):
def testLinkBandwidth( self ):
"Verify that link bandwidths are accurate within a bound."
if self.switchClass is UserSwitch:
self.skipTest( 'UserSwitch has very poor performance -'
' skipping for now' )
self.skipTest ( 'UserSwitch has very poor performance, so skip for now' )
BW = 5 # Mbps
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first;
@@ -135,7 +124,7 @@ class testOptionsTopoCommon( object ):
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass,
waitConnected=True )
bw_strs = mn.run( mn.iperf, fmt='m' )
bw_strs = mn.run( mn.iperf, format='m' )
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting link bandwidth limited to %d Mbps per link\n'
@@ -152,7 +141,7 @@ class testOptionsTopoCommon( object ):
# As long as the kernel doesn't wait a long time before
# delivering bytes to the iperf server, its reported data rate
# should be close to the actual receive rate.
serverRate, _clientRate = bw_strs
serverRate, clientRate = bw_strs
bw = float( serverRate.split(' ')[0] )
self.assertWithinTolerance( bw, BW, BW_TOLERANCE, msg )
@@ -171,12 +160,12 @@ class testOptionsTopoCommon( object ):
mn.stop()
test_outputs = ping_delays[0]
# Ignore unused variables below
# pylint: disable=W0612
# pylint: disable-msg=W0612
node, dest, ping_outputs = test_outputs
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
pingFailMsg = 'sent %s pings, only received %s' % ( sent, received )
self.assertEqual( sent, received, msg=pingFailMsg )
# pylint: enable=W0612
# pylint: enable-msg=W0612
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting Link Delay of %s ms\n'
@@ -192,9 +181,10 @@ class testOptionsTopoCommon( object ):
for rttval in [rttmin, rttavg, rttmax]:
# Multiply delay by 4 to cover there & back on two links
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
DELAY_TOLERANCE, msg )
def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate."
LOSS_PERCENT = 99
@@ -222,8 +212,7 @@ class testOptionsTopoCommon( object ):
'lopts = %s\n'
'host = default\n'
'switch = %s\n'
% ( LOSS_PERCENT, dropped_total, N, loptsStr,
self.switchClass ) )
% ( LOSS_PERCENT, dropped_total, N, loptsStr, self.switchClass ) )
self.assertGreater( dropped_total, 0, msg )
@@ -256,10 +245,9 @@ class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
'Reference user switch is not installed' )
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
"""Verify ability to create networks with host and link options
(UserSwitch)."""
"Verify ability to create networks with host and link options (UserSwitch)."
longMessage = True
switchClass = UserSwitch
+4 -13
View File
@@ -4,7 +4,6 @@
Test creation and all-pairs ping for each included mininet topo type."""
import unittest
import sys
from functools import partial
from mininet.net import Mininet
@@ -13,7 +12,6 @@ from mininet.node import UserSwitch, OVSSwitch, IVSSwitch
from mininet.topo import SingleSwitchTopo, LinearTopo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
@@ -21,13 +19,7 @@ from mininet.clean import cleanup
class testSingleSwitchCommon( object ):
"Test ping with single switch topology (common code)."
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
cleanup()
switchClass = None # overridden in subclasses
def testMinimal( self ):
"Ping test on minimal topology"
@@ -59,7 +51,7 @@ class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ):
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
'Reference user switch is not installed' )
class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (Userspace switch)."
switchClass = UserSwitch
@@ -71,12 +63,11 @@ class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
class testLinearCommon( object ):
"Test all-pairs ping with LinearNet (common code)."
switchClass = None # overridden in subclasses
switchClass = None # overridden in subclasses
def testLinear5( self ):
"Ping test on a 5-switch topology"
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host,
Controller, waitConnected=True )
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller, waitConnected=True )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
-104
View File
@@ -1,104 +0,0 @@
#!/usr/bin/env python
"""Package: mininet
Regression tests for switch dpid assignment."""
import unittest
import sys
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import ( UserSwitch, OVSSwitch, OVSLegacyKernelSwitch,
IVSSwitch )
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"Verify Switch dpid assignment."
switchClass = OVSSwitch # overridden in subclasses
def tearDown( self ):
"Clean up if necessary"
# satisfy pylint
assert self
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."""
switch = Mininet( Topo(),
self.switchClass,
Host, Controller ).addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
def dpidFrom( self, num ):
"Compute default dpid from number"
fmt = ( '%0' + str( self.switchClass.dpidLen ) + 'x' )
return fmt % num
def testActualDpidAssignment( self ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation."""
dpid = self.dpidFrom( 0xABCD )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch(
's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid )
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."""
with self.assertRaises( Exception ) as raises_cm:
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.')
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."""
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
class OVSUser( OVSSwitch):
"OVS User Switch convenience class"
def __init__( self, *args, **kwargs ):
kwargs.update( datapath='user' )
OVSSwitch.__init__( self, *args, **kwargs )
class testSwitchOVSUser( TestSwitchDpidAssignmentOVS ):
"Test dpid assignnment of OVS User Switch."
switchClass = OVSUser
@unittest.skipUnless( quietRun( 'which ovs-openflowd' ),
'OVS Legacy Kernel switch is not installed' )
class testSwitchOVSLegacyKernel( TestSwitchDpidAssignmentOVS ):
"Test dpid assignnment of OVS Legacy Kernel Switch."
switchClass = OVSLegacyKernelSwitch
@unittest.skipUnless( quietRun( 'which ivs-ctl' ),
'IVS switch is not installed' )
class testSwitchIVS( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of IVS switch."
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of Userspace switch."
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+15 -31
View File
@@ -14,16 +14,11 @@ from mininet.util import quietRun
from distutils.version import StrictVersion
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
versionMatch = re.findall( r'TShark \d+.\d+.\d+', versionStr )[0]
versionMatch = re.findall( 'TShark \d+.\d+.\d+', versionStr )[0]
return versionMatch.split()[ 1 ]
# pylint doesn't understand pexpect.match, unfortunately!
# pylint:disable=maybe-no-member
class testWalkthrough( unittest.TestCase ):
"Test Mininet walkthrough"
prompt = 'mininet>'
@@ -36,8 +31,6 @@ class testWalkthrough( unittest.TestCase ):
def testWireshark( self ):
"Use tshark to test the of dissector"
# Satisfy pylint
assert self
if StrictVersion( tsharkVersion() ) < StrictVersion( '1.12.0' ):
tshark = pexpect.spawn( 'tshark -i lo -R of' )
else:
@@ -58,7 +51,7 @@ class testWalkthrough( unittest.TestCase ):
self.assertEqual( index, 0, 'No output for "help" command')
# nodes command
p.sendline( 'nodes' )
p.expect( r'([chs]\d ?){4}' )
p.expect( '([chs]\d ?){4}' )
nodes = p.match.group( 0 ).split()
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
p.expect( self.prompt )
@@ -74,15 +67,14 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
# dump command
p.sendline( 'dump' )
expected = [ r'<\w+ (%s)' % n for n in nodes ]
expected = [ '<\w+ (%s)' % n for n in nodes ]
actual = []
for _ in nodes:
index = p.expect( expected )
node = p.match.group( 1 )
actual.append( node )
p.expect( '\n' )
self.assertEqual( actual.sort(), nodes.sort(),
'"nodes" and "dump" differ' )
self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@@ -169,7 +161,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( pexpect.EOF )
# test iperf
p = pexpect.spawn( 'mn --test iperf' )
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
@@ -178,7 +170,7 @@ class testWalkthrough( unittest.TestCase ):
"Test pingall on single,3 and linear,4 topos"
# testing single,3
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
p.expect( r'(\d+)/(\d+) received')
p.expect( '(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
@@ -186,7 +178,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( pexpect.EOF )
# testing linear,4
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
p.expect( r'(\d+)/(\d+) received')
p.expect( '(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
@@ -199,15 +191,14 @@ class testWalkthrough( unittest.TestCase ):
# test bw
p.expect( self.prompt )
p.sendline( 'iperf' )
p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
p.expect( "Results: \['([\d\.]+) Mbits/sec'," )
bw = float( p.match.group( 1 ) )
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' )
p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay > 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' )
@@ -231,13 +222,10 @@ class testWalkthrough( unittest.TestCase ):
def testCustomTopo( self ):
"Start Mininet using a custom topo, then run pingall"
# Satisfy pylint
assert self
custom = os.path.dirname( os.path.realpath( __file__ ) )
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
custom = os.path.normpath( custom )
p = pexpect.spawn(
'mn --custom %s --topo mytopo --test pingall' % custom )
p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom )
p.expect( '0% dropped' )
p.expect( pexpect.EOF )
@@ -255,7 +243,7 @@ class testWalkthrough( unittest.TestCase ):
switches = [ 'user', 'ovsk' ]
for sw in switches:
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
@@ -263,7 +251,7 @@ class testWalkthrough( unittest.TestCase ):
def testBenchmark( self ):
"Run benchmark and verify that it takes less than 2 seconds"
p = pexpect.spawn( 'mn --test none' )
p.expect( r'completed in ([\d\.]+) seconds' )
p.expect( 'completed in ([\d\.]+) seconds' )
time = float( p.match.group( 1 ) )
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
@@ -287,7 +275,7 @@ class testWalkthrough( unittest.TestCase ):
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
# verify that all hosts a reachable
p.sendline( 'pingall' )
p.expect( r'(\d+)% dropped' )
p.expect( '(\d+)% dropped' )
dropped = int( p.match.group( 1 ) )
self.assertEqual( dropped, 0, 'pingall failed')
p.expect( self.prompt )
@@ -339,15 +327,11 @@ class testWalkthrough( unittest.TestCase ):
'Github is not reachable; cannot download Pox' )
def testRemoteController( self ):
"Test Mininet using Pox controller"
# Satisfy pylint
assert self
if not os.path.exists( '/tmp/pox' ):
p = pexpect.spawn(
'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p.expect( pexpect.EOF )
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
net = pexpect.spawn(
'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net.expect( '0% dropped' )
net.expect( pexpect.EOF )
pox.sendintr()
+110 -202
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
"""@package topo
'''@package topo
Network topology creation.
@@ -9,291 +9,203 @@ This package includes code to represent network topologies.
A Topo object can be a topology database for NOX, can represent a physical
setup for testing, and can even be emulated with the Mininet package.
"""
'''
from mininet.util import irange, natural, naturalSeq
class MultiGraph( object ):
"Utility class to track nodes and edges - replaces networkx.MultiGraph"
"Utility class to track nodes and edges - replaces networkx.Graph"
def __init__( self ):
self.node = {}
self.edge = {}
self.data = {}
def add_node( self, node, attr_dict=None, **attrs):
"""Add node to graph
attr_dict: attribute dict (optional)
attrs: more attributes (optional)
warning: updates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node[ node ] = attr_dict
def add_node( self, node ):
"Add node to graph"
self.data.setdefault( node, [] )
def add_edge( self, src, dst, key=None, attr_dict=None, **attrs ):
"""Add edge to graph
key: optional key
attr_dict: optional attribute dict
attrs: more attributes
warning: udpates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node.setdefault( src, {} )
self.node.setdefault( dst, {} )
self.edge.setdefault( src, {} )
self.edge.setdefault( dst, {} )
self.edge[ src ].setdefault( dst, {} )
entry = self.edge[ dst ][ src ] = self.edge[ src ][ dst ]
# If no key, pick next ordinal number
if key is None:
keys = [ k for k in entry.keys() if isinstance( k, int ) ]
key = max( [ 0 ] + keys ) + 1
entry[ key ] = attr_dict
return key
def add_edge( self, src, dest ):
"Add edge to graph"
src, dest = sorted( ( src, dest ) )
self.add_node( src )
self.add_node( dest )
self.data[ src ].append( dest )
def nodes( self, data=False):
"""Return list of graph nodes
data: return list of ( node, attrs)"""
return self.node.items() if data else self.node.keys()
def nodes( self ):
"Return list of graph nodes"
return self.data.keys()
def edges_iter( self, data=False, keys=False ):
def edges( self ):
"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 keys.iteritems():
if data:
if keys:
yield( src, dst, k, attrs )
else:
yield( src, dst, attrs )
else:
if keys:
yield( src, dst, k )
else:
yield( src, dst )
def edges( self, data=False, keys=False ):
"Return list of graph edges"
return list( self.edges_iter( data=data, keys=keys ) )
for src in self.data.keys():
for dest in self.data[ src ]:
yield ( src, dest )
def __getitem__( self, node ):
"Return link dict for given src node"
return self.edge[ node ]
def __len__( self ):
"Return the number of nodes"
return len( self.node )
def convertTo( self, cls, data=False, keys=False ):
"""Convert to a new object of networkx.MultiGraph-like class cls
data: include node and edge data
keys: include edge keys as well as edge data"""
g = cls()
g.add_nodes_from( self.nodes( data=data ) )
g.add_edges_from( self.edges( data=( data or keys ), keys=keys ) )
return g
"Return link dict for the given node"
return self.data[node]
class Topo( object ):
class Topo(object):
"Data center network representation for structured multi-trees."
def __init__( self, *args, **params ):
"""Topo object.
def __init__(self, *args, **params):
"""Topo object.
Optional named parameters:
hinfo: default host options
sopts: default switch options
lopts: default link options
calls build()"""
self.g = MultiGraph()
self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = params.pop( 'hopts', {} )
self.sopts = params.pop( 'sopts', {} )
self.lopts = params.pop( 'lopts', {} )
# ports[src][dst][sport] is port on dst that connects to src
self.ports = {}
self.ports = {} # ports[src][dst] is port on src that connects to dst
self.build( *args, **params )
def build( self, *args, **params ):
"Override this method to build your topology."
pass
def addNode( self, name, **opts ):
def addNode(self, name, **opts):
"""Add Node to graph.
name: name
opts: node options
returns: node name"""
self.g.add_node( name, **opts )
self.g.add_node(name)
self.node_info[name] = opts
return name
def addHost( self, name, **opts ):
def addHost(self, name, **opts):
"""Convenience method: Add host to graph.
name: host name
opts: host options
returns: host name"""
if not opts and self.hopts:
opts = self.hopts
return self.addNode( name, **opts )
return self.addNode(name, **opts)
def addSwitch( self, name, **opts ):
def addSwitch(self, name, **opts):
"""Convenience method: Add switch to graph.
name: switch name
opts: switch options
returns: switch name"""
if not opts and self.sopts:
opts = self.sopts
result = self.addNode( name, isSwitch=True, **opts )
result = self.addNode(name, isSwitch=True, **opts)
return result
def addLink( self, node1, node2, port1=None, port2=None,
key=None, **opts ):
def addLink(self, node1, node2, port1=None, port2=None,
**opts):
"""node1, node2: nodes to link together
port1, port2: ports (optional)
opts: link options (optional)
returns: link info key"""
if not opts and self.lopts:
opts = self.lopts
port1, port2 = self.addPort( node1, node2, port1, port2 )
opts = dict( opts )
opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
self.g.add_edge(node1, node2, key, opts )
self.addPort(node1, node2, port1, port2)
key = tuple(self.sorted([node1, node2]))
self.link_info[key] = opts
self.g.add_edge(*key)
return key
def nodes( self, sort=True ):
def addPort(self, src, dst, sport=None, dport=None):
'''Generate port mapping for new edge.
@param src source switch name
@param dst destination switch name
'''
self.ports.setdefault(src, {})
self.ports.setdefault(dst, {})
# New port: number of outlinks + base
src_base = 1 if self.isSwitch(src) else 0
dst_base = 1 if self.isSwitch(dst) else 0
if sport is None:
sport = len(self.ports[src]) + src_base
if dport is None:
dport = len(self.ports[dst]) + dst_base
self.ports[src][dst] = sport
self.ports[dst][src] = dport
def nodes(self, sort=True):
"Return nodes in graph"
if sort:
return self.sorted( self.g.nodes() )
else:
return self.g.nodes()
def isSwitch( self, n ):
"Returns true if node is a switch."
return self.g.node[ n ].get( 'isSwitch', False )
def isSwitch(self, n):
'''Returns true if node is a switch.'''
info = self.node_info[n]
return info and info.get('isSwitch', False)
def switches( self, sort=True ):
"""Return switches.
sort: sort switches alphabetically
returns: dpids list of dpids"""
return [ n for n in self.nodes( sort ) if self.isSwitch( n ) ]
def switches(self, sort=True):
'''Return switches.
sort: sort switches alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if self.isSwitch(n)]
def hosts( self, sort=True ):
"""Return hosts.
sort: sort hosts alphabetically
returns: list of hosts"""
return [ n for n in self.nodes( sort ) if not self.isSwitch( n ) ]
def hosts(self, sort=True):
'''Return hosts.
sort: sort hosts alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
def iterLinks( self, withKeys=False, withInfo=False ):
"""Return links (iterator)
withKeys: return link keys
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
for _src, _dst, key, info in self.g.edges_iter( data=True, keys=True ):
node1, node2 = info[ 'node1' ], info[ 'node2' ]
if withKeys:
if withInfo:
yield( node1, node2, key, info )
else:
yield( node1, node2, key )
else:
if withInfo:
yield( node1, node2, info )
else:
yield( node1, node2 )
def links( self, sort=False, withKeys=False, withInfo=False ):
"""Return links
sort: sort links alphabetically, preserving (src, dst) order
withKeys: return link keys
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
links = list( self.iterLinks( withKeys, withInfo ) )
def links(self, sort=True):
'''Return links.
sort: sort links alphabetically
@return links list of name pairs
'''
if not sort:
return links
# Ignore info when sorting
tupleSize = 3 if withKeys else 2
return sorted( links, key=( lambda l: naturalSeq( l[ :tupleSize ] ) ) )
return self.g.edges()
else:
links = [tuple(self.sorted(e)) for e in self.g.edges()]
return sorted( links, key=naturalSeq )
# This legacy port management mechanism is clunky and will probably
# be removed at some point.
def port(self, src, dst):
'''Get port number.
def addPort( self, src, dst, sport=None, dport=None ):
"""Generate port mapping for new edge.
src: source switch name
dst: destination switch name"""
# Initialize if necessary
ports = self.ports
ports.setdefault( src, {} )
ports.setdefault( dst, {} )
# New port: number of outlinks + base
if sport is None:
src_base = 1 if self.isSwitch( src ) else 0
sport = len( ports[ src ] ) + src_base
if dport is None:
dst_base = 1 if self.isSwitch( dst ) else 0
dport = len( ports[ dst ] ) + dst_base
ports[ src ][ sport ] = ( dst, dport )
ports[ dst ][ dport ] = ( src, sport )
return sport, dport
@param src source switch name
@param dst destination switch name
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
if src in self.ports and dst in self.ports[src]:
assert dst in self.ports and src in self.ports[dst]
return self.ports[src][dst], self.ports[dst][src]
def port( self, src, dst ):
"""Get port numbers.
src: source switch name
dst: destination switch name
sport: optional source port (otherwise use lowest src port)
returns: tuple (sport, dport), where
sport = port on source switch leading to the destination switch
dport = port on destination switch leading to the source switch
Note that you can also look up ports using linkInfo()"""
# A bit ugly and slow vs. single-link implementation ;-(
ports = [ ( sport, entry[ 1 ] )
for sport, entry in self.ports[ src ].items()
if entry[ 0 ] == dst ]
return ports if len( ports ) != 1 else ports[ 0 ]
def linkInfo( self, src, dst ):
"Return link metadata"
src, dst = self.sorted([src, dst])
return self.link_info[(src, dst)]
def _linkEntry( self, src, dst, key=None ):
"Helper function: return link entry and key"
entry = self.g[ src ][ dst ]
if key is None:
key = min( entry )
return entry, key
def linkInfo( self, src, dst, key=None ):
"Return link metadata dict"
entry, key = self._linkEntry( src, dst, key )
return entry[ key ]
def setlinkInfo( self, src, dst, info, key=None ):
"Set link metadata dict"
entry, key = self._linkEntry( src, dst, key )
entry[ key ] = info
def setlinkInfo( self, src, dst, info ):
"Set link metadata"
src, dst = self.sorted([src, dst])
self.link_info[(src, dst)] = info
def nodeInfo( self, name ):
"Return metadata (dict) for node"
return self.g.node[ name ]
info = self.node_info[ name ]
return info if info is not None else {}
def setNodeInfo( self, name, info ):
"Set metadata (dict) for node"
self.g.node[ name ] = info
def convertTo( self, cls, data=True, keys=True ):
"""Convert to a new object of networkx.MultiGraph-like class cls
data: include node and edge data (default True)
keys: include edge keys as well as edge data (default True)"""
return self.g.convertTo( cls, data=data, keys=keys )
self.node_info[ name ] = info
@staticmethod
def sorted( items ):
"Items sorted in natural (i.e. alphabetical) order"
return sorted( items, key=natural )
return sorted(items, key=natural)
# Our idiom defines additional parameters in build(param...)
# pylint: disable=arguments-differ
class SingleSwitchTopo( Topo ):
"Single switch connected to k hosts."
def build( self, k=2, **_opts ):
def build( self, k=2, **opts ):
"k: number of hosts"
self.k = k
switch = self.addSwitch( 's1' )
@@ -305,8 +217,7 @@ class SingleSwitchTopo( Topo ):
class SingleSwitchReversedTopo( Topo ):
"""Single switch connected to k hosts, with reversed ports.
The lowest-numbered host is connected to the highest-numbered port.
Useful to verify that Mininet properly handles custom port
numberings."""
Useful to verify that Mininet properly handles custom port numberings."""
def build( self, k=2 ):
"k: number of hosts"
@@ -317,11 +228,10 @@ class SingleSwitchReversedTopo( Topo ):
self.addLink( host, switch,
port1=0, port2=( k - h + 1 ) )
class LinearTopo( Topo ):
"Linear topology of k switches, with n hosts per switch."
def build( self, k=2, n=1, **_opts):
def build( self, k=2, n=1, **opts):
"""k: number of switches
n: number of hosts per switch"""
self.k = k
@@ -344,5 +254,3 @@ class LinearTopo( Topo ):
if lastSwitch:
self.addLink( switch, lastSwitch )
lastSwitch = switch
# pylint: enable=arguments-differ
+5 -8
View File
@@ -3,9 +3,6 @@
from mininet.topo import Topo
from mininet.net import Mininet
# The build() method is expected to do this:
# pylint: disable=arguments-differ
class TreeTopo( Topo ):
"Topology for a tree network with a given depth and fanout."
@@ -44,11 +41,11 @@ class TorusTopo( Topo ):
with the default controller or any Ethernet bridge
without STP turned on! It can be used with STP, e.g.:
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
def build( self, x, y ):
if x < 3 or y < 3:
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
'with 2.1' )
hosts, switches, dpid = {}, {}, 0
# Create and wire interior
for i in range( 0, x ):
@@ -56,8 +53,7 @@ class TorusTopo( Topo ):
loc = '%dx%d' % ( i + 1, j + 1 )
# dpid cannot be zero for OVS
dpid = ( i + 1 ) * 256 + ( j + 1 )
switch = switches[ i, j ] = self.addSwitch(
's' + loc, dpid='%016x' % dpid )
switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%016x' % dpid )
host = hosts[ i, j ] = self.addHost( 'h' + loc )
self.addLink( host, switch )
# Connect switches
@@ -69,4 +65,5 @@ class TorusTopo( Topo ):
self.addLink( sw1, sw2 )
self.addLink( sw1, sw3 )
# pylint: enable=arguments-differ
+117 -23
View File
@@ -11,6 +11,7 @@ from fcntl import fcntl, F_GETFL, F_SETFL
from os import O_NONBLOCK
import os
from functools import partial
import tempfile
# Command execution support
@@ -25,7 +26,7 @@ def checkRun( cmd ):
return check_call( cmd.split( ' ' ) )
# pylint doesn't understand explicit type checking
# pylint: disable=maybe-no-member
# pylint: disable-msg=E1103
def oldQuietRun( *cmd ):
"""Run a command, routing stderr to stdout, and return the output.
@@ -119,7 +120,8 @@ def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
# pylint: enable=maybe-no-member
# pylint: enable-msg=E1103
# pylint: disable-msg=E1101
def isShellBuiltin( cmd ):
"Return True if cmd is a bash builtin."
@@ -132,6 +134,8 @@ def isShellBuiltin( cmd ):
isShellBuiltin.builtIns = None
# pylint: enable-msg=E1101
# Interface management
#
# Interfaces are managed as strings which are simply the
@@ -145,22 +149,22 @@ isShellBuiltin.builtIns = None
# live in the root namespace and thus do not have to be
# explicitly moved.
def makeIntfPair( intf1, intf2, addr1=None, addr2=None, runCmd=quietRun ):
def makeIntfPair( intf1, intf2, addr1=None, addr2=None, run=quietRun ):
"""Make a veth pair connecting intf1 and intf2.
intf1: string, interface
intf2: string, interface
runCmd: function to run shell commands (quietRun)
node: node to run on or None (default)
returns: ip link add result"""
# Delete any old interfaces with the same names
runCmd( 'ip link del ' + intf1 )
runCmd( 'ip link del ' + intf2 )
run( 'ip link del ' + intf1 )
run( 'ip link del ' + intf2 )
# Create new pair
if addr1 is None and addr2 is None:
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
else:
cmd = ( 'ip link add name ' + intf1 + ' address ' + addr1 +
' type veth peer name ' + intf2 + ' address ' + addr2 )
cmdOutput = runCmd( cmd )
cmdOutput = run( cmd )
if cmdOutput == '':
return True
else:
@@ -199,8 +203,8 @@ def moveIntfNoRetry( intf, dstNode, printError=False ):
return False
return True
def moveIntf( intf, dstNode, printError=True,
retries=3, delaySecs=0.001 ):
def moveIntf( intf, dstNode, srcNode=None, printError=True,
retries=3, delaySecs=0.001 ):
"""Move interface to node, retrying on failure.
intf: string, interface
dstNode: destination Node
@@ -293,7 +297,7 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
def ipParse( ip ):
"Parse an IP address and return an unsigned int."
args = [ int( arg ) for arg in ip.split( '.' ) ]
while len(args) < 4:
while ( len(args) < 4 ):
args.append( 0 )
return ipNum( *args )
@@ -385,7 +389,7 @@ def sysctlTestAndSet( name, limit ):
#read limit
with open( name, 'r' ) as readFile:
oldLimit = readFile.readline()
if isinstance( limit, int ):
if type( limit ) is int:
#compare integer limits before overriding
if int( oldLimit ) < limit:
with open( name, 'w' ) as writeFile:
@@ -424,12 +428,9 @@ def fixLimits():
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
#Increase number of PTYs for nodes
sysctlTestAndSet( 'kernel.pty.max', 20000 )
# pylint: disable=broad-except
except Exception:
except:
warn( "*** Error setting resource limits. "
"Mininet's performance may be affected.\n" )
# pylint: enable=broad-except
def mountCgroups():
"Make sure cgroups file system is mounted"
@@ -448,7 +449,7 @@ def natural( text ):
def num( s ):
"Convert text segment to int if necessary"
return int( s ) if s.isdigit() else s
return [ num( s ) for s in re.split( r'(\d+)', str( text ) ) ]
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
def naturalSeq( t ):
"Natural sort key function for sequences"
@@ -545,16 +546,15 @@ def ensureRoot():
def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
"""Wait until server is listening on port.
returns True if server is listening"""
runCmd = ( client.cmd if client else
partial( quietRun, shell=True ) )
if not runCmd( 'which telnet' ):
run = ( client.cmd if client else
partial( quietRun, shell=True ) )
if not run( '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 type( server ) is str else server.IP()
cmd = ( 'sh -c "echo A | telnet -e A %s %s"' %
( serverIP, port ) )
( serverIP, port ) )
time = 0
while 'Connected' not in runCmd( cmd ):
while 'Connected' not in run( cmd ):
if timeout:
print time
if time >= timeout:
@@ -566,3 +566,97 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
sleep( .5 )
time += .5
return True
def updateMininet( option, opt_str, value, parser, *args, **kwargs ):
# TODO check root
# TODO check dependencies (git and make)
url = 'https://github.com/mininet/mininet'
choice = None
if parser.rargs:
choice = parser.rargs[ 0 ]
current = kwargs['version']
print 'Your current version is: %s' % current
# get available releases
if not choice:
s = quietRun('git ls-remote --tags %s' % url)
#TODO check output for network failure
tags = []
for t in re.finditer(r'refs/tags/(\d\S*)', s):
v = t.group(1)
if v > current:
tags.append( t.group(1) )
if tags:
print 'Available versions:\n', '\n'.join( tags )
else:
print 'Congratulations! You are up-to-date.'
# ask user to select a version
while not choice:
choice = raw_input('Select a version: ')
if choice in tags:
break
elif 'exit' in choice:
exit()
else:
print 'Error: Please type an available version.'
choice = None
# sanity check
if choice < current:
print 'WARNING: The version that you have selected is a downgrade.'
elif choice == current:
print 'Version %s is already installed' % current
exit()
# confirm choice
if not re.match( r'[yY][eE]?[sS]?',
raw_input( 'Do you want to upgrade to %s? [y/N] ' % choice ) ):
print 'ABORTED'
exit()
# proceed with installation
# 1. detect make develop
if 'dist-packages' in __file__:
## mkdir; cd; git init
develop = False
installDir = tempfile.mkdtemp() + '/mininet'
os.mkdir( installDir )
os.chdir( installDir )
quietRun( 'git init' )
else:
## cd to mininet directory
develop = True
print ( 'FAILURE: You have enabled "sudo make develop".\n\n'
'To upgrade, check out the latest code and run "util/install.sh -n" '
'and then re-run "sudo make develop".' )
exit()
# 2. fetch the tag
if run( 'git fetch https://github.com/mininet/mininet %s' % choice ):
print 'FAILURE: Could not update to %s' % choice
exit()
# 3. check to see if there is a branch conflict
if run( 'git show-ref --verify --quiet refs/heads/%s' % choice ):
# branch does not exist
print run( 'git checkout -b %s FETCH_HEAD' % choice )
else:
# This should not happen until develop is supported...
# TODO: branch already exists
## git checkout %s
## git reset --hard FETCH_HEAD
print '... make develop enabled'
# 4. perform installation
if develop:
## sudo make develop
print '... make develop enabled'
else:
# remove mininet prior to upgrade
oldDir = __file__.split( 'mininet/util' )[0]
for f in os.listdir( oldDir + '/..' ):
print f
if 'mininet' in f:
print run( 'rm -rfv %s' % oldDir + '/../' + f )
# run the install script
os.execl( installDir+'/util/install.sh', 'install.sh', '-n' )
#TODO remove temp dir
exit()
+6 -6
View File
@@ -99,7 +99,6 @@ int main(int argc, char *argv[])
char path[PATH_MAX];
int nsid;
int pid;
char *cwd = get_current_dir_name();
static struct sched_param sp;
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
@@ -158,17 +157,18 @@ int main(int argc, char *argv[])
sprintf(path, "/proc/%d/ns/mnt", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0 || setns(nsid, 0) != 0) {
char *cwd = get_current_dir_name();
/* Plan B: chroot/chdir into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
return 1;
}
}
/* chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
/* need to chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
}
}
break;
case 'g':
+3 -3
View File
@@ -30,7 +30,7 @@ def fixParam( line ):
def fixReturns( line ):
"Change returns: foo to @return foo"
return re.sub( 'returns:', r'@returns', line )
def fixLine( line ):
global comment
match = spaces.match( line )
@@ -69,7 +69,7 @@ def funTest():
).splitlines( True )
fixLines( testFun )
def fixLines( lines, fid ):
for line in lines:
os.write( fid, fixLine( line ) )
@@ -86,4 +86,4 @@ if __name__ == '__main__':
-3
View File
@@ -140,9 +140,6 @@ function mn_deps {
function mn_dev {
echo "Installing Mininet developer dependencies"
$install doxygen doxypy texlive-fonts-recommended
if ! $install doxygen-latex; then
echo "doxygen-latex not needed"
fi
}
# The following will cause a full OF install, covering:
+1 -1
View File
@@ -9,7 +9,7 @@ else
host=$1
fi
pid=`ps ax | grep "mininet:$host$" | grep bash | grep -v mnexec | awk '{print $1};'`
pid=`ps ax | grep "mininet:$host$" | grep bash | awk '{print $1};'`
if echo $pid | grep -q ' '; then
echo "Error: found multiple mininet:$host processes"
+1 -1
View File
@@ -8,7 +8,7 @@ version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
version = version.strip()
# Find all Mininet path references
lines = co( "egrep -or 'Mininet [0-9\.]+\w*' *", shell=True )
lines = co( "grep -or 'Mininet \w\+\.\w\+\.\w\+[+]*' *", shell=True )
error = False
+38 -51
View File
@@ -162,8 +162,8 @@ def srun( cmd, **kwargs ):
def depend():
"Install package dependencies"
log( '* Installing package dependencies' )
run( 'sudo apt-get -qy update' )
run( 'sudo apt-get -qy install'
run( 'sudo apt-get -y update' )
run( 'sudo apt-get install -y'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs dnsmasq curl'
' python-setuptools mtools zip' )
@@ -333,11 +333,9 @@ skipx
# Tell the Ubuntu/Debian installer to stop asking stupid questions
PreseedText = ( """
"""
#d-i mirror/country string manual
#d-i mirror/http/hostname string mirrors.kernel.org
"""
PreseedText = """
d-i mirror/country string manual
d-i mirror/http/hostname string mirrors.kernel.org
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string
d-i partman/confirm_write_new_label boolean true
@@ -347,7 +345,7 @@ d-i partman/confirm_nooverwrite boolean true
d-i user-setup/allow-password-weak boolean true
d-i finish-install/reboot_in_progress note
d-i debian-installer/exit/poweroff boolean true
""" )
"""
def makeKickstartFloppy():
"Create and return kickstart floppy, kickstart, preseed"
@@ -489,24 +487,22 @@ def login( vm, user='mininet', password='mininet' ):
log( '* Waiting for login...' )
def removeNtpd( vm, prompt=Prompt, ntpPackage='ntp' ):
"Remove ntpd and set clock immediately"
log( '* Removing ntpd' )
vm.sendline( 'sudo -n apt-get -qy remove ' + ntpPackage )
def disableNtpd( vm, prompt=Prompt ):
"Turn off ntpd and set clock immediately"
log( '* Turning off ntpd' )
vm.sendline( 'sudo -n service ntp stop' )
vm.expect( prompt )
# Try to make sure that it isn't still running
vm.sendline( 'sudo -n pkill ntpd' )
log( '* Setting clock' )
# -gq: set and quit immediately
vm.sendline( 'sudo -n ntpd -gq' )
vm.expect( prompt )
log( '* Getting seconds since epoch from this server' )
# Note r'date +%s' specifies a format for 'date', not python!
seconds = int( run( r'date +%s' ) )
log( '* Setting VM clock' )
vm.sendline( 'sudo -n date -s @%d' % seconds )
log( '* Waiting one second and running date command' )
vm.sendline( 'sleep 1 && date ' )
def sanityTest( vm ):
"Run Mininet sanity test (pingall) in vm"
vm.sendline( 'sudo -n mn --test pingall' )
vm.sendline( 'sudo mn --test pingall' )
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0:
log( '* Sanity check OK' )
else:
@@ -518,9 +514,9 @@ def sanityTest( vm ):
def coreTest( vm, prompt=Prompt ):
"Run core tests (make test) in VM"
log( '* Making sure cgroups are mounted' )
vm.sendline( 'sudo -n service cgroup-lite restart' )
vm.sendline( 'sudo service cgroup-lite restart' )
vm.expect( prompt )
vm.sendline( 'sudo -n cgroups-mount' )
vm.sendline( 'sudo cgroups-mount' )
vm.expect( prompt )
log( '* Running make test' )
vm.sendline( 'cd ~/mininet; sudo make test' )
@@ -536,35 +532,29 @@ def coreTest( vm, prompt=Prompt ):
log( '* Test', test, 'output:' )
log( vm.before )
def installPexpect( vm, prompt=Prompt ):
"install pexpect"
vm.sendline( 'sudo -n apt-get -qy install python-pexpect' )
vm.expect( prompt )
def noneTest( vm, prompt=Prompt ):
def noneTest( vm ):
"This test does nothing"
installPexpect( vm, prompt )
vm.sendline( 'echo' )
def examplesquickTest( vm, prompt=Prompt ):
"Quick test of mininet examples"
installPexpect( vm, prompt )
vm.sendline( 'sudo -n python ~/mininet/examples/test/runner.py -v -quick' )
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v -quick' )
def examplesfullTest( vm, prompt=Prompt ):
"Full (slow) test of mininet examples"
installPexpect( vm, prompt )
vm.sendline( 'sudo -n python ~/mininet/examples/test/runner.py -v' )
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v' )
def walkthroughTest( vm, prompt=Prompt ):
"Test mininet walkthrough"
installPexpect( vm, prompt )
vm.sendline( 'sudo -n python ~/mininet/mininet/test/test_walkthrough.py -v' )
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/mininet/test/test_walkthrough.py -v' )
def useTest( vm, prompt=Prompt ):
@@ -590,7 +580,7 @@ def checkOutBranch( vm, branch, prompt=Prompt ):
vm.sendline( 'cd ~/mininet; git fetch --all; git checkout '
+ branch + '; git pull --rebase origin ' + branch )
vm.expect( prompt )
vm.sendline( 'sudo -n make install' )
vm.sendline( 'sudo make install' )
def interact( vm, tests, pre='', post='', prompt=Prompt ):
@@ -609,7 +599,7 @@ def interact( vm, tests, pre='', post='', prompt=Prompt ):
'install-mininet-vm.sh' % branch )
vm.expect( prompt )
log( '* Running VM install script' )
installcmd = 'bash -v install-mininet-vm.sh'
installcmd = 'bash install-mininet-vm.sh'
if Branch:
installcmd += ' ' + Branch
vm.sendline( installcmd )
@@ -840,14 +830,12 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
os.chdir( '..' )
def runTests( vm, tests=None, pre='', post='', prompt=Prompt, uninstallNtpd=False ):
def runTests( vm, tests=None, pre='', post='', prompt=Prompt ):
"Run tests (list) in vm (pexpect object)"
# We disable ntpd and set the time so that ntpd won't be
# messing with the time during tests. Set to true for a COW
# disk and False for a non-COW disk.
if uninstallNtpd:
removeNtpd( vm )
vm.expect( prompt )
# We disable ntpd and set the time so that it won't be
# messing with the time during tests
disableNtpd( vm )
vm.expect( prompt )
if Branch:
checkOutBranch( vm, branch=Branch )
vm.expect( prompt )
@@ -881,7 +869,7 @@ def getMininetVersion( vm ):
return version
def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
runFunction=None, **runArgs ):
"""Boot and test VM
tests: list of tests to run
@@ -914,7 +902,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
if runFunction:
runFunction( vm, **runArgs )
log( '* Shutting down' )
vm.sendline( 'sudo -n shutdown -h now ' )
vm.sendline( 'sudo shutdown -h now ' )
log( '* Waiting for shutdown' )
vm.wait()
if outputFile:
@@ -1023,8 +1011,7 @@ def parseArgs():
exit( 1 )
for image in args.image:
bootAndRun( image, runFunction=runTests, tests=args.test, pre=args.run,
post=args.post, memory=args.memory, outputFile=args.out,
uninstallNtpd=True )
post=args.post, memory=args.memory, outputFile=args.out )
if not ( args.depend or args.list or args.clean or args.flavor
or args.image ):
parser.print_help()
+7 -10
View File
@@ -6,21 +6,20 @@
#
# optional argument: Mininet branch to install
set -e
echo "$(whoami) ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers > /dev/null
echo `whoami` ALL=NOPASSWD: ALL | sudo tee -a /etc/sudoers > /dev/null
sudo sed -i -e 's/Default/#Default/' /etc/sudoers
echo mininet-vm | sudo tee /etc/hostname > /dev/null
sudo sed -i -e 's/ubuntu/mininet-vm/g' /etc/hosts
sudo hostname `cat /etc/hostname`
sudo sed -i -e 's/quiet splash/text/' /etc/default/grub
sudo update-grub
# Update from official archive
sudo apt-get update
# 12.10 and earlier
#sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
# /etc/apt/sources.list
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
sudo sed -i -e 's/\/archive.ubuntu.com/\/mirrors.kernel.org/' \
/etc/apt/sources.list
sudo apt-get update
# Clean up vmware easy install junk if present
if [ -e /etc/issue.backup ]; then
sudo mv /etc/issue.backup /etc/issue
@@ -34,9 +33,7 @@ git clone git://github.com/mininet/mininet
# Optionally check out branch
if [ "$1" != "" ]; then
pushd mininet
#git checkout -b $1 $1
# TODO branch will in detached HEAD state if it is not master
git checkout $1
git checkout -b $1 $1
popd
fi
# Install Mininet