Compare commits

..

30 Commits

Author SHA1 Message Date
Bob Lantz 31b1093d86 2.2.2rc1 -> 2.2.2 2017-03-21 13:52:48 -07:00
Bob Lantz ea48a0e6e8 2.2.2rc1 -> 2.2.2rc2 2017-03-20 18:11:15 -07:00
Bob Lantz ae8c12c471 Workaround to fix cluster.py error message
Newer linux kernels report interfaces in a different format, so
we need to accept them.

Note however that this probably isn't entirely correct or
necessary for interfaces created in the right namespace to
begin with!
2017-03-19 20:07:10 -07:00
Bob Lantz a30d2fb42d 2.2.2rc1 2017-03-16 15:57:01 -07:00
Bob Lantz 2c0f29d361 Add travis code check for 14.04 build (only)
Note that we pass pylint on 14.04 but not on 16.04 currently
2017-03-15 18:24:22 -07:00
Bob Lantz aacd47136c pass 14.04 code check 2017-03-14 16:24:34 -07:00
Bob Lantz 38a4c3309c Add travis CI badge 2017-02-15 17:10:22 -08:00
lantz c8aff1dab0 Multiple builds: trusty and xenial 2017-02-15 17:07:53 -08:00
Bob Lantz 71bdfd37bd Changes for OVS 2.6.0 + Ubuntu 16.04 + Debian 8.6 2016-10-28 18:15:31 -07:00
Bob Lantz b558055c3e Add small wait before wget in web test.
Although the walkthrough doesn't specify waiting, it is a race
condition that we occasionally hit in tests. Adding a 2 second
delay seems realistic (for when a human is doing the walkthrough)
and should reduce the likelihood of hitting the race condition.
2016-10-21 15:34:35 -07:00
Bob Lantz 2554a2d02d For 10Mb/s links, we should certainly be network-limited rather
than CPU-limited, which should expose the feature we are trying
to demonstrate (TCP data rate slowing down as latency increases
due to congestion control.)
2016-10-21 13:26:22 -07:00
Bob Lantz b21782d121 Wait for exit in testWireshark()
If mn doesn't shut down cleanly, the subsequent test may fail.
2016-09-27 15:15:24 -07:00
Bob Lantz 5e216acca4 For some time, UserSwitch has suffered from abysmal performance due
to rx and tx offload settings. In the latest Ubuntu 16.04/Linux 4.4
kernel, it simply doesn't work with the default settings, since
packets with bad TCP checksums are generated at one end, passed through
the user switch, and dropped at the other. It doesn't seem to be
something we can fix in UserSwitch itself although it may be possible
to do something in the C code of the Stanford reference switch and
CPqD switch.

This patch is slightly different than mainline, as we haven't moved
bin/mn forward (yet?) However, it seems important enough to put into
2.2.2.
2016-09-27 14:57:19 -07:00
Bob Lantz fe923b2a18 Enable TESTS={'mytest':test} in --custom files 2016-09-26 16:34:54 -07:00
Bob Lantz 66ccff9275 Clean shutdown for testStaticMAC
Without waiting for mn to exit, OVS keeps listening on the
static listening port, which prevents UserSwitch from listening
on it (and starting up) in the next test.
2016-09-23 12:55:09 -07:00
Bob Lantz 89ec246bd2 2.2.2b3 2016-09-21 17:08:51 -07:00
Jason Croft ac4a673e6f Set limit on command history length 2016-09-21 14:49:32 -07:00
Bob Lantz d2d27043a8 Change default --listenport to 6654
Previously the standard OpenFlow port (6653) that Controller()
may be listening at would conflict with the default listenPort
for switch 20 (usually UserSwitch 20), breaking waitConnected()
and disconnecting that switch from the network in general.

Fixes #668
2016-09-21 14:09:37 -07:00
Bob Lantz 023a6169a8 testHostCommands: accept both ethX and en.*X interfaces
systemd (Ubuntu 16) names interfaces like this:
enp1s3 for "ethernet, PCI bus 1, slot 3"

We now accept both in testHostCommands

Fixes #665
2016-09-21 14:07:47 -07:00
Bob Lantz 6fdf8dcf2b Try specifying timeout in pexpect.spawn() + adjust error msg
Unfortunately pexpect() seems to be timing out with a 30 second
timeout rather than the 600 seconds we are passing in. How
could this even be working normally? It is puzzling. We are
going to try specifying the timeout in the spawn() call.

Also if the test fails, we use %e format for readability.
2016-08-29 23:59:50 -07:00
Bob Lantz dbba9dc711 Increase scratchnet timeout to see if it's just slow. 2016-08-29 13:00:04 -07:00
Bob Lantz 305651c006 Increase link delay to 2ms for more robust effect
With a 1 ms delay, the performance tests periodically fails
under nested virtualization, which is how we are testing it.
Increasing the delay should make the effect (lower TCP
data rates as latency increases, due to TCP's congestion
control algorithm) more pronounced and robust.

Conflicts:
	examples/linearbandwidth.py
2016-08-25 05:03:17 -07:00
Bob Lantz c359825be9 Reduce CPU so that iperf client is CPU bound
If we want to observe a monotonic affect, we should
make sure that we are in fact CPU limited where it
matters. In this case, we are CPU limiting the hosts,
and the iperf client uses a lot of CPU. We need to
reduce the CPU allocation so that iperf is in fact
CPU bound.

We also correct the CPU allocation so that the client
and server each receive 50% of the total. Previously
we were specifying the per-host CPU allocation, so
45% meant we were allocating 90% of the overall CPU,
which seems a bit confusing. On the other hand, now
at 40% each host gets 20% of the CPU, which could also
be considered slightly confusing!

Although the client transmit rate is going to be the
limiting factor, we still measure the received data
rate at the server, because that is more interesting
than the initial burst of buffering at the client.
Measuring at the server becomes more important as
we reduce the iperf time.

The output is also changed slightly, and the test has
been updated appropriately.

Conflicts:
	examples/cpu.py
2016-08-25 00:13:52 -07:00
Bob Lantz f86fe7f9d2 Change tshark version check for compatibility
The version string changed in tshark 2.0.2 in Ubuntu 16.
Perhaps we should just check for the version string itself
to be more robust but this is probably OK for now.
2016-08-22 16:51:09 -07:00
Bob Lantz befde4825b Allow some diffs in h1 ps vs. h2 ps
Presumably daemons, race conditions, or ephemeral processes
can cause the ps output to vary slightly. We allow up to
two differing lines to account for this.

Fixes #651
2016-08-22 16:01:39 -07:00
Bob Lantz dc9399b308 Add 2.2.2 contributors 2016-08-19 15:12:20 -07:00
Bob Lantz 095e4e299a 2.2.2b1 -> 2.2.2b2 2016-08-19 14:28:20 -07:00
Bob Lantz b1d84855cc 14.04.3 -> 14.04.4
We should probably do this in a more automatic way.
2016-08-15 23:07:40 -07:00
Bob Lantz 7476c5e051 Fixes for Unbuntu 16.04/Xenial
- add xenial
- partx returns an error (warn rather than fail)
- remove dnsmasq build.py dependency (not really needed, side effects)

Conflicts:
	util/vm/build.py
2016-08-15 23:07:27 -07:00
Olivier Tilmans a8a9e7d2eb TCIntf: Don't delete a non-existing root qdisc
In recent kernels, virtual interfaces come without any associated
qdisc, resulting in errors when spawning the network.
Checking for "noqueue" in the tc output, enables to detect that
case and thus avoid deleting the non-existent qdisc.
2016-08-15 23:05:32 -07:00
52 changed files with 573 additions and 1270 deletions
-19
View File
@@ -1,19 +0,0 @@
Mininet uses GitHub issues for bug reports and feature requests only.
These issues can be viewed at bugs.mininet.org
If you have a question that is not a bug report or a feature request,
please use the documentation at docs.mininet.org, the FAQ
at faq.mininet.org, and the mininet-discuss mailing list.
For bug reports, please fill in the following information in detail,
and also feel free to include additional information such as debug
output from mn -v debug, etc.
--- Cut Here ---
### Expected/Desired Behavior:
### Actual Behavior:
### Detailed Steps to Reproduce the Behavior:
### Additional Information:
+5 -7
View File
@@ -44,9 +44,7 @@ load-plugins=
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
locally-disabled, locally-enabled
# bad-continuation, wrong-import-order
locally-disabled
[REPORTS]
@@ -63,7 +61,7 @@ include-ids=yes
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
# Tells wether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highes
@@ -113,10 +111,10 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9]{2,30}$
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9]{2,30}$
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
@@ -125,7 +123,7 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9]{2,30}$
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
+3 -6
View File
@@ -5,11 +5,8 @@ matrix:
include:
- dist: trusty
env: dist="14.04 LTS trusty"
# - dist: xenial
# env: dist="16.04 LTS xenial"
# Travis-CI only proposes 14.04 LTS Trusty and there is no plan to update to 16.04 xenial
# (c.f. https://github.com/travis-ci/travis-ci/issues/5821)
# It is useless to add a second job because it will run in the same Ubuntu version (14.04)
- dist: xenial
env: dist="16.04 LTS xenial"
before_install:
- sudo apt-get update -qq
@@ -17,7 +14,7 @@ before_install:
- sudo util/install.sh -n
install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- bash -c "[ `lsb_release -rs` == '14.04' ] && make codecheck"
- sudo util/install.sh -fnvw
script:
+4
View File
@@ -18,6 +18,7 @@ Cody Burkard
Additional Mininet Contributors
M S Vishwanath Bhat
Tomasz Buchert
Gustavo Pantuza Coelho Pinto
Fernando Cappi
@@ -29,6 +30,7 @@ Andrew Ferguson
Eder Leao Fernandes
Gregory Gee
Jon Hall
Jono Hart
Roan Huang
Vitaly Ivanov
Babis Kaidos
@@ -39,10 +41,12 @@ David Mahler
Murphy McCauley
José Pedro Oliveira
James Page
Rahman Pujianto
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Olivier Tilmans
Andreas Wundsam
Isaku Yamahata
Baohua Yang
+1 -1
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0d1
Mininet 2.2.2
---
The supported installation methods for Mininet are 1) using a
+2 -2
View File
@@ -1,6 +1,6 @@
Mininet 2.3.0d1 License
Mininet 2.2.2 License
Copyright (c) 2013-2016 Open Networking Laboratory
Copyright (c) 2013-2017 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+4 -3
View File
@@ -1,8 +1,9 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.3.0d1
Mininet 2.2.2
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
@@ -126,7 +127,7 @@ 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
Bob Lantz
Mininet Core Team
[1]: https://travis-ci.org/mininet/mininet.svg?branch=master
[1]: https://travis-ci.org/mininet/mininet.svg?branch=devel/2.2.2
+90 -106
View File
@@ -21,19 +21,19 @@ if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
from mininet.clean import cleanup
import mininet.cli
from mininet.log import lg, LEVELS, info, debug, warn, error, output
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, warn, error
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
Ryu, NOX, RemoteController, findController,
DefaultController, NullController,
UserSwitch, OVSSwitch, OVSBridge,
IVSSwitch )
from mininet.nodelib import LinuxBridge, Server
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink, TCULink, OVSLink
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
SingleSwitchReversedTopo, MinimalTopo )
import mininet.topolib
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import customClass, specialClass, splitArgs
from mininet.util import buildTopo
@@ -53,8 +53,9 @@ TOPODEF = 'minimal'
TOPOS = { 'minimal': MinimalTopo,
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo }
TOPOS.update( mininet.topolib.topos)
'single': SingleSwitchTopo,
'tree': TreeTopo,
'torus': TorusTopo }
SWITCHDEF = 'default'
SWITCHES = { 'user': UserSwitch,
@@ -68,7 +69,6 @@ SWITCHES = { 'user': UserSwitch,
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'server': Server,
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
@@ -87,51 +87,16 @@ LINKS = { 'default': Link, # Note: overridden below
'tcu': TCULink,
'ovs': OVSLink }
# TESTS dict can contain functions and/or Mininet() method names
# XXX: it would be nice if we could specify a default test, but
# this may be tricky
TESTS = { name: True
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
CLI = None # Set below if needed
# Names of tests that are Mininet() methods
TESTNAMES = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all',
'iperfudp', 'none' ]
# Locally defined tests
def allTest( net ):
"Run ping and iperf tests"
net.waitConnected()
net.start()
net.ping()
net.iperf()
def nullTest( _net ):
"Null 'test' (does nothing)"
pass
TESTS.update( all=allTest, none=nullTest, build=nullTest )
# Map to alternate spellings of Mininet() methods
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp' }
def runTests( mn, options ):
"""Run tests
mn: Mininet object
option: list of test optinos """
# Split option into test name and parameters
for option in options:
# Multiple tests may be separated by '+' for now
for test in option.split( '+' ):
test, args, kwargs = splitArgs( test )
test = ALTSPELLING.get( test.lower(), test )
testfn = TESTS.get( test, test )
if callable( testfn ):
testfn( mn, *args, **kwargs )
elif hasattr( mn, test ):
mn.waitConnected()
getattr( mn, test )( *args, **kwargs )
else:
raise Exception( 'Test %s is unknown - please specify one of '
'%s ' % ( test, TESTS.keys() ) )
# Map to alternate functions and/or spellings of Mininet() methods
TESTS = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
def addDictOption( opts, choicesDict, default, name, **kwargs ):
@@ -152,7 +117,7 @@ def addDictOption( opts, choicesDict, default, name, **kwargs ):
def version( *_args ):
"Print Mininet version and exit"
output( "%s\n" % VERSION )
print "%s" % VERSION
exit()
@@ -245,8 +210,9 @@ 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='test', help='|'.join( TESTS.keys() ) )
dest='tests', help='|'.join( testList ) )
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',
@@ -303,106 +269,124 @@ class MininetRunner( object ):
# set logging verbosity
if LEVELS[self.options.verbosity] > LEVELS['output']:
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
'output!\n'
'Please restart Mininet with -v [debug, info, output].\n'
'Please restart Mininet with -v [debug, info, output].'
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# Maybe we'll reorganize this someday...
# pylint: disable=too-many-branches,too-many-statements,global-statement
# pylint: disable=too-many-branches,too-many-statements
def begin( self ):
"Create and run mininet."
global CLI
opts = self.options
if opts.cluster:
servers = opts.cluster.split( ',' )
if self.options.cluster:
servers = self.options.cluster.split( ',' )
for server in servers:
ClusterCleanup.add( server )
if opts.clean:
if self.options.clean:
cleanup()
exit()
start = time.time()
if not opts.controller:
if not self.options.controller:
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
opts.controller = [ 'default' ]
self.options.controller = [ 'default' ]
if not CONTROLLERS[ 'default' ]:
opts.controller = [ 'none' ]
if opts.switch == 'default':
self.options.controller = [ 'none' ]
if self.options.switch == 'default':
info( '*** No default OpenFlow controller found '
'for default switch!\n' )
info( '*** Falling back to OVS Bridge\n' )
opts.switch = 'ovsbr'
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
self.options.switch = 'ovsbr'
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
raise Exception( "Could not find a default controller "
"for switch %s" %
opts.switch )
self.options.switch )
topo = buildTopo( TOPOS, opts.topo )
switch = customClass( SWITCHES, opts.switch )
host = customClass( HOSTS, opts.host )
topo = buildTopo( TOPOS, self.options.topo )
switch = customClass( SWITCHES, self.options.switch )
host = customClass( HOSTS, self.options.host )
controller = [ customClass( CONTROLLERS, c )
for c in opts.controller ]
if opts.switch == 'user' and opts.link == 'default':
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
opts.link = 'tcu'
link = customClass( LINKS, opts.link )
self.options.link = 'tcu'
link = customClass( LINKS, self.options.link )
if self.validate:
self.validate( opts )
self.validate( self.options )
if opts.nolistenport:
opts.listenport = None
ipBase = self.options.ipbase
xterms = self.options.xterms
mac = self.options.mac
arp = self.options.arp
pin = self.options.pin
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
# Handle innamespace, cluster options
if opts.innamespace and opts.cluster:
error( "Please specify --innamespace OR --cluster\n" )
# Handle inNamespace, cluster options
inNamespace = self.options.innamespace
cluster = self.options.cluster
if inNamespace and cluster:
print "Please specify --innamespace OR --cluster"
exit()
Net = MininetWithControlNet if opts.innamespace else Mininet
if opts.cluster:
Net = MininetWithControlNet if inNamespace else Mininet
cli = ClusterCLI if cluster else CLI
if cluster:
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
Net = partial( MininetCluster, servers=servers,
placement=PLACEMENT[ opts.placement ] )
mininet.cli.CLI = ClusterCLI
placement=PLACEMENT[ self.options.placement ] )
mn = Net( topo=topo,
switch=switch, host=host, controller=controller, link=link,
ipBase=opts.ipbase, inNamespace=opts.innamespace,
xterms=opts.xterms, autoSetMacs=opts.mac,
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
listenPort=opts.listenport )
switch=switch, host=host, controller=controller,
link=link,
ipBase=ipBase,
inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
if opts.ensure_value( 'nat', False ):
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
if self.options.ensure_value( 'nat', False ):
nat = mn.addNAT( *self.options.nat_args,
**self.options.nat_kwargs )
nat.configDefault()
# --custom files can set CLI or change mininet.cli.CLI
CLI = mininet.cli.CLI if CLI is None else CLI
if opts.pre:
CLI( mn, script=opts.pre )
if self.options.pre:
cli( mn, script=self.options.pre )
mn.start()
if opts.test:
runTests( mn, opts.test )
else:
CLI( mn )
if not self.options.tests:
cli( mn )
if opts.post:
CLI( mn, script=opts.post )
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 )()
if self.options.post:
cli( mn, script=self.options.post )
mn.stop()
+9 -12
View File
@@ -3,35 +3,32 @@
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
import sys
from mininet.node import Host
from mininet.util import ensureRoot, waitListening
from mininet.log import info, warn, output
ensureRoot()
timeout = 5
info( "*** Creating nodes\n" )
print "*** Creating nodes"
h1 = Host( 'h1' )
root = Host( 'root', inNamespace=False )
info( "*** Creating link\n" )
print "*** Creating links"
h1.linkTo( root )
info( h1 )
print h1
info( "*** Configuring nodes\n" )
print "*** Configuring nodes"
h1.setIP( '10.0.0.1', 8 )
root.setIP( '10.0.0.2', 8 )
info( "*** Creating banner file\n" )
print "*** Creating banner file"
f = open( '/tmp/%s.banner' % h1.name, 'w' )
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
f.close()
info( "*** Running sshd\n" )
print "*** Running sshd"
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
# add arguments from the command line
if len( sys.argv ) > 1:
@@ -40,7 +37,7 @@ h1.cmd( cmd )
listening = waitListening( server=h1, port=22, timeout=timeout )
if listening:
output( "*** You may now ssh into", h1.name, "at", h1.IP(), '\n' )
print "*** You may now ssh into", h1.name, "at", h1.IP()
else:
warn( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ), '\n' )
print ( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ) )
+53 -144
View File
@@ -74,7 +74,6 @@ Things to do:
- hifi support (e.g. delay compensation)
"""
from mininet.node import Node, Host, OVSSwitch, Controller
from mininet.link import Link, Intf
from mininet.net import Mininet
@@ -157,7 +156,7 @@ class RemoteMixin( object ):
'-o', 'ForwardAgent=yes', '-tt' ]
def __init__( self, name, server='localhost', user=None, serverIP=None,
controlPath=True, splitInit=False, **kwargs):
controlPath=False, splitInit=False, **kwargs):
"""Instantiate a remote node
name: name of remote node
server: remote server (optional)
@@ -213,7 +212,7 @@ class RemoteMixin( object ):
def startShell( self, *args, **kwargs ):
"Start a shell process for running commands"
if self.isRemote:
kwargs.update( mnopts='-cp' )
kwargs.update( mnopts='-c' )
super( RemoteMixin, self ).startShell( *args, **kwargs )
# Optional split initialization
self.sendCmd( 'echo $$' )
@@ -239,7 +238,7 @@ class RemoteMixin( object ):
args: string or list of strings
returns: stdout and stderr"""
popen = self.rpopen( *cmd, **opts )
# info( 'RCMD: POPEN:', popen, '\n' )
# print 'RCMD: POPEN:', popen
# These loops are tricky to get right.
# Once the process exits, we can read
# EOF twice if necessary.
@@ -288,7 +287,7 @@ class RemoteMixin( object ):
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
# kwargs.update( moveIntfFn=RemoteLink.moveIntf )
kwargs.update( moveIntfFn=RemoteLink.moveIntf )
return super( RemoteMixin, self).addIntf( *args, **kwargs )
@@ -386,38 +385,40 @@ class RemoteLink( Link ):
if server1 == server2:
# Link within same server
return Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs,
runCmd=None )
node1, node2, deleteIntfs=deleteIntfs )
# Otherwise, make a tunnel
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
addr1, addr2 )
return self.tunnel
@staticmethod
def moveIntf( intf, node ):
def moveIntf( intf, node, printError=True ):
"""Move remote interface from root ns to node
intf: string, interface
dstNode: destination Node
srcNode: source Node or None (default) for root ns"""
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
result = node.rcmd( cmd )
if result:
raise Exception('error executing command %s' % cmd)
node.rcmd( cmd )
links = node.cmd( 'ip link show' )
if not re.search( r' %s[:@]' % intf, links ):
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
return True
def makeTunnel( self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None ):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != node2.server
assert node1.server != 'localhost' or node2.server != 'localhost'
# And we can't ssh into this server remotely as 'localhost',
# so try again swappping node1 and node2
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
debug( '\n*** Make SSH tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
# 1. Create tap interfaces
for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename
@@ -473,91 +474,6 @@ class RemoteLink( Link ):
return result
class RemoteSSHLink( RemoteLink ):
"Remote link using SSH tunnels"
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
class RemoteGRELink( RemoteLink ):
"Remote link using GRE tunnels"
GRE_KEY = 0
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
def stop( self ):
"Stop this link"
if self.tunnel:
self.intf1.delete()
self.intf2.delete()
else:
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
node1 = self.node1 if node1 is None else node1
node2 = self.node2 if node2 is None else node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == server2:
# Link within same server
Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs )
# Need to reduce the MTU of all emulated hosts to 1450 for GRE
# tunneling, otherwise packets larger than 1400 bytes cannot be
# successfully transmitted through the tunnel.
node1.cmd('ip link set dev %s mtu 1450' % intfname1)
node2.cmd('ip link set dev %s mtu 1450' % intfname2)
else:
# Otherwise, make a tunnel
self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
self.tunnel = 1
def makeTunnel(self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != node2.server
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
IP1, IP2 = node1.serverIP, node2.serverIP
# GRE tunnel needs to be set up with the IP of the local interface
# that connects the remote node, NOT '127.0.0.1' of localhost
if node1.server == 'localhost':
output = quietRun('ip route get %s' % node2.serverIP)
IP1 = output.split(' src ')[1].split()[0]
debug( '\n*** Make GRE tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
tun1 = 'local ' + IP1 + ' remote ' + IP2
tun2 = 'local ' + IP2 + ' remote ' + IP1
self.__class__.GRE_KEY += 1
for (node, intfname, addr, tun) in [(node1, intfname1, addr1, tun1),
(node2, intfname2, addr2, tun2)]:
node.rcmd('ip link delete ' + intfname)
result = node.rcmd('ip link add name ' + intfname + ' type gretap '
+ tun + ' ttl 64 key '
+ str( self.__class__.GRE_KEY) )
if result:
raise Exception('error creating gretap on %s: %s'
% (node, result))
if addr:
node.rcmd('ip link set %s address %s' % (intfname, addr))
node.rcmd('ip link set dev %s up' % intfname)
node.rcmd('ip link set dev %s mtu 1450' % intfname)
if not self.moveIntf(intfname, node):
raise Exception('interface move failed on node %s' % node)
# Some simple placement algorithms for MininetCluster
class Placer( object ):
@@ -845,13 +761,10 @@ class MininetCluster( Mininet ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
# Update IP address for controller that may not be local
if ( isinstance( controller, Controller )
and controller.IP() == '127.0.0.1' ):
links = controller.cmd( 'ip link show' )
eth0 = re.findall( ' (.*eth0):', links )
if not eth0:
raise Exception( 'Cannot find IP address for controller eth0' )
Intf( eth0[ 0 ], node=controller ).updateIP()
if ( isinstance( controller, Controller)
and controller.IP() == '127.0.0.1'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
return controller
def buildFromTopo( self, *args, **kwargs ):
@@ -862,11 +775,11 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
def testNsTunnels():
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=link )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server=remote )
net = Mininet( host=RemoteHost, link=RemoteLink )
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
@@ -877,30 +790,30 @@ def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteNet( remote='ubuntu2' ):
"Test remote Node classes"
info( '*** Remote Node Test\n' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link )
print '*** Remote Node Test'
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=RemoteLink )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
info( "*** Creating local h1\n" )
print "*** Creating local h1"
h1 = net.addHost( 'h1' )
info( "*** Creating remote h2\n" )
print "*** Creating remote h2"
h2 = net.addHost( 'h2', server=remote )
info( "*** Creating local s1\n" )
print "*** Creating local s1"
s1 = net.addSwitch( 's1' )
info( "*** Creating remote s2\n" )
print "*** Creating remote s2"
s2 = net.addSwitch( 's2', server=remote )
info( "*** Adding links\n" )
print "*** Adding links"
net.addLink( h1, s1 )
net.addLink( s1, s2 )
net.addLink( h2, s2 )
net.start()
info( 'Mininet is running on', quietRun( 'hostname' ).strip(), '\n' )
print 'Mininet is running on', quietRun( 'hostname' ).strip()
for node in c0, h1, h2, s1, s2:
info( 'Node', node, 'is running on',
node.cmd( 'hostname' ).strip(), '\n' )
print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip()
net.pingAll()
CLI( net )
net.stop()
@@ -938,11 +851,11 @@ def ClusterController( *args, **kwargs):
Intf( 'eth0', node=controller ).updateIP()
return controller
def testRemoteTopo( link=RemoteGRELink ):
def testRemoteTopo():
"Test remote Node classes using Mininet()/Topo() API"
topo = LinearTopo( 2 )
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
link=link, controller=ClusterController )
link=RemoteLink, controller=ClusterController )
net.start()
net.pingAll()
net.stop()
@@ -952,17 +865,18 @@ def testRemoteTopo( link=RemoteGRELink ):
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteSwitches():
"Test with local hosts and remote switches"
servers = [ 'localhost', remote]
servers = [ 'localhost', 'ubuntu2']
topo = TreeTopo( depth=4, fanout=2 )
net = MininetCluster( topo=topo, servers=servers, link=link,
net = MininetCluster( topo=topo, servers=servers,
placement=RoundRobinPlacer )
net.start()
net.pingAll()
net.stop()
#
# For testing and demo purposes it would be nice to draw the
# network graph and color it based on server.
@@ -970,36 +884,31 @@ def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
def testMininetCluster():
"Test MininetCluster()"
servers = [ 'localhost', remote ]
servers = [ 'localhost', 'ubuntu2' ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers, link=link,
net = MininetCluster( topo=topo, servers=servers,
placement=SwitchBinPlacer )
net.start()
net.pingAll()
net.stop()
def signalTest( remote='ubuntu2'):
def signalTest():
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server=remote )
h = RemoteHost( 'h0', server='ubuntu1' )
h.shell.send_signal( SIGINT )
h.shell.poll()
if h.shell.returncode is None:
info( 'signalTest: SUCCESS: ', h, 'has not exited after SIGINT', '\n' )
print 'OK: ', h, 'has not exited'
else:
info( 'signalTest: FAILURE:', h, 'exited with code',
h.shell.returncode, '\n' )
print 'FAILURE:', h, 'exited with code', h.shell.returncode
h.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
remoteServer = 'ubuntu2'
remoteLink = RemoteSSHLink
testRemoteTopo(link=remoteLink)
testNsTunnels( remote=remoteServer, link=remoteLink )
testRemoteNet( remote=remoteServer, link=remoteLink)
testMininetCluster( remote=remoteServer, link=remoteLink)
testRemoteSwitches( remote=remoteServer, link=remoteLink)
signalTest( remote=remoteServer )
# testRemoteTopo()
# testRemoteNet()
# testMininetCluster()
# testRemoteSwitches()
signalTest()
+4 -10
View File
@@ -27,21 +27,16 @@ class ClusterCLI( CLI ):
def do_plot( self, _line ):
"Plot topology colored by node placement"
# Import networkx if needed
global nx, plt, graphviz_layout
global nx, plt
if not nx:
try:
# pylint: disable=import-error
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
plt = pyplot # satisfy pylint
plt = pyplot # satisfiy pylint
import pygraphviz
assert pygraphviz # silence pyflakes
# Networkx moved this around
if hasattr( nx, 'graphviz_layout' ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
@@ -50,8 +45,7 @@ class ClusterCLI( CLI ):
# Make a networkx Graph
g = nx.Graph()
mn = self.mn
servers = getattr( mn, 'servers', [ 'localhost' ] )
hosts, switches = mn.hosts, mn.switches
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
nodes = hosts + switches
g.add_nodes_from( nodes )
links = [ ( link.intf1.node, link.intf2.node )
@@ -61,7 +55,7 @@ class ClusterCLI( CLI ):
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
color = dict( zip( servers, self.colorsFor( servers ) ) )
# Plot it!
pos = graphviz_layout( g )
pos = nx.graphviz_layout( g )
opts = { 'ax': None, 'font_weight': 'bold',
'width': 2, 'edge_color': 'darkblue' }
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ]
+2 -4
View File
@@ -2,9 +2,7 @@
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
from mininet.examples.cluster import ( MininetCluster, SwitchBinPlacer,
RemoteLink )
# ^ Could also use: RemoteSSHLink, RemoteGRELink
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
@@ -13,7 +11,7 @@ def demo():
"Simple Demo of Cluster Mode"
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers, Link=RemoteLink,
net = MininetCluster( topo=topo, servers=servers,
placement=SwitchBinPlacer )
net.start()
CLI( net )
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
from mininet.net import Mininet
from mininet.log import setLogLevel
def perf(Link):
"Test connectivity nand performance over Link"
net = Mininet( host=RemoteHost, link=Link )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
net.iperf()
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perf( RemoteSSHLink )
perf( RemoteGRELink )
+11 -12
View File
@@ -11,50 +11,49 @@ Note that one could also create a custom switch class and pass it into
the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel
def multiControllerNet():
"Create a network from semi-scratch with multiple controllers."
net = Mininet( controller=Controller, switch=OVSSwitch )
info( "*** Creating (reference) controllers\n" )
print "*** Creating (reference) controllers"
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
info( "*** Creating switches\n" )
print "*** Creating switches"
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
info( "*** Creating hosts\n" )
hosts1 = [ net.addHost( 'h%d' % n ) for n in ( 3, 4 ) ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in ( 5, 6 ) ]
print "*** Creating hosts"
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
info( "*** Creating links\n" )
print "*** Creating links"
for h in hosts1:
net.addLink( s1, h )
for h in hosts2:
net.addLink( s2, h )
net.addLink( s1, s2 )
info( "*** Starting network\n" )
print "*** Starting network"
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
info( "*** Testing network\n" )
print "*** Testing network"
net.pingAll()
info( "*** Running CLI\n" )
print "*** Running CLI"
CLI( net )
info( "*** Stopping network\n" )
print "*** Stopping network"
net.stop()
if __name__ == '__main__':
-86
View File
@@ -1,86 +0,0 @@
#!/usr/bin/python
"""
fakecluster.py: a fake cluster for testing Mininet cluster edition!!!
We are going to self-host Mininet by creating a virtual cluster
for cluster edition.
Note: ssh is kind of a mess - you end up having to do things
like h1 sudo -E -u openflow ssh 10.2
"""
from mininet.net import Mininet
from mininet.nodelib import LinuxBridge, Server
from mininet.cli import CLI
from mininet.topo import Topo, SingleSwitchTopo
from mininet.log import setLogLevel, warn
from mininet.util import errRun, quietRun
from mininet.link import Link
from functools import partial
from sys import argv
class MininetServer( Server ):
"A server (for nested Mininet) that runs ssh and ovs"
privateDirs = [ '/var/run/sshd', '/etc/openvswitch',
'/var/run/openvswitch', '/var/log/openvswitch' ]
def __init__( self, *args, **kwargs ):
"Turn on ovs by default"
kwargs.setdefault( 'ovs', True )
super( MininetServer, self ).__init__( *args, **kwargs )
def config( self, **kwargs ):
"""Configure/start sshd and other stuff
ovs: start Open vSwitch?"""
self.ovs = kwargs.get( 'ovs' )
super( MininetServer, self ).config( **kwargs )
if self.ovs:
self.service( 'openvswitch-switch start' )
def terminate( self, *args, **kwargs ):
"Shut down services and terminate server"
if self.ovs:
self.service( 'openvswitch-switch stop' )
super( MininetServer, self ).terminate( *args, **kwargs )
class ServerLink( Link ):
def intfName( self, node, n ):
"Override to avoid destruction by cleanup!"
# This is kind of ugly... for some reason 'eth0' fails so
# we just use 'm1eth0'; however, this should nest reasonably.
return ( node.name + 'eth' + repr( n ) if isinstance( node, Server )
else node.name + '-eth' + repr( n ) )
def makeIntfPair( self, *args, **kwargs ):
"Override to use quietRun"
kwargs.update( runCmd=quietRun )
super( ServerLink, self ).makeIntfPair( *args, **kwargs )
class ClusterTopo( Topo ):
"Cluster topology: m1..mN"
def build( self, n ):
ms1 = self.addSwitch( 'ms1' )
for i in range( 1, n + 1 ):
h = self.addHost( 'm%d' % i )
self.addLink( h, ms1, cls=ServerLink )
def test( serverCount ):
"Test this setup"
setLogLevel( 'info' )
topo = ClusterTopo( serverCount )
host = partial( MininetServer, ssh=True, ovs=True)
net = Mininet( topo=topo, host=host, switch=LinuxBridge, ipBase='10.0/24' )
MininetServer.updateHostsFiles( net.hosts )
# addNAT().configDefault() also connects root namespace to Mininet
net.addNAT().configDefault()
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
n = 8 if len( argv ) != 2 else int( argv[ 1 ] )
test( n )
+8 -6
View File
@@ -23,7 +23,6 @@ of switches, this example demonstrates:
"""
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
@@ -84,7 +83,7 @@ def linearBandwidthTest( lengths ):
assert 'reno' in output
for datapath in switches.keys():
info( "*** testing", datapath, "datapath\n" )
print "*** testing", datapath, "datapath"
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='2ms', bw=10 )
@@ -92,10 +91,10 @@ def linearBandwidthTest( lengths ):
controller=Controller, waitConnected=True,
link=link )
net.start()
info( "*** testing basic connectivity\n" )
print "*** testing basic connectivity"
for n in lengths:
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
info( "*** testing bandwidth\n" )
print "*** testing bandwidth"
for n in lengths:
src, dst = net.hosts[ 0 ], net.hosts[ n ]
# Try to prime the pump to reduce PACKET_INs during test
@@ -110,7 +109,9 @@ def linearBandwidthTest( lengths ):
net.stop()
for datapath in switches.keys():
info( "\n*** Linear network results for", datapath, "datapath:\n" )
print
print "*** Linear network results for", datapath, "datapath:"
print
result = results[ datapath ]
info( "SwitchCount\tiperf Results\n" )
for switchCount, serverbw in result:
@@ -119,8 +120,9 @@ def linearBandwidthTest( lengths ):
info( '\n')
info( '\n' )
if __name__ == '__main__':
lg.setLogLevel( 'info' )
sizes = [ 1, 10, 20, 40, 60, 80 ]
info( "*** Running linearBandwidthTest", sizes, '\n' )
print "*** Running linearBandwidthTest", sizes
linearBandwidthTest( sizes )
+2 -4
View File
@@ -27,14 +27,12 @@ Additional routes may be added to the router or hosts by
executing 'ip route' or 'route' commands on the router or hosts.
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
class LinuxRouter( Node ):
"A Node with IP forwarding enabled."
@@ -56,7 +54,7 @@ class NetworkTopo( Topo ):
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
s1, s2, s3 = [ self.addSwitch( s ) for s in ( 's1', 's2', 's3' ) ]
s1, s2, s3 = [ self.addSwitch( s ) for s in 's1', 's2', 's3' ]
self.addLink( s1, router, intfName2='r0-eth1',
params2={ 'ip' : defaultIP } ) # for clarity
@@ -82,7 +80,7 @@ def run():
net = Mininet( topo=topo ) # controller is used by s1-s3
net.start()
info( '*** Routing Table on Router:\n' )
info( net[ 'r0' ].cmd( 'route' ) )
print net[ 'r0' ].cmd( 'route' )
CLI( net )
net.stop()
+43 -43
View File
@@ -45,7 +45,7 @@ if 'PYTHONPATH' in os.environ:
# someday: from ttk import *
from mininet.log import info, debug, warn, setLogLevel
from mininet.log import info, setLogLevel
from mininet.net import Mininet, VERSION
from mininet.util import netParse, ipAdd, quietRun
from mininet.util import buildTopo
@@ -60,7 +60,7 @@ from mininet.moduledeps import moduleDeps
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
info( 'MiniEdit running against Mininet '+VERSION, '\n' )
print 'MiniEdit running against Mininet '+VERSION
MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
from mininet.node import IVSSwitch
@@ -379,14 +379,14 @@ class PrefsDialog(tkSimpleDialog.Dialog):
@staticmethod
def getOvsVersion():
"Return OVS version"
outp = quietRun("ovs-vsctl --version")
r = r'ovs-vsctl \(Open vSwitch\) (.*)'
outp = quietRun("ovs-vsctl show")
r = r'ovs_version: "(.*)"'
m = re.search(r, outp)
if m is None:
warn( 'Version check failed' )
print 'Version check failed'
return None
else:
info( 'Open vSwitch version is '+m.group(1), '\n' )
print 'Open vSwitch version is '+m.group(1)
return m.group(1)
@@ -755,7 +755,7 @@ class SwitchDialog(CustomDialog):
def apply(self):
externalInterfaces = []
for row in range(self.tableFrame.rows):
# debug( 'Interface is ' + self.tableFrame.get(row, 0), '\n' )
#print 'Interface is ' + self.tableFrame.get(row, 0)
if len(self.tableFrame.get(row, 0)) > 0:
externalInterfaces.append(self.tableFrame.get(row, 0))
@@ -866,7 +866,7 @@ class TableFrame(Frame):
return widget.get()
def addRow( self, value=None, readonly=False ):
# debug( "Adding row " + str(self.rows +1), '\n' )
#print "Adding row " + str(self.rows +1)
current_row = []
for column in range(self.columns):
label = Entry(self, borderwidth=0)
@@ -1669,7 +1669,7 @@ class MiniEdit( Frame ):
f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
# pylint: disable=broad-except
except Exception as er:
warn( er, '\n' )
print er
# pylint: enable=broad-except
finally:
f.close()
@@ -1683,7 +1683,7 @@ class MiniEdit( Frame ):
fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
if len(fileName ) > 0:
# debug( "Now saving under %s\n" % fileName )
#print "Now saving under %s" % fileName
f = open(fileName, 'wb')
f.write("#!/usr/bin/python\n")
@@ -2489,7 +2489,7 @@ class MiniEdit( Frame ):
if len(hostBox.result['privateDirectory']) > 0:
newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
self.hostOpts[name] = newHostOpts
info( 'New host details for ' + name + ' = ' + str(newHostOpts), '\n' )
print 'New host details for ' + name + ' = ' + str(newHostOpts)
def switchDetails( self, _ignore=None ):
if ( self.selection is None or
@@ -2527,7 +2527,7 @@ class MiniEdit( Frame ):
newSwitchOpts['sflow'] = switchBox.result['sflow']
newSwitchOpts['netflow'] = switchBox.result['netflow']
self.switchOpts[name] = newSwitchOpts
info( 'New switch details for ' + name + ' = ' + str(newSwitchOpts), '\n' )
print 'New switch details for ' + name + ' = ' + str(newSwitchOpts)
def linkUp( self ):
if ( self.selection is None or
@@ -2566,12 +2566,12 @@ class MiniEdit( Frame ):
linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
if linkBox.result is not None:
linkDetail['linkOpts'] = linkBox.result
info( 'New link details = ' + str(linkBox.result), '\n' )
print 'New link details = ' + str(linkBox.result)
def prefDetails( self ):
prefDefaults = self.appPrefs
prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
info( 'New Prefs = ' + str(prefBox.result), '\n' )
print 'New Prefs = ' + str(prefBox.result)
if prefBox.result:
self.appPrefs = prefBox.result
@@ -2590,14 +2590,14 @@ class MiniEdit( Frame ):
ctrlrBox = ControllerDialog(self, title='Controller Details', ctrlrDefaults=self.controllers[name])
if ctrlrBox.result:
# debug( 'Controller is ' + ctrlrBox.result[0], '\n' )
#print 'Controller is ' + ctrlrBox.result[0]
if len(ctrlrBox.result['hostname']) > 0:
name = ctrlrBox.result['hostname']
widget[ 'text' ] = name
else:
ctrlrBox.result['hostname'] = name
self.controllers[name] = ctrlrBox.result
info( 'New controller details for ' + name + ' = ' + str(self.controllers[name]), '\n' )
print 'New controller details for ' + name + ' = ' + str(self.controllers[name])
# Find references to controller and change name
if oldName != name:
for widget in self.widgetToItem:
@@ -2698,15 +2698,15 @@ class MiniEdit( Frame ):
def buildNodes( self, net):
# Make nodes
info( "Getting Hosts and Switches.\n" )
print "Getting Hosts and Switches."
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
# debug( name+' has '+str(tags), '\n' )
#print name+' has '+str(tags)
if 'Switch' in tags:
opts = self.switchOpts[name]
# debug( str(opts), '\n' )
#print str(opts)
# Create the correct switch class
switchClass = customOvs
@@ -2772,7 +2772,7 @@ class MiniEdit( Frame ):
newSwitch = net.addHost( name , cls=LegacyRouter)
elif 'Host' in tags:
opts = self.hostOpts[name]
# debug( str(opts), '\n' )
#print str(opts)
ip = None
defaultRoute = None
if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
@@ -2797,7 +2797,7 @@ class MiniEdit( Frame ):
privateDirs=opts['privateDirectory'] )
else:
hostCls=Host
debug( hostCls, '\n' )
print hostCls
newHost = net.addHost( name,
cls=hostCls,
ip=ip,
@@ -2817,7 +2817,7 @@ class MiniEdit( Frame ):
Intf( extInterface, node=newHost )
if 'vlanInterfaces' in opts:
if len(opts['vlanInterfaces']) > 0:
info( 'Checking that OS is VLAN prepared\n' )
print 'Checking that OS is VLAN prepared'
self.pathCheck('vconfig', moduleName='vlan package')
moduleDeps( add='8021q' )
elif 'Controller' in tags:
@@ -2834,7 +2834,7 @@ class MiniEdit( Frame ):
controllerPort = opts['remotePort']
# Make controller
info( 'Getting controller selection:'+controllerType, '\n' )
print 'Getting controller selection:'+controllerType
if controllerType == 'remote':
net.addController(name=name,
controller=RemoteController,
@@ -2874,7 +2874,7 @@ class MiniEdit( Frame ):
def buildLinks( self, net):
# Make links
info( "Getting Links.\n" )
print "Getting Links."
for key,link in self.links.iteritems():
tags = self.canvas.gettags(key)
if 'data' in tags:
@@ -2886,14 +2886,14 @@ class MiniEdit( Frame ):
if linkopts:
net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
else:
# debug( str(srcNode) )
# debug( str(dstNode), '\n' )
#print str(srcNode)
#print str(dstNode)
net.addLink(srcNode, dstNode)
self.canvas.itemconfig(key, dash=())
def build( self ):
"Build network based on our topology."
print "Build network based on our topology."
dpctl = None
if len(self.appPrefs['dpctl']) > 0:
@@ -2924,7 +2924,7 @@ class MiniEdit( Frame ):
# Attach vlan interfaces
if 'vlanInterfaces' in opts:
for vlanInterface in opts['vlanInterfaces']:
info( 'adding vlan interface '+vlanInterface[1], '\n' )
print 'adding vlan interface '+vlanInterface[1]
newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
# Run User Defined Start Command
if 'startCommand' in opts:
@@ -2950,7 +2950,7 @@ class MiniEdit( Frame ):
opts = self.switchOpts[name]
if 'netflow' in opts:
if opts['netflow'] == '1':
info( name+' has Netflow enabled\n' )
print name+' has Netflow enabled'
nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
nflowEnabled=True
if nflowEnabled:
@@ -2959,13 +2959,13 @@ class MiniEdit( Frame ):
nflowCmd = nflowCmd + ' add_id_to_interface=true'
else:
nflowCmd = nflowCmd + ' add_id_to_interface=false'
info( 'cmd = '+nflowCmd+nflowSwitches, '\n' )
print 'cmd = '+nflowCmd+nflowSwitches
call(nflowCmd+nflowSwitches, shell=True)
else:
info( 'No switches with Netflow\n' )
print 'No switches with Netflow'
else:
info( 'No NetFlow targets specified.\n' )
print 'No NetFlow targets specified.'
# Configure sFlow
sflowValues = self.appPrefs['sflow']
@@ -2980,18 +2980,18 @@ class MiniEdit( Frame ):
opts = self.switchOpts[name]
if 'sflow' in opts:
if opts['sflow'] == '1':
info( name+' has sflow enabled\n' )
print name+' has sflow enabled'
sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
sflowEnabled=True
if sflowEnabled:
sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
info( 'cmd = '+sflowCmd+sflowSwitches, '\n' )
print 'cmd = '+sflowCmd+sflowSwitches
call(sflowCmd+sflowSwitches, shell=True)
else:
info( 'No switches with sflow\n' )
print 'No switches with sflow'
else:
info( 'No sFlow targets specified.\n' )
print 'No sFlow targets specified.'
## NOTE: MAKE SURE THIS IS LAST THING CALLED
# Start the CLI if enabled
@@ -3217,7 +3217,7 @@ class MiniEdit( Frame ):
raise Exception( 'could not find custom file: %s' % fileName )
def importTopo( self ):
info( 'topo='+self.options.topo, '\n' )
print 'topo='+self.options.topo
if self.options.topo == 'none':
return
self.newTopology()
@@ -3231,7 +3231,7 @@ class MiniEdit( Frame ):
currentY = 100
# Add Controllers
info( 'controllers:'+str(len(importNet.controllers)), '\n' )
print 'controllers:'+str(len(importNet.controllers))
for controller in importNet.controllers:
name = controller.name
x = self.controllerCount*100+100
@@ -3251,7 +3251,7 @@ class MiniEdit( Frame ):
currentY = currentY + rowIncrement
# Add switches
info( 'switches:'+str(len(importNet.switches)), '\n' )
print 'switches:'+str(len(importNet.switches))
columnCount = 0
for switch in importNet.switches:
name = switch.name
@@ -3292,7 +3292,7 @@ class MiniEdit( Frame ):
currentY = currentY + rowIncrement
# Add hosts
info( 'hosts:'+str(len(importNet.hosts)), '\n' )
print 'hosts:'+str(len(importNet.hosts))
columnCount = 0
for host in importNet.hosts:
name = host.name
@@ -3312,10 +3312,10 @@ class MiniEdit( Frame ):
else:
columnCount =columnCount+1
info( 'links:'+str(len(topo.links())), '\n' )
print 'links:'+str(len(topo.links()))
#[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
for link in topo.links():
info( str(link), '\n' )
print str(link)
srcNode = link[0]
src = self.findWidgetByName(srcNode)
sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
@@ -3325,7 +3325,7 @@ class MiniEdit( Frame ):
dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
params = topo.linkInfo( srcNode, destNode )
info( 'Link Parameters='+str(params), '\n' )
print 'Link Parameters='+str(params)
self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
fill='blue', tag='link' )
+10 -12
View File
@@ -19,11 +19,10 @@ to-do:
- think about clearing last hop - why doesn't that work?
"""
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.log import info, output, warn, setLogLevel
from mininet.log import output, warn
from random import randint
@@ -106,31 +105,30 @@ def moveHost( host, oldSwitch, newSwitch, newPort=None ):
def mobilityTest():
"A simple test of mobility"
info( '* Simple mobility test\n' )
print '* Simple mobility test'
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
info( '* Starting network:\n' )
print '* Starting network:'
net.start()
printConnections( net.switches )
info( '* Testing network\n' )
print '* Testing network'
net.pingAll()
info( '* Identifying switch interface for h1\n' )
print '* Identifying switch interface for h1'
h1, old = net.get( 'h1', 's1' )
for s in 2, 3, 1:
new = net[ 's%d' % s ]
port = randint( 10, 20 )
info( '* Moving', h1, 'from', old, 'to', new, 'port', port, '\n' )
print '* Moving', h1, 'from', old, 'to', new, 'port', port
hintf, sintf = moveHost( h1, old, new, newPort=port )
info( '*', hintf, 'is now connected to', sintf, '\n' )
info( '* Clearing out old flows\n' )
print '*', hintf, 'is now connected to', sintf
print '* Clearing out old flows'
for sw in net.switches:
sw.dpctl( 'del-flows' )
info( '* New network:\n' )
print '* New network:'
printConnections( net.switches )
info( '* Testing connectivity:\n' )
print '* Testing connectivity:'
net.pingAll()
old = new
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
mobilityTest()
+4 -5
View File
@@ -8,11 +8,10 @@ multiple hosts and monitor their output interactively for a period=
of time.
"""
from mininet.net import Mininet
from mininet.node import Node
from mininet.topo import SingleSwitchTopo
from mininet.log import info, setLogLevel
from mininet.log import setLogLevel
from select import poll, POLLIN
from time import time
@@ -35,8 +34,8 @@ def startpings( host, targetips ):
' done; '
'done &' )
info( '*** Host %s (%s) will be pinging ips: %s\n' %
( host.name, host.IP(), targetips ) )
print ( '*** Host %s (%s) will be pinging ips: %s' %
( host.name, host.IP(), targetips ) )
host.cmd( cmd )
@@ -70,7 +69,7 @@ def multiping( netsize, chunksize, seconds):
readable = poller.poll(1000)
for fd, _mask in readable:
node = Node.outToNode[ fd ]
info( '%s:' % node.name, node.monitor().strip(), '\n' )
print '%s:' % node.name, node.monitor().strip()
# Stop pings
for host in hosts:
+4 -6
View File
@@ -5,16 +5,14 @@ Simple example of sending output to multiple files and
monitoring them
"""
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import info, setLogLevel
from mininet.log import setLogLevel
from time import time
from select import poll, POLLIN
from subprocess import Popen, PIPE
def monitorFiles( outfiles, seconds, timeoutms ):
"Monitor set of files and return [(host, line)...]"
devnull = open( '/dev/null', 'w' )
@@ -55,7 +53,7 @@ def monitorTest( N=3, seconds=3 ):
net = Mininet( topo )
net.start()
hosts = net.hosts
info( "Starting test...\n" )
print "Starting test..."
server = hosts[ 0 ]
outfiles, errfiles = {}, {}
for h in hosts:
@@ -69,10 +67,10 @@ def monitorTest( N=3, seconds=3 ):
'>', outfiles[ h ],
'2>', errfiles[ h ],
'&' )
info( "Monitoring output for", seconds, "seconds\n" )
print "Monitoring output for", seconds, "seconds"
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
if h:
info( '%s: %s\n' % ( h.name, line ) )
print '%s: %s' % ( h.name, line )
for h in hosts:
h.cmd('kill %ping')
net.stop()
+3 -5
View File
@@ -4,20 +4,18 @@
Example to create a Mininet topology and connect it to the internet via NAT
"""
from mininet.cli import CLI
from mininet.log import lg, info
from mininet.log import lg
from mininet.topolib import TreeNet
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
# Add NAT connectivity
net.addNAT().configDefault()
net.start()
info( "*** Hosts are running and should have internet connectivity\n" )
info( "*** Type 'exit' or control-D to shut down network\n" )
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
CLI( net )
# Shut down NAT
net.stop()
+3 -4
View File
@@ -6,7 +6,6 @@ Validate that the port numbers match to the interface name,
and that the ovs ports match the mininet ports.
"""
from mininet.net import Mininet
from mininet.node import Controller
from mininet.log import setLogLevel, info, warn
@@ -66,13 +65,13 @@ def testPortNumbering():
'is actually on port', s1.ports[intfs], '... ' )
if validatePort( s1, intfs ):
info( 'Validated.\n' )
info( '\n' )
print '\n'
# test the network with pingall
net.pingAll()
info( '\n' )
print '\n'
info( '*** Stopping network\n' )
info( '*** Stopping network' )
net.stop()
if __name__ == '__main__':
+2 -3
View File
@@ -5,11 +5,10 @@ This example monitors a number of hosts using host.popen() and
pmonitor()
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel
from mininet.util import custom, pmonitor
def monitorhosts( hosts=5, sched='cfs' ):
@@ -28,7 +27,7 @@ def monitorhosts( hosts=5, sched='cfs' ):
# Monitor them and print output
for host, line in pmonitor( popens ):
if host:
info( "<%s>: %s" % ( host.name, line ) )
print "<%s>: %s" % ( host.name, line.strip() )
# Done
net.stop()
+3 -6
View File
@@ -5,8 +5,6 @@
from mininet.net import Mininet
from mininet.topo import SingleSwitchTopo
from mininet.util import pmonitor
from mininet.log import setLogLevel, info
from time import time
from signal import SIGINT
@@ -16,21 +14,20 @@ def pmonitorTest( N=3, seconds=10 ):
net = Mininet( topo )
net.start()
hosts = net.hosts
info( "Starting test...\n" )
print "Starting test..."
server = hosts[ 0 ]
popens = {}
for h in hosts:
popens[ h ] = h.popen('ping', server.IP() )
info( "Monitoring output for", seconds, "seconds\n" )
print "Monitoring output for", seconds, "seconds"
endTime = time() + seconds
for h, line in pmonitor( popens, timeoutms=500 ):
if h:
info( '<%s>: %s' % ( h.name, line ) )
print '<%s>: %s' % ( h.name, line ),
if time() >= endTime:
for p in popens.values():
p.send_signal( SIGINT )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
pmonitorTest()
+1 -2
View File
@@ -8,7 +8,6 @@ but it exposes the configuration details and allows customization.
For most tasks, the higher-level API will be preferable.
"""
from mininet.net import Mininet
from mininet.node import Node
from mininet.link import Link
@@ -41,7 +40,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
switch.cmd( 'ovs-vsctl del-br dp0' )
switch.cmd( 'ovs-vsctl add-br dp0' )
for intf in switch.intfs.values():
switch.cmd( 'ovs-vsctl add-port dp0 %s\n' % intf )
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
# Note: controller and switch are in root namespace, and we
# can connect via loopback interface
+1 -1
View File
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' )
intfs = str( sintf1 ), str( sintf2 )
intfs = [ str( i ) for i in sintf1, sintf2 ]
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
+6 -9
View File
@@ -9,22 +9,19 @@ iperf will hang indefinitely if the TCP handshake fails
to complete.
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel
from sys import argv
# It would be nice if we didn't have to do this:
# pylint: disable=arguments-differ
class SingleSwitchTopo( Topo ):
class SingleSwitchTopo(Topo):
"Single switch connected to n hosts."
def build( self, n=2, lossy=True ):
def __init__(self, n=2, lossy=True, **opts):
Topo.__init__(self, **opts)
switch = self.addSwitch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
@@ -47,9 +44,9 @@ def perfTest( lossy=True ):
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
net.start()
info( "Dumping host connections\n" )
print "Dumping host connections"
dumpNodeConnections(net.hosts)
info( "Testing bandwidth between h1 and h4\n" )
print "Testing bandwidth between h1 and h4"
h1, h4 = net.getNodeByName('h1', 'h4')
net.iperf( ( h1, h4 ), l4Type='UDP' )
net.stop()
+8 -6
View File
@@ -20,12 +20,11 @@ import sys
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg, info
from mininet.log import lg
from mininet.node import Node
from mininet.topolib import TreeTopo
from mininet.util import waitListening
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout )
@@ -60,14 +59,17 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
connectToRootNS( network, switch, ip, routes )
for host in network.hosts:
host.cmd( cmd + ' ' + opts + '&' )
info( "*** Waiting for ssh daemons to start\n" )
print "*** Waiting for ssh daemons to start"
for server in network.hosts:
waitListening( server=server, port=22, timeout=5 )
info( "\n*** Hosts are running sshd at the following addresses:\n" )
print
print "*** Hosts are running sshd at the following addresses:"
print
for host in network.hosts:
info( host.name, host.IP(), '\n' )
info( "\n*** Type 'exit' or control-D to shut down network\n" )
print host.name, host.IP()
print
print "*** Type 'exit' or control-D to shut down network"
CLI( network )
for host in network.hosts:
host.cmd( 'kill %' + cmd )
+1 -2
View File
@@ -32,8 +32,7 @@ def runTests( testDir, verbosity=1 ):
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
success = MininetTestRunner( verbosity=verbosity ).run( testSuite ).wasSuccessful()
sys.exit( 0 if success else 1 )
MininetTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
# get the directory containing example tests
+2 -2
View File
@@ -57,8 +57,8 @@ class testHwintf( unittest.TestCase ):
p.wait()
def tearDown( self ):
self.h3.stop( deleteIntfs=True )
self.n0.stop( deleteIntfs=True )
self.h3.terminate()
self.n0.terminate()
if __name__ == '__main__':
setLogLevel( 'warning' )
+2 -3
View File
@@ -16,7 +16,7 @@ class testMultiPoll( unittest.TestCase ):
"(h\d+): \d+ bytes from",
"Monitoring output for (\d+) seconds",
pexpect.EOF ]
pings, seconds = {}, -1
pings = {}
while True:
index = p.expect( opts )
if index == 0:
@@ -32,8 +32,7 @@ class testMultiPoll( unittest.TestCase ):
self.assertTrue( len( pings ) > 0 )
# make sure we have received at least one ping per second
for count in pings.values():
self.assertTrue( count >= seconds,
'%d pings < %d seconds' % ( count, seconds ) )
self.assertTrue( count >= seconds )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -20,7 +20,7 @@ class testSSHD( unittest.TestCase ):
while True:
index = p.expect( self.opts )
if index == 0:
print( p.match.group(0) )
print p.match.group(0)
p.sendline( 'yes' )
elif index == 1:
return False
+6 -6
View File
@@ -2,8 +2,7 @@
"Create a 64-node tree network, and test connectivity using ping."
from mininet.log import setLogLevel, info
from mininet.log import setLogLevel
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
from mininet.topolib import TreeNet
@@ -16,16 +15,17 @@ def treePing64():
'Open vSwitch kernel': OVSKernelSwitch }
for name in switches:
info( "*** Testing", name, "datapath\n" )
print "*** Testing", name, "datapath"
switch = switches[ name ]
network = TreeNet( depth=2, fanout=8, switch=switch )
result = network.run( network.pingAll )
results[ name ] = result
info( "\n*** Tree network ping results:\n" )
print
print "*** Tree network ping results:"
for name in switches:
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
info( '\n' )
print "%s: %d%% packet loss" % ( name, results[ name ] )
print
if __name__ == '__main__':
setLogLevel( 'info' )
+8 -9
View File
@@ -25,15 +25,15 @@ def sh( cmd ):
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
sh( 'pkill -9 -f %s' % pattern )
# Make sure they are gone
while True:
try:
pids = co( [ 'pgrep', '-f', pattern ] ).split( '\n' )
pids = co( [ 'pgrep', '-f', pattern ] )
except CalledProcessError:
pids = []
# Don't kill init
pids = [ pid for pid in pids if pid and pid != '1' ]
pids = ''
if pids:
sh( "kill -9 %s" % ' '.join( pids ) )
sh( 'pkill -9 -f %s' % pattern )
time.sleep( .5 )
else:
break
@@ -50,9 +50,8 @@ class Cleanup( object ):
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
"pings/noxes\n" )
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
'lt-nox_core ovs-openflowd ovs-controller'
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
@@ -93,7 +92,7 @@ class Cleanup( object ):
).splitlines()
# Delete blocks of links
n = 1000 # chunk size
for i in range( 0, len( links ), n ):
for i in xrange( 0, len( links ), n ):
cmd = ';'.join( 'ip link del %s' % link
for link in links[ i : i + n ] )
sh( '( %s ) 2> /dev/null' % cmd )
+7 -8
View File
@@ -178,7 +178,7 @@ class CLI( Cmd ):
output( result + '\n' )
else:
output( repr( result ) + '\n' )
except Exception as e:
except Exception, e:
output( str( e ) + '\n' )
# We are in fact using the exec() pseudo-function
@@ -189,7 +189,7 @@ class CLI( Cmd ):
Node names may be used, e.g.: px print h1.cmd('ls')"""
try:
exec( line, globals(), self.getLocals() )
except Exception as e:
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable=broad-except,exec-used
@@ -373,7 +373,7 @@ class CLI( Cmd ):
def do_links( self, _line ):
"Report on links"
for link in self.mn.links:
output( link, link.status(), '\n' )
print link, link.status()
def do_switch( self, line ):
"Starts or stops a switch"
@@ -399,16 +399,15 @@ class CLI( Cmd ):
def default( self, line ):
"""Called on an input line when the command prefix is not recognized.
Overridden to run shell commands when a node is the first
CLI argument. Past the first CLI argument, node names are
automatically replaced with corresponding IP addrs."""
Overridden to run shell commands when a node is the first CLI argument.
Past the first CLI argument, node names are automatically replaced with
corresponding IP addrs."""
first, args, line = self.parseline( line )
if first in self.mn:
if not args:
error( '*** Please enter a command for node: %s <cmd>\n'
% first )
print "*** Enter a command for node: %s <cmd>" % first
return
node = self.mn[ first ]
rest = args.split( ' ' )
+16 -22
View File
@@ -25,9 +25,8 @@ Link: basic link class for creating veth pairs
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair, quietRun
from mininet.util import makeIntfPair
import mininet.node
import re
class Intf( object ):
@@ -52,12 +51,11 @@ class Intf( object ):
self.ip = '127.0.0.1'
self.prefixLen = 8
# Add to node (and move ourselves if necessary )
if node:
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
# Save params for future reference
self.params = params
self.config( **params )
@@ -204,8 +202,6 @@ class Intf( object ):
# if self.node.inNamespace:
# Link may have been dumped into root NS
# quietRun( 'ip link del ' + self.name )
self.node.delIntf( self )
self.link = None
def status( self ):
"Return intf status as a string"
@@ -255,7 +251,7 @@ class TCIntf( Intf ):
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
if latency_ms is None:
latency_ms = 15.0 * 8 / bw
latency_ms = 15 * 8 / bw
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
@@ -298,7 +294,7 @@ class TCIntf( Intf ):
netemargs = '%s%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'%s ' % jitter if jitter is not None else '',
'loss %.5f ' % loss if loss is not None else '',
'loss %d ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
if netemargs:
@@ -382,7 +378,7 @@ class TCIntf( Intf ):
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
( ['%.5f%% loss' % loss ] if loss is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
@@ -481,8 +477,7 @@ class Link( object ):
@classmethod
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True,
runCmd=quietRun ):
node1=None, node2=None, deleteIntfs=True ):
"""Create pair of interfaces
intfname1: name for interface 1
intfname2: name for interface 2
@@ -495,14 +490,14 @@ class Link( object ):
# Leave this as a class method for now
assert cls
return makeIntfPair( intfname1, intfname2, addr1, addr2, node1, node2,
deleteIntfs=deleteIntfs, runCmd=runCmd )
deleteIntfs=deleteIntfs )
def delete( self ):
"Delete this link"
self.intf1.delete()
self.intf1 = None
self.intf2.delete()
self.intf2 = None
# We only need to delete one side, though this doesn't seem to
# cost us much and might help subclasses.
# self.intf2.delete()
def stop( self ):
"Override to stop and clean up link as needed"
@@ -535,10 +530,9 @@ class OVSLink( Link ):
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
from mininet.node import OVSSwitch
self.isPatchLink = False
if ( isinstance( node1, OVSSwitch ) and
isinstance( node2, OVSSwitch ) ):
if ( isinstance( node1, mininet.node.OVSSwitch ) and
isinstance( node2, mininet.node.OVSSwitch ) ):
self.isPatchLink = True
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
+5 -6
View File
@@ -160,7 +160,7 @@ def makeListCompatible( fn ):
"Generated function. Closure-ish."
if len( args ) == 1:
return fn( *args )
args = ' '.join( str( arg ) for arg in args )
args = ' '.join( [ str( arg ) for arg in args ] )
return fn( args )
# Fix newfn's name and docstring
@@ -168,10 +168,9 @@ def makeListCompatible( fn ):
setattr( newfn, '__doc__', fn.__doc__ )
return newfn
_loggers = lg.info, lg.output, lg.warn, lg.error, lg.debug
_loggers = tuple( makeListCompatible( logger )
for logger in _loggers )
lg.info, lg.output, lg.warn, lg.error, lg.debug = _loggers
info, output, warn, error, debug = _loggers
info, output, warn, error, debug = (
lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
makeListCompatible( f ) for f in
lg.info, lg.output, lg.warn, lg.error, lg.debug ]
setLogLevel = lg.setLogLevel
+13 -68
View File
@@ -89,7 +89,7 @@ method may be called to shut down the network.
import os
import re
import select
from signal import SIGKILL
import signal
import random
from time import sleep
@@ -108,7 +108,7 @@ from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.0d1"
VERSION = "2.2.2"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -144,9 +144,7 @@ class Mininet( object ):
self.intf = intf
self.ipBase = ipBase
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
hostIP = ( 0xffffffff >> self.prefixLen ) & self.ipBaseNum
# Start for address allocation
self.nextIP = hostIP if hostIP > 0 else 1
self.nextIP = 1 # start for address allocation
self.inNamespace = inNamespace
self.xterms = xterms
self.cleanup = cleanup
@@ -228,24 +226,6 @@ class Mininet( object ):
self.nameToNode[ name ] = h
return h
def delNode( self, node, nodes=None):
"""Delete node
node: node to delete
nodes: optional list to delete from (e.g. self.hosts)"""
if nodes is None:
nodes = ( self.hosts if node in self.hosts else
( self.switches if node in self.switches else
( self.controllers if node in self.controllers else
[] ) ) )
node.stop( deleteIntfs=True )
node.terminate()
nodes.remove( node )
del self.nameToNode[ node.name ]
def delHost( self, host ):
"Delete a host"
self.delNode( host, nodes=self.hosts )
def addSwitch( self, name, cls=None, **params ):
"""Add switch.
name: name of switch to add
@@ -264,10 +244,6 @@ class Mininet( object ):
self.nameToNode[ name ] = sw
return sw
def delSwitch( self, switch ):
"Delete a switch"
self.delNode( switch, nodes=self.switches )
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
@@ -289,12 +265,6 @@ class Mininet( object ):
self.nameToNode[ name ] = controller_new
return controller_new
def delController( self, controller ):
"""Delete a controller
Warning - does not reconfigure switches, so they
may still attempt to connect to it!"""
self.delNode( controller )
def addNAT( self, name='nat0', connect=True, inNamespace=False,
**params):
"""Add a NAT to the Mininet network
@@ -333,13 +303,9 @@ class Mininet( object ):
# Even more convenient syntax for node lookup and iteration
def __getitem__( self, key ):
"net[ name ] operator: Return node with given name"
"""net [ name ] operator: Return node(s) with given name(s)"""
return self.nameToNode[ key ]
def __delitem__( self, key ):
"del net[ name ] operator - delete node with given name"
self.delNode( self.nameToNode[ key ] )
def __iter__( self ):
"return iterator over node names"
for node in chain( self.hosts, self.switches, self.controllers ):
@@ -401,30 +367,6 @@ class Mininet( object ):
self.links.append( link )
return link
def delLink( self, link ):
"Remove a link from this network"
link.delete()
self.links.remove( link )
def linksBetween( self, node1, node2 ):
"Return Links between node1 and node2"
return [ link for link in self.links
if ( node1, node2 ) in (
( link.intf1.node, link.intf2.node ),
( link.intf2.node, link.intf1.node ) ) ]
def delLinkBetween( self, node1, node2, index=0, allLinks=False ):
"""Delete link(s) between node1 and node2
index: index of link to delete if multiple links (0)
allLinks: ignore index and delete all such links (False)
returns: deleted link(s)"""
links = self.linksBetween( node1, node2 )
if not allLinks:
links = [ links[ index ] ]
for link in links:
self.delLink( link )
return links
def configHosts( self ):
"Configure a set of hosts."
for host in self.hosts:
@@ -524,7 +466,7 @@ class Mininet( object ):
def stopXterms( self ):
"Kill each xterm."
for term in self.terms:
term.send_signal( SIGKILL )
os.kill( term.pid, signal.SIGKILL )
cleanUpScreens()
def staticArp( self ):
@@ -634,7 +576,7 @@ class Mininet( object ):
# Check for downed link
if 'connect: Network is unreachable' in pingOutput:
return 1, 0
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -696,7 +638,7 @@ class Mininet( object ):
m = re.search( r, pingOutput )
if m is not None:
return errorTuple
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
r = r'(\d+) packets transmitted, (\d+) received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -827,7 +769,7 @@ class Mininet( object ):
debug( 'Client output: %s\n' % cliout )
servout = ''
# We want the last *b/sec from the iperf server output
# for TCP, there are two of them because of waitListening
# 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 )
@@ -846,6 +788,7 @@ class Mininet( object ):
duration: test duration in seconds (integer)
returns a single list of measured CPU fractions as floats.
"""
cores = int( quietRun( 'nproc' ) )
pct = cpu * 100
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
hosts = self.hosts
@@ -897,8 +840,10 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
src = self.nameToNode[ src ]
dst = self.nameToNode[ dst ]
if isinstance( src, basestring ):
src = self.nameToNode[ src ]
if isinstance( dst, basestring ):
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
+40 -116
View File
@@ -74,11 +74,10 @@ class Node( object ):
portBase = 0 # Nodes always start with eth0/port0, even in OF 1.0
def __init__( self, name, **params ):
def __init__( self, name, inNamespace=True, **params ):
"""name: name of node
ns: private namespaces to use ['net','mnt']
inNamespace: in network namespace?
privateDirs: list of private directory strings or tuples
overlayDirs: list of overlay directory strings or tuples
params: Node parameters (see config() for details)"""
# Make sure class actually works
@@ -86,14 +85,7 @@ class Node( object ):
self.name = params.get( 'name', name )
self.privateDirs = params.get( 'privateDirs', [] )
self.overlayDirs = params.get( 'overlayDirs', [] )
# Support old inNamespace param
self.ns = params.get( 'ns', ( 'net', 'mnt' ) )
inNamespace = params.get( 'inNamespace', True )
if not inNamespace:
self.ns = []
self.inNamespace = 'net' in self.ns
self.inNamespace = params.get( 'inNamespace', inNamespace )
# Stash configuration parameters for future reference
self.params = params
@@ -110,9 +102,8 @@ class Node( object ):
self.waiting = False
self.readbuf = ''
# Start command interpreter shell and mount any local dirs
# Start command interpreter shell
self.startShell()
self.mountOverlayDirs()
self.mountPrivateDirs()
# File descriptor to node mapping support
@@ -129,21 +120,17 @@ class Node( object ):
node = cls.outToNode.get( fd )
return node or cls.inToNode.get( fd )
_marker = re.compile( chr( 1 ) + r'(\d+)\r\n' )
# Command support via shell process in namespace
def startShell( self, mnopts=None ):
"Start a shell process for running commands"
if self.shell:
error( "%s: shell is already running\n" % self.name )
return
# mnexec: (c)lose descriptors
# (p)rint pid, and run in (n)etwork and (m)ount namespace
opts = '-cdp' if mnopts is None else mnopts
# Handle additional namespaces if specified
nsmap = { 'pid': 'P', 'mnt': 'm', 'net': 'n', 'uts': 'u' }
chars = [ nsmap.get( ns, '' ) for ns in self.ns ]
opts += ''.join( chars )
# mnexec: (c)lose descriptors, (d)etach from tty,
# (p)rint pid, and run in (n)amespace
opts = '-cd' if mnopts is None else mnopts
if self.inNamespace:
opts += 'n'
# bash -i: force interactive
# -s: pass $* to shell, and make process easy to find in ps
# prompt is set to sentinel chr( 127 )
@@ -170,19 +157,17 @@ class Node( object ):
self.lastPid = None
self.readbuf = ''
# Wait for prompt
self.waiting = True
self.waitOutput()
if 'P' in opts:
assert self.lastPid is not None
self.pid = self.lastPid
while True:
data = self.read( 1024 )
if data[ -1 ] == chr( 127 ):
break
self.pollOut.poll()
self.waiting = False
# +m: disable job control notification
initcmd = 'unset HISTFILE; stty -echo; set +m'
if 'uts' in self.ns:
initcmd += '; hostname ' + self.name
self.cmd( initcmd )
self.cmd( 'unset HISTFILE; stty -echo; set +m' )
def mountPrivateDirs( self ):
"Mount private directories"
"mount private directories"
# Avoid expanding a string into a list of chars
assert not isinstance( self.privateDirs, basestring )
for directory in self.privateDirs:
@@ -200,61 +185,13 @@ class Node( object ):
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
def unmountPrivateDirs( self ):
"Unmount private and overlay directories"
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
self.cmd( 'umount ', directory[ 0 ] )
else:
self.cmd( 'umount ', directory )
# XXX We should make overlayDirs as consistent as possible
# with privateDirs.
def _overlayFrom( self, entry ):
"Helper function: return mountpaint, overlay, tmpfs from entry"
if type( entry ) is str:
# '/mountpoint'
mountpoint, overlay = entry, None
elif len( entry ) is 1:
# [ '/mountpoint' ]
mountpoint, overlay = entry[ 0 ], None
else:
# [ '/mountpoint', '/overlay' ]
mountpoint, overlay = entry
tmpfs = None if overlay else '/tmp/%s/%s' % ( self, mountpoint )
return mountpoint, overlay, tmpfs
def mountOverlayDirs( self ):
"""Mount overlay directories. Overlay directories are similar
to private directories except they are copy-on-write copies
of directories in the host file system.
overlayDirs is of the form ((mountpoint,overlaydir), ...)
much like privateDirs. If overlaydir doesn't exist, we
mount a tmpfs at the specified mount point."""
# Avoid expanding a string into a list of chars
assert not isinstance( self.overlayDirs, basestring )
for entry in self.overlayDirs:
mountpoint, overlay, tmpfs = self._overlayFrom( entry )
# Create tmpfs if overlay dir is not specified
if not overlay:
overlay = tmpfs
self.cmd( 'mkdir -p', overlay )
self.cmd( 'mount -t tmpfs tmpfs', overlay )
# Mount overlay dir at mount point
self.cmd( ( 'mount -t overlayfs -o upperdir=%s,lowerdir=%s'
' overlayfs %s' ) % ( overlay, mountpoint, mountpoint ) )
def unmountOverlayDirs( self ):
"Unmount overlay directories"
for entry in self.overlayDirs:
mountpoint, overlay, tmpfs = self._overlayFrom( entry )
# Unfortunately these umounts can fail if the mount point
# is in use, possibly leaving tmpfs garbage in the root
# mount namespace / file system
self.cmd( 'umount', mountpoint )
if not overlay:
self.cmd( 'umount', tmpfs )
def _popen( self, cmd, **params ):
"""Internal method: spawn and return a process
cmd: command to run (list)
@@ -308,10 +245,9 @@ class Node( object ):
def terminate( self ):
"Send kill signal to Node and clean up after it."
self.unmountPrivateDirs()
self.unmountOverlayDirs()
if self.shell:
if self.shell.poll() is None:
os.killpg( self.pid, signal.SIGHUP )
os.killpg( self.shell.pid, signal.SIGHUP )
self.cleanup()
def stop( self, deleteIntfs=False ):
@@ -358,10 +294,10 @@ class Node( object ):
self.lastPid = None
self.waiting = True
def sendInt( self, signal=signal.SIGINT ):
def sendInt( self, intr=chr( 3 ) ):
"Interrupt running command."
debug( "sending signal %d to pgrp %d" % ( signal, self.pid ) )
os.killpg( self.pid, signal )
debug( 'sendInt: writing chr(%d)\n' % ord( intr ) )
self.write( intr )
def monitor( self, timeoutms=None, findPid=True ):
"""Monitor and return the output of a command.
@@ -374,18 +310,18 @@ class Node( object ):
data = self.read( 1024 )
pidre = r'\[\d+\] \d+\r\n'
# Look for PID
marker = chr( 1 ) + r'\d+\r\n'
if findPid and chr( 1 ) in data:
# suppress the job and PID of a backgrounded command
if re.findall( pidre, data ):
data = re.sub( pidre, '', data )
# Marker can be read in chunks; continue until all of it is read
while True:
markers = self._marker.findall( data )
if markers:
self.lastPid = int( markers[ -1 ] )
data = self._marker.sub( '', data )
break
while not re.findall( marker, data ):
data += self.read( 1024 )
markers = re.findall( marker, data )
if markers:
self.lastPid = int( markers[ 0 ][ 1: ] )
data = re.sub( marker, '', data )
# Look for sentinel/EOF
if len( data ) > 0 and data[ -1 ] == chr( 127 ):
self.waiting = False
@@ -451,7 +387,6 @@ class Node( object ):
# Shell requires a string, not a list!
if defaults.get( 'shell', False ):
cmd = ' '.join( cmd )
debug( cmd, defaults )
popen = self._popen( cmd, **defaults )
return popen
@@ -496,15 +431,6 @@ class Node( object ):
debug( 'moving', intf, 'into namespace for', self.name, '\n' )
moveIntfFn( intf.name, self )
def delIntf( self, intf ):
"""Remove interface from Node's known interfaces
Note: to fully delete interface, call intf.delete() instead"""
port = self.ports.get( intf )
if port is not None:
del self.intfs[ port ]
del self.ports[ intf ]
del self.nameToIntf[ intf.name ]
def defaultIntf( self ):
"Return interface for lowest port"
ports = self.intfs.keys()
@@ -707,8 +633,6 @@ class Node( object ):
def setup( cls ):
"Make sure our class dependencies are available"
pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
if '-m:' not in quietRun( 'mnexec -h' ):
raise Exception( 'Please update mnexec (e.g. make install)' )
class Host( Node ):
"A host is simply a Node"
@@ -769,8 +693,8 @@ class CPULimitedHost( Host ):
args: Popen() args, single list, or string
kwargs: Popen() keyword args"""
# Tell mnexec to execute command in our cgroup
mncmd = kwargs.pop( 'mncmd', [ 'mnexec', '-g', self.name,
'-da', str( self.pid ) ] )
mncmd = [ 'mnexec', '-g', self.name,
'-da', str( self.pid ) ]
# if our cgroup is not given any cpu time,
# we cannot assign the RR Scheduler.
if self.sched == 'rt':
@@ -939,7 +863,7 @@ class Switch( Node ):
self.dpid = self.defaultDpid( dpid )
self.opts = opts
self.listenPort = listenPort
if 'net' not in self.ns:
if not self.inNamespace:
self.controlIntf = Intf( 'lo', self, port=0 )
def defaultDpid( self, dpid=None ):
@@ -1145,9 +1069,10 @@ class OVSSwitch( Switch ):
version = quietRun( 'ovs-vsctl --version' )
cls.OVSVersion = findall( r'\d+\.\d+', version )[ 0 ]
def isOldOVS( self ):
@classmethod
def isOldOVS( cls ):
"Is OVS ersion < 1.10?"
return ( StrictVersion( self.OVSVersion ) <
return ( StrictVersion( cls.OVSVersion ) <
StrictVersion( '1.10' ) )
def dpctl( self, *args ):
@@ -1226,14 +1151,13 @@ class OVSSwitch( Switch ):
opts += ' protocols=%s' % self.protocols
if self.stp and self.failMode == 'standalone':
opts += ' stp_enable=true'
opts += ' other-config:dp-desc=%s' % self.name
return opts
def start( self, controllers ):
"Start up a new OVS OpenFlow switch using ovs-vsctl"
if self.inNamespace:
raise Exception(
'OVS kernel switch does not work in a network namespace' )
'OVS kernel switch does not work in a namespace' )
int( self.dpid, 16 ) # DPID must be a hex string
# Command to add interfaces
intfs = ''.join( ' -- add-port %s %s' % ( self, intf ) +
@@ -1499,16 +1423,16 @@ class Controller( Node ):
class OVSController( Controller ):
"Open vSwitch controller"
def __init__( self, name, **kwargs ):
kwargs.setdefault( 'command', self.isAvailable() or
'ovs-controller' )
Controller.__init__( self, name, **kwargs )
def __init__( self, name, command='ovs-controller', **kwargs ):
if quietRun( 'which test-controller' ):
command = 'test-controller'
Controller.__init__( self, name, command=command, **kwargs )
@classmethod
def isAvailable( cls ):
return ( quietRun( 'which ovs-controller' ) or
quietRun( 'which test-controller' ) or
quietRun( 'which ovs-testcontroller' ) ).strip()
quietRun( 'which ovs-testcontroller' ) )
class NOX( Controller ):
"Controller to run a NOX application."
+1 -86
View File
@@ -4,14 +4,11 @@ Node Library for Mininet
This contains additional Node types which you may find to be useful.
"""
from mininet.node import Node, Host, Switch
from mininet.node import Node, Switch
from mininet.log import info, warn
from mininet.moduledeps import pathCheck
from mininet.util import quietRun
import re
from tempfile import NamedTemporaryFile
class LinuxBridge( Switch ):
"Linux Bridge (with optional spanning tree)"
@@ -146,85 +143,3 @@ class NAT( Node ):
# Put the forwarding state back to what it was
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
super( NAT, self ).terminate()
class Server( Host ):
"Run sshd in a net/mnt/pid/uts namespace, with private /etc/hosts"
ns = [ 'net', 'mnt', 'pid', 'uts' ]
overlayDirs = [ '/etc', '/var/run', '/var/log' ]
privateDirs = [ '/var/run/sshd', ]
def __init__( self, *args, **kwargs ):
"""Add overlay dirs and private dirs, and change permissions
ssh: run sshd? (True)"""
kwargs.setdefault( 'inNamespace', True )
kwargs.setdefault( 'ns', self.ns )
kwargs.setdefault( 'privateDirs', self.privateDirs )
kwargs.setdefault( 'overlayDirs', self.overlayDirs )
kwargs.setdefault( 'ssh', True )
super( Server, self ).__init__( *args, **kwargs )
# Change permissions, mainly for ssh
for pdir in self.privateDirs:
self.cmd( 'chown root:root', pdir )
self.cmd( 'chmod 755', pdir )
@staticmethod
def updateHostsFiles( servers, tmpdirs=[ '/tmp', '/var/tmp' ] ):
"""Update local hosts files on a list of servers
servers: list of servers
tmpdir: tmp dir shared between mn and servers"""
# For large configurations it will be more efficient
# to use a DNS server
hosts = ( '# Mininet hosts file\n'
'127.0.0.1 localhost %s\n' +
''.join( '%s %s\n' % ( t.IP(), t )
for t in servers ) )
for s in servers:
dirs = ( getattr( s, 'overlayDirs', [] ) +
getattr( s, 'privateDirs', [] ) )
if '/etc' in dirs:
tmpdirs = [ d for d in tmpdirs if d not in dirs ]
if tmpdirs:
with NamedTemporaryFile( dir=tmpdirs[ 0 ] ) as tmpfile:
tmpfile.write( hosts % s )
tmpfile.flush()
s.cmd( 'cp', tmpfile.name, '/etc/hosts' )
else:
warn( 'not updating hosts file on %s\n' % s )
def service( self, cmd ):
"""Start or stop a service
usage: service( 'ssh stop' )"""
self.cmd( '/etc/init.d/%s' % cmd )
def motd( self ):
"Return login message as a string"
return 'Welcome to Mininet host %s at %s' % ( self, self.IP() )
def startSSH( self, motdPath='/var/run/motd.dynamic' ):
"Update motd, Clear out utmp/wtmp/btmp, and start sshd"
# Note: /var/run and /var/log must be overlays!
assert ( '/var/run' in ( self.overlayDirs + self.privateDirs ) and
'/var/log' in ( self.overlayDirs + self.privateDirs ) )
self.cmd( "echo '%s' > %s" % ( self.motd(), motdPath ) )
self.cmd( 'truncate -s0 /var/run/utmp /var/log/wtmp* /var/log/btmp*' )
# sshd.pid should really be in /var/run/sshd instead of /var/run
self.cmd( 'rm /var/run/sshd.pid' )
self.cmd( '/etc/init.d/ssh start' )
def config( self, **kwargs ):
"""Configure/start sshd and other stuff
ssh: start sshd? (True )"""
super( Server, self ).config( **kwargs )
self.ssh = kwargs.get( 'ssh' )
if self.ssh:
self.startSSH()
if 'uts' in self.ns:
self.cmd( 'hostname', self )
def terminate( self, *args, **kwargs ):
"Shut down services and terminate server"
if self.ssh:
self.service( 'ssh stop' )
super( Server, self ).terminate( *args, **kwargs )
+13 -64
View File
@@ -5,43 +5,13 @@ Utility functions to run a terminal (connected via socat(1)) on each host.
Requires socat(1) and xterm(1).
Optionally uses gnome-terminal.
"""
from os import environ
from mininet.log import error
from mininet.util import quietRun, errRun
from os import environ, getpid, path, setsid
from subprocess import Popen, PIPE, STDOUT
from tempfile import NamedTemporaryFile
def getAuthX11( display ):
"Return X11 credentials for display"
host, screen = display.split( ':' )
host = host.split( '/' )[ 0 ]
hostname = quietRun( 'hostname' ).strip()
# First, try hostname:display
if host == 'localhost':
host = hostname
result = quietRun( 'xauth list %s:%s' % ( host, screen ) )
# Otherwise, try hostname/unix:display
if not result:
result = quietRun( 'xauth list %s/unix:%s' % ( host, screen ) )
items = result.strip().split()
if len( items ) != 3:
raise Exception( "getAuthX11: could not fetch credentials for " +
display )
return items
# This is tricky with uts and pid namespaces
# For uts namespaces, we create and use a private $XAUTHORITY
# and add credentials for the node's hostname.
# To enable pid namespaces to work, we proxy the X11
# socket twice using socat - first with a shared socket in /tmp, and
# second with a TCP listener in the host network namespace.
# Note that this will fail if /tmp is not shared - we should
# probably think about this some more. We could potentially
# specify a globally shared directory somehow if /tmp is
# private.
def tunnelX11( node, display=None ):
def tunnelX11( node, display=None):
"""Create an X11 tunnel from node:6000 to the root host
display: display on root host (optional)
returns: node $DISPLAY, Popen object for tunnel"""
@@ -58,30 +28,11 @@ def tunnelX11( node, display=None ):
quietRun( 'xhost +si:localuser:root' )
return display, None
else:
hostname = quietRun( 'hostname' ).strip()
port = 6000 + int( float( screen ) )
if 'uts' in node.ns and ( hostname in display or
'localhost' in display ):
# Use private xauth file, and add credentials
# for this hostname
if not hasattr( node, 'xauthFile' ):
node.xauthFile = NamedTemporaryFile()
_display, proto, cookie = getAuthX11( display )
creds = '%s %s %s' % ( '%s/unix:%s' % ( node.name, screen ),
proto, cookie )
node.cmd( 'export XAUTHORITY=' + node.xauthFile.name )
node.cmd( 'xauth -f $XAUTHORITY add ' + creds )
# Create a shared unix socket in /tmp
# This can conflict if we are running nested Mininet
# in a pid namespace, and it will also fail if /tmp is not
# shared
socket = '/tmp/mininet.x11.%d' % getpid()
if not hasattr( tunnelX11, 'socket' ):
cmd = ( 'socat unix-listen:%s,fork tcp:localhost:%d' %
( socket, port ) ).split()
tunnelX11.socket = Popen( cmd )
# Create a tunnel for the TCP connection
cmd = 'socat tcp-listen:%d,fork,reuseaddr unix:%s' % ( port, socket )
port = 6000 + int( float( screen ) )
connection = r'TCP\:%s\:%s' % ( host, port )
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
@@ -94,8 +45,8 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
if not node.inNamespace:
title += ' (root)'
cmds = {
'xterm': [ 'xterm', '-title', title ],
'gterm': [ 'gnome-terminal', '--title', title ]
'xterm': [ 'xterm', '-title', title, '-display' ],
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
@@ -103,10 +54,8 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
display, tunnel = tunnelX11( node, display )
if display is None:
return []
env = [ 'env', 'TERM=ansi', 'DISPLAY=%s' % display ]
if hasattr( node, 'xauthFile' ):
env += [ 'XAUTHORITY=%s' % node.xauthFile.name ]
term = node.popen( env + cmds[ term ] + [ '-e', cmd ] )
term = node.popen( cmds[ term ] +
[ display, '-e', 'env TERM=ansi %s' % cmd ] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
@@ -119,7 +68,7 @@ def runX11( node, cmd ):
def cleanUpScreens():
"Remove moldy socat X11 tunnels."
errRun( "pkill -9 -f socat.*mininet" )
errRun( "pkill -9 -f mnexec.*socat" )
def makeTerms( nodes, title='Node', term='xterm' ):
"""Create terminals.
+1 -3
View File
@@ -21,9 +21,7 @@ def runTests( testDir, verbosity=1 ):
# discover all tests in testDir
testSuite = defaultTestLoader.discover( testDir )
# run tests
success = ( TextTestRunner( verbosity=verbosity )
.run( testSuite ).wasSuccessful() )
sys.exit( 0 if success else 1 )
TextTestRunner( verbosity=verbosity ).run( testSuite )
if __name__ == '__main__':
setLogLevel( 'warning' )
+2 -2
View File
@@ -214,8 +214,8 @@ class testWalkthrough( unittest.TestCase ):
p.sendline( 'iperf' )
p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
p.expect( self.prompt )
# test delay
p.sendline( 'h1 ping -c 4 h2' )
+10 -33
View File
@@ -45,11 +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, wrap=True ):
"""x: number of switches per row
y: number of rows
n: number of hosts per switch
wrap: torus rather than grid? (True)"""
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"""
if x < 3 or y < 3:
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
@@ -73,33 +72,11 @@ class TorusTopo( Topo ):
self.addLink( host, switch )
# Connect switches
for i in range( 0, x ):
for j in range( 0, y):
sw = switches[ i, j ]
right = switches[ ( i + 1 ) % x, j ]
down = switches[ i, ( j + 1 ) % y ]
if wrap or i + 1 < x:
self.addLink( sw, right )
if wrap or j + 1 < y:
self.addLink( sw, down )
class GridTopo( TorusTopo ):
"""2-D Grid topology
WARNING: this topology has LOOPS and WILL NOT WORK
with the default controller or any Ethernet bridge
without STP turned on! It can be used with STP, e.g.:
# mn --topo grid,3,3 --switch lxbr,stp=1 --test pingall"""
def build( self, x, y, n=1, wrap=False ):
"""x: number of switches per row
y: number of rows
n: number of hosts per switch
wrap: torus rather than grid (False)"""
super( GridTopo, self ).build( x, y, n, wrap )
topos = { 'tree': TreeTopo,
'torus': TorusTopo,
'grid': GridTopo }
for j in range( 0, y ):
sw1 = switches[ i, j ]
sw2 = switches[ i, ( j + 1 ) % y ]
sw3 = switches[ ( i + 1 ) % x, j ]
self.addLink( sw1, sw2 )
self.addLink( sw1, sw3 )
# pylint: enable=arguments-differ
+17 -40
View File
@@ -1,6 +1,5 @@
"Utility functions for Mininet."
from mininet.log import output, info, error, warn, debug
from time import sleep
@@ -178,19 +177,18 @@ def makeIntfPair( intf1, intf2, addr1=None, addr2=None, node1=None, node2=None,
runCmd( 'ip link del ' + intf1 )
runCmd2( 'ip link del ' + intf2 )
# Create new pair
ns1 = 1 if not node1 else node1.pid
ns2 = 1 if not node2 else node2.pid
netns = 1 if not node2 else node2.pid
if addr1 is None and addr2 is None:
cmdOutput = runCmd( 'ip link add name %s netns %s '
cmdOutput = runCmd( 'ip link add name %s '
'type veth peer name %s '
'netns %s' % ( intf1, ns1, intf2, ns2 ) )
'netns %s' % ( intf1, intf2, netns ) )
else:
cmdOutput = runCmd( 'ip link add name %s '
'address %s netns %s '
'address %s '
'type veth peer name %s '
'address %s '
'netns %s' %
( intf1, addr1, ns1, intf2, addr2, ns2 ) )
( intf1, addr1, intf2, addr2, netns ) )
if cmdOutput:
raise Exception( "Error creating interface pair (%s,%s): %s " %
( intf1, intf2, cmdOutput ) )
@@ -322,7 +320,7 @@ def ipParse( ip ):
"Parse an IP address and return an unsigned int."
args = [ int( arg ) for arg in ip.split( '.' ) ]
while len(args) < 4:
args.insert( len(args) - 1, 0 )
args.append( 0 )
return ipNum( *args )
def netParse( ipstr ):
@@ -509,42 +507,21 @@ def custom( cls, **params ):
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
return customized
def parseArgs( argstr ):
"""Parse argument string
returns args, kwargs"""
# One step at a time: support param=[a,b,c]
paramre = r'\[[^\]]*\]|[^,\[\]]+'
paramre = r'\w+=\[[^\]]*\]|\w+\[^,\[\]]+' + paramre
params = re.findall( paramre, argstr )
# Parse lists
for i, arg in enumerate( params ):
if arg.startswith( '[' ) and arg.endswith( ']' ):
arg = arg[ 1 : -1 ]
print "recurse on", arg
params[ i ] = parseArgs( args )
def splitArgs( argstr ):
"""Split argument string into usable python arguments
argstr: argument string with format fn,arg2,kw1=arg3...
returns: fn, args, kwargs"""
split = argstr.split( ',' )
fn = split[ 0 ]
params = split[ 1: ]
# Convert int and float args; removes the need for function
# to be flexible with input arg formats.
args = [ makeNumeric( s ) for s in params if '=' not in s ]
kwargs = {}
for s in [ p for p in params if '=' in p ]:
key, arg = s.split( '=', 1 )
if arg.startswith( '[' ) and arg.endswith( ']' ):
arg = arg[ 1 : -1 ]
print 'recurse on', arg
arg = parseArgs( arg )[ 0 ]
else:
arg = makeNumeric( arg )
kwargs[ key ] = arg
return args, kwargs
def splitArgs( argstr ):
"""Split argument string into usable python arguments
argstr: argument string with format fn,arg2,kw1=arg3...
returns: fn, args, kwargs"""
args, kwargs = parseArgs( argstr )
return args[ 0 ], args[ 1: ], kwargs
key, val = s.split( '=', 1 )
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
def customClass( classes, argStr ):
"""Return customized class based on argStr
@@ -610,7 +587,7 @@ def ensureRoot():
Probably we should only sudo when needed as per Big Switch's patch.
"""
if os.getuid() != 0:
error( '*** Mininet must run as root.\n' )
print "*** Mininet must run as root."
exit( 1 )
return
+108 -196
View File
@@ -5,9 +5,9 @@
*
* - closing all file descriptors except stdin/out/error
* - detaching from a controlling tty using setsid
* - running in network and other namespaces
* - running in network and mount namespaces
* - printing out the pid of a process so we can identify it later
* - attaching to namespace(s) and cgroup
* - attaching to a namespace and cgroup
* - setting RT scheduling
*
* Partially based on public domain setsid(1)
@@ -24,29 +24,21 @@
#include <sched.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#if !defined(VERSION)
#define VERSION "(devel)"
#endif
void usage(char *name)
{
printf("Execution utility for Mininet\n\n"
"Usage: %s [-cdmnPpu] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Options:\n"
" -c: close all file descriptors except stdin/out/error\n"
" -d: detach from tty by calling setsid()\n"
" -m: run in a new mount namespace\n"
" -n: run in a new network namespace\n"
" -P: run in a new pid namespace (implies -m)\n"
" -u: run in a new UTS (ipc, hostname) namespace\n"
" -n: run in new network and mount namespaces\n"
" -p: print ^A + pid\n"
" -a pid: attach to pid's namespaces\n"
" -a pid: attach to pid's network and mount namespaces\n"
" -g group: add to cgroup\n"
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
" -v: print version\n",
@@ -54,13 +46,10 @@ void usage(char *name)
}
#if !defined(setns)
int setns(int fd, int nstype)
{
return syscall(__NR_setns, fd, nstype);
}
#endif
/* Validate alphanumeric path foo1/bar2/baz */
void validate(char *path)
@@ -74,7 +63,6 @@ void validate(char *path)
}
}
/* Add our pid to cgroup */
void cgroup(char *gname)
{
@@ -104,193 +92,117 @@ void cgroup(char *gname)
}
}
/* Attach to ns 'name' if present; return 1 if pidns */
int attachns( pid_t pid, char *name ) {
char path[PATH_MAX];
int nsid, err;
int pidns = 0;
snprintf(path, PATH_MAX, "/proc/%d/ns/%s", pid, name) ;
if ((nsid = open(path, O_RDONLY)) < 0)
return nsid;
if (strcmp(name, "pid") == 0) {
struct stat buf1, buf2;
int stat1 = stat(path, &buf1);
int stat2 = stat("/proc/self/ns/pid", &buf2);
/* Don't reattach to the same pid ns */
if (stat1 == 0 && stat2 == 0 &&
buf1.st_dev == buf2.st_dev &&
buf1.st_ino == buf2.st_ino)
return 0;
pidns = 1;
}
if ((err = setns(nsid, 0)) < 0) {
perror("setns");
return err;
}
return pidns;
}
/* Attach to pid's namespaces - returns 1 if pidns */
int attach(int pid) {
char *cwd = get_current_dir_name();
char path[PATH_MAX];
int pidns = 0;
attachns(pid, "net");
attachns(pid, "uts");
if (attachns(pid, "pid") == 1)
pidns = 1;
if (attachns(pid, "mnt") != 0) {
/* Plan B: chroot into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
}
}
/* chdir to correct working directory */
if (chdir(cwd) != 0)
perror(cwd);
return pidns;
}
int main(int argc, char *argv[])
{
int c;
int fd;
char path[PATH_MAX];
int nsid;
int pid;
char *cwd = get_current_dir_name();
/* Argument flags */
int flags = 0;
int closefds = 0;
int attachpid = 0;
char *cgrouparg = NULL;
int detachtty = 0;
int printpid = 0;
int rtprio = 0;
int pidns = 0;
int dofork = 0;
while ((c = getopt(argc, argv, "+cdmnPpa:g:r:uvh")) != -1)
static struct sched_param sp;
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
switch(c) {
case 'c': closefds = 1; break;
case 'd': detachtty = 1; break;
case 'm': flags |= CLONE_NEWNS; break;
case 'n': flags |= CLONE_NEWNET; break;
case 'p': printpid = 1; break;
case 'P': flags |= CLONE_NEWPID | CLONE_NEWNS; break;
case 'a': attachpid = atoi(optarg);break;
case 'g': cgrouparg = optarg ; break;
case 'r': rtprio = atoi(optarg); break;
case 'u': flags |= CLONE_NEWUTS; break;
case 'v': printf("%s\n", VERSION); exit(0);
case 'h': usage(argv[0]); exit(0);
default: usage(argv[0]); exit(1);
}
if (closefds) {
/* close file descriptors except stdin/out/error */
int fd;
for (fd = getdtablesize(); fd > 2; fd--) close(fd);
}
if (attachpid)
/* Attach to existing namespace(s) */
pidns = attach(attachpid);
else {
/* Create new namespace(s) */
if (unshare(flags) == -1) {
perror("unshare");
return 1;
}
}
if (flags & CLONE_NEWPID || pidns)
/* pidns requires fork/wait; child will be pid 1 */
dofork = 1;
if (detachtty && getpgrp() == getpid())
/* Fork so that we will no longer be pgroup leader */
dofork = 1;
else
/* We don't need a new session, only a new pgroup */
detachtty = 0;
if (detachtty)
/* Create a new session - and by implication a new process group */
setsid();
else
/* Use a new process group (in the current session)
* so Mininet can use killpg without unintended effects */
setpgid(0, 0);
if (dofork) {
/* Fork and then wait if necessary */
pid_t pid = fork();
switch(pid) {
int status;
case -1:
perror("fork");
case 'c':
/* close file descriptors except stdin/out/error */
for (fd = getdtablesize(); fd > 2; fd--)
close(fd);
break;
case 'd':
/* detach from tty */
if (getpgrp() == getpid()) {
switch(fork()) {
case -1:
perror("fork");
return 1;
case 0: /* child */
break;
default: /* parent */
return 0;
}
}
setsid();
break;
case 'n':
/* run in network and mount namespaces */
if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
perror("unshare");
return 1;
case 0:
/* Child continues below */
break;
default:
/* We print the *child pid* in *parent's pidns* if needed */
if (printpid) {
printf("\001%d\n", pid);
fflush(stdout);
}
/* 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");
return 1;
}
break;
case 'p':
/* print pid */
printf("\001%d\n", getpid());
fflush(stdout);
break;
case 'a':
/* Attach to pid's network namespace and mount namespace */
pid = atoi(optarg);
sprintf(path, "/proc/%d/ns/net", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0) {
perror(path);
return 1;
}
if (setns(nsid, 0) != 0) {
perror("setns");
return 1;
}
/* Plan A: call setns() to attach to mount namespace */
sprintf(path, "/proc/%d/ns/mnt", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0 || setns(nsid, 0) != 0) {
/* Plan B: chroot/chdir into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
return 1;
}
/* For pid namespace, we need to fork and wait for child ;-( */
if (flags & CLONE_NEWPID || pidns) {
wait(&status);
}
return 0;
}
/* chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
}
break;
case 'g':
/* Attach to cgroup */
cgroup(optarg);
break;
case 'r':
/* Set RT scheduling priority */
sp.sched_priority = atoi(optarg);
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
perror("sched_setscheduler");
return 1;
}
break;
case 'v':
printf("%s\n", VERSION);
exit(0);
case 'h':
usage(argv[0]);
exit(0);
default:
usage(argv[0]);
exit(1);
}
}
if (printpid && !dofork) {
/* Print child pid if we didn't fork/aren't in a pidns */
assert(!pidns);
printf("\001%d\n", getpid());
fflush(stdout);
}
if (flags & CLONE_NEWPID) {
/* Child remounts /proc for ps */
if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
perror("mountproc");
}
}
/* Attach to cgroup if necessary */
if (cgrouparg) cgroup(cgrouparg);
if (flags & CLONE_NEWNET & CLONE_NEWNS) {
/* Mount sysfs to pick up the new network namespace */
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
perror("mount");
return 1;
}
}
if (rtprio != 0) {
/* Set RT scheduling priority */
static struct sched_param sp;
sp.sched_priority = atoi(optarg);
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
perror("sched_setscheduler");
return 1;
}
}
if (optind < argc) {
execvp(argv[optind], &argv[optind]);
+16 -44
View File
@@ -40,7 +40,6 @@ if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
install='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q install'
remove='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q remove'
pkginst='sudo dpkg -i'
update='sudo apt-get'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install lsb-release
@@ -52,22 +51,11 @@ if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
install='sudo yum -y install'
remove='sudo yum -y erase'
pkginst='sudo rpm -ivh'
update='sudo yum'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install redhat-lsb-core
fi
fi
test -e /etc/SuSE-release && DIST="SUSE Linux"
if [ "$DIST" = "SUSE Linux" ]; then
install='sudo zypper --non-interactive install '
remove='sudo zypper --non-interactive remove '
pkginst='sudo rpm -ivh'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install openSUSE-release
fi
fi
if which lsb_release &> /dev/null; then
DIST=`lsb_release -is`
RELEASE=`lsb_release -rs`
@@ -80,12 +68,8 @@ echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
KERNEL_NAME=`uname -r`
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
# Treat Raspbian as Debian
[ "$DIST" = 'Raspbian' ] && DIST='Debian'
DISTS='Ubuntu|Debian|Fedora|RedHatEnterpriseServer|SUSE LINUX'
if ! echo $DIST | egrep "$DISTS" >/dev/null; then
echo "Install.sh currently only supports $DISTS."
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora|RedHatEnterpriseServer'; then
echo "Install.sh currently only supports Ubuntu, Debian, RedHat and Fedora."
exit 1
fi
@@ -119,7 +103,7 @@ OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
function kernel {
echo "Install Mininet-compatible kernel if necessary"
$update update
sudo apt-get update
if ! $install linux-image-$KERNEL_NAME; then
echo "Could not install linux-image-$KERNEL_NAME"
echo "Skipping - assuming installed kernel is OK."
@@ -145,12 +129,8 @@ function mn_deps {
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8 python-pexpect
elif [ "$DIST" = "SUSE LINUX" ]; then
$install gcc make socat psmisc xterm openssh iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man python-pyflakes python3-pylint python-pep8 python-pexpect
else
$install gcc make socat psmisc xterm ssh iperf iproute2 telnet \
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8 python-pexpect
fi
@@ -180,8 +160,6 @@ function of {
$install autoconf automake libtool make gcc
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install git pkgconfig glibc-devel
elif [ "$DIST" = "SUSE LINUX" ]; then
$install git pkgconfig glibc-devel
else
$install git-core autotools-dev pkg-config libc6-dev
fi
@@ -252,8 +230,6 @@ function install_wireshark {
echo "Installing Wireshark"
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install wireshark wireshark-gnome
elif [ "$DIST" = "SUSE LINUX" ]; then
$install wireshark
else
$install wireshark tshark
fi
@@ -438,19 +414,12 @@ function ivs {
IVS_SRC=$BUILD_DIR/ivs
# Install dependencies
$install gcc make
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install git pkgconfig libnl3-devel libcap-devel openssl-devel
else
$install git-core pkg-config libnl-3-dev libnl-route-3-dev \
libnl-genl-3-dev
fi
$install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
# Install IVS from source
cd $BUILD_DIR
git clone git://github.com/floodlight/ivs $IVS_SRC
git clone git://github.com/floodlight/ivs $IVS_SRC --recursive
cd $IVS_SRC
git submodule update --init
make
sudo make install
}
@@ -462,19 +431,24 @@ function ryu {
# install Ryu dependencies"
$install autoconf automake g++ libtool python make
if [ "$DIST" = "Ubuntu" -o "$DIST" = "Debian" ]; then
$install gcc python-pip python-dev libffi-dev libssl-dev \
libxml2-dev libxslt1-dev zlib1g-dev
$install libxml2 libxslt-dev python-pip python-dev
sudo pip install --upgrade gevent pbr webob routes paramiko \\
oslo.config
fi
# if needed, update python-six
SIX_VER=`pip show six | grep Version | awk '{print $2}'`
if version_ge 1.7.0 $SIX_VER; then
echo "Installing python-six version 1.7.0..."
sudo pip install -I six==1.7.0
fi
# fetch RYU
cd $BUILD_DIR/
git clone git://github.com/osrg/ryu.git ryu
cd ryu
# install ryu
sudo pip install -r tools/pip-requires -r tools/optional-requires \
-r tools/test-requires
sudo python setup.py install
sudo python ./setup.py install
# Add symbolic link to /usr/bin
sudo ln -s ./bin/ryu-manager /usr/local/bin/ryu-manager
@@ -587,8 +561,6 @@ function cbench {
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
elif [ "$DIST" = "SUSE LINUX" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
else
$install libsnmp-dev libpcap-dev libconfig-dev
fi
+8 -1
View File
@@ -33,5 +33,12 @@ if [ -d "$cgroup" ]; then
cg="-g $host"
fi
cmd="exec sudo -E mnexec $cg -a $pid $cmd"
# Check whether host should be running in a chroot dir
rootdir="/var/run/mn/$host/root"
if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
cmd="'cd `pwd`; exec $cmd'"
cmd="chroot $rootdir /bin/bash -c $cmd"
fi
cmd="exec sudo mnexec $cg -a $pid $cmd"
eval $cmd
+1 -2
View File
@@ -40,7 +40,6 @@ Bob Lantz, rlantz@cs.stanford.edu
1/24/2010
"""
from __future__ import print_function
import re, sys
def fixUnderscoreTriplet( match ):
@@ -196,4 +195,4 @@ def convertFromPep8( program ):
return program
if __name__ == '__main__':
print( convertFromPep8( sys.stdin.read() ) )
print convertFromPep8( sys.stdin.read() )
+3 -3
View File
@@ -4,7 +4,7 @@ from subprocess import check_output as co
from sys import exit
# Actually run bin/mn rather than importing via python path
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
version = version.strip()
# Find all Mininet path references
@@ -16,8 +16,8 @@ for line in lines.split( '\n' ):
if line and 'Binary' not in line:
fname, fversion = line.split( ':' )
if version != fversion:
print( "%s: incorrect version '%s' (should be '%s')" % (
fname, fversion, version ) )
print "%s: incorrect version '%s' (should be '%s')" % (
fname, fversion, version )
error = True
if error:
+6 -6
View File
@@ -118,9 +118,9 @@ def log( *args, **kwargs ):
msg = ' '.join( str( arg ) for arg in args )
output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg )
if cr:
print( output )
print output
else:
print( output, )
print output,
# Optionally mirror to LogFile
if type( LogFile ) is file:
if cr:
@@ -208,7 +208,7 @@ def attachNBD( cow, flags='' ):
continue
srun( 'modprobe nbd max-part=64' )
srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
print()
print
return nbd
raise Exception( "Error: could not find unused /dev/nbdX device" )
@@ -1002,7 +1002,7 @@ def parseArgs():
if args.depend:
depend()
if args.list:
print( buildFlavorString() )
print buildFlavorString()
if args.clean:
cleanup()
if args.verbose:
@@ -1023,8 +1023,8 @@ def parseArgs():
Chown = args.chown
for flavor in args.flavor:
if flavor not in isoURLs:
print( "Unknown build flavor:", flavor )
print( buildFlavorString() )
print "Unknown build flavor:", flavor
print buildFlavorString()
break
try:
build( flavor, tests=args.test, pre=args.run, post=args.post,