Compare commits

..
2 Commits
43 changed files with 934 additions and 1356 deletions

No files matched your search

-28
View File
@@ -1,28 +0,0 @@
language: python
sudo: required
matrix:
include:
- dist: trusty
env: dist="14.04 LTS trusty"
- dist: xenial
env: dist="16.04 LTS xenial"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq vlan
- sudo util/install.sh -n
install:
- bash -c "[ `lsb_release -rs` == '14.04' ] && make codecheck"
- sudo util/install.sh -fnvw
script:
- sudo mn --test pingall
- sudo python mininet/test/runner.py -v -quick
- sudo python examples/test/runner.py -v -quick
notifications:
email:
on_success: never
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
+2 -8
View File
@@ -18,7 +18,6 @@ Cody Burkard
Additional Mininet Contributors
M S Vishwanath Bhat
Tomasz Buchert
Gustavo Pantuza Coelho Pinto
Fernando Cappi
@@ -30,23 +29,18 @@ Andrew Ferguson
Eder Leao Fernandes
Gregory Gee
Jon Hall
Jono Hart
Roan Huang
Vitaly Ivanov
Babis Kaidos
Rich Lane
Rémy Léone
Zi Shen Lim
David Mahler
Murphy McCauley
José Pedro Oliveira
James Page
Rahman Pujianto
Rich Lane
Rémy Léone
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Olivier Tilmans
Andreas Wundsam
Isaku Yamahata
Baohua Yang
+6 -6
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.2.2
Mininet 2.2.0
---
The supported installation methods for Mininet are 1) using a
@@ -65,7 +65,7 @@ like to contribute an installation script, we would welcome it!)
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 `util/`.
our handy `install.sh` script, which is in `mininet/util`.
*WARNING: USE AT YOUR OWN RISK!*
@@ -81,7 +81,7 @@ like to contribute an installation script, we would welcome it!)
To install Mininet itself, the OpenFlow reference implementation, and
Open vSwitch, you may use:
util/install.sh -fnv
mininet/util/install.sh -fnv
This should be reasonably quick, and the following command should
work after the installation:
@@ -92,14 +92,14 @@ like to contribute an installation script, we would welcome it!)
including POX, the OpenFlow WireShark dissector, the `oftest`
framework, and other potentially useful software, you may use:
util/install.sh -a
mininet/util/install.sh -a
This takes about 4 minutes on our test system.
You can change the directory where the dependencies are installed using
the -s <directory> flag.
util/install.sh -s <directory> -a
mininet/util/install.sh -s <directory> -a
3.2. Native installation from source on Fedora 18+.
@@ -128,7 +128,7 @@ like to contribute an installation script, we would welcome it!)
* install Mininet, the OpenFlow reference implementation, and
Open vSwitch
util/install.sh -fnv
mininet/util/install.sh -fnv
* enable and start openvswitch
+2 -2
View File
@@ -1,6 +1,6 @@
Mininet 2.2.2 License
Mininet 2.2.0 License
Copyright (c) 2013-2017 Open Networking Laboratory
Copyright (c) 2013-2014 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+2 -3
View File
@@ -2,7 +2,6 @@ MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
PYMN = python -B bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
@@ -44,7 +43,7 @@ slowtest: $(MININET)
mininet/examples/test/runner.py -v
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
@@ -61,7 +60,7 @@ man: $(MANPAGES)
mn.1: $(MN)
PYTHONPATH=. help2man -N -n "create a Mininet network." \
--no-discard-stderr "$(PYMN)" -o $@
--no-discard-stderr $< -o $@
mnexec.1: mnexec
help2man -N -n "execution utility for Mininet." \
+42 -22
View File
@@ -3,9 +3,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
*The best way to emulate almost any network on your laptop!*
Mininet 2.2.2
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
Mininet 2.2.0
### What is Mininet?
@@ -70,25 +68,44 @@ Mininet includes:
### New features in this release
This is primarily a performance improvement and bug fix release.
This release provides a number of bug fixes as well as
several new features, including:
- Batch startup has been implemented for Open vSwitch, improving
startup performance.
* Improved OpenFlow 1.3 support
- OVS patch links have been implemented via OVSLink and --link ovs
- `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
Warning! These links have *serious limitations* compared to
virtual Ethernet pairs: they are not attached to real Linux
interfaces so you cannot use tcpdump or wireshark with them;
they also cannot be used in long chains - we don't recommend more
than 64 OVSLinks, for example --linear,64. However, they can offer
significantly better performance than veth pairs, for certain
configurations.
* A new `nodelib.py` node library, and new `Node` types including
`LinuxBridge`, `OVSBridge`, `LinuxRouter` (see `examples/`)
and `NAT`
- You can now easily install Mininet on a Raspberry Pi ;-)
* 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)
- Additional information for this release and previous releases
may be found in the release notes on docs.mininet.org
* 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`
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
@@ -112,8 +129,6 @@ Mininet mailing list, `mininet-discuss` at:
### Join Us
Thanks again to all of the Mininet contributors!
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,
@@ -127,7 +142,12 @@ hard work that Mininet continues to grow and improve.
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
Bob Lantz
Mininet Core Team
The Mininet Core Team:
* Bob Lantz
* Brian O'Connor
* Cody Burkard
Thanks again to all of the Mininet contributors, particularly Gregory
Gee for his work on MiniEdit.
[1]: https://travis-ci.org/mininet/mininet.svg?branch=devel/2.2.2
+68 -81
View File
@@ -25,16 +25,15 @@ 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, NullController,
RYU, NOX, RemoteController, findController,
DefaultController,
UserSwitch, OVSSwitch, OVSBridge,
IVSSwitch )
OVSLegacyKernelSwitch, IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink, TCULink, OVSLink
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
SingleSwitchReversedTopo, MinimalTopo )
from mininet.link import Link, TCLink, OVSLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import customClass, specialClass, splitArgs
from mininet.util import customConstructor, splitArgs
from mininet.util import buildTopo
from functools import partial
@@ -42,15 +41,14 @@ from functools import partial
# Experimental! cluster edition prototype
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
RemoteOVSSwitch, RemoteLink,
SwitchBinPlacer, RandomPlacer,
ClusterCleanup )
SwitchBinPlacer, RandomPlacer )
from mininet.examples.clustercli import ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
# built in topologies, created only when run
TOPODEF = 'minimal'
TOPOS = { 'minimal': MinimalTopo,
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
@@ -63,57 +61,59 @@ SWITCHES = { 'user': UserSwitch,
'ovsbr' : OVSBridge,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch,
'lxbr': LinuxBridge,
'default': OVSSwitch }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
'rt': partial( CPULimitedHost, sched='rt' ),
'cfs': partial( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'default'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'ryu': Ryu,
'default': DefaultController, # Note: overridden below
'none': NullController }
'ryu': RYU,
'default': DefaultController, # Note: replaced below
'none': lambda name: None }
LINKDEF = 'default'
LINKS = { 'default': Link, # Note: overridden below
LINKS = { 'default': Link,
'tc': TCLink,
'tcu': TCULink,
'ovs': OVSLink }
# Names of tests that are Mininet() methods
TESTNAMES = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all',
'iperfudp', 'none' ]
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
'none' ]
# Map to alternate functions and/or spellings of Mininet() methods
TESTS = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
ALTSPELLING = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
def addDictOption( opts, choicesDict, default, name, **kwargs ):
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
"""Convenience function to add choices dicts to OptionParser.
opts: OptionParser instance
choicesDict: dictionary of valid choices, must include default
default: default choice key
name: long option name
kwargs: additional arguments to add_option"""
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
helpList = [ '%s=%s' % ( k, v.__name__ )
for k, v in choicesDict.items() ]
helpStr += ' ' + ( ' '.join( helpList ) )
params = dict( type='string', default=default, help=helpStr )
params.update( **kwargs )
opts.add_option( '--' + name, **params )
help: string"""
if default not in choicesDict:
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
if not helpStr:
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
help = helpStr )
def version( *_args ):
"Print Mininet version and exit"
@@ -159,8 +159,7 @@ class MininetRunner( object ):
def setCustom( self, name, value ):
"Set custom parameters for MininetRunner."
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links'
'testnames', 'tests' ):
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
# Update dictionaries
param = name.upper()
globals()[ param ].update( value )
@@ -199,7 +198,7 @@ class MininetRunner( object ):
opts = OptionParser( description=desc, usage=usage )
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
addDictOption( opts, CONTROLLERS, [], 'controller', action='append' )
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
addDictOption( opts, LINKS, LINKDEF, 'link' )
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
@@ -210,9 +209,10 @@ class MininetRunner( object ):
type='string',
help='read custom classes or params from .py file(s)'
)
testList = TESTNAMES + TESTS.keys()
opts.add_option( '--test', default=[], action='append',
dest='tests', help='|'.join( testList ) )
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
@@ -226,7 +226,7 @@ class MininetRunner( object ):
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6654,
opts.add_option( '--listenport', type='int', default=6634,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening " +
@@ -239,7 +239,7 @@ class MininetRunner( object ):
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
opts.add_option( '--nat', action='callback', callback=self.setNat,
help="[option=val...] adds a NAT to the topology that"
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"
@@ -281,43 +281,34 @@ class MininetRunner( object ):
def begin( self ):
"Create and run mininet."
if self.options.cluster:
servers = self.options.cluster.split( ',' )
for server in servers:
ClusterCleanup.add( server )
if self.options.clean:
cleanup()
exit()
start = time.time()
if not self.options.controller:
if self.options.controller == 'default':
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
self.options.controller = [ 'default' ]
if not CONTROLLERS[ 'default' ]:
self.options.controller = [ 'none' ]
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'
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
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 = customClass( SWITCHES, self.options.switch )
host = customClass( HOSTS, self.options.host )
controller = [ customClass( CONTROLLERS, c )
for c in self.options.controller ]
if self.options.switch == 'user' and self.options.link == 'default':
debug( '*** Using TCULink with UserSwitch\n' )
# Use link configured correctly for UserSwitch
self.options.link = 'tcu'
link = customClass( LINKS, self.options.link )
switch = customConstructor( SWITCHES, self.options.switch )
host = customConstructor( HOSTS, self.options.host )
controller = customConstructor( CONTROLLERS, self.options.controller )
link = customConstructor( LINKS, self.options.link )
if self.validate:
self.validate( self.options )
@@ -343,7 +334,7 @@ class MininetRunner( object ):
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
Net = partial( MininetCluster, servers=servers,
Net = partial( MininetCluster, servers=cluster.split( ',' ),
placement=PLACEMENT[ self.options.placement ] )
mn = Net( topo=topo,
@@ -363,27 +354,23 @@ class MininetRunner( object ):
if self.options.pre:
cli( mn, script=self.options.pre )
test = self.options.test
test = ALTSPELLING.get( test, test )
mn.start()
if not self.options.tests:
if test == 'none':
pass
elif test == 'all':
mn.waitConnected()
mn.start()
mn.ping()
mn.iperf()
elif test == 'cli':
cli( mn )
for test in self.options.tests:
test = TESTS.get( test, test )
if callable( test ): # user added TESTMAP={'mytest': testfn}
test( mn )
elif test == 'none':
pass
elif test == 'all':
mn.waitConnected()
mn.start()
mn.ping()
mn.iperf()
elif test == 'cli':
cli( mn )
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
if self.options.post:
cli( mn, script=self.options.post )
+59 -108
View File
@@ -79,10 +79,9 @@ from mininet.link import Link, Intf
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, errRun
from mininet.util import quietRun, makeIntfPair, errRun, retry
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from mininet.clean import addCleanupCallback
from signal import signal, SIGINT, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
@@ -90,51 +89,9 @@ import os
from random import randrange
import sys
import re
from itertools import groupby
from operator import attrgetter
from distutils.version import StrictVersion
def findUser():
"Try to return logged-in (usually non-root) user"
return (
# 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' ).strip() )
class ClusterCleanup( object ):
"Cleanup callback"
inited = False
serveruser = {}
@classmethod
def add( cls, server, user='' ):
"Add an entry to server: user dict"
if not cls.inited:
addCleanupCallback( cls.cleanup )
if not user:
user = findUser()
cls.serveruser[ server ] = user
@classmethod
def cleanup( cls ):
"Clean up"
info( '*** Cleaning up cluster\n' )
for server, user in cls.serveruser.iteritems():
if server == 'localhost':
# Handled by mininet.clean.cleanup()
continue
else:
cmd = [ 'su', user, '-c',
'ssh %s@%s sudo mn -c' % ( user, server ) ]
info( cmd, '\n' )
info( quietRun( cmd ) )
# BL note: so little code is required for remote nodes,
# we will probably just want to update the main Node()
# class to enable it for remote access! However, there
@@ -168,8 +125,7 @@ class RemoteMixin( object ):
self.server = server if server else 'localhost'
self.serverIP = ( serverIP if serverIP
else self.findServerIP( self.server ) )
self.user = user if user else findUser()
ClusterCleanup.add( server=server, user=user )
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'
@@ -182,7 +138,7 @@ class RemoteMixin( object ):
self.sshcmd += [ '-o', 'ControlPath=' + self.controlPath,
'-o', 'ControlMaster=auto',
'-o', 'ControlPersist=' + '1' ]
self.sshcmd += [ self.dest ]
self.sshcmd = self.sshcmd + [ self.dest ]
self.isRemote = True
else:
self.dest = None
@@ -192,6 +148,17 @@ class RemoteMixin( object ):
self.shell, self.pid = None, None
super( RemoteMixin, self ).__init__( name, **kwargs )
@staticmethod
def findUser():
"Try to return logged-in (usually non-root) user"
return (
# 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' ) )
# Determine IP address of local host
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
@@ -277,7 +244,7 @@ class RemoteMixin( object ):
# Drop privileges
cmd = [ 'sudo', '-E', '-u', self.user ] + cmd
params.update( preexec_fn=self._ignoreSignal )
debug( '_popen', cmd, '\n' )
debug( '_popen', ' '.join(cmd), params )
popen = super( RemoteMixin, self )._popen( cmd, **params )
return popen
@@ -287,9 +254,18 @@ class RemoteMixin( object ):
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
kwargs.update( moveIntfFn=RemoteLink.moveIntf )
return super( RemoteMixin, self).addIntf( *args, **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"
@@ -306,11 +282,6 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
OVSVersions = {}
def __init__( self, *args, **kwargs ):
# No batch startup yet
kwargs.update( batch=True )
super( RemoteOVSSwitch, self ).__init__( *args, **kwargs )
def isOldOVS( self ):
"Is remote switch using an old OVS version?"
cls = type( self )
@@ -323,28 +294,6 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
return ( StrictVersion( cls.OVSVersions[ self.server ] ) <
StrictVersion( '1.10' ) )
@classmethod
def batchStartup( cls, switches, **_kwargs ):
"Start up switches in per-server batches"
key = attrgetter( 'server' )
for server, switchGroup in groupby( sorted( switches, key=key ), key ):
info( '(%s)' % server )
group = tuple( switchGroup )
switch = group[ 0 ]
OVSSwitch.batchStartup( group, run=switch.cmd )
return switches
@classmethod
def batchShutdown( cls, switches, **_kwargs ):
"Stop switches in per-server batches"
key = attrgetter( 'server' )
for server, switchGroup in groupby( sorted( switches, key=key ), key ):
info( '(%s)' % server )
group = tuple( switchGroup )
switch = group[ 0 ]
OVSSwitch.batchShutdown( group, run=switch.rcmd )
return switches
class RemoteLink( Link ):
"A RemoteLink is a link between nodes which may be on different servers"
@@ -365,27 +314,24 @@ class RemoteLink( Link ):
"Stop this link"
if self.tunnel:
self.tunnel.terminate()
self.intf1.delete()
self.intf2.delete()
else:
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
def makeIntfPair( self, 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)"""
node1 = self.node1 if node1 is None else node1
node2 = self.node2 if node2 is None else node2
node1, node2 = self.node1, self.node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == server2:
# Link within same server
return Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs )
if server1 == 'localhost' and server2 == 'localhost':
# 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 )
# Otherwise, make a tunnel
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
addr1, addr2 )
@@ -402,7 +348,7 @@ class RemoteLink( Link ):
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
node.rcmd( cmd )
links = node.cmd( 'ip link show' )
if not re.search( r' %s[:@]' % intf, links ):
if not ' %s:' % intf in links:
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
@@ -422,11 +368,12 @@ class RemoteLink( Link ):
# 1. Create tap interfaces
for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename
node.rcmd( 'ip link delete tap9', stderr=PIPE )
cmd = 'ip tuntap add dev tap9 mode tap user ' + node.user
result = node.rcmd( cmd )
if result:
raise Exception( 'error creating tap9 on %s: %s' %
( node, result ) )
node.rcmd( cmd )
links = node.rcmd( 'ip link show' )
# print 'after add, links =', links
assert 'tap9' in links
# 2. Create ssh tunnel between tap interfaces
# -n: close stdin
dest = '%s@%s' % ( node2.user, node2.serverIP )
@@ -439,25 +386,29 @@ class RemoteLink( Link ):
debug( 'Waiting for tunnel to come up...\n' )
ch = tunnel.stdout.read( 1 )
if ch != '@':
raise Exception( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
error( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
tunnel.terminate()
tunnel.wait()
error( ch + tunnel.stdout.read() )
error( tunnel.stderr.read() )
sys.exit( 1 )
# 3. Move interfaces if necessary
for node in node1, node2:
if not self.moveIntf( 'tap9', node ):
raise Exception( 'interface move failed on node %s' % node )
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 ) ):
if not addr:
result = node.cmd( 'ip link set tap9 name', intf )
node.cmd( 'ip link set tap9 name', intf )
else:
result = node.cmd( 'ip link set tap9 name', intf,
'address', addr )
if result:
raise Exception( 'error renaming %s: %s' % ( intf, result ) )
node.cmd( 'ip link set tap9 name', intf, 'address', addr )
for node, intf in ( ( node1, intfname1 ), ( node2, intfname2 ) ):
assert intf in node.cmd( 'ip link show' )
return tunnel
def status( self ):
@@ -673,7 +624,7 @@ class MininetCluster( Mininet ):
if not self.serverIP:
self.serverIP = { server: RemoteMixin.findServerIP( server )
for server in self.servers }
self.user = params.pop( 'user', findUser() )
self.user = params.pop( 'user', RemoteMixin.findUser() )
if params.pop( 'precheck' ):
self.precheck()
self.connections = {}
+1 -1
View File
@@ -17,7 +17,7 @@ setLogLevel( 'info' )
# Ignore the warning message that the remote isn't (yet) running
c0 = Controller( 'c0', port=6633 )
c1 = Controller( 'c1', port=6634 )
c2 = RemoteController( 'c2', ip='127.0.0.1', port=6633 )
c2 = RemoteController( 'c2', ip='127.0.0.1' )
cmap = { 's1': c0, 's2': c1, 's3': c2 }
+1 -8
View File
@@ -27,18 +27,11 @@ from mininet.log import setLogLevel, info
class DataController( Controller ):
"""Data Network Controller.
patched to avoid checkListening error and to delete intfs"""
patched to avoid checkListening error"""
def checkListening( self ):
"Ignore spurious error"
pass
def stop( self, *args, **kwargs ):
"Make sure intfs are deleted"
kwargs.update( deleteIntfs=True )
super( DataController, self ).stop( *args, **kwargs )
class MininetFacade( object ):
"""Mininet object facade that allows a single CLI to
talk to one or more networks"""
+16 -49
View File
@@ -2,30 +2,6 @@
"""
cpu.py: test iperf bandwidth for varying cpu limits
Since we are limiting the hosts (only), we should expect the iperf
processes to be affected, as well as any system processing which is
billed to the hosts.
We reserve >50% of cycles for system processing; we assume that
this is enough for it not to affect results. Hosts are limited to
40% of total cycles, which we assume is enough to make them CPU
bound.
As CPU performance increases over time, we may have to reduce the
overall CPU allocation so that the host processing is still CPU bound.
This is perhaps an argument for specifying performance in a more
system-independent manner.
It would also be nice to have a better handle on limiting packet
processing cycles. It's not entirely clear to me how those are
billed to user or system processes if we are using OVS with a kernel
datapath. With a user datapath, they are easier to account for, but
overall performance is usually lower.
Although the iperf client uses more CPU and should be CPU bound (?),
we measure the received data at the server since the client transmit
rate includes buffering.
"""
from mininet.net import Mininet
@@ -35,7 +11,7 @@ from mininet.util import custom, waitListening
from mininet.log import setLogLevel, info
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
"""Example/test of link and CPU bandwidth limits
cpu: cpu limit as fraction of overall CPU time"""
@@ -44,35 +20,27 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
results = {}
for sched in 'rt', 'cfs':
info( '*** Testing with', sched, 'bandwidth limiting\n' )
print '*** Testing with', sched, 'bandwidth limiting'
for cpu in cpuLimits:
# cpu is the cpu fraction for all hosts, so we divide
# it across two hosts
host = custom( CPULimitedHost, sched=sched,
period_us=period_us,
cpu=.5*cpu )
cpu=cpu )
try:
net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except
except:
info( '*** Skipping scheduler %s\n' % sched )
info( '*** Skipping host %s\n' % sched )
break
net.start()
net.pingAll()
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
client, server = hosts[ 0 ], hosts[ -1 ]
info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' %
( 100.0 * cpu ) )
# We measure at the server because it doesn't include
# the client's buffer fill rate
popen = server.popen( 'iperf -yc -s -p 5001' )
server.cmd( 'iperf -s -p 5001 &' )
waitListening( client, server, 5001 )
# ignore empty result from waitListening/telnet
popen.stdout.readline()
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
result = popen.stdout.readline().split( ',' )
result = client.cmd( 'iperf -yc -t %s -c %s' % (
seconds, server.IP() ) ).split( ',' )
bps = float( result[ -1 ] )
popen.terminate()
server.cmdPrint( 'kill %iperf' )
net.stop()
updated = results.get( sched, [] )
updated += [ ( cpu, bps ) ]
@@ -84,23 +52,22 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
def dump( results ):
"Dump results"
fmt = '%s\t%s\t%s\n'
fmt = '%s\t%s\t%s'
info( '\n' )
info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) )
print
print fmt % ( 'sched', 'cpu', 'client MB/s' )
print
for sched in sorted( results.keys() ):
entries = results[ sched ]
for cpu, bps in entries:
pct = '%d%%' % ( cpu * 100 )
mbps = '%.2e' % bps
info( fmt % ( sched, pct, mbps ) )
pct = '%.2f%%' % ( cpu * 100 )
mbps = bps / 1e6
print fmt % ( sched, pct, mbps )
if __name__ == '__main__':
setLogLevel( 'info' )
# These are the limits for the hosts/iperfs - the
# rest is for system processes
limits = [ .5, .4, .3, .2, .1 ]
limits = [ .45, .4, .3, .2, .1 ]
out = bwtest( limits )
dump( out )
+2 -3
View File
@@ -17,11 +17,10 @@ from mininet.util import quietRun
def checkIntf( intf ):
"Make sure intf exists and is not configured."
config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True )
if not config:
if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
error( 'Error:', intf, 'does not exist!\n' )
exit( 1 )
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', config )
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
if ips:
error( 'Error:', intf, 'has an IP address,'
'and is probably in use!\n' )
+13 -15
View File
@@ -26,7 +26,7 @@ of switches, this example demonstrates:
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
from mininet.log import lg, info
from mininet.log import lg
from mininet.util import irange, quietRun
from mininet.link import TCLink
from functools import partial
@@ -86,7 +86,7 @@ def linearBandwidthTest( lengths ):
print "*** testing", datapath, "datapath"
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='2ms', bw=10 )
link = partial( TCLink, delay='1ms' )
net = Mininet( topo=topo, switch=Switch,
controller=Controller, waitConnected=True,
link=link )
@@ -100,12 +100,11 @@ def linearBandwidthTest( lengths ):
# Try to prime the pump to reduce PACKET_INs during test
# since the reference controller is reactive
src.cmd( 'telnet', dst.IP(), '5001' )
info( "testing", src.name, "<->", dst.name, '\n' )
# serverbw = received; _clientbw = buffered
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=10 )
info( serverbw, '\n' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ], seconds=10 )
print bandwidth
flush()
results[ datapath ] += [ ( n, serverbw ) ]
results[ datapath ] += [ ( n, bandwidth ) ]
net.stop()
for datapath in switches.keys():
@@ -113,16 +112,15 @@ def linearBandwidthTest( lengths ):
print "*** Linear network results for", datapath, "datapath:"
print
result = results[ datapath ]
info( "SwitchCount\tiperf Results\n" )
for switchCount, serverbw in result:
info( switchCount, '\t\t' )
info( serverbw, '\n' )
info( '\n')
info( '\n' )
print "SwitchCount\tiperf Results"
for switchCount, bandwidth in result:
print switchCount, '\t\t',
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
print
print
if __name__ == '__main__':
lg.setLogLevel( 'info' )
sizes = [ 1, 10, 20, 40, 60, 80 ]
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
print "*** Running linearBandwidthTest", sizes
linearBandwidthTest( sizes )
+21 -36
View File
@@ -6,25 +6,20 @@ linuxrouter.py: Example network with Linux IP router
This example converts a Node into a router using IP forwarding
already built into Linux.
The example topology creates a router and three IP subnets:
The topology contains a router with three IP subnets:
- 192.168.1.0/24 (interface IP: 192.168.1.1)
- 172.16.0.0/12 (interface IP: 172.16.0.1)
- 10.0.0.0/8 (interface IP: 10.0.0.1)
- 192.168.1.0/24 (r0-eth1, IP: 192.168.1.1)
- 172.16.0.0/12 (r0-eth2, IP: 172.16.0.1)
- 10.0.0.0/8 (r0-eth3, IP: 10.0.0.1)
It also contains three hosts, one in each subnet:
- h1 (IP: 192.168.1.100)
- h2 (IP: 172.16.0.100)
- h3 (IP: 10.0.0.100)
Each subnet consists of a single host connected to
a single switch:
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.
r0-eth1 - s1-eth1 - h1-eth0 (IP: 192.168.1.100)
r0-eth2 - s2-eth1 - h2-eth0 (IP: 172.16.0.100)
r0-eth3 - s3-eth1 - h3-eth0 (IP: 10.0.0.100)
The example relies on default routing entries that are
automatically created for each router interface, as well
as 'defaultRoute' parameters for the host interfaces.
Additional routes may be added to the router or hosts by
executing 'ip route' or 'route' commands on the router or hosts.
"""
from mininet.topo import Topo
@@ -47,39 +42,29 @@ class LinuxRouter( Node ):
class NetworkTopo( Topo ):
"A LinuxRouter connecting three IP subnets"
"A simple topology of a router with three subnets (one host in each)."
def build( self, **_opts ):
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
s1, s2, s3 = [ self.addSwitch( s ) for s in 's1', 's2', 's3' ]
self.addLink( s1, router, intfName2='r0-eth1',
params2={ 'ip' : defaultIP } ) # for clarity
self.addLink( s2, router, intfName2='r0-eth2',
params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( s3, router, intfName2='r0-eth3',
params2={ 'ip' : '10.0.0.1/8' } )
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' )
for h, s in [ (h1, s1), (h2, s2), (h3, s3) ]:
self.addLink( h, s )
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 is used by s1-s3
net = Mininet( topo=topo, controller=None ) # no controller needed
net.start()
info( '*** Routing Table on Router:\n' )
info( '*** Routing Table on Router\n' )
print net[ 'r0' ].cmd( 'route' )
CLI( net )
net.stop()
+2 -2
View File
@@ -49,7 +49,7 @@ from mininet.log import info, setLogLevel
from mininet.net import Mininet, VERSION
from mininet.util import netParse, ipAdd, quietRun
from mininet.util import buildTopo
from mininet.util import custom, customClass
from mininet.util import custom, customConstructor
from mininet.term import makeTerm, cleanUpScreens
from mininet.node import Controller, RemoteController, NOX, OVSController
from mininet.node import CPULimitedHost, Host, Node
@@ -3222,7 +3222,7 @@ class MiniEdit( Frame ):
return
self.newTopology()
topo = buildTopo( TOPOS, self.options.topo )
link = customClass( LINKS, self.options.link )
link = customConstructor( LINKS, self.options.link )
importNet = Mininet(topo=topo, build=False, link=link)
importNet.build()
+94 -3
View File
@@ -2,20 +2,111 @@
"""
Example to create a Mininet topology and connect it to the internet via NAT
through eth0 on the host.
Glen Gibb, February 2011
(slight modifications by BL, 5/13)
"""
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeNet
#################################
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
"""Start NAT/forwarding between Mininet and external network
root: node to access iptables from
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
# Identify the interface connecting to the mininet network
localIntf = root.defaultIntf()
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
root.cmd( 'iptables -P INPUT ACCEPT' )
root.cmd( 'iptables -P OUTPUT ACCEPT' )
root.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
def stopNAT( root ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
def fixNetworkManager( root, intf ):
"""Prevent network-manager from messing with our interface,
by specifying manual configuration in /etc/network/interfaces
root: a node in the root namespace (for running commands)
intf: interface name"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if line not in config:
print '*** Adding', line.strip(), 'to', cfile
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
root.cmd( 'service network-manager restart' )
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
"""Connect the network to the internet
switch: switch to connect to root namespace
rootip: address for interface in root namespace
subnet: Mininet subnet"""
switch = network.get( switch )
prefixLen = subnet.split( '/' )[ 1 ]
# Create a node in root namespace
root = Node( 'root', inNamespace=False )
# Prevent network-manager from interfering with our interface
fixNetworkManager( root, 'root-eth0' )
# Create link between root NS and switch
link = network.addLink( root, switch )
link.intf1.setIP( rootip, prefixLen )
# Start network that now includes link to root namespace
network.start()
# Start NAT and establish forwarding
startNAT( root )
# Establish routes from end hosts
for host in network.hosts:
host.cmd( 'ip route flush root 0/0' )
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
host.cmd( 'route add default gw', rootip )
return root
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
# Add NAT connectivity
net.addNAT().configDefault()
net.start()
# Configure and start NATted connectivity
rootnode = connectToInternet( net )
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
CLI( net )
# Shut down NAT
stopNAT( rootnode )
net.stop()
+8 -17
View File
@@ -16,30 +16,22 @@ from mininet.link import TCLink
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel
from sys import argv
class SingleSwitchTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, lossy=True, **opts):
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
switch = self.addSwitch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
host = self.addHost('h%s' % (h + 1),
cpu=.5 / n)
if lossy:
# 10 Mbps, 5ms delay, 10% packet loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
else:
# 10 Mbps, 5ms delay, no packet loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=0, use_htb=True)
# 10 Mbps, 5ms delay, 10% loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
def perfTest( lossy=True ):
def perfTest():
"Create network and run simple performance test"
topo = SingleSwitchTopo( n=4, lossy=lossy )
topo = SingleSwitchTopo( n=4 )
net = Mininet( topo=topo,
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
@@ -52,6 +44,5 @@ def perfTest( lossy=True ):
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
# Prevent test_simpleperf from failing due to packet loss
perfTest( lossy=( 'testmode' not in argv ) )
setLogLevel('info')
perfTest()
+2 -1
View File
@@ -23,6 +23,7 @@ from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeTopo
from mininet.link import Link
from mininet.util import waitListening
def TreeNet( depth=1, fanout=2, **kwargs ):
@@ -38,7 +39,7 @@ def connectToRootNS( network, switch, ip, routes ):
routes: host networks to route to"""
# Create a node in root namespace and link to switch 0
root = Node( 'root', inNamespace=False )
intf = network.addLink( root, switch ).intf1
intf = Link( root, switch ).intf1
root.setIP( ip, intf=intf )
# Start network that now includes link to root namespace
network.start()
+11 -12
View File
@@ -5,12 +5,13 @@ Test for cpu.py
results format:
sched cpu received bits/sec
cfs 50% 8.14e+09
cfs 40% 6.48e+09
cfs 30% 4.56e+09
cfs 20% 2.84e+09
cfs 10% 1.29e+09
sched cpu client MB/s
cfs 45.00% 13254.669841
cfs 40.00% 11822.441399
cfs 30.00% 5112.963009
cfs 20.00% 3449.090009
cfs 10.00% 2271.741564
"""
@@ -25,13 +26,13 @@ class testCPU( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testCPU( self ):
"Verify that CPU utilization is monotonically decreasing for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.cpu', timeout=300 )
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
# matches each line from results( shown above )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)',
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts )
index = p.expect( opts, timeout=600 )
if index == 0:
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
@@ -39,9 +40,7 @@ class testCPU( unittest.TestCase ):
if sched not in scheds:
scheds.append( sched )
else:
self.assertTrue( bw < previous_bw,
"%e should be less than %e\n" %
( bw, previous_bw ) )
self.assertTrue( bw < previous_bw )
previous_bw = bw
else:
break
+8 -10
View File
@@ -13,29 +13,27 @@ class testIntfOptions( unittest.TestCase ):
def testIntfOptions( self ):
"verify that intf.config is correctly limiting traffic"
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
tolerance = .2 # plus or minus 20%
tolerance = .8
opts = [ "Results: \['([\d\.]+) .bits/sec",
"Results: \['10M', '([\d\.]+) .bits/sec",
"h(\d+)->h(\d+): (\d)/(\d),"
"rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
"h(\d+)->h(\d+): (\d)/(\d), rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
BW = 5
bw = float( p.match.group( 1 ) )
self.assertGreaterEqual( bw, BW * ( 1 - tolerance ) )
self.assertLessEqual( bw, BW * ( 1 + tolerance ) )
self.assertGreaterEqual( bw, float( 5 * tolerance ) )
self.assertLessEqual( bw, float( 5 + 5 * ( 1 - tolerance ) ) )
elif index == 1:
BW = 10
measuredBw = float( p.match.group( 1 ) )
loss = ( measuredBw / BW ) * 100
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ) )
self.assertLessEqual( loss, 50 * ( 1 + tolerance ) )
self.assertGreaterEqual( loss, 50 * tolerance )
self.assertLessEqual( loss, 50 + 50 * ( 1 - tolerance ) )
elif index == 2:
delay = float( p.match.group( 6 ) )
self.assertGreaterEqual( delay, 15 * ( 1 - tolerance ) )
self.assertLessEqual( delay, 15 * ( 1 + tolerance ) )
self.assertGreaterEqual( delay, 15 * tolerance )
self.assertLessEqual( delay, 15 + 15 * ( 1 - tolerance ) )
else:
break
+2 -2
View File
@@ -34,8 +34,8 @@ class testLinearBandwidth( unittest.TestCase ):
bw *= 10 ** 9
# check that we have a previous result to compare to
if n != 1:
info = ( 'bw: %.2e bits/s across %d switches, '
'previous: %.2e bits/s across %d switches' %
info = ( 'bw: %d bits/s across %d switches, '
'previous: %d bits/s across %d switches' %
( bw, n, previous_bw, previous_n ) )
self.assertTrue( bw < previous_bw, info )
previous_bw, previous_n = bw, n
+1 -1
View File
@@ -14,7 +14,7 @@ class testScratchNet( unittest.TestCase ):
def pingTest( self, name ):
"Verify that no ping packets were dropped"
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts, timeout=120 )
index = p.expect( self.opts )
self.assertEqual( index, 0 )
def testPingKernel( self ):
+5 -5
View File
@@ -16,15 +16,15 @@ class testSimplePerf( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testE2E( self ):
"Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
TOLERANCE = .8
expectedBw = BW * TOLERANCE
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
measuredBw = float( p.match.group( 1 ) )
lowerBound = BW * ( 1 - TOLERANCE )
upperBound = BW + ( 1 + TOLERANCE )
lowerBound = expectedBw * TOLERANCE
upperBound = expectedBw + expectedBw * ( 1 - TOLERANCE )
self.assertGreaterEqual( measuredBw, lowerBound )
self.assertLessEqual( measuredBw, upperBound )
p.wait()
+3 -5
View File
@@ -17,15 +17,13 @@ class testTree1024( unittest.TestCase ):
"Run the example and do a simple ping test from h1 to h1024"
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
p.sendline( 'h1 ping -c 20 h1024' )
p.sendline( 'h1 ping -c 1 h1024' )
p.expect ( '(\d+)% packet loss' )
packetLossPercent = int( p.match.group( 1 ) ) if p.match else -1
percent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
# Tolerate slow startup on some systems - we should revisit this
# and determine the root cause.
self.assertLess( packetLossPercent, 60 )
self.assertEqual( percent, 0 )
if __name__ == '__main__':
unittest.main()
+2 -2
View File
@@ -9,10 +9,10 @@ and running sysctl -p. Check util/sysctl_addon.
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.node import OVSSwitch
from mininet.node import OVSKernelSwitch
from mininet.topolib import TreeNet
if __name__ == '__main__':
setLogLevel( 'info' )
network = TreeNet( depth=2, fanout=32, switch=OVSSwitch )
network = TreeNet( depth=2, fanout=32, switch=OVSKernelSwitch )
network.run( CLI, network )
+47 -76
View File
@@ -38,89 +38,60 @@ def killprocs( pattern ):
else:
break
class Cleanup( object ):
"Wrapper for cleanup()"
def cleanup():
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
callbacks = []
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
"\n")
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
@classmethod
def cleanup( cls):
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
"pings/noxes\n" )
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).splitlines()
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
info( "*** Removing OVS datapaths" )
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 ) )
# And in case the above didn't work...
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
for dp in dps:
sh( 'ovs-vsctl del-br ' + dp )
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'"
).splitlines()
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | "
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'" ).splitlines()
for link in links:
if link:
sh( "ip link del " + link )
info( "*** Removing OVS datapaths\n" )
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 ) )
# And in case the above didn't work...
dps = sh( "ovs-vsctl --timeout=1 list-br" ).strip().splitlines()
for dp in dps:
sh( 'ovs-vsctl del-br ' + dp )
info( "*** Killing stale mininet node processes\n" )
killprocs( 'mininet:' )
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | "
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'"
).splitlines()
# Delete blocks of links
n = 1000 # chunk size
for i in xrange( 0, len( links ), n ):
cmd = ';'.join( 'ip link del %s' % link
for link in links[ i : i + n ] )
sh( '( %s ) 2> /dev/null' % cmd )
info( "*** Shutting down stale tunnels\n" )
killprocs( 'Tunnel=Ethernet' )
killprocs( '.ssh/mn')
sh( 'rm -f ~/.ssh/mn/*' )
if 'tap9' in sh( 'ip link show' ):
info( "*** Removing tap9 - assuming it's from cluster edition\n" )
sh( 'ip link del tap9' )
info( "*** Killing stale mininet node processes\n" )
killprocs( 'mininet:' )
info( "*** Shutting down stale tunnels\n" )
killprocs( 'Tunnel=Ethernet' )
killprocs( '.ssh/mn')
sh( 'rm -f ~/.ssh/mn/*' )
# Call any additional cleanup code if necessary
for callback in cls.callbacks:
callback()
info( "*** Cleanup complete.\n" )
@classmethod
def addCleanupCallback( cls, callback ):
"Add cleanup callback"
if callback not in cls.callbacks:
cls.callbacks.append( callback )
cleanup = Cleanup.cleanup
addCleanupCallback = Cleanup.addCleanupCallback
info( "*** Cleanup complete.\n" )
+11 -38
View File
@@ -45,10 +45,6 @@ class CLI( Cmd ):
prompt = 'mininet> '
def __init__( self, mininet, stdin=sys.stdin, script=None ):
"""Start and run interactive or batch mode CLI
mininet: Mininet network object
stdin: standard input for CLI
script: script to run in batch mode"""
self.mn = mininet
# Local variable bindings for py command
self.locals = { 'net': mininet }
@@ -60,56 +56,33 @@ class CLI( Cmd ):
Cmd.__init__( self )
info( '*** Starting CLI:\n' )
if self.inputFile:
self.do_source( self.inputFile )
return
self.initReadline()
self.run()
readlineInited = False
@classmethod
def initReadline( cls ):
"Set up history if readline is available"
# Only set up readline once to prevent multiplying the history file
if cls.readlineInited:
return
cls.readlineInited = True
# Set up history if readline is available
try:
from readline import ( read_history_file, write_history_file,
set_history_length )
import readline
except ImportError:
pass
else:
history_path = os.path.expanduser( '~/.mininet_history' )
if os.path.isfile( history_path ):
read_history_file( history_path )
set_history_length( 1000 )
atexit.register( lambda: write_history_file( history_path ) )
history_path = os.path.expanduser('~/.mininet_history')
if os.path.isfile(history_path):
readline.read_history_file(history_path)
atexit.register(lambda: readline.write_history_file(history_path))
def run( self ):
"Run our cmdloop(), catching KeyboardInterrupt"
if self.inputFile:
self.do_source( self.inputFile )
return
while True:
try:
# Make sure no nodes are still waiting
for node in self.mn.values():
while node.waiting:
info( 'stopping', node, '\n' )
node.sendInt()
node.waitOutput()
if self.isatty():
quietRun( 'stty echo sane intr ^C' )
quietRun( 'stty echo sane intr "^C"' )
self.cmdloop()
break
except KeyboardInterrupt:
# Output a message - unless it's also interrupted
# pylint: disable=broad-except
try:
output( '\nInterrupt\n' )
except Exception:
pass
# pylint: enable=broad-except
output( '\nInterrupt\n' )
def emptyline( self ):
"Don't repeat last command when you hit return."
+29 -103
View File
@@ -25,7 +25,7 @@ Link: basic link class for creating veth pairs
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
from mininet.util import makeIntfPair, quietRun
import mininet.node
import re
@@ -49,13 +49,8 @@ class Intf( object ):
# This saves an ifconfig command per node
if self.name == 'lo':
self.ip = '127.0.0.1'
self.prefixLen = 8
# Add to node (and move ourselves if necessary )
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
node.addIntf( self, port=port )
# Save params for future reference
self.params = params
self.config( **params )
@@ -198,10 +193,9 @@ class Intf( object ):
def delete( self ):
"Delete interface"
self.cmd( 'ip link del ' + self.name )
# We used to do this, but it slows us down:
# if self.node.inNamespace:
# Link may have been dumped into root NS
# quietRun( 'ip link del ' + self.name )
if self.node.inNamespace:
# Link may have been dumped into root NS
quietRun( 'ip link del ' + self.name )
def status( self ):
"Return intf status as a string"
@@ -223,19 +217,15 @@ class TCIntf( Intf ):
Allows specification of bandwidth limits (various methods)
as well as delay, loss and max queue length"""
# The parameters we use seem to work reasonably up to 1 Gb/sec
# For higher data rates, we will probably need to change them.
bwParamMax = 1000
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > self.bwParamMax ):
error( 'Bandwidth limit', bw, 'is outside supported range 0..%d'
% self.bwParamMax, '- ignoring\n' )
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
elif bw is not None:
# BL: this seems a bit brittle...
if ( speedup > 0 and
@@ -311,40 +301,16 @@ class TCIntf( Intf ):
return self.cmd( c )
def config( self, bw=None, delay=None, jitter=None, loss=None,
gro=False, txo=True, rxo=True,
speedup=0, use_hfsc=False, use_tbf=False,
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False,
max_queue_size=None, **params ):
"""Configure the port and set its properties.
bw: bandwidth in b/s (e.g. '10m')
delay: transmit delay (e.g. '1ms' )
jitter: jitter (e.g. '1ms')
loss: loss (e.g. '1%' )
gro: enable GRO (False)
txo: enable transmit checksum offload (True)
rxo: enable receive checksum offload (True)
speedup: experimental switch-side bw option
use_hfsc: use HFSC scheduling
use_tbf: use TBF scheduling
latency_ms: TBF latency parameter
enable_ecn: enable ECN (False)
enable_red: enable RED (False)
max_queue_size: queue limit parameter for netem"""
# Support old names for parameters
gro = not params.pop( 'disable_gro', not gro )
"Configure the port and set its properties."
result = Intf.config( self, **params)
def on( isOn ):
"Helper method: bool -> 'on'/'off'"
return 'on' if isOn else 'off'
# Set offload parameters with ethool
self.cmd( 'ethtool -K', self,
'gro', on( gro ),
'tx', on( txo ),
'rx', on( rxo ) )
# Disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
# Optimization: return if nothing else to configure
# Question: what happens if we want to reset things?
@@ -354,7 +320,7 @@ class TCIntf( Intf ):
# Clear existing configuration
tcoutput = self.tc( '%s qdisc show dev %s' )
if "priomap" not in tcoutput and "noqueue" not in tcoutput:
if "priomap" not in tcoutput:
cmds = [ '%s qdisc del dev %s root' ]
else:
cmds = []
@@ -402,11 +368,10 @@ class Link( object ):
"""A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc.."""
# pylint: disable=too-many-branches
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, addr1=None, addr2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None, fast=True ):
params2=None ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
@@ -441,14 +406,7 @@ class Link( object ):
if not intfName2:
intfName2 = self.intfName( node2, params2[ 'port' ] )
self.fast = fast
if fast:
params1.setdefault( 'moveIntfFn', self._ignore )
params2.setdefault( 'moveIntfFn', self._ignore )
self.makeIntfPair( intfName1, intfName2, addr1, addr2,
node1, node2, deleteIntfs=False )
else:
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
if not cls1:
cls1 = intf
@@ -462,12 +420,6 @@ class Link( object ):
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
# pylint: enable=too-many-branches
@staticmethod
def _ignore( *args, **kwargs ):
"Ignore any arguments"
pass
def intfName( self, node, n ):
"Construct a canonical interface name node-ethN for interface n."
@@ -476,32 +428,24 @@ class Link( object ):
return node.name + '-eth' + repr( n )
@classmethod
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None ):
"""Create pair of interfaces
intfname1: name for interface 1
intfname2: name for interface 2
addr1: MAC address for interface 1 (optional)
addr2: MAC address for interface 2 (optional)
node1: home node for interface 1 (optional)
node2: home node for interface 2 (optional)
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, node1, node2,
deleteIntfs=deleteIntfs )
return makeIntfPair( intfname1, intfname2, addr1, addr2 )
def delete( self ):
"Delete this link"
self.intf1.delete()
# We only need to delete one side, though this doesn't seem to
# cost us much and might help subclasses.
# self.intf2.delete()
self.intf2.delete()
def stop( self ):
"Override to stop and clean up link as needed"
self.delete()
pass
def status( self ):
"Return link status as a string"
@@ -514,27 +458,24 @@ class Link( object ):
class OVSIntf( Intf ):
"Patch interface on an OVSSwitch"
def ifconfig( self, *args ):
cmd = ' '.join( args )
def ifconfig( self, cmd ):
if cmd == 'up':
# OVSIntf is always up
"OVSIntf is always up"
return
else:
raise Exception( 'OVSIntf cannot do ifconfig ' + cmd )
class OVSLink( Link ):
"""Link that makes patch links between OVSSwitches
Warning: in testing we have found that no more
than ~64 OVS patch links should be used in row."""
"Link that makes patch links between OVSSwitches"
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
self.isPatchLink = False
if ( isinstance( node1, mininet.node.OVSSwitch ) and
isinstance( node2, mininet.node.OVSSwitch ) ):
self.isPatchLink = True
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
if ( type( node1 ) is mininet.node.OVSSwitch and
type( node2 ) is mininet.node.OVSSwitch ):
self.isPatchLink = True
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
def makeIntfPair( self, *args, **kwargs ):
@@ -557,18 +498,3 @@ class TCLink( Link ):
addr1=addr1, addr2=addr2,
params1=params,
params2=params )
class TCULink( TCLink ):
"""TCLink with default settings optimized for UserSwitch
(txo=rxo=0/False). Unfortunately with recent Linux kernels,
enabling TX and RX checksum offload on veth pairs doesn't work
well with UserSwitch: either it gets terrible performance or
TCP packets with bad checksums are generated, forwarded, and
*dropped* due to having bad checksums! OVS and LinuxBridge seem
to cope with this somehow, but it is likely to be an issue with
many software Ethernet bridges."""
def __init__( self, *args, **kwargs ):
kwargs.update( txo=False, rxo=False )
TCLink.__init__( self, *args, **kwargs )
+27 -47
View File
@@ -102,13 +102,12 @@ from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
Controller )
from mininet.nodelib import NAT
from mininet.link import Link, Intf
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening )
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
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.2"
VERSION = "2.2.0"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -281,7 +280,7 @@ class Mininet( object ):
# Use first switch if not specified
connect = self.switches[ 0 ]
# Connect the nat to the switch
self.addLink( nat, connect )
self.addLink( nat, self.switches[ 0 ] )
# Set the default route on hosts
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
for host in self.hosts:
@@ -357,8 +356,6 @@ class Mininet( object ):
options.setdefault( 'port1', port1 )
if port2 is not None:
options.setdefault( 'port2', port2 )
if self.intf is not None:
options.setdefault( 'intf', self.intf )
# Set default MAC - this should probably be in Link
options.setdefault( 'addr1', self.randMac() )
options.setdefault( 'addr2', self.randMac() )
@@ -404,7 +401,7 @@ class Mininet( object ):
if not isinstance( classes, list ):
classes = [ classes ]
for i, cls in enumerate( classes ):
# Allow Controller objects because nobody understands partial()
# Allow Controller objects because nobody understands currying
if isinstance( cls, Controller ):
self.addController( cls )
else:
@@ -417,12 +414,7 @@ class Mininet( object ):
info( '\n*** Adding switches:\n' )
for switchName in topo.switches():
# A bit ugly: add batch parameter if appropriate
params = topo.nodeInfo( switchName)
cls = params.get( 'cls', self.switch )
if hasattr( cls, 'batchStartup' ):
params.setdefault( 'batch', True )
self.addSwitch( switchName, **params )
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
info( switchName + ' ' )
info( '\n*** Adding links:\n' )
@@ -489,13 +481,6 @@ class Mininet( object ):
for switch in self.switches:
info( switch.name + ' ')
switch.start( self.controllers )
started = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches )
started.update( { s: s for s in success } )
info( '\n' )
if self.waitConn:
self.waitConnected()
@@ -510,25 +495,20 @@ class Mininet( object ):
if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
info( '*** Stopping %i links\n' % len( self.links ) )
for link in self.links:
info( '.' )
link.stop()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches )
stopped.update( { s: s for s in success } )
swclass.batchShutdown( switches )
for switch in self.switches:
info( switch.name + ' ' )
if switch not in stopped:
switch.stop()
switch.stop()
switch.terminate()
info( '\n' )
info( '*** Stopping %i links\n' % len( self.links ) )
for link in self.links:
link.stop()
info( '\n' )
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( host.name + ' ' )
@@ -732,25 +712,27 @@ class Mininet( object ):
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
seconds=5, port=5001):
seconds=5):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses first and last hosts
l4Type: string, one of [ TCP, UDP ]
udpBw: bandwidth target for UDP test
fmt: iperf format argument if any
seconds: iperf time to transmit
port: iperf port
returns: 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
client, server = hosts
output( '*** Iperf: testing', l4Type, 'bandwidth between',
client, 'and', server, '\n' )
output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
output( "%s and %s\n" % ( client.name, server.name ) )
server.cmd( 'killall -9 iperf' )
iperfArgs = 'iperf -p %d ' % port
iperfArgs = 'iperf '
bwArgs = ''
if l4Type == 'UDP':
iperfArgs += '-u '
@@ -759,20 +741,18 @@ class Mininet( object ):
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if fmt:
iperfArgs += '-f %s ' % fmt
server.sendCmd( iperfArgs + '-s' )
server.sendCmd( iperfArgs + '-s', printPid=True )
servout = ''
while server.lastPid is None:
servout += server.monitor()
if l4Type == 'TCP':
if not waitListening( client, server.IP(), port ):
raise Exception( 'Could not connect to iperf on port %d'
% port )
while 'Connected' not in client.cmd(
'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 )
debug( 'Client output: %s\n' % cliout )
servout = ''
# We want the last *b/sec from the iperf server output
# for TCP, there are two fo them because of waitListening
count = 2 if l4Type == 'TCP' else 1
while len( re.findall( '/sec', servout ) ) < count:
servout += server.monitor( timeoutms=5000 )
server.sendInt()
servout += server.waitOutput()
debug( 'Server output: %s\n' % servout )
+163 -232
View File
@@ -23,26 +23,20 @@ Switch: superclass for switch nodes.
UserSwitch: a switch using the user-space switch from the OpenFlow
reference implementation.
OVSSwitch: a switch using the Open vSwitch OpenFlow-compatible switch
KernelSwitch: a switch using the kernel switch from the OpenFlow reference
implementation.
OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
implementation (openvswitch.org).
OVSBridge: an Ethernet bridge implemented using Open vSwitch.
Supports STP.
IVSSwitch: OpenFlow switch using the Indigo Virtual Switch.
Controller: superclass for OpenFlow controllers. The default controller
is controller(8) from the reference implementation.
OVSController: The test controller from Open vSwitch.
NOXController: a controller node using NOX (noxrepo.org).
Ryu: The Ryu controller (https://osrg.github.io/ryu/)
RemoteController: a remote controller node, which may use any
arbitrary OpenFlow-compatible controller, and which is not
created or managed by Mininet.
created or managed by mininet.
Future enhancements:
@@ -63,7 +57,7 @@ from time import sleep
from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
numCores, retry, mountCgroups )
from mininet.moduledeps import moduleDeps, pathCheck, TUN
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
from mininet.link import Link, Intf, TCIntf, OVSIntf
from re import findall
from distutils.version import StrictVersion
@@ -131,11 +125,11 @@ class Node( object ):
opts = '-cd' if mnopts is None else mnopts
if self.inNamespace:
opts += 'n'
# bash -i: force interactive
# 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 ),
'bash', '--norc', '-is', 'mininet:' + self.name ]
'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)
# received by the parent
@@ -163,13 +157,11 @@ class Node( object ):
break
self.pollOut.poll()
self.waiting = False
# +m: disable job control notification
self.cmd( 'unset HISTFILE; stty -echo; set +m' )
self.cmd( 'stty -echo' )
self.cmd( 'set +m' )
def mountPrivateDirs( self ):
"mount private directories"
# Avoid expanding a string into a list of chars
assert not isinstance( self.privateDirs, basestring )
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
@@ -202,17 +194,16 @@ class Node( object ):
def cleanup( self ):
"Help python collect its garbage."
# We used to do this, but it slows us down:
# Intfs may end up in root NS
# for intfName in self.intfNames():
# if self.name in intfName:
# quietRun( 'ip link del ' + intfName )
for intfName in self.intfNames():
if self.name in intfName:
quietRun( 'ip link del ' + intfName )
self.shell = None
# Subshell I/O, commands and control
def read( self, maxbytes=1024 ):
"""Buffered read from node, potentially blocking.
"""Buffered read from node, non-blocking.
maxbytes: maximum number of bytes to return"""
count = len( self.readbuf )
if count < maxbytes:
@@ -227,7 +218,7 @@ class Node( object ):
return result
def readline( self ):
"""Buffered readline from node, potentially blocking.
"""Buffered readline from node, non-blocking.
returns: line (minus newline) or None"""
self.readbuf += self.read( 1024 )
if '\n' not in self.readbuf:
@@ -259,18 +250,17 @@ class Node( object ):
def waitReadable( self, timeoutms=None ):
"""Wait until node's output is readable.
timeoutms: timeout in ms or None to wait indefinitely.
returns: result of poll()"""
timeoutms: timeout in ms or None to wait indefinitely."""
if len( self.readbuf ) == 0:
return self.pollOut.poll( timeoutms )
self.pollOut.poll( timeoutms )
def sendCmd( self, *args, **kwargs ):
"""Send a command, followed by a command to echo a sentinel,
and return without waiting for the command to complete.
args: command and arguments, or string
printPid: print command's PID? (False)"""
assert self.shell and not self.waiting
printPid = kwargs.get( 'printPid', False )
printPid: print command's PID?"""
assert not self.waiting
printPid = kwargs.get( 'printPid', True )
# Allow sendCmd( [ list ] )
if len( args ) == 1 and isinstance( args[ 0 ], list ):
cmd = args[ 0 ]
@@ -304,9 +294,7 @@ class Node( object ):
Set self.waiting to False if command has completed.
timeoutms: timeout in ms or None to wait indefinitely
findPid: look for PID from mnexec -p"""
ready = self.waitReadable( timeoutms )
if not ready:
return ''
self.waitReadable( timeoutms )
data = self.read( 1024 )
pidre = r'\[\d+\] \d+\r\n'
# Look for PID
@@ -351,11 +339,8 @@ class Node( object ):
verbose = kwargs.get( 'verbose', False )
log = info if verbose else debug
log( '*** %s : %s\n' % ( self.name, args ) )
if self.shell:
self.sendCmd( *args, **kwargs )
return self.waitOutput( verbose )
else:
warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
self.sendCmd( *args, **kwargs )
return self.waitOutput( verbose )
def cmdPrint( self, *args):
"""Call cmd and printing its output
@@ -518,13 +503,15 @@ class Node( object ):
mac: MAC address as string"""
return self.intf( intf ).setMAC( mac )
def setIP( self, ip, prefixLen=8, intf=None, **kwargs ):
def setIP( self, ip, prefixLen=8, intf=None ):
"""Set the IP address for an interface.
intf: intf or intf name
ip: IP address as a string
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs
kwargs: any additional arguments for intf.setIP"""
return self.intf( intf ).setIP( ip, prefixLen, **kwargs )
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
# This should probably be rethought
if '/' not in ip:
ip = '%s/%s' % ( ip, prefixLen )
return self.intf( intf ).setIP( ip )
def IP( self, intf=None ):
"Return IP address of a node or specific interface."
@@ -684,9 +671,7 @@ class CPULimitedHost( Host ):
"Clean up our cgroup"
# info( '*** deleting cgroup', self.cgroup, '\n' )
_out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
# Sometimes cgdelete returns a resource busy error but still
# deletes the group; next attempt will give "no such file"
return exitcode == 0 or ( 'no such file' in _err.lower() )
return exitcode != 0
def popen( self, *args, **kwargs ):
"""Return a Popen() object in node's namespace
@@ -708,7 +693,7 @@ class CPULimitedHost( Host ):
def cleanup( self ):
"Clean up Node, then clean up our cgroup"
super( CPULimitedHost, self ).cleanup()
retry( retries=3, delaySecs=.1, fn=self.cgroupDel )
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
_rtGroupSched = False # internal class var: Is CONFIG_RT_GROUP_SCHED set?
@@ -908,12 +893,6 @@ class Switch( Node ):
debug( 'Assuming', repr( self ), 'is connected to a controller\n' )
return True
def stop( self, deleteIntfs=True ):
"""Stop switch
deleteIntfs: delete interfaces? (True)"""
if deleteIntfs:
self.deleteIntfs()
def __repr__( self ):
"More informative string representation"
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
@@ -1020,32 +999,73 @@ class UserSwitch( Switch ):
self.cmd( 'kill %ofprotocol' )
super( UserSwitch, self ).stop( deleteIntfs )
class OVSLegacyKernelSwitch( Switch ):
"""Open VSwitch legacy kernel-space switch using ovs-openflowd.
Currently only works in the root namespace."""
def __init__( self, name, dp=None, **kwargs ):
"""Init.
name: name for switch
dp: netlink id (0, 1, 2, ...)
defaultMAC: default MAC as unsigned int; random value if None"""
Switch.__init__( self, name, **kwargs )
self.dp = dp if dp else self.name
self.intf = self.dp
if self.inNamespace:
error( "OVSKernelSwitch currently only works"
" in the root namespace.\n" )
exit( 1 )
@classmethod
def setup( cls ):
"Ensure any dependencies are loaded; if not, try to load them."
pathCheck( 'ovs-dpctl', 'ovs-openflowd',
moduleName='Open vSwitch (openvswitch.org)')
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
def start( self, controllers ):
"Start up kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log'
# Delete local datapath if it exists;
# then create a new one monitoring the given interfaces
self.cmd( 'ovs-dpctl del-dp ' + self.dp )
self.cmd( 'ovs-dpctl add-dp ' + self.dp )
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
# Run protocol daemon
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
for c in controllers ] )
self.cmd( 'ovs-openflowd ' + self.dp +
' ' + clist +
' --fail=secure ' + self.opts +
' --datapath-id=' + self.dpid +
' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False
def stop( self, deleteIntfs=True ):
"""Terminate kernel datapath."
deleteIntfs: delete interfaces? (True)"""
quietRun( 'ovs-dpctl del-dp ' + self.dp )
self.cmd( 'kill %ovs-openflowd' )
super( OVSLegacyKernelSwitch, self ).stop( deleteIntfs )
class OVSSwitch( Switch ):
"Open vSwitch switch. Depends on ovs-vsctl."
def __init__( self, name, failMode='secure', datapath='kernel',
inband=False, protocols=None,
reconnectms=1000, stp=False, batch=False, **params ):
"""name: name for switch
failMode: controller loss behavior (secure|standalone)
inband=False, protocols=None, **params ):
"""Init.
name: name for switch
failMode: controller loss behavior (secure|open)
datapath: userspace or kernel mode (kernel|user)
inband: use in-band control (False)
protocols: use specific OpenFlow version(s) (e.g. OpenFlow13)
Unspecified (or old OVS version) uses OVS default
reconnectms: max reconnect timeout in ms (0/None for default)
stp: enable STP (False, requires failMode=standalone)
batch: enable batch startup (False)"""
inband: use in-band control (False)"""
Switch.__init__( self, name, **params )
self.failMode = failMode
self.datapath = datapath
self.inband = inband
self.protocols = protocols
self.reconnectms = reconnectms
self.stp = stp
self._uuids = [] # controller UUIDs
self.batch = batch
self.commands = [] # saved commands for batch startup
@classmethod
def setup( cls ):
@@ -1075,18 +1095,17 @@ class OVSSwitch( Switch ):
return ( StrictVersion( cls.OVSVersion ) <
StrictVersion( '1.10' ) )
@classmethod
def batchShutdown( cls, switches ):
"Call ovs-vsctl del-br on all OVSSwitches in a list"
quietRun( 'ovs-vsctl ' +
' -- '.join( '--if-exists del-br %s' % s
for s in switches ) )
def dpctl( self, *args ):
"Run ovs-ofctl command"
return self.cmd( 'ovs-ofctl', args[ 0 ], self, *args[ 1: ] )
def vsctl( self, *args, **kwargs ):
"Run ovs-vsctl command (or queue for later execution)"
if self.batch:
cmd = ' '.join( str( arg ).strip() for arg in args )
self.commands.append( cmd )
else:
return self.cmd( 'ovs-vsctl', *args, **kwargs )
@staticmethod
def TCReapply( intf ):
"""Unfortunately OVS and Mininet are fighting
@@ -1097,13 +1116,13 @@ class OVSSwitch( Switch ):
def attach( self, intf ):
"Connect a data port"
self.vsctl( 'add-port', self, intf )
self.cmd( 'ovs-vsctl add-port', self, intf )
self.cmd( 'ifconfig', intf, 'up' )
self.TCReapply( intf )
def detach( self, intf ):
"Disconnect a data port"
self.vsctl( 'del-port', self, intf )
self.cmd( 'ovs-vsctl del-port', self, intf )
def controllerUUIDs( self, update=False ):
"""Return ovsdb UUIDs for our controllers
@@ -1121,109 +1140,77 @@ class OVSSwitch( Switch ):
def connected( self ):
"Are we connected to at least one of our controllers?"
for uuid in self.controllerUUIDs():
if 'true' in self.vsctl( '-- get Controller',
uuid, 'is_connected' ):
if 'true' in self.cmd( 'ovs-vsctl -- get Controller',
uuid, 'is_connected' ):
return True
return self.failMode == 'standalone'
def intfOpts( self, intf ):
"Return OVS interface options for intf"
opts = ''
if not self.isOldOVS():
# ofport_request is not supported on old OVS
opts += ' ofport_request=%s' % self.ports[ intf ]
# Patch ports don't work well with old OVS
if isinstance( intf, OVSIntf ):
intf1, intf2 = intf.link.intf1, intf.link.intf2
peer = intf1 if intf1 != intf else intf2
opts += ' type=patch options:peer=%s' % peer
return '' if not opts else ' -- set Interface %s' % intf + opts
def bridgeOpts( self ):
"Return OVS bridge options"
opts = ( ' other_config:datapath-id=%s' % self.dpid +
' fail_mode=%s' % self.failMode )
if not self.inband:
opts += ' other-config:disable-in-band=true'
if self.datapath == 'user':
opts += ' datapath_type=netdev'
if self.protocols and not self.isOldOVS():
opts += ' protocols=%s' % self.protocols
if self.stp and self.failMode == 'standalone':
opts += ' stp_enable=true'
return opts
@staticmethod
def patchOpts( intf ):
"Return OVS patch port options (if any) for intf"
if not isinstance( intf, OVSIntf ):
# Ignore if it's not a patch link
return ''
intf1, intf2 = intf.link.intf1, intf.link.intf2
peer = intf1 if intf1 != intf else intf2
return ( '-- set Interface %s type=patch '
'-- set Interface %s options:peer=%s ' %
( intf, intf, peer ) )
def start( self, controllers ):
"Start up a new OVS OpenFlow switch using ovs-vsctl"
if self.inNamespace:
raise Exception(
'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
# Command to add interfaces
intfs = ''.join( ' -- add-port %s %s' % ( self, intf ) +
self.intfOpts( intf )
for intf in self.intfList()
if self.ports[ intf ] and not intf.IP() )
# Command to create controller entries
clist = [ ( self.name + c.name, '%s:%s:%d' %
( c.protocol, c.IP(), c.port ) )
for c in controllers ]
# Interfaces and controllers
intfs = ' '.join( '-- add-port %s %s ' % ( self, intf ) +
'-- set Interface %s ' % intf +
'ofport_request=%s ' % self.ports[ intf ]
+ self.patchOpts( intf )
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 )
if self.listenPort:
clist.append( ( self.name + '-listen',
'ptcp:%s' % self.listenPort ) )
ccmd = '-- --id=@%s create Controller target=\\"%s\\"'
if self.reconnectms:
ccmd += ' max_backoff=%d' % self.reconnectms
cargs = ' '.join( ccmd % ( name, target )
for name, target in clist )
# Controller ID list
cids = ','.join( '@%s' % name for name, _target in clist )
# Try to delete any existing bridges with the same name
clist += ' ptcp:%s' % self.listenPort
# Construct big ovs-vsctl command for new versions of OVS
if not self.isOldOVS():
cargs += ' -- --if-exists del-br %s' % self
# One ovs-vsctl command to rule them all!
self.vsctl( cargs +
' -- add-br %s' % self +
' -- set bridge %s controller=[%s]' % ( self, cids ) +
self.bridgeOpts() +
intfs )
# If necessary, restore TC config overwritten by OVS
if not self.batch:
cmd = ( 'ovs-vsctl add-br %s ' % self +
'-- set Bridge %s ' % self +
'other_config:datapath-id=%s ' % self.dpid +
'-- set-fail-mode %s %s ' % ( self, self.failMode ) +
intfs +
'-- set-controller %s %s ' % ( self, clist ) )
# Construct ovs-vsctl commands for old versions of OVS
else:
self.cmd( 'ovs-vsctl add-br', self )
for intf in self.intfList():
self.TCReapply( intf )
# This should be ~ int( quietRun( 'getconf ARG_MAX' ) ),
# but the real limit seems to be much lower
argmax = 128000
@classmethod
def batchStartup( cls, switches, run=errRun ):
"""Batch startup for OVS
switches: switches to start up
run: function to run commands (errRun)"""
info( '...' )
cmds = 'ovs-vsctl'
for switch in switches:
if switch.isOldOVS():
# Ideally we'd optimize this also
run( 'ovs-vsctl del-br %s' % switch )
for cmd in switch.commands:
cmd = cmd.strip()
# Don't exceed ARG_MAX
if len( cmds ) + len( cmd ) >= cls.argmax:
run( cmds, shell=True )
cmds = 'ovs-vsctl'
cmds += ' ' + cmd
switch.cmds = []
switch.batch = False
if cmds:
run( cmds, shell=True )
# Reapply link config if necessary...
for switch in switches:
for intf in switch.intfs.itervalues():
if isinstance( intf, TCIntf ):
intf.config( **intf.params )
return switches
if not intf.IP():
self.cmd( 'ovs-vsctl add-port', self, intf )
cmd = ( 'ovs-vsctl set Bridge %s ' % self +
'other_config:datapath-id=%s ' % self.dpid +
'-- set-fail-mode %s %s ' % ( self, self.failMode ) +
'-- set-controller %s %s ' % ( self, clist ) )
if not self.inband:
cmd += ( '-- set bridge %s '
'other-config:disable-in-band=true ' % self )
if self.datapath == 'user':
cmd += '-- set bridge %s datapath_type=netdev ' % self
if self.protocols:
cmd += '-- set bridge %s protocols=%s' % ( self, self.protocols )
# Do it!!
self.cmd( cmd )
# Reconnect quickly to controllers (1s vs. 15s max_backoff)
uuids = [ '-- set Controller %s max_backoff=1000' % uuid
for uuid in self.controllerUUIDs() ]
if uuids:
self.cmd( 'ovs-vsctl', *uuids )
# If necessary, restore TC config overwritten by OVS
for intf in self.intfList():
self.TCReapply( intf )
def stop( self, deleteIntfs=True ):
"""Terminate OVS switch.
@@ -1233,22 +1220,6 @@ class OVSSwitch( Switch ):
self.cmd( 'ip link del', self )
super( OVSSwitch, self ).stop( deleteIntfs )
@classmethod
def batchShutdown( cls, switches, run=errRun ):
"Shut down a list of OVS switches"
delcmd = 'del-br %s'
if switches and not switches[ 0 ].isOldOVS():
delcmd = '--if-exists ' + delcmd
# First, delete them all from ovsdb
run( 'ovs-vsctl ' +
' -- '.join( delcmd % s for s in switches ) )
# Next, shut down all of the processes
pids = ' '.join( str( switch.pid ) for switch in switches )
run( 'kill -HUP ' + pids )
for switch in switches:
switch.shell = None
return switches
OVSKernelSwitch = OVSSwitch
@@ -1256,24 +1227,13 @@ OVSKernelSwitch = OVSSwitch
class OVSBridge( OVSSwitch ):
"OVSBridge is an OVSSwitch in standalone/bridge mode"
def __init__( self, *args, **kwargs ):
"""stp: enable Spanning Tree Protocol (False)
see OVSSwitch for other options"""
def __init__( self, args, **kwargs ):
kwargs.update( failMode='standalone' )
OVSSwitch.__init__( self, *args, **kwargs )
OVSSwitch.__init__( self, args, **kwargs )
def start( self, controllers ):
"Start bridge, ignoring controllers argument"
OVSSwitch.start( self, controllers=[] )
def connected( self ):
"Are we forwarding yet?"
if self.stp:
status = self.dpctl( 'show' )
return 'STP_FORWARD' in status and not 'STP_LEARN' in status
else:
return True
class IVSSwitch( Switch ):
"Indigo Virtual Switch"
@@ -1300,7 +1260,6 @@ class IVSSwitch( Switch ):
"Kill each IVS switch, to be waited on later in stop()"
for switch in switches:
switch.cmd( 'kill %ivs' )
return switches
def start( self, controllers ):
"Start up a new IVS switch"
@@ -1351,14 +1310,10 @@ class Controller( Node ):
def __init__( self, name, inNamespace=False, command='controller',
cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
port=6653, protocol='tcp', **params ):
port=6633, protocol='tcp', **params ):
self.command = command
self.cargs = cargs
self.cdir = cdir
# Accept 'ip:port' syntax as shorthand
if ':' in ip:
ip, port = ip.split( ':' )
port = int( port )
self.ip = ip
self.port = port
self.protocol = protocol
@@ -1431,8 +1386,7 @@ class OVSController( Controller ):
@classmethod
def isAvailable( cls ):
return ( quietRun( 'which ovs-controller' ) or
quietRun( 'which test-controller' ) or
quietRun( 'which ovs-testcontroller' ) )
quietRun( 'which test-controller' ) )
class NOX( Controller ):
"Controller to run a NOX application."
@@ -1459,7 +1413,7 @@ class NOX( Controller ):
cdir=noxCoreDir,
**kwargs )
class Ryu( Controller ):
class RYU( Controller ):
"Controller to run Ryu application"
def __init__( self, name, *ryuArgs, **kwargs ):
"""Init.
@@ -1481,12 +1435,11 @@ class Ryu( Controller ):
cdir=ryuCoreDir,
**kwargs )
class RemoteController( Controller ):
"Controller running outside of Mininet's control."
def __init__( self, name, ip='127.0.0.1',
port=None, **kwargs):
port=6633, **kwargs):
"""Init.
name: name to give controller
ip: the IP address where the remote controller is
@@ -1504,30 +1457,12 @@ class RemoteController( Controller ):
def checkListening( self ):
"Warn if remote controller is not accessible"
if self.port is not None:
self.isListening( self.ip, self.port )
else:
for port in 6653, 6633:
if self.isListening( self.ip, port ):
self.port = port
info( "Connecting to remote controller"
" at %s:%d\n" % ( self.ip, self.port ))
break
if self.port is None:
self.port = 6653
warn( "Setting remote controller"
" to %s:%d\n" % ( self.ip, self.port ))
def isListening( self, ip, port ):
"Check if a remote controller is listening at a specific ip and port"
listening = self.cmd( "echo A | telnet -e A %s %d" % ( ip, port ) )
listening = self.cmd( "echo A | telnet -e A %s %d" %
( self.ip, self.port ) )
if 'Connected' not in listening:
warn( "Unable to contact the remote controller"
" at %s:%d\n" % ( ip, port ) )
return False
else:
return True
" at %s:%d\n" % ( self.ip, self.port ) )
DefaultControllers = ( Controller, OVSController )
@@ -1543,7 +1478,3 @@ def DefaultController( name, controllers=DefaultControllers, **kwargs ):
if not controller:
raise Exception( 'Could not find a default OpenFlow controller' )
return controller( name, **kwargs )
def NullController( *_args, **_kwargs ):
"Nonexistent controller - simply returns None"
return None
+42 -39
View File
@@ -7,8 +7,8 @@ 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
from mininet.util import quietRun
import re
class LinuxBridge( Switch ):
"Linux Bridge (with optional spanning tree)"
@@ -59,30 +59,23 @@ class LinuxBridge( Switch ):
@classmethod
def setup( cls ):
"Check dependencies and warn about firewalling"
"Make sure our class dependencies are available"
pathCheck( 'brctl', moduleName='bridge-utils' )
# Disable Linux bridge firewalling so that traffic can flow!
for table in 'arp', 'ip', 'ip6':
cmd = 'sysctl net.bridge.bridge-nf-call-%stables' % table
out = quietRun( cmd ).strip()
if out.endswith( '1' ):
warn( 'Warning: Linux bridge may not work with', out, '\n' )
class NAT( Node ):
"NAT: Provides connectivity to external network"
def __init__( self, name, subnet='10.0/8',
localIntf=None, flush=False, **params):
def __init__( self, name, inetIntf=None, subnet='10.0/8',
localIntf=None, **params):
"""Start NAT/forwarding between Mininet and external network
subnet: Mininet subnet (default 10.0/8)
flush: flush iptables before installing NAT rules"""
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
super( NAT, self ).__init__( name, **params )
self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
self.subnet = subnet
self.localIntf = localIntf
self.flush = flush
self.forwardState = self.cmd( 'sysctl -n net.ipv4.ip_forward' ).strip()
def config( self, **params ):
"""Configure the NAT and iptables"""
@@ -91,25 +84,27 @@ class NAT( Node ):
if not self.localIntf:
self.localIntf = self.defaultIntf()
if self.flush:
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
self.cmd( 'iptables -P INPUT ACCEPT' )
self.cmd( 'iptables -P OUTPUT ACCEPT' )
self.cmd( 'iptables -P FORWARD DROP' )
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
# Install NAT rules
# Flush any currently active rules
# TODO: is this safe?
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
self.cmd( 'iptables -P INPUT ACCEPT' )
self.cmd( 'iptables -P OUTPUT ACCEPT' )
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',
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
'-i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING',
'-s', self.subnet, "'!'", '-d', self.subnet,
'-j MASQUERADE' )
'-o', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
@@ -128,18 +123,26 @@ class NAT( Node ):
# hopefully this won't disconnect you
self.cmd( 'service network-manager restart' )
def getGatewayIntf( self, fallback='eth0' ):
"""Return gateway interface name
fallback: default device to fall back to"""
routes = self.cmd( 'ip route show' )
match = re.search( r'default via \S+ dev (\S+)', routes )
if match:
return match.group( 1 )
else:
warn( 'There is no default route set.',
'Using', fallback, 'as gateway interface...\n' )
return fallback
def terminate( self ):
"Stop NAT/forwarding between Mininet and external network"
# Remote NAT rules
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -D FORWARD',
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -D POSTROUTING',
'-s', self.subnet, '\'!\'', '-d', self.subnet,
'-j MASQUERADE' )
# Put the forwarding state back to what it was
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
# TODO: is this safe?
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
super( NAT, self ).terminate()
+2 -3
View File
@@ -35,7 +35,7 @@ def tunnelX11( node, display=None):
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
def makeTerm( node, title='Node', term='xterm', display=None ):
"""Create an X11 tunnel to the node and start up a terminal.
node: Node object
title: base title
@@ -54,8 +54,7 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
display, tunnel = tunnelX11( node, display )
if display is None:
return []
term = node.popen( cmds[ term ] +
[ display, '-e', 'env TERM=ansi %s' % cmd ] )
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
+8 -1
View File
@@ -8,7 +8,8 @@ import sys
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
from mininet.node import ( UserSwitch, OVSSwitch, OVSLegacyKernelSwitch,
IVSSwitch )
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
@@ -80,6 +81,12 @@ 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 ):
+8 -23
View File
@@ -12,13 +12,12 @@ import os
import re
from mininet.util import quietRun
from distutils.version import StrictVersion
from time import sleep
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
return versionMatch[ 0 ]
versionMatch = re.findall( r'TShark \d+.\d+.\d+', versionStr )[0]
return versionMatch.split()[ 1 ]
# pylint doesn't understand pexpect.match, unfortunately!
# pylint:disable=maybe-no-member
@@ -48,8 +47,6 @@ class testWalkthrough( unittest.TestCase ):
mn.expect( '0% dropped' )
tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
tshark.sendintr()
mn.expect( pexpect.EOF )
tshark.expect( pexpect.EOF )
def testBasic( self ):
"Test basic CLI commands (help, nodes, net, dump)"
@@ -94,9 +91,7 @@ class testWalkthrough( unittest.TestCase ):
"Test ifconfig and ps on h1 and s1"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ 'h1-eth0', 's1-eth1', r'[^-](eth|en)\w*\d', 'lo',
self.prompt ]
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
ifcount = 0
@@ -122,7 +117,7 @@ class testWalkthrough( unittest.TestCase ):
ifcount += 1
else:
break
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
@@ -131,13 +126,10 @@ class testWalkthrough( unittest.TestCase ):
p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
s1Output = p.before
# strip command from ps output and compute diffs
h1Output = h1Output.split( '\n' )[ 1: ]
s1Output = s1Output.split( '\n' )[ 1: ]
diffs = set( h1Output ).difference( set( s1Output ) )
# allow up to two diffs to account for daemons, etc.
self.assertTrue( len( diffs ) <= 2,
'h1 and s1 "ps" output differ too much: %s' % diffs )
# strip command from ps output
h1Output = h1Output.split( '\n', 1 )[ 1 ]
s1Output = s1Output.split( '\n', 1 )[ 1 ]
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
p.sendline( 'exit' )
p.wait()
@@ -159,11 +151,6 @@ class testWalkthrough( unittest.TestCase ):
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
# The walkthrough doesn't specify a delay here, and
# we also don't read the output (also a possible problem),
# but for now let's wait a couple of seconds to make
# it less likely to fail due to the race condition.
sleep( 2 )
p.expect( self.prompt )
p.sendline( ' h2 wget -O - h1' )
p.expect( '200 OK' )
@@ -262,8 +249,6 @@ class testWalkthrough( unittest.TestCase ):
p.sendline( 'h%d ifconfig' % i )
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
p.expect( self.prompt )
p.sendline( 'exit' )
p.expect( pexpect.EOF )
def testSwitches( self ):
"Run iperf test using user and ovsk switches"
-6
View File
@@ -318,12 +318,6 @@ class SingleSwitchReversedTopo( Topo ):
port1=0, port2=( k - h + 1 ) )
class MinimalTopo( SingleSwitchTopo ):
"Minimal topology with two hosts and one switch"
def build( self ):
return SingleSwitchTopo.build( self, k=2 )
class LinearTopo( Topo ):
"Linear topology of k switches, with n hosts per switch."
+4 -14
View File
@@ -45,18 +45,10 @@ class TorusTopo( Topo ):
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, n=1 ):
"""x: dimension of torus in x-direction
y: dimension of torus in y-direction
n: number of hosts per switch"""
def build( self, x, y ):
if x < 3 or y < 3:
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
if n == 1:
genHostName = lambda loc, k: 'h%s' % ( loc )
else:
genHostName = lambda loc, k: 'h%sx%d' % ( loc, k )
hosts, switches, dpid = {}, {}, 0
# Create and wire interior
for i in range( 0, x ):
@@ -65,11 +57,9 @@ class TorusTopo( Topo ):
# dpid cannot be zero for OVS
dpid = ( i + 1 ) * 256 + ( j + 1 )
switch = switches[ i, j ] = self.addSwitch(
's' + loc, dpid='%x' % dpid )
for k in range( 0, n ):
host = hosts[ i, j, k ] = self.addHost(
genHostName( loc, k + 1 ) )
self.addLink( host, switch )
's' + loc, dpid='%016x' % dpid )
host = hosts[ i, j ] = self.addHost( 'h' + loc )
self.addLink( host, switch )
# Connect switches
for i in range( 0, x ):
for j in range( 0, y ):
+59 -110
View File
@@ -56,7 +56,6 @@ def oldQuietRun( *cmd ):
# This is a bit complicated, but it enables us to
# monitor command output as it is happening
# pylint: disable=too-many-branches
def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code
cmd: string or list of command and args
@@ -78,7 +77,6 @@ def errRun( *cmd, **kwargs ):
cmd = [ str( arg ) for arg in cmd ]
elif isinstance( cmd, list ) and shell:
cmd = " ".join( arg for arg in cmd )
debug( '*** errRun:', cmd, '\n' )
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
# We use poll() because select() doesn't work with large fd numbers,
# and thus communicate() doesn't work either
@@ -93,31 +91,21 @@ def errRun( *cmd, **kwargs ):
errDone = False
while not outDone or not errDone:
readable = poller.poll()
for fd, event in readable:
for fd, _event in readable:
f = fdtofile[ fd ]
if event & POLLIN:
data = f.read( 1024 )
if echo:
output( data )
if f == popen.stdout:
out += data
if data == '':
outDone = True
elif f == popen.stderr:
err += data
if data == '':
errDone = True
else: # POLLHUP or something unexpected
if f == popen.stdout:
data = f.read( 1024 )
if echo:
output( data )
if f == popen.stdout:
out += data
if data == '':
outDone = True
elif f == popen.stderr:
elif f == popen.stderr:
err += data
if data == '':
errDone = True
poller.unregister( fd )
returncode = popen.wait()
debug( out, err, returncode )
return out, err, returncode
# pylint: enable=too-many-branches
def errFail( *cmd, **kwargs ):
"Run a command using errRun and raise exception on nonzero exit"
@@ -157,41 +145,27 @@ 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, node1=None, node2=None,
deleteIntfs=True, runCmd=None ):
"""Make a veth pair connnecting new interfaces intf1 and intf2
intf1: name for interface 1
intf2: name for interface 2
addr1: MAC address for interface 1 (optional)
addr2: MAC address for interface 2 (optional)
node1: home node for interface 1 (optional)
node2: home node for interface 2 (optional)
deleteIntfs: delete intfs before creating them
def makeIntfPair( intf1, intf2, addr1=None, addr2=None, runCmd=quietRun ):
"""Make a veth pair connecting intf1 and intf2.
intf1: string, interface
intf2: string, interface
runCmd: function to run shell commands (quietRun)
raises Exception on failure"""
if not runCmd:
runCmd = quietRun if not node1 else node1.cmd
runCmd2 = quietRun if not node2 else node2.cmd
if deleteIntfs:
# Delete any old interfaces with the same names
runCmd( 'ip link del ' + intf1 )
runCmd2( 'ip link del ' + intf2 )
returns: ip link add result"""
# Delete any old interfaces with the same names
runCmd( 'ip link del ' + intf1 )
runCmd( 'ip link del ' + intf2 )
# Create new pair
netns = 1 if not node2 else node2.pid
if addr1 is None and addr2 is None:
cmdOutput = runCmd( 'ip link add name %s '
'type veth peer name %s '
'netns %s' % ( intf1, intf2, netns ) )
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
else:
cmdOutput = runCmd( 'ip link add name %s '
'address %s '
'type veth peer name %s '
'address %s '
'netns %s' %
( intf1, addr1, intf2, addr2, netns ) )
if cmdOutput:
raise Exception( "Error creating interface pair (%s,%s): %s " %
( intf1, intf2, cmdOutput ) )
cmd = ( 'ip link add name ' + intf1 + ' address ' + addr1 +
' type veth peer name ' + intf2 + ' address ' + addr2 )
cmdOutput = runCmd( cmd )
if cmdOutput == '':
return True
else:
error( "Error creating interface pair: %s " % cmdOutput )
return False
def retry( retries, delaySecs, fn, *args, **keywords ):
"""Try something several times before giving up.
@@ -523,54 +497,31 @@ def splitArgs( argstr ):
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
def customClass( classes, argStr ):
"""Return customized class based on argStr
The args and key/val pairs in argStr will be automatically applied
when the generated class is later used.
def customConstructor( constructors, argStr ):
"""Return custom constructor based on argStr
The args and key/val pairs in argsStr will be automatically applied
when the generated constructor is later used.
"""
cname, args, kwargs = splitArgs( argStr )
cls = classes.get( cname, None )
if not cls:
cname, newargs, kwargs = splitArgs( argStr )
constructor = constructors.get( cname, None )
if not constructor:
raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, classes.keys() ) )
if not args and not kwargs:
return cls
( cname, constructors.keys() ) )
return specialClass( cls, append=args, defaults=kwargs )
def specialClass( cls, prepend=None, append=None,
defaults=None, override=None ):
"""Like functools.partial, but it returns a class
prepend: arguments to prepend to argument list
append: arguments to append to argument list
defaults: default values for keyword arguments
override: keyword arguments to override"""
if prepend is None:
prepend = []
if append is None:
append = []
if defaults is None:
defaults = {}
if override is None:
override = {}
class CustomClass( cls ):
"Customized subclass with preset args/params"
def __init__( self, *args, **params ):
newparams = defaults.copy()
newparams.update( params )
newparams.update( override )
cls.__init__( self, *( list( prepend ) + list( args ) +
list( append ) ),
**newparams )
CustomClass.__name__ = '%s%s' % ( cls.__name__, defaults )
return CustomClass
def customized( name, *args, **params ):
"Customized constructor, useful for Node, Link, and other classes"
params = params.copy()
params.update( kwargs )
if not newargs:
return constructor( name, *args, **params )
if args:
warn( 'warning: %s replacing %s with %s\n' % (
constructor, args, newargs ) )
return constructor( name, *newargs, **params )
customized.__name__ = 'customConstructor(%s)' % argStr
return customized
def buildTopo( topos, topoStr ):
"""Create topology from string with format (object, arg1, arg2,...).
@@ -600,20 +551,18 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
raise Exception('Could not find telnet' )
# pylint: disable=maybe-no-member
serverIP = server if isinstance( server, basestring ) else server.IP()
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
cmd = ( 'sh -c "echo A | telnet -e A %s %s"' %
( serverIP, port ) )
time = 0
result = runCmd( cmd )
while 'Connected' not in result:
if 'No route' in result:
rtable = runCmd( 'route' )
error( 'no route to %s:\n%s' % ( server, rtable ) )
return False
if timeout and time >= timeout:
error( 'could not connect to %s on port %d\n' % ( server, port ) )
return False
debug( 'waiting for', server, 'to listen on port', port, '\n' )
info( '.' )
while 'Connected' not in runCmd( cmd ):
if timeout:
print time
if time >= timeout:
error( 'could not connect to %s on port %d\n'
% ( server, port ) )
return False
output('waiting for', server,
'to listen on port', port, '\n')
sleep( .5 )
time += .5
result = runCmd( cmd )
return True
-10
View File
@@ -130,16 +130,6 @@ int main(int argc, char *argv[])
perror("unshare");
return 1;
}
/* Mark our whole hierarchy recursively as private, so that our
* mounts do not propagate to other processes.
*/
if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1) {
perror("remount");
return 1;
}
/* mount sysfs to pick up the new network namespace */
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
perror("mount");
+24 -24
View File
@@ -25,20 +25,20 @@ clean=false
declare -a hosts=()
user=$(whoami)
SSHDIR=/tmp/mn/ssh
USERDIR=$HOME/.ssh
usage="./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
USERDIR=/home/$user/.ssh
usage=$'./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
Authenticate yourself and other cluster nodes to each other
via ssh for mininet cluster edition. By default, we use a
temporary ssh setup. An ssh directory is mounted over
$USERDIR on each machine in the cluster.
/home/user/.ssh on each machine in the cluster.
-h: display this help
-p: create a persistent ssh setup. This will add
new ssh keys and known_hosts to each nodes
$USERDIR directory
/home/user/.ssh directory
-c: method to clean up a temporary ssh setup.
Any hosts taken as arguments will be cleaned
"
'
persistentSetup() {
echo "***creating key pair"
@@ -74,40 +74,40 @@ tempSetup() {
echo "***creating temporary ssh directory"
mkdir -p $SSHDIR
echo "***creating key pair"
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f $SSHDIR/id_rsa -N '' &> /dev/null
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f /tmp/mn/ssh/id_rsa -N '' &> /dev/null
echo "***mounting temporary ssh directory"
sudo mount --bind $SSHDIR $USERDIR
sudo mount --bind $SSHDIR /home/$user/.ssh
cp $SSHDIR/id_rsa.pub $SSHDIR/authorized_keys
for host in $hosts; do
echo "***copying public key to $host"
ssh-copy-id $user@$host &> /dev/null
echo "***mounting remote temporary ssh directory for $host"
ssh -o ForwardAgent=yes $user@$host "
mkdir -p $SSHDIR
cp $USERDIR/authorized_keys $SSHDIR/authorized_keys
sudo mount --bind $SSHDIR $USERDIR"
echo "***copying key pair to $host"
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying public key to $host"
ssh-copy-id $user@$host &> /dev/null
echo "***mounting remote temporary ssh directory for $host"
ssh -o ForwardAgent=yes $user@$host "
mkdir -p /tmp/mn/ssh
cp /home/$user/.ssh/authorized_keys $SSHDIR/authorized_keys
sudo mount --bind $SSHDIR /home/$user/.ssh"
echo "***copying key pair to $host"
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying known_hosts to $host"
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying known_hosts to $host"
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
done
}
cleanup() {
for host in $hosts; do
echo "***cleaning up $host"
ssh $user@$host "sudo umount $USERDIR
ssh $user@$host "sudo umount /home/$user/.ssh
sudo rm -rf $SSHDIR"
done
echo "**unmounting local directories"
sudo umount $USERDIR
sudo umount /home/$user/.ssh
echo "***removing temporary ssh directory"
sudo rm -rf $SSHDIR
echo "done!"
+90 -142
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Mininet install script for Ubuntu (and Debian Wheezy+)
# Mininet install script for Ubuntu (and Debian Lenny)
# Brandon Heller (brandonh@stanford.edu)
# Fail on error
@@ -13,7 +13,7 @@ set -o nounset
MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )"
# Set up build directory, which by default is the working directory
# unless the working directory is a subdirectory of mininet,
# unless the working directory is a subdirectory of mininet,
# in which case we use the directory containing mininet
BUILD_DIR="$(pwd -P)"
case $BUILD_DIR in
@@ -36,9 +36,8 @@ if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi
test -e /etc/debian_version && DIST="Debian"
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
# Truly non-interactive apt-get installation
install='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q install'
remove='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q remove'
install='sudo apt-get -y install'
remove='sudo apt-get -y remove'
pkginst='sudo dpkg -i'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
@@ -46,8 +45,7 @@ if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
fi
fi
test -e /etc/fedora-release && DIST="Fedora"
test -e /etc/redhat-release && DIST="RedHatEnterpriseServer"
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" = "Fedora" ]; then
install='sudo yum -y install'
remove='sudo yum -y erase'
pkginst='sudo rpm -ivh'
@@ -68,8 +66,8 @@ echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
KERNEL_NAME=`uname -r`
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora|RedHatEnterpriseServer'; then
echo "Install.sh currently only supports Ubuntu, Debian, RedHat and Fedora."
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora'; then
echo "Install.sh currently only supports Ubuntu, Debian and Fedora."
exit 1
fi
@@ -104,10 +102,7 @@ OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
function kernel {
echo "Install Mininet-compatible kernel if necessary"
sudo apt-get update
if ! $install linux-image-$KERNEL_NAME; then
echo "Could not install linux-image-$KERNEL_NAME"
echo "Skipping - assuming installed kernel is OK."
fi
$install linux-image-$KERNEL_NAME
}
function kernel_clean {
@@ -125,14 +120,14 @@ function kernel_clean {
# Install Mininet deps
function mn_deps {
echo "Installing Mininet dependencies"
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" = "Fedora" ]; then
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8 python-pexpect
ethtool help2man pyflakes pylint python-pep8
else
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8 python-pexpect
pyflakes pylint pep8
fi
echo "Installing Mininet core"
@@ -158,14 +153,12 @@ function of {
echo "Installing OpenFlow reference implementation..."
cd $BUILD_DIR
$install autoconf automake libtool make gcc
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" = "Fedora" ]; then
$install git pkgconfig glibc-devel
else
$install git-core autotools-dev pkg-config libc6-dev
fi
# was: git clone git://openflowswitch.org/openflow.git
# Use our own fork on github for now:
git clone git://github.com/mininet/openflow
git clone git://openflowswitch.org/openflow.git
cd $BUILD_DIR/openflow
# Patch controller to handle more than 16 switches
@@ -196,24 +189,17 @@ function of13 {
fi
# Install netbee
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
NBEESRC="nbeesrc-feb-24-2015"
NBEEDIR="netbee"
else
NBEESRC="nbeesrc-jan-10-2013"
NBEEDIR="nbeesrc-jan-10-2013"
fi
NBEESRC="nbeesrc-jan-10-2013"
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
wget -nc ${NBEEURL}${NBEESRC}.zip
unzip ${NBEESRC}.zip
cd ${NBEEDIR}/src
cd ${NBEESRC}/src
cmake .
make
cd $BUILD_DIR/
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib
sudo ldconfig
sudo cp -R ${NBEEDIR}/include/ /usr/
sudo cp -R ${NBEESRC}/include/ /usr/
# Resume the install:
cd $BUILD_DIR/ofsoftswitch13
@@ -228,7 +214,7 @@ function of13 {
function install_wireshark {
if ! which wireshark; then
echo "Installing Wireshark"
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" = "Fedora" ]; then
$install wireshark wireshark-gnome
else
$install wireshark tshark
@@ -272,69 +258,53 @@ function ubuntuOvs {
OVS_SRC=$BUILD_DIR/openvswitch
OVS_TARBALL_LOC=http://openvswitch.org/releases
if ! echo "$DIST" | egrep "Ubuntu|Debian" > /dev/null; then
echo "OS must be Ubuntu or Debian"
$cd BUILD_DIR
return
fi
if [ "$DIST" = "Ubuntu" ] && ! version_ge $RELEASE 12.04; then
echo "Ubuntu version must be >= 12.04"
cd $BUILD_DIR
return
fi
if [ "$DIST" = "Debian" ] && ! version_ge $RELEASE 7.0; then
echo "Debian version must be >= 7.0"
cd $BUILD_DIR
return
fi
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 12.04; then
rm -rf $OVS_SRC
mkdir -p $OVS_SRC
cd $OVS_SRC
rm -rf $OVS_SRC
mkdir -p $OVS_SRC
cd $OVS_SRC
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
tar xzf openvswitch-$OVS_RELEASE.tar.gz
else
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
cd $BUILD_DIR
return
fi
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
tar xzf openvswitch-$OVS_RELEASE.tar.gz
# Remove any old packages
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
openvswitch-pki openvswitch-switch
# Get build deps
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
pkg-config bzip2 openssl python-all procps python-qt4 \
python-zopeinterface python-twisted-conch dkms
# Build OVS
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
cd ..
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
echo "Ignoring error installing openvswitch-controller"
fi
modinfo openvswitch
sudo ovs-vsctl show
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service openvswitch-controller stop; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/openvswitch-controller ]; then
sudo update-rc.d openvswitch-controller disable
fi
else
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
cd $BUILD_DIR
return
fi
# Remove any old packages
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-pki openvswitch-switch \
openvswitch-controller || true
# Get build deps
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
pkg-config bzip2 openssl python-all procps python-qt4 \
python-zopeinterface python-twisted-conch dkms dh-python dh-autoreconf \
uuid-runtime
# Build OVS
parallel=`grep processor /proc/cpuinfo | wc -l`
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
DEB_BUILD_OPTIONS='parallel=$parallel nocheck' fakeroot debian/rules binary
cd ..
for pkg in common datapath-dkms pki switch; do
pkg=openvswitch-${pkg}_$OVS_RELEASE*.deb
echo "Installing $pkg"
$pkginst $pkg
done
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb 2>/dev/null; then
echo "Ignoring error installing openvswitch-controller"
fi
/sbin/modinfo openvswitch
sudo ovs-vsctl show
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service openvswitch-controller stop 2>/dev/null; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/openvswitch-controller ]; then
sudo update-rc.d openvswitch-controller disable
echo "Failed to install Open vSwitch. OS must be Ubuntu >= 12.04"
cd $BUILD_DIR
return
fi
}
@@ -344,47 +314,39 @@ function ubuntuOvs {
function ovs {
echo "Installing Open vSwitch..."
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" == "Fedora" ]; then
$install openvswitch openvswitch-controller
return
fi
if [ "$DIST" = "Ubuntu" ] && ! version_ge $RELEASE 14.04; then
# Older Ubuntu versions need openvswitch-datapath/-dkms
# Manually installing openvswitch-datapath may be necessary
# for manually built kernel .debs using Debian's defective kernel
# packaging, which doesn't yield usable headers.
if ! dpkg --get-selections | grep openvswitch-datapath; then
# If you've already installed a datapath, assume you
# know what you're doing and don't need dkms datapath.
# Otherwise, install it.
$install openvswitch-datapath-dkms
fi
# Manually installing openvswitch-datapath may be necessary
# for manually built kernel .debs using Debian's defective kernel
# packaging, which doesn't yield usable headers.
if ! dpkg --get-selections | grep openvswitch-datapath; then
# If you've already installed a datapath, assume you
# know what you're doing and don't need dkms datapath.
# Otherwise, install it.
$install openvswitch-datapath-dkms
fi
$install openvswitch-switch
OVSC=""
if $install openvswitch-controller; then
OVSC="openvswitch-controller"
else
echo "Attempting to install openvswitch-testcontroller"
if $install openvswitch-testcontroller; then
OVSC="openvswitch-testcontroller"
else
echo "Failed - skipping openvswitch-testcontroller"
fi
fi
if [ "$OVSC" ]; then
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service $OVSC stop; then
if sudo service openvswitch-controller stop; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/$OVSC ]; then
sudo update-rc.d $OVSC disable
if [ -e /etc/init.d/openvswitch-controller ]; then
sudo update-rc.d openvswitch-controller disable
fi
else
echo "Attempting to install openvswitch-testcontroller"
if ! $install openvswitch-testcontroller; then
echo "Failed - giving up"
fi
fi
}
function remove_ovs {
@@ -430,10 +392,12 @@ function ryu {
# install Ryu dependencies"
$install autoconf automake g++ libtool python make
if [ "$DIST" = "Ubuntu" -o "$DIST" = "Debian" ]; then
if [ "$DIST" = "Ubuntu" ]; then
$install libxml2 libxslt-dev python-pip python-dev
sudo pip install --upgrade gevent pbr webob routes paramiko \\
oslo.config
sudo pip install gevent
elif [ "$DIST" = "Debian" ]; then
$install libxml2 libxslt-dev python-pip python-dev
sudo pip install gevent
fi
# if needed, update python-six
@@ -559,15 +523,13 @@ function oftest {
function cbench {
echo "Installing cbench..."
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" = "Fedora" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
else
$install libsnmp-dev libpcap-dev libconfig-dev
fi
cd $BUILD_DIR/
# was: git clone git://gitosis.stanford.edu/oflops.git
# Use our own fork on github for now:
git clone git://github.com/mininet/oflops
git clone git://gitosis.stanford.edu/oflops.git
cd oflops
sh boot.sh || true # possible error in autoreconf, so run twice
sh boot.sh
@@ -605,7 +567,7 @@ net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
# Since the above doesn't disable neighbor discovery, also do this:
if ! grep 'ipv6.disable' /etc/default/grub; then
sudo sed -i -e \
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 /' \
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1/' \
/etc/default/grub
sudo update-grub
fi
@@ -631,7 +593,7 @@ net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
$install ntp
# Install vconfig for VLAN example
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
if [ "$DIST" = "Fedora" ]; then
$install vconfig
else
$install vlan
@@ -712,20 +674,6 @@ function vm_clean {
rm -f ~/.ssh/id_rsa* ~/.ssh/known_hosts
sudo rm -f ~/.ssh/authorized_keys*
# Remove SSH keys and regenerate on boot
echo 'Removing SSH keys from /etc/ssh/'
sudo rm -f /etc/ssh/*key*
if ! grep mininet /etc/rc.local >& /dev/null; then
sudo sed -i -e "s/exit 0//" /etc/rc.local
echo '
# mininet: regenerate ssh keys if we deleted them
if ! stat -t /etc/ssh/*key* >/dev/null 2>&1; then
/usr/sbin/dpkg-reconfigure openssh-server
fi
exit 0
' | sudo tee -a /etc/rc.local > /dev/null
fi
# Remove Mininet files
#sudo rm -f /lib/modules/python2.5/site-packages/mininet*
#sudo rm -f /usr/bin/mnexec
@@ -740,7 +688,7 @@ exit 0
# Note: you can shrink the .vmdk in vmware using
# vmware-vdiskmanager -k *.vmdk
echo "Zeroing out disk blocks for efficient compaction..."
time sudo dd if=/dev/zero of=/tmp/zero bs=1M || true
time sudo dd if=/dev/zero of=/tmp/zero bs=1M
sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero
}
+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( "egrep -or 'Mininet [0-9\.]+\w*' *", shell=True )
error = False
+45 -56
View File
@@ -54,7 +54,6 @@ NoKVM = False # Don't use kvm and use emulation instead
Branch = None # Branch to update and check out before testing
Zip = False # Archive .ovf and .vmdk into a .zip file
Forward = [] # VM port forwarding options (-redir)
Chown = '' # Build directory owner
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
@@ -67,24 +66,36 @@ isoURLs = {
'precise64server':
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
'ubuntu-12.04.5-server-amd64.iso',
'quantal32server':
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
'ubuntu-12.10-server-i386.iso',
'quantal64server':
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
'ubuntu-12.10-server-amd64.iso',
'raring32server':
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
'ubuntu-13.04-server-i386.iso',
'raring64server':
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
'ubuntu-13.04-server-amd64.iso',
'saucy32server':
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
'ubuntu-13.10-server-i386.iso',
'saucy64server':
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
'ubuntu-13.10-server-amd64.iso',
'trusty32server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04.4-server-i386.iso',
'ubuntu-14.04-server-i386.iso',
'trusty64server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04.4-server-amd64.iso',
'wily32server':
'http://mirrors.kernel.org/ubuntu-releases/15.10/'
'ubuntu-15.10-server-i386.iso',
'wily64server':
'http://mirrors.kernel.org/ubuntu-releases/15.10/'
'ubuntu-15.10-server-amd64.iso',
'xenial32server':
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
'ubuntu-16.04.1-server-i386.iso',
'xenial64server':
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
'ubuntu-16.04.1-server-amd64.iso',
'ubuntu-14.04-server-amd64.iso',
'utopic32server':
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
'ubuntu-14.10-server-i386.iso',
'utopic64server':
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
'ubuntu-14.10-server-amd64.iso',
}
@@ -154,7 +165,7 @@ def depend():
run( 'sudo apt-get -qy update' )
run( 'sudo apt-get -qy install'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs curl'
' e2fsprogs dnsmasq curl'
' python-setuptools mtools zip' )
run( 'sudo easy_install pexpect' )
@@ -227,10 +238,7 @@ def extractKernel( image, flavor, imageDir=VMImageDir ):
return kernel, initrd
log( '* Extracting kernel to', kernel )
nbd = attachNBD( image, flags='-r' )
try:
print( srun( 'partx ' + nbd ) )
except:
log( 'Warning - partx failed with error' )
print srun( 'partx ' + nbd )
# Assume kernel is in partition 1/boot/vmlinuz*generic for now
part = nbd + 'p1'
mnt = mkdtemp()
@@ -254,9 +262,8 @@ def findBaseImage( flavor, size='8G' ):
# Detect race condition with multiple builds
perms = stat( image )[ ST_MODE ] & 0777
if perms != 0444:
raise Exception( 'Error - base image %s is writable.' % image +
' Are multiple builds running? if not,'
' remove %s and try again.' % image )
raise Exception( 'Error - %s is writable ' % image +
'; are multiple builds running?' )
else:
# We create VMImageDir here since we are called first
run( 'mkdir -p %s' % VMImageDir )
@@ -316,7 +323,7 @@ zerombr yes
clearpart --all --initlabel
#Automatic partitioning
autopart
#System authorization information
#System authorization infomation
auth --useshadow --enablemd5
#Firewall configuration
firewall --disabled
@@ -388,10 +395,6 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
accel = 'tcg'
else:
accel = 'kvm'
try:
run( 'kvm-ok' )
except:
raise Exception( 'kvm-ok failed; try using --nokvm' )
cmd = [ 'sudo', kvm,
'-machine', 'accel=%s' % accel,
'-nographic',
@@ -432,13 +435,12 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1 ):
def boot( cow, kernel, initrd, logfile, memory=1024 ):
"""Boot qemu/kvm with a COW disk and local/user data store
cow: COW disk path
kernel: kernel path
logfile: log file for pexpect object
memory: memory size in MB
cpuCores: number of CPU cores to use
returns: pexpect object to qemu process"""
# pexpect might not be installed until after depend() is called
global pexpect
@@ -467,8 +469,6 @@ def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1 ):
'-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
if Forward:
cmd += sum( [ [ '-redir', f ] for f in Forward ], [] )
if cpuCores > 1:
cmd += [ '-smp cores=%s' % cpuCores ]
cmd = ' '.join( cmd )
log( '* BOOTING VM FROM', cow )
log( cmd )
@@ -580,9 +580,6 @@ def useTest( vm, prompt=Prompt ):
log( '* Restoring logging to stdout' )
vm.logfile = stdout
# A convenient alias for use - 'run'; we might want to allow
# 'run' to take a parameter
runTest = useTest
def checkOutBranch( vm, branch, prompt=Prompt ):
# This is a bit subtle; it will check out an existing branch (e.g. master)
@@ -792,20 +789,18 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
post: command line to run in VM after tests
prompt: shell prompt (default '$ ')
memory: memory size in MB"""
global LogFile, Zip, Chown
global LogFile, Zip
start = time()
lstart = localtime()
date = strftime( '%y%m%d-%H-%M-%S', lstart)
ovfdate = strftime( '%y%m%d', lstart )
dir = 'mn-%s-%s' % ( flavor, date )
if Branch:
dirname = 'mn-%s-%s-%s' % ( Branch, flavor, date )
dir = 'mn-%s-%s-%s' % ( Branch, flavor, date )
try:
os.mkdir( dir)
os.mkdir( dir )
except:
raise Exception( "Failed to create build directory %s" % dir )
if Chown:
run( 'chown %s %s' % ( Chown, dir ) )
os.chdir( dir )
LogFile = open( 'build.log', 'w' )
log( '* Logging to', abspath( LogFile.name ) )
@@ -886,15 +881,14 @@ def getMininetVersion( vm ):
return version
def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
runFunction=None, **runArgs ):
"""Boot and test VM
tests: list of tests to run
pre: command line to run in VM before tests
post: command line to run in VM after tests
prompt: shell prompt (default '$ ')
memory: VM memory size in MB
cpuCores: number of CPU cores to use"""
memory: VM memory size in MB"""
bootTestStart = time()
basename = path.basename( image )
image = abspath( image )
@@ -912,7 +906,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
suffix='.testlog', delete=False )
log( '* Logging VM output to', logfile.name )
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
memory=memory, cpuCores=cpuCores )
memory=memory )
login( vm )
log( '* Waiting for prompt after login' )
vm.expect( prompt )
@@ -934,7 +928,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
def buildFlavorString():
"Return string listing valid build flavors"
return 'valid build flavors: %s' % ' '.join( sorted( isoURLs ) )
return 'valid build flavors: ( %s )' % ' '.join( sorted( isoURLs ) )
def testDict():
@@ -950,16 +944,15 @@ def testDict():
def testString():
"Return string listing valid tests"
tests = [ '%s <%s>' % ( name, func.__doc__ )
for name, func in testDict().iteritems() ]
return 'valid tests: %s' % ', '.join( tests )
return 'valid tests: ( %s )' % ' '.join( testDict().keys() )
def parseArgs():
"Parse command line arguments and run"
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward, Chown
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward
parser = argparse.ArgumentParser( description='Mininet VM build script',
epilog='' )
epilog=buildFlavorString() + ' ' +
testString() )
parser.add_argument( '-v', '--verbose', action='store_true',
help='send VM output to console rather than log file' )
parser.add_argument( '-d', '--depend', action='store_true',
@@ -979,7 +972,7 @@ def parseArgs():
help='Boot and test an existing VM image' )
parser.add_argument( '-t', '--test', metavar='test', default=[],
action='append',
help='specify a test to run; ' + testString() )
help='specify a test to run' )
parser.add_argument( '-w', '--timeout', metavar='timeout', type=int,
default=0, help='set expect timeout' )
parser.add_argument( '-r', '--run', metavar='cmd', default='',
@@ -989,15 +982,13 @@ def parseArgs():
parser.add_argument( '-b', '--branch', metavar='branch',
help='branch to install and/or check out and test' )
parser.add_argument( 'flavor', nargs='*',
help='VM flavor(s) to build; ' + buildFlavorString() )
help='VM flavor(s) to build (e.g. raring32server)' )
parser.add_argument( '-z', '--zip', action='store_true',
help='archive .ovf and .vmdk into .zip file' )
parser.add_argument( '-o', '--out',
help='output file for test image (vmdk)' )
parser.add_argument( '-f', '--forward', default=[], action='append',
help='forward VM ports to local server, e.g. tcp:5555::22' )
parser.add_argument( '-u', '--chown', metavar='user',
help='specify an owner for build directory' )
args = parser.parse_args()
if args.depend:
depend()
@@ -1019,8 +1010,6 @@ def parseArgs():
Forward = args.forward
if not args.test and not args.run and not args.post:
args.test = [ 'sanity', 'core' ]
if args.chown:
Chown = args.chown
for flavor in args.flavor:
if flavor not in isoURLs:
print "Unknown build flavor:", flavor
+1 -1
View File
@@ -43,7 +43,7 @@ fi
# Install Mininet
time mininet/util/install.sh
# Finalize VM
time mininet/util/install.sh -tcd
time mininet/util/install.sh -tc
# Ignoring this since NOX classic is deprecated
#if ! grep NOX_CORE_DIR .bashrc; then
# echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc