Compare commits

..
Author SHA1 Message Date
Bob Lantz 9bd75bdfb6 Fix typo 2018-11-02 14:29:18 -07:00
Bob Lantz fd97ab4462 Set hostname and update X11 credentials when needed 2018-05-12 00:17:26 +00:00
Bob Lantz 5ee31199c1 Remove obsolete private root dir check.
mnexec should check and attach to the proper root fs either
using mount namespaces or chroot.
2018-05-12 00:17:26 +00:00
Bob Lantz 2ecc637ff4 Cleanup and reorganization
Creating a new process group in a pidns child doesn't seem to
work, so we create it in the parent. This may have the side
effect of SIGKILL rather than SIGHUP being sent to processes
in a pid ns, but it doesn't seem to work otherwise.

We also try to avoid forking twice.
2018-05-12 00:17:26 +00:00
Bob Lantz 62d1121be7 Load topology dictionary, and add grid topo
Topolib should export topologies the same way custom
files do. We should probably do the same thing for
other classes such as hosts, switches, links, controllers,
etc..
2018-05-12 00:17:26 +00:00
Bob Lantz c522ba2cee Kill self.pid, which will be subprocess for Server 2018-05-12 00:17:26 +00:00
Bob Lantz a598baf943 Fix undefined SIGKILL 2018-05-12 00:17:26 +00:00
Bob Lantz 79aa54096e Raise exception if we don't have a compatible mnexec. 2018-05-12 00:17:26 +00:00
Bob Lantz 5175bc7b9a Avoid forking twice.
Also minor cleanups:
- don't redefine setns() if it's defined
- clarify when we want to make a new session or pgrp
2018-05-12 00:17:26 +00:00
Bob Lantz 8badfe4879 Use send_signal to kill (possibly remote) xterms 2018-05-12 00:17:26 +00:00
Bob Lantz 98bd9ad2f2 Add command line argument for cluster size 2018-05-12 00:17:26 +00:00
Bob Lantz 21506f909c Change sendInt() to kill the process group
Note this will work with pid namespaces.
2018-05-12 00:17:26 +00:00
Bob Lantz 72c1275078 Set self.ns correctly 2018-05-12 00:17:26 +00:00
Bob Lantz e3ac746a39 More changes to use ns param
You can either specify the old inNamespace parameter, in which case
you'll get a net + mnt namespace, or the new ns parameter, which
allows you to pick namespaces explicitly.

inNamespace is set to true if you're in a network namespace.
2018-05-12 00:17:26 +00:00
Bob Lantz a114eb78a0 Fix to use self.ns 2018-05-12 00:17:26 +00:00
Bob Lantz 7924b15331 Change to ns=['net','mnt'] and deprecate inNamespace 2018-05-12 00:17:26 +00:00
Bob Lantz 48131b13b8 Check for /var/log and /var/run in overlay dirs 2018-05-12 00:17:26 +00:00
Bob Lantz 277e4d2e09 Move Server to nodelib and add --host server option 2018-05-12 00:17:25 +00:00
Bob Lantz 81da901b6b Enable control path 2018-05-12 00:16:45 +00:00
Bob Lantz 5916830268 Delete junk at end of file 2018-05-12 00:16:45 +00:00
Bob Lantz 6f00369dff 8 servers + LinuxBridge 2018-05-12 00:16:45 +00:00
Bob Lantz 7c83f036df debug -> info 2018-05-12 00:16:45 +00:00
Bob Lantz b340488fdc Add /etc/openvswitch to private dirs 2018-05-12 00:16:45 +00:00
Bob Lantz fc462e3bdd Specify runCmd, and update controller IP correctly 2018-05-12 00:16:45 +00:00
Bob Lantz 76f3db36a2 Fix ^c on cluster edition; still broken for pid ns 2018-05-12 00:16:45 +00:00
Bob Lantz 160b665d09 still working on this... 2018-05-12 00:16:45 +00:00
Bob Lantz 29f15487ea Revert a bit to node.py from master 2018-05-12 00:14:53 +00:00
Bob Lantz a9c3844830 use runCmd 2018-05-12 00:14:53 +00:00
Bob Lantz eea5d0b602 Allow runCmd 2018-05-12 00:14:53 +00:00
Bob Lantz 1687605cf0 Remove debug print 2018-05-12 00:14:53 +00:00
Bob Lantz 0a5c1f6026 Create new pgrp so that we can use killpg 2018-05-12 00:14:53 +00:00
Bob Lantz c46608710f Remove debug print 2018-05-12 00:14:53 +00:00
Bob Lantz 020f276d10 Respect runCmd in makeIntfPair() 2018-05-12 00:14:53 +00:00
Bob Lantz 43aa659e08 Still working on it. 2018-05-12 00:14:53 +00:00
Bob Lantz 034c16d95a Change to sudo -E to preserve ssh forwarding 2018-05-12 00:14:52 +00:00
Bob Lantz d30f7bd931 Add API and mn support for --host proc,ns=[net,mnt,pid] 2018-05-12 00:14:52 +00:00
Bob Lantz 90281b1d44 First attempt fixing x11 to work with pid namespace 2018-05-12 00:14:52 +00:00
Bob Lantz b141594a29 Fix pid namespace by making sure we fork on attach 2018-05-12 00:14:52 +00:00
Bob Lantz 9b86b5b7de First crack at a pid namespace-capable mnexec
The detach is a bit ugly because we fork twice, which shouldn't be
necessary!!
2018-05-12 00:14:52 +00:00
Bob Lantz 1d2ce39a01 Experiments with pid ns 2018-05-12 00:14:52 +00:00
Bob Lantz 7dee9ce563 Avoid expanding a string into a list of chars 2018-05-12 00:14:52 +00:00
Bob Lantz 24dd285b1a Use ['/etc/foo'] vs. ('/etc/foo',) to avoid deleted comma problem 2018-05-12 00:14:52 +00:00
Bob Lantz 2b7eb2ed99 Use motd instead of ssh banner message 2018-05-12 00:14:52 +00:00
Bob Lantz 7dae4e4b44 Clarify comment 2018-05-12 00:14:52 +00:00
Bob Lantz 5960e4ef89 Add docstring for terminate() 2018-05-12 00:14:52 +00:00
Bob Lantz 4d4d4719ff Document config params 2018-05-12 00:14:52 +00:00
Bob Lantz e67c9d9ea7 add private btmp 2018-05-12 00:14:52 +00:00
Bob Lantz 4c70429587 Comment changes 2018-05-12 00:14:52 +00:00
Bob Lantz d830b2331b Add documentation for overlayDirs 2018-05-12 00:14:52 +00:00
Bob Lantz c616476c33 Change to super() call in __init__ 2018-05-12 00:14:52 +00:00
Bob Lantz 1c1fa3af96 Simplify startup/shutdown, and a few comments 2018-05-12 00:14:52 +00:00
Bob Lantz 0a9a5af67b Private utmp and wtmp for w and last 2018-05-12 00:14:52 +00:00
Bob Lantz 57ba8bcd2e Minor changes 2018-05-12 00:14:52 +00:00
Bob Lantz c447d17362 Clarify parameter processing for overlayDirs 2018-05-12 00:14:52 +00:00
Bob Lantz 7ff615acea Fake cluster example 2018-05-12 00:14:52 +00:00
Bob Lantz 7263c16e89 Add overlayDirs 2018-05-12 00:14:52 +00:00
55 changed files with 828 additions and 515 deletions

No files matched your search

+11 -15
View File
@@ -4,32 +4,28 @@ sudo: required
matrix:
include:
- dist: trusty
python: 2.7
env: dist="14.04 LTS trusty"
- dist: trusty
python: 3.6
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)
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq vlan
- PYTHON=`which python` util/install.sh -n
- sudo util/install.sh -n
install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- pip install pexpect || pip3 install pexpect
- util/install.sh -nfvw
- sudo util/install.sh -fnvw
script:
- alias sudo="sudo env PATH=$PATH"
- export PYTHON=`which python`
- echo 'px import sys; print(sys.version_info)' | sudo $PYTHON bin/mn -v output
- sudo $PYTHON bin/mn --test pingall
- sudo $PYTHON mininet/test/runner.py -v -quick
- sudo $PYTHON examples/test/runner.py -v -quick
- sudo mn --test pingall
- sudo python mininet/test/runner.py -v -quick
- sudo python examples/test/runner.py -v -quick
notifications:
email:
on_success: never
# More details: https://docs.travis-ci.com/user/notifications
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
+1 -1
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0d3
Mininet 2.3.0d1
---
The supported installation methods for Mininet are 1) using a
+2 -2
View File
@@ -1,6 +1,6 @@
Mininet 2.3.0d3 License
Mininet 2.3.0d1 License
Copyright (c) 2013-2018 Open Networking Laboratory
Copyright (c) 2013-2016 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+8 -14
View File
@@ -2,16 +2,14 @@ MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
PYTHON ?= python
PYMN = $(PYTHON) -B bin/mn
PYMN = python -B bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
PREFIX ?= /usr
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man/man1
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
DOCDIRS = doc/html doc/latex
PDF = doc/latex/refman.pdf
@@ -48,20 +46,16 @@ slowtest: $(MININET)
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
install-mnexec: $(MNEXEC)
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
install-manpages: $(MANPAGES)
install -D -t $(MANDIR) $(MANPAGES)
install: install-mnexec install-manpages
$(PYTHON) setup.py install
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py install
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
$(PYTHON) setup.py develop
python setup.py develop
man: $(MANPAGES)
+2 -2
View File
@@ -2,7 +2,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.3.0d3
Mininet 2.3.0d1
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
@@ -126,7 +126,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
+9 -11
View File
@@ -29,11 +29,11 @@ from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
DefaultController, NullController,
UserSwitch, OVSSwitch, OVSBridge,
IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.nodelib import LinuxBridge, Server
from mininet.link import Link, TCLink, TCULink, OVSLink
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
SingleSwitchReversedTopo, MinimalTopo )
from mininet.topolib import TreeTopo, TorusTopo
import mininet.topolib
from mininet.util import customClass, specialClass, splitArgs
from mininet.util import buildTopo
@@ -53,9 +53,8 @@ TOPODEF = 'minimal'
TOPOS = { 'minimal': MinimalTopo,
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo,
'torus': TorusTopo }
'single': SingleSwitchTopo }
TOPOS.update( mininet.topolib.topos)
SWITCHDEF = 'default'
SWITCHES = { 'user': UserSwitch,
@@ -69,6 +68,7 @@ SWITCHES = { 'user': UserSwitch,
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'server': Server,
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
@@ -186,10 +186,8 @@ class MininetRunner( object ):
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
# pylint: disable=exec-used
exec( compile( open( fileName ).read(), fileName, 'exec' ),
customs, customs )
for name, val in customs.items():
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
@@ -258,7 +256,7 @@ class MininetRunner( object ):
opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice',
choices=list( LEVELS.keys() ), default = 'info',
choices=LEVELS.keys(), default = 'info',
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
@@ -288,7 +286,7 @@ class MininetRunner( object ):
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
opts.add_option( '--placement', type='choice',
choices=list( PLACEMENT.keys() ), default='block',
choices=PLACEMENT.keys(), default='block',
metavar='block|random',
help=( 'node placement for --cluster '
'(experimental!) ' ) )
+12 -8
View File
@@ -126,7 +126,7 @@ class ClusterCleanup( object ):
def cleanup( cls ):
"Clean up"
info( '*** Cleaning up cluster\n' )
for server, user in cls.serveruser.items():
for server, user in cls.serveruser.iteritems():
if server == 'localhost':
# Handled by mininet.clean.cleanup()
continue
@@ -157,7 +157,7 @@ class RemoteMixin( object ):
'-o', 'ForwardAgent=yes', '-tt' ]
def __init__( self, name, server='localhost', user=None, serverIP=None,
controlPath=False, splitInit=False, **kwargs):
controlPath=True, splitInit=False, **kwargs):
"""Instantiate a remote node
name: name of remote node
server: remote server (optional)
@@ -213,7 +213,7 @@ class RemoteMixin( object ):
def startShell( self, *args, **kwargs ):
"Start a shell process for running commands"
if self.isRemote:
kwargs.update( mnopts='-c' )
kwargs.update( mnopts='-cp' )
super( RemoteMixin, self ).startShell( *args, **kwargs )
# Optional split initialization
self.sendCmd( 'echo $$' )
@@ -386,7 +386,8 @@ class RemoteLink( Link ):
if server1 == server2:
# Link within same server
return Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs )
node1, node2, deleteIntfs=deleteIntfs,
runCmd=None )
# Otherwise, make a tunnel
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
addr1, addr2 )
@@ -844,10 +845,13 @@ 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'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
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()
return controller
def buildFromTopo( self, *args, **kwargs ):
+1 -1
View File
@@ -49,7 +49,7 @@ class MininetFacade( object ):
args: unnamed networks passed as arguments
kwargs: named networks passed as arguments"""
self.net = net
self.nets = [ net ] + list( args ) + list( kwargs.values() )
self.nets = [ net ] + list( args ) + kwargs.values()
self.nameToNet = kwargs
self.nameToNet['net'] = net
+4 -5
View File
@@ -31,9 +31,9 @@ rate includes buffering.
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, waitListening, decode
from mininet.util import custom, waitListening
from mininet.log import setLogLevel, info
from mininet.clean import cleanup
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
"""Example/test of link and CPU bandwidth limits
@@ -55,8 +55,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except
except:
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
cleanup()
info( '*** Skipping scheduler %s\n' % sched )
break
net.start()
net.pingAll()
@@ -71,7 +70,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
# ignore empty result from waitListening/telnet
popen.stdout.readline()
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
result = decode( popen.stdout.readline() ).split( ',' )
result = popen.stdout.readline().split( ',' )
bps = float( result[ -1 ] )
popen.terminate()
net.stop()
+86
View File
@@ -0,0 +1,86 @@
#!/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 )
+16 -31
View File
@@ -20,39 +20,24 @@ OpenFlow icon from https://www.opennetworking.org/
MINIEDIT_VERSION = '2.2.0.1'
import sys
from optparse import OptionParser
# from Tkinter import *
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton,
Menu, Toplevel, Button, BitmapImage, PhotoImage, Canvas,
Scrollbar, Wm, TclError, StringVar, IntVar,
E, W, EW, NW, Y, VERTICAL, SOLID, CENTER,
RIGHT, LEFT, BOTH, TRUE, FALSE )
from ttk import Notebook
from tkMessageBox import showerror
from subprocess import call
# pylint: disable=import-error
if sys.version_info[0] == 2:
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
Checkbutton, Menu, Toplevel, Button, BitmapImage,
PhotoImage, Canvas, Scrollbar, Wm, TclError,
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
from ttk import Notebook
from tkMessageBox import showerror
import tkFont
import tkFileDialog
import tkSimpleDialog
else:
from tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
Checkbutton, Menu, Toplevel, Button, BitmapImage,
PhotoImage, Canvas, Scrollbar, Wm, TclError,
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
from tkinter.ttk import Notebook
from tkinter.messagebox import showerror
from tkinter import font as tkFont
from tkinter import simpledialog as tkSimpleDialog
from tkinter import filedialog as tkFileDialog
# pylint: enable=import-error
import tkFont
import tkFileDialog
import tkSimpleDialog
import re
import json
from distutils.version import StrictVersion
import os
import sys
from functools import partial
if 'PYTHONPATH' in os.environ:
@@ -1423,7 +1408,7 @@ class MiniEdit( Frame ):
def convertJsonUnicode(self, text):
"Some part of Mininet don't like Unicode"
if isinstance(text, dict):
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.iteritems()}
elif isinstance(text, list):
return [self.convertJsonUnicode(element) for element in text]
elif isinstance(text, unicode):
@@ -1850,7 +1835,7 @@ class MiniEdit( Frame ):
# Save Links
f.write(" info( '*** Add links\\n')\n")
for key,linkDetail in self.links.items():
for key,linkDetail in self.links.iteritems():
tags = self.canvas.gettags(key)
if 'data' in tags:
optsExist = False
@@ -2890,7 +2875,7 @@ class MiniEdit( Frame ):
def buildLinks( self, net):
# Make links
info( "Getting Links.\n" )
for key,link in self.links.items():
for key,link in self.links.iteritems():
tags = self.canvas.gettags(key)
if 'data' in tags:
src=link['src']
@@ -3226,7 +3211,7 @@ class MiniEdit( Frame ):
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.items():
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
+1 -1
View File
@@ -45,7 +45,7 @@ def multiping( netsize, chunksize, seconds):
# Create network and identify subnets
topo = SingleSwitchTopo( netsize )
net = Mininet( topo=topo, waitConnected=True )
net = Mininet( topo=topo )
net.start()
hosts = net.hosts
subnets = chunks( hosts, chunksize )
+2 -3
View File
@@ -9,7 +9,6 @@ monitoring them
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import info, setLogLevel
from mininet.util import decode
from time import time
from select import poll, POLLIN
@@ -20,7 +19,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
"Monitor set of files and return [(host, line)...]"
devnull = open( '/dev/null', 'w' )
tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.items():
for h, outfile in outfiles.iteritems():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
@@ -41,7 +40,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
host = fdToHost[ fd ]
# Wait for a line of output
line = f.readline().strip()
yield host, decode( line )
yield host, line
else:
# If we timed out, return nothing
yield None, ''
+3 -7
View File
@@ -5,9 +5,8 @@ Tests for baresshd.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from mininet.clean import cleanup, sh
from sys import stdout
class testBareSSHD( unittest.TestCase ):
@@ -15,9 +14,7 @@ class testBareSSHD( unittest.TestCase ):
def connected( self ):
"Log into ssh server, check banner, then exit"
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
'-o StrictHostKeyChecking=no '
'-i /tmp/ssh/test_rsa exit' )
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
while True:
index = p.expect( self.opts )
if index == 0:
@@ -25,7 +22,6 @@ class testBareSSHD( unittest.TestCase ):
else:
return False
def setUp( self ):
# verify that sshd is not running
self.assertFalse( self.connected() )
@@ -59,7 +55,7 @@ class testBareSSHD( unittest.TestCase ):
def tearDown( self ):
# kill the ssh process
sh( "ps aux | grep ssh |grep Banner| awk '{ print $2 }' | xargs kill" )
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
cleanup()
# remove public key pair
sh( 'rm -rf /tmp/ssh' )
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for bind.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testBind( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ A simple sanity check test for cluster edition
'''
import unittest
from mininet.util import pexpect
import pexpect
class clusterSanityCheck( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testControllers( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for controlnet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testControlNet( unittest.TestCase ):
+1 -1
View File
@@ -15,7 +15,7 @@ cfs 10% 1.29e+09
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testCPU( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for emptynet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testEmptyNet( unittest.TestCase ):
+1 -1
View File
@@ -7,7 +7,7 @@ Test for hwintf.py
import unittest
import re
from mininet.util import pexpect
import pexpect
from mininet.log import setLogLevel
from mininet.node import Node
+1 -1
View File
@@ -5,7 +5,7 @@ Test for intfOptions.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testIntfOptions( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for limit.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testLimit( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testLinearBandwidth( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linuxrouter.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from mininet.util import quietRun
class testLinuxRouter( unittest.TestCase ):
+1 -1
View File
@@ -6,7 +6,7 @@ validates mininet interfaces against systems interfaces
'''
import unittest
from mininet.util import pexpect
import pexpect
class testMultiLink( unittest.TestCase ):
+5 -7
View File
@@ -5,7 +5,7 @@ Test for multiping.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from collections import defaultdict
class testMultiPing( unittest.TestCase ):
@@ -31,20 +31,18 @@ class testMultiPing( unittest.TestCase ):
target = p.match.group(3)
received = int( p.match.group(4) )
if target == '10.0.0.200':
self.assertEqual( received, 0, p.match.group(0) + '\n' +
target + ' received %d != 0 packets' % received )
self.assertEqual( received, 0 )
else:
self.assertEqual( received, 1, p.match.group(0) + '\n' +
target + ' received %d != 1 packets' % received )
self.assertEqual( received, 1 )
try:
pings[ name ].remove( target )
except:
pass
else:
break
self.assertTrue( len( pings ) > 0, 'too few pings' )
self.assertTrue( len( pings ) > 0 )
for t in pings.values():
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
self.assertEqual( len( t ), 0 )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multipoll.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testMultiPoll( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multitest.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testMultiTest( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for nat.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from mininet.util import quietRun
destIP = '8.8.8.8' # Google DNS
+1 -1
View File
@@ -5,7 +5,7 @@ Test for natnet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from mininet.util import quietRun
class testNATNet( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for numberedports.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from collections import defaultdict
from mininet.node import OVSSwitch
+1 -1
View File
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testPopen( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for scratchnet.py
"""
import unittest
from mininet.util import pexpect
import pexpect
class testScratchNet( unittest.TestCase ):
+2 -2
View File
@@ -5,7 +5,7 @@ Test for simpleperf.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
from mininet.log import setLogLevel
@@ -18,7 +18,7 @@ class testSimplePerf( unittest.TestCase ):
"Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .2
TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
+1 -1
View File
@@ -5,7 +5,7 @@ Test for sshd.py
"""
import unittest
from mininet.util import pexpect
import pexpect
from mininet.clean import sh
class testSSHD( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for tree1024.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testTree1024( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for treeping64.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
class testTreePing64( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for vlanhost.py
"""
import unittest
from mininet.util import pexpect
import pexpect
import sys
from mininet.util import quietRun
+8 -8
View File
@@ -16,25 +16,24 @@ import time
from mininet.log import info
from mininet.term import cleanUpScreens
from mininet.util import decode
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
result = Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
return decode( result )
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
sh( 'pkill -9 -f %s' % pattern )
# Make sure they are gone
while True:
try:
pids = co( [ 'pgrep', '-f', pattern ] )
pids = co( [ 'pgrep', '-f', pattern ] ).split( '\n' )
except CalledProcessError:
pids = ''
pids = []
# Don't kill init
pids = [ pid for pid in pids if pid and pid != '1' ]
if pids:
sh( 'pkill -9 -f %s' % pattern )
sh( "kill -9 %s" % ' '.join( pids ) )
time.sleep( .5 )
else:
break
@@ -77,6 +76,7 @@ class Cleanup( object ):
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths\n" )
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps:
+12 -5
View File
@@ -25,7 +25,9 @@ Link: basic link class for creating veth pairs
"""
from mininet.log import info, error, debug
from mininet.util import makeIntfPair
from mininet.util import makeIntfPair, quietRun
import mininet.node
import re
class Intf( object ):
@@ -164,7 +166,7 @@ class Intf( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = list( param.items() )[ 0 ]
name, value = param.items()[ 0 ]
f = getattr( self, method, None )
if not f or value is None:
return
@@ -285,7 +287,11 @@ class TCIntf( Intf ):
loss=None, max_queue_size=None ):
"Internal method: return tc commands for delay and loss"
cmds = []
if loss and ( loss < 0 or loss > 100 ):
if delay and delay < 0:
error( 'Negative delay', delay, '\n' )
elif jitter and jitter < 0:
error( 'Negative jitter', jitter, '\n' )
elif loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# Delay/jitter/loss/max queue size
@@ -475,7 +481,8 @@ class Link( object ):
@classmethod
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
node1=None, node2=None, deleteIntfs=True,
runCmd=quietRun ):
"""Create pair of interfaces
intfname1: name for interface 1
intfname2: name for interface 2
@@ -488,7 +495,7 @@ class Link( object ):
# Leave this as a class method for now
assert cls
return makeIntfPair( intfname1, intfname2, addr1, addr2, node1, node2,
deleteIntfs=deleteIntfs )
deleteIntfs=deleteIntfs, runCmd=runCmd )
def delete( self ):
"Delete this link"
+3 -3
View File
@@ -1,6 +1,6 @@
"Module dependency utility functions for Mininet."
from mininet.util import quietRun, BaseString
from mininet.util import quietRun
from mininet.log import info, error, debug
from os import environ
@@ -28,9 +28,9 @@ def moduleDeps( subtract=None, add=None ):
add: string or list of module names to add, if not already loaded"""
subtract = subtract if subtract is not None else []
add = add if add is not None else []
if isinstance( subtract, BaseString ):
if isinstance( subtract, basestring ):
subtract = [ subtract ]
if isinstance( add, BaseString ):
if isinstance( add, basestring ):
add = [ add ]
for mod in subtract:
if mod in lsmod():
+9 -11
View File
@@ -89,7 +89,7 @@ method may be called to shut down the network.
import os
import re
import select
import signal
from signal import SIGKILL
import random
from time import sleep
@@ -104,11 +104,11 @@ from mininet.nodelib import NAT
from mininet.link import Link, Intf
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening, BaseString )
waitListening )
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.0d3"
VERSION = "2.3.0d1"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -190,7 +190,7 @@ class Mininet( object ):
if not remaining:
info( '\n' )
return True
if timeout is not None and time > timeout:
if time > timeout and timeout is not None:
break
sleep( delay )
time += delay
@@ -383,8 +383,8 @@ class Mininet( object ):
params: additional link params (optional)
returns: link object"""
# Accept node objects or names
node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ]
node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ]
options = dict( params )
# Port is optional
if port1 is not None:
@@ -524,7 +524,7 @@ class Mininet( object ):
def stopXterms( self ):
"Kill each xterm."
for term in self.terms:
os.kill( term.pid, signal.SIGKILL )
term.send_signal( SIGKILL )
cleanUpScreens()
def staticArp( self ):
@@ -549,8 +549,7 @@ class Mininet( object ):
switch.start( self.controllers )
started = {}
for swclass, switches in groupby(
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
sorted( self.switches, key=type ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches )
@@ -577,8 +576,7 @@ class Mininet( object ):
info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {}
for swclass, switches in groupby(
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
sorted( self.switches, key=type ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches )
+119 -67
View File
@@ -62,8 +62,7 @@ from time import sleep
from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
numCores, retry, mountCgroups, BaseString, decode,
encode, Python3 )
numCores, retry, mountCgroups )
from mininet.moduledeps import moduleDeps, pathCheck, TUN
from mininet.link import Link, Intf, TCIntf, OVSIntf
from re import findall
@@ -75,10 +74,11 @@ class Node( object ):
portBase = 0 # Nodes always start with eth0/port0, even in OF 1.0
def __init__( self, name, inNamespace=True, **params ):
def __init__( self, name, **params ):
"""name: name of node
inNamespace: in network namespace?
ns: private namespaces to use ['net','mnt']
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,10 +86,14 @@ class Node( object ):
self.name = params.get( 'name', name )
self.privateDirs = params.get( 'privateDirs', [] )
self.inNamespace = params.get( 'inNamespace', inNamespace )
self.overlayDirs = params.get( 'overlayDirs', [] )
# Python 3 complains if we don't wait for shell exit
self.waitExited = params.get( 'waitExited', Python3 )
# 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
# Stash configuration parameters for future reference
self.params = params
@@ -106,8 +110,9 @@ class Node( object ):
self.waiting = False
self.readbuf = ''
# Start command interpreter shell
# Start command interpreter shell and mount any local dirs
self.startShell()
self.mountOverlayDirs()
self.mountPrivateDirs()
# File descriptor to node mapping support
@@ -124,33 +129,33 @@ 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, (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'
# 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 )
# bash -i: force interactive
# -s: pass $* to shell, and make process easy to find in ps
# prompt is set to sentinel chr( 127 )
cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
'bash', '--norc', '--noediting',
'-is', 'mininet:' + self.name ]
'bash', '--norc', '-is', 'mininet:' + self.name ]
# Spawn a shell subprocess in a pseudo-tty, to disable buffering
# in the subprocess and insulate it from signals (e.g. SIGINT)
# received by the parent
master, slave = pty.openpty()
self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
close_fds=False )
# XXX BL: This doesn't seem right, and we should also probably
# close our files when we exit...
self.stdin = os.fdopen( master, 'r' )
self.stdin = os.fdopen( master, 'rw' )
self.stdout = self.stdin
self.pid = self.shell.pid
self.pollOut = select.poll()
@@ -165,19 +170,21 @@ class Node( object ):
self.lastPid = None
self.readbuf = ''
# Wait for prompt
while True:
data = self.read( 1024 )
if data[ -1 ] == chr( 127 ):
break
self.pollOut.poll()
self.waiting = False
self.waiting = True
self.waitOutput()
if 'P' in opts:
assert self.lastPid is not None
self.pid = self.lastPid
# +m: disable job control notification
self.cmd( 'unset HISTFILE; stty -echo; set +m' )
initcmd = 'unset HISTFILE; stty -echo; set +m'
if 'uts' in self.ns:
initcmd += '; hostname ' + self.name
self.cmd( initcmd )
def mountPrivateDirs( self ):
"mount private directories"
"Mount private directories"
# Avoid expanding a string into a list of chars
assert not isinstance( self.privateDirs, BaseString )
assert not isinstance( self.privateDirs, basestring )
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
@@ -193,22 +200,68 @@ class Node( object ):
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
def unmountPrivateDirs( self ):
"mount private directories"
"Unmount private and overlay 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)
params: parameters to Popen()"""
# Leave this is as an instance method for now
assert self
popen = Popen( cmd, **params )
debug( '_popen', cmd, popen.pid )
return popen
return Popen( cmd, **params )
def cleanup( self ):
"Help python collect its garbage."
@@ -217,9 +270,6 @@ class Node( object ):
# for intfName in self.intfNames():
# if self.name in intfName:
# quietRun( 'ip link del ' + intfName )
if self.waitExited and self.shell:
debug( 'waiting for', self.pid, 'to terminate\n' )
self.shell.wait()
self.shell = None
# Subshell I/O, commands and control
@@ -229,7 +279,7 @@ class Node( object ):
maxbytes: maximum number of bytes to return"""
count = len( self.readbuf )
if count < maxbytes:
data = decode( os.read( self.stdout.fileno(), maxbytes - count ) )
data = os.read( self.stdout.fileno(), maxbytes - count )
self.readbuf += data
if maxbytes >= len( self.readbuf ):
result = self.readbuf
@@ -253,14 +303,15 @@ class Node( object ):
def write( self, data ):
"""Write data to node.
data: string"""
os.write( self.stdin.fileno(), encode( data ) )
os.write( self.stdin.fileno(), data )
def terminate( self ):
"Send kill signal to Node and clean up after it."
self.unmountPrivateDirs()
self.unmountOverlayDirs()
if self.shell:
if self.shell.poll() is None:
os.killpg( self.shell.pid, signal.SIGHUP )
os.killpg( self.pid, signal.SIGHUP )
self.cleanup()
def stop( self, deleteIntfs=False ):
@@ -307,10 +358,10 @@ class Node( object ):
self.lastPid = None
self.waiting = True
def sendInt( self, intr=chr( 3 ) ):
def sendInt( self, signal=signal.SIGINT ):
"Interrupt running command."
debug( 'sendInt: writing chr(%d)\n' % ord( intr ) )
self.write( intr )
debug( "sending signal %d to pgrp %d" % ( signal, self.pid ) )
os.killpg( self.pid, signal )
def monitor( self, timeoutms=None, findPid=True ):
"""Monitor and return the output of a command.
@@ -323,18 +374,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 not re.findall( marker, data ):
while True:
markers = self._marker.findall( data )
if markers:
self.lastPid = int( markers[ -1 ] )
data = self._marker.sub( '', data )
break
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
@@ -383,23 +434,24 @@ class Node( object ):
'mncmd':
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
shell = defaults.pop( 'shell', False )
if len( args ) == 1:
if isinstance( args[ 0 ], list ):
# popen([cmd, arg1, arg2...])
cmd = args[ 0 ]
elif isinstance( args[ 0 ], BaseString ):
elif isinstance( args[ 0 ], basestring ):
# popen("cmd arg1 arg2...")
cmd = [ args[ 0 ] ] if shell else args[ 0 ].split()
cmd = args[ 0 ].split()
else:
raise Exception( 'popen() requires a string or list' )
elif len( args ) > 0:
# popen( cmd, arg1, arg2... )
cmd = list( args )
if shell:
cmd = [ os.environ[ 'SHELL' ], '-c' ] + [ ' '.join( cmd ) ]
# Attach to our namespace using mnexec -a
cmd = defaults.pop( 'mncmd' ) + cmd
# Shell requires a string, not a list!
if defaults.get( 'shell', False ):
cmd = ' '.join( cmd )
debug( cmd, defaults )
popen = self._popen( cmd, **defaults )
return popen
@@ -411,7 +463,7 @@ class Node( object ):
# Warning: this can fail with large numbers of fds!
out, err = popen.communicate()
exitcode = popen.wait()
return decode( out ), decode( err ), exitcode
return out, err, exitcode
# Interface management, configuration, and routing
@@ -473,7 +525,7 @@ class Node( object ):
"""
if not intf:
return self.defaultIntf()
elif isinstance( intf, BaseString):
elif isinstance( intf, basestring):
return self.nameToIntf[ intf ]
else:
return intf
@@ -500,7 +552,7 @@ class Node( object ):
# explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow,
# at least with Linux kernels before 2.6.33
for intf in list( self.intfs.values() ):
for intf in self.intfs.values():
# Protect against deleting hardware interfaces
if ( self.name in intf.name ) or ( not checkName ):
intf.delete()
@@ -525,7 +577,7 @@ class Node( object ):
"""Set the default route to go through intf.
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
# Note setParam won't call us if intf is none
if isinstance( intf, BaseString ) and ' ' in intf:
if isinstance( intf, basestring ) and ' ' in intf:
params = intf
else:
params = 'dev %s' % intf
@@ -572,7 +624,7 @@ class Node( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = list( param.items() )[ 0 ]
name, value = param.items()[ 0 ]
if value is None:
return
f = getattr( self, method, None )
@@ -621,7 +673,7 @@ class Node( object ):
def intfList( self ):
"List of our interfaces sorted by port number"
return [ self.intfs[ p ] for p in sorted( self.intfs.keys() ) ]
return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
def intfNames( self ):
"The names of our interfaces sorted by port number"
@@ -655,6 +707,8 @@ 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"
@@ -885,14 +939,14 @@ class Switch( Node ):
self.dpid = self.defaultDpid( dpid )
self.opts = opts
self.listenPort = listenPort
if not self.inNamespace:
if 'net' not in self.ns:
self.controlIntf = Intf( 'lo', self, port=0 )
def defaultDpid( self, dpid=None ):
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
if dpid:
# Remove any colons and make sure it's a good hex number
dpid = dpid.replace( ':', '' )
dpid = dpid.translate( None, ':' )
assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
else:
# Use hex of the first number in the switch name
@@ -900,7 +954,6 @@ class Switch( Node ):
if nums:
dpid = hex( int( nums[ 0 ] ) )[ 2: ]
else:
self.terminate() # Python 3.6 crash workaround
raise Exception( 'Unable to derive default datapath ID - '
'please either specify a dpid or use a '
'canonical switch name such as s23.' )
@@ -1092,10 +1145,9 @@ class OVSSwitch( Switch ):
version = quietRun( 'ovs-vsctl --version' )
cls.OVSVersion = findall( r'\d+\.\d+', version )[ 0 ]
@classmethod
def isOldOVS( cls ):
def isOldOVS( self ):
"Is OVS ersion < 1.10?"
return ( StrictVersion( cls.OVSVersion ) <
return ( StrictVersion( self.OVSVersion ) <
StrictVersion( '1.10' ) )
def dpctl( self, *args ):
@@ -1181,7 +1233,7 @@ class OVSSwitch( Switch ):
"Start up a new OVS OpenFlow switch using ovs-vsctl"
if self.inNamespace:
raise Exception(
'OVS kernel switch does not work in a namespace' )
'OVS kernel switch does not work in a network namespace' )
int( self.dpid, 16 ) # DPID must be a hex string
# Command to add interfaces
intfs = ''.join( ' -- add-port %s %s' % ( self, intf ) +
@@ -1244,7 +1296,7 @@ class OVSSwitch( Switch ):
run( cmds, shell=True )
# Reapply link config if necessary...
for switch in switches:
for intf in switch.intfs.values():
for intf in switch.intfs.itervalues():
if isinstance( intf, TCIntf ):
intf.config( **intf.params )
return switches
@@ -1270,7 +1322,7 @@ class OVSSwitch( Switch ):
pids = ' '.join( str( switch.pid ) for switch in switches )
run( 'kill -HUP ' + pids )
for switch in switches:
switch.terminate()
switch.shell = None
return switches
+86 -1
View File
@@ -4,11 +4,14 @@ Node Library for Mininet
This contains additional Node types which you may find to be useful.
"""
from mininet.node import Node, Switch
from mininet.node import Node, Host, 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)"
@@ -143,3 +146,85 @@ 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 )
+64 -13
View File
@@ -5,13 +5,43 @@ 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
def tunnelX11( node, display=None):
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 ):
"""Create an X11 tunnel from node:6000 to the root host
display: display on root host (optional)
returns: node $DISPLAY, Popen object for tunnel"""
@@ -28,11 +58,30 @@ def tunnelX11( node, display=None):
quietRun( 'xhost +si:localuser:root' )
return display, None
else:
# Create a tunnel for the TCP connection
hostname = quietRun( 'hostname' ).strip()
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 ]
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 )
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
@@ -45,8 +94,8 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
if not node.inNamespace:
title += ' (root)'
cmds = {
'xterm': [ 'xterm', '-title', title, '-display' ],
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
'xterm': [ 'xterm', '-title', title ],
'gterm': [ 'gnome-terminal', '--title', title ]
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
@@ -54,8 +103,10 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
display, tunnel = tunnelX11( node, display )
if display is None:
return []
term = node.popen( cmds[ term ] +
[ display, '-e', 'env TERM=ansi %s' % cmd ] )
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 ] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
@@ -68,7 +119,7 @@ def runX11( node, cmd ):
def cleanUpScreens():
"Remove moldy socat X11 tunnels."
errRun( "pkill -9 -f mnexec.*socat" )
errRun( "pkill -9 -f socat.*mininet" )
def makeTerms( nodes, title='Node', term='xterm' ):
"""Create terminals.
+13 -17
View File
@@ -30,10 +30,10 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
def testDefaultDpid( self ):
"""Verify that the default dpid is assigned using a valid provided
canonical switchname if no dpid is passed in switch creation."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1' )
switch = Mininet( Topo(),
self.switchClass,
Host, Controller ).addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
net.stop()
def dpidFrom( self, num ):
"Compute default dpid from number"
@@ -44,34 +44,31 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation."""
dpid = self.dpidFrom( 0xABCD )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1', dpid=dpid )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch(
's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid )
net.stop()
def testDefaultDpidAssignmentFailure( self ):
"""Verify that Default dpid assignment raises an Exception if the
name of the switch does not contin a digit. Also verify the
exception message."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
with self.assertRaises( Exception ) as raises_cm:
net.addSwitch( 'A' )
self.assertTrue( 'Unable to derive '
Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 'A' )
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
'default datapath ID - please either specify a dpid '
'or use a canonical switch name such as s23.'
in str( raises_cm.exception ) )
net.stop()
'or use a canonical switch name such as s23.')
def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch
name) 0's followed by hex of first string of contiguous digits passed
in switch name."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
net.stop()
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
class OVSUser( OVSSwitch):
"OVS User Switch convenience class"
@@ -98,4 +95,3 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
cleanup()
+9 -16
View File
@@ -7,13 +7,13 @@ TODO: missing xterm test
"""
import unittest
import pexpect
import os
import re
from mininet.util import quietRun, pexpect
from mininet.util import quietRun
from distutils.version import StrictVersion
from time import sleep
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
@@ -95,8 +95,7 @@ class testWalkthrough( unittest.TestCase ):
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
interfaces = [ 'h1-eth0', 's1-eth1', r'[^-](eth|en)\w*\d', 'lo',
self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
@@ -123,7 +122,7 @@ class testWalkthrough( unittest.TestCase ):
ifcount += 1
else:
break
self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
@@ -157,13 +156,9 @@ class testWalkthrough( unittest.TestCase ):
def testSimpleHTTP( self ):
"Start an HTTP server on h1 and wget from h2"
if 'Python 2' in quietRun( 'python --version' ):
httpserver = 'SimpleHTTPServer'
else:
httpserver = 'http.server'
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 python -m %s 80 &' % httpserver )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
# The walkthrough doesn't specify a delay here, and
# we also don't read the output (also a possible problem),
# but for now let's wait a couple of seconds to make
@@ -227,8 +222,8 @@ class testWalkthrough( unittest.TestCase ):
p.expect( r'rtt min/avg/max/mdev = '
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay >= 40, 'Delay < 40ms' )
self.assertTrue( delay <= 50, 'Delay > 50ms' )
self.assertTrue( delay > 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@@ -265,7 +260,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
for i in range( 1, 3 ):
p.sendline( 'h%d ifconfig' % i )
p.expect( r'\s00:00:00:00:00:0%d\s' % i )
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
p.expect( self.prompt )
p.sendline( 'exit' )
p.expect( pexpect.EOF )
@@ -291,9 +286,7 @@ class testWalkthrough( unittest.TestCase ):
"Test running user switch in its own namespace"
p = pexpect.spawn( 'mn --innamespace --switch user' )
p.expect( self.prompt )
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
self.prompt ]
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
p.sendline( 's1 ifconfig -a' )
ifcount = 0
while True:
+4 -4
View File
@@ -56,13 +56,13 @@ class MultiGraph( object ):
return self.node.items() if data else self.node.keys()
def edges_iter( self, data=False, keys=False ):
"Iterator: return graph edges, optionally with data and keys"
for src, entry in self.edge.items():
for dst, entrykeys in entry.items():
"Iterator: return graph edges"
for src, entry in self.edge.iteritems():
for dst, keys in entry.iteritems():
if src > dst:
# Skip duplicate edges
continue
for k, attrs in entrykeys.items():
for k, attrs in keys.iteritems():
if data:
if keys:
yield( src, dst, k, attrs )
+33 -10
View File
@@ -45,10 +45,11 @@ class TorusTopo( Topo ):
without STP turned on! It can be used with STP, e.g.:
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
def build( self, x, y, n=1 ):
"""x: dimension of torus in x-direction
y: dimension of torus in y-direction
n: number of hosts per switch"""
def build( self, x, y, 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)"""
if x < 3 or y < 3:
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
@@ -72,11 +73,33 @@ class TorusTopo( Topo ):
self.addLink( host, switch )
# Connect switches
for i in range( 0, x ):
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 )
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 }
# pylint: enable=arguments-differ
+56 -73
View File
@@ -12,39 +12,6 @@ from fcntl import fcntl, F_GETFL, F_SETFL
from os import O_NONBLOCK
import os
from functools import partial
import sys
# Python 2/3 compatibility
Python3 = sys.version_info[0] == 3
BaseString = str if Python3 else getattr( str, '__base__' )
Encoding = 'utf-8' if Python3 else None
def decode( s ):
"Decode a byte string if needed for Python 3"
return s.decode( Encoding ) if Python3 else s
def encode( s ):
"Encode a byte string if needed for Python 3"
return s.encode( Encoding ) if Python3 else s
try:
# pylint: disable=import-error
oldpexpect = None
import pexpect as oldpexpect
# pylint: enable=import-error
class Pexpect( object ):
"Custom pexpect that is compatible with str"
@staticmethod
def spawn( *args, **kwargs):
"pexpect.spawn that is compatible with str"
if Python3 and 'encoding' not in kwargs:
kwargs.update( encoding='utf-8' )
return oldpexpect.spawn( *args, **kwargs )
def __getattr__( self, name ):
return getattr( oldpexpect, name )
pexpect = Pexpect()
except ImportError:
pass
# Command execution support
@@ -90,7 +57,7 @@ def oldQuietRun( *cmd ):
# This is a bit complicated, but it enables us to
# monitor command output as it is happening
# pylint: disable=too-many-branches,too-many-statements
# pylint: disable=too-many-branches
def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code
cmd: string or list of command and args
@@ -131,8 +98,6 @@ def errRun( *cmd, **kwargs ):
f = fdtofile[ fd ]
if event & POLLIN:
data = f.read( 1024 )
if Python3:
data = data.decode( Encoding )
if echo:
output( data )
if f == popen.stdout:
@@ -151,10 +116,6 @@ def errRun( *cmd, **kwargs ):
poller.unregister( fd )
returncode = popen.wait()
# Python 3 complains if we don't explicitly close these
popen.stdout.close()
if stderr == PIPE:
popen.stderr.close()
debug( out, err, returncode )
return out, err, returncode
# pylint: enable=too-many-branches
@@ -217,18 +178,19 @@ 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
netns = 1 if not node2 else node2.pid
ns1 = 1 if not node1 else node1.pid
ns2 = 1 if not node2 else node2.pid
if addr1 is None and addr2 is None:
cmdOutput = runCmd( 'ip link add name %s '
cmdOutput = runCmd( 'ip link add name %s netns %s '
'type veth peer name %s '
'netns %s' % ( intf1, intf2, netns ) )
'netns %s' % ( intf1, ns1, intf2, ns2 ) )
else:
cmdOutput = runCmd( 'ip link add name %s '
'address %s '
'address %s netns %s '
'type veth peer name %s '
'address %s '
'netns %s' %
( intf1, addr1, intf2, addr2, netns ) )
( intf1, addr1, ns1, intf2, addr2, ns2 ) )
if cmdOutput:
raise Exception( "Error creating interface pair (%s,%s): %s " %
( intf1, intf2, cmdOutput ) )
@@ -413,30 +375,30 @@ def pmonitor(popens, timeoutms=500, readline=True,
terminates: when all EOFs received"""
poller = poll()
fdToHost = {}
for host, popen in popens.items():
for host, popen in popens.iteritems():
fd = popen.stdout.fileno()
fdToHost[ fd ] = host
poller.register( fd, POLLIN | POLLHUP )
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
poller.register( fd, POLLIN )
if not readline:
# Use non-blocking reads
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
while popens:
fds = poller.poll( timeoutms )
if fds:
for fd, event in fds:
host = fdToHost[ fd ]
popen = popens[ host ]
if event & POLLIN or event & POLLHUP:
while True:
try:
f = popen.stdout
line = decode( f.readline() if readline
else f.read( readmax ) )
except IOError:
line = ''
if line == '':
break
yield host, line
if event & POLLHUP:
if event & POLLIN:
if readline:
# Attempt to read a line of output
# This blocks until we receive a newline!
line = popen.stdout.readline()
else:
line = popen.stdout.read( readmax )
yield host, line
# Check for EOF
elif event & POLLHUP:
poller.unregister( fd )
del popens[ host ]
else:
@@ -499,7 +461,7 @@ def fixLimits():
def mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'grep cgroup /proc/mounts' )
mounts = quietRun( 'cat /proc/mounts' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if ('cgroup %s' % cgdir not in mounts and
@@ -547,21 +509,42 @@ def custom( cls, **params ):
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
return customized
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: ]
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 )
# 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, val = s.split( '=', 1 )
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
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
def customClass( classes, argStr ):
"""Return customized class based on argStr
@@ -639,7 +622,7 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
if not runCmd( 'which telnet' ):
raise Exception('Could not find telnet' )
# pylint: disable=maybe-no-member
serverIP = server if isinstance( server, BaseString ) else server.IP()
serverIP = server if isinstance( server, basestring ) else server.IP()
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
time = 0
result = runCmd( cmd )
+196 -108
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 mount namespaces
* - running in network and other namespaces
* - printing out the pid of a process so we can identify it later
* - attaching to a namespace and cgroup
* - attaching to namespace(s) and cgroup
* - setting RT scheduling
*
* Partially based on public domain setsid(1)
@@ -24,21 +24,29 @@
#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 [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Usage: %s [-cdmnPpu] [-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"
" -n: run in new network and mount namespaces\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"
" -p: print ^A + pid\n"
" -a pid: attach to pid's network and mount namespaces\n"
" -a pid: attach to pid's namespaces\n"
" -g group: add to cgroup\n"
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
" -v: print version\n",
@@ -46,10 +54,13 @@ 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)
@@ -63,6 +74,7 @@ void validate(char *path)
}
}
/* Add our pid to cgroup */
void cgroup(char *gname)
{
@@ -92,117 +104,193 @@ 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();
static struct sched_param sp;
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
/* 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)
switch(c) {
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 '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);
}
/* Mark our whole hierarchy recursively as private, so that our
* mounts do not propagate to other processes.
*/
if (closefds) {
/* close file descriptors except stdin/out/error */
int fd;
for (fd = getdtablesize(); fd > 2; fd--) close(fd);
}
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;
}
}
/* 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 (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");
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);
}
/* For pid namespace, we need to fork and wait for child ;-( */
if (flags & CLONE_NEWPID || pidns) {
wait(&status);
}
return 0;
}
}
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]);
+24 -30
View File
@@ -102,14 +102,6 @@ function version_ge {
[ "$1" == "$latest" ]
}
# Attempt to identify Python version
PYTHON=${PYTHON:-python}
if $PYTHON --version |& grep 'Python 2' > /dev/null; then
PYTHON_VERSION=2; PYPKG=python
else
PYTHON_VERSION=3; PYPKG=python3
fi
echo "${PYTHON} is version ${PYTHON_VERSION}"
# Kernel Deb pkg to be removed:
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
@@ -153,20 +145,19 @@ 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
elif [ "$DIST" = "SUSE LINUX" ]; then
$install gcc make socat psmisc xterm openssh iperf \
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
ethtool help2man python-pyflakes python3-pylint \
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
else # Debian/Ubuntu
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 \
cgroup-bin ethtool help2man pyflakes pylint pep8 \
${PYPKG}-setuptools ${PYPKG}-pexpect ${PYPKG}-tk
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8 python-pexpect
fi
echo "Installing Mininet core"
pushd $MININET_DIR/mininet
sudo PYTHON=${PYTHON} make install
sudo make install
popd
}
@@ -213,14 +204,10 @@ function of {
function of13 {
echo "Installing OpenFlow 1.3 soft switch implementation..."
cd $BUILD_DIR/
$install git-core autoconf automake autotools-dev pkg-config \
make gcc g++ libtool libc6-dev cmake libpcap-dev \
$install git-core autoconf automake autotools-dev pkg-config \
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
unzip libpcre3-dev flex bison libboost-dev
if [ "$DIST" = "Ubuntu" ] && version_le $RELEASE 16.04; then
$install libxerces-c2-dev
else
$install libxerces-c-dev
fi
if [ ! -d "ofsoftswitch13" ]; then
git clone https://github.com/CPqD/ofsoftswitch13.git
if [[ -n "$OF13_SWITCH_REV" ]]; then
@@ -231,17 +218,24 @@ function of13 {
fi
# Install netbee
if [ ! -d "netbee" ]; then
git clone https://github.com/netgroup-polito/netbee.git
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
NBEESRC="nbeesrc-feb-24-2015"
NBEEDIR="netbee"
else
NBEESRC="nbeesrc-jan-10-2013"
NBEEDIR="nbeesrc-jan-10-2013"
fi
cd netbee/src
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
wget -nc ${NBEEURL}${NBEESRC}.zip
unzip ${NBEESRC}.zip
cd ${NBEEDIR}/src
cmake .
make
cd $BUILD_DIR
sudo cp netbee/bin/libn*.so /usr/local/lib
cd $BUILD_DIR/
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
sudo ldconfig
sudo cp -R netbee/include/ /usr/
sudo cp -R ${NBEEDIR}/include/ /usr/
# Resume the install:
cd $BUILD_DIR/ofsoftswitch13
+1 -8
View File
@@ -33,12 +33,5 @@ if [ -d "$cgroup" ]; then
cg="-g $host"
fi
# 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"
cmd="exec sudo -E mnexec $cg -a $pid $cmd"
eval $cmd
+3 -8
View File
@@ -1,19 +1,14 @@
#!/usr/bin/python
from subprocess import check_output as co
from sys import exit, version_info
def run(*args, **kwargs):
"Run co and decode for python3"
result = co(*args, **kwargs)
return result.decode() if version_info[ 0 ] >= 3 else result
from sys import exit
# Actually run bin/mn rather than importing via python path
version = 'Mininet ' + run( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = version.strip()
# Find all Mininet path references
lines = run( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
lines = co( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
error = False