Compare commits

...

69 Commits

Author SHA1 Message Date
Bob Lantz b9f723becd Optionally install libovs for OVS 2.8+
We also unmask it after installation if we're on systemd.
2019-03-13 21:41:59 -07:00
lantz 36d2b21187 Merge pull request #852 from gabay/patch/rename_init_to_build
Replaced example topologies __init__ with build
2019-02-06 13:22:44 -08:00
None 1888001555 fix kwargs names so they wont spark a warning 2019-01-17 13:06:53 -08:00
None fdc3156a91 Replaced example topologies __init__ with build 2019-01-17 12:01:09 -08:00
lantz cf6da391fa Merge pull request #847 from yousong/poll-eintr
cli: ignore EINTR caused by SIGWINCH when calling poll()
2018-12-20 11:36:42 -08:00
Yousong Zhou f170cc6f64 cli: ignore EINTR caused by SIGWINCH when calling poll()
This can happen when adjusting a tmux pane running mininet cli.  The
process will receive SIGWINCH causing the poll() call to raise
select.error

Below is a session dump with strace

	09:55:52.498106 poll([{fd=0, events=POLLIN}, {fd=28, events=POLLIN}], 2, -1) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
	09:55:52.606734 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
	09:55:52.606770 rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
	Traceback (most recent call last):
	  File "examples/topofac.py", line 527, in <module>
	    run()
	  File "examples/topofac.py", line 522, in run
	    CLI( net )
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 68, in __init__
	    self.run()
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 103, in run
	    self.cmdloop()
	  File "/home/yunion/.usr/lib/python2.7/cmd.py", line 142, in cmdloop
	    stop = self.onecmd(line)
	  File "/home/yunion/.usr/lib/python2.7/cmd.py", line 220, in onecmd
	    return self.default(line)
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 422, in default
	    self.waitForNode( node )
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 440, in waitForNode
	    bothPoller.poll()
	select.error: (4, 'Interrupted system call')
	09:55:52.622076 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7ff9a9b936d0}, {0x7ff9a9ef2230, [], SA_RESTORER, 0x7ff9a9b936d0}, 8) = 0
	09:55:52.631905 +++ exited with 1 +++
2018-12-14 02:46:08 +00:00
Bob Lantz fd96de6485 Add iproute back in for older dists 2018-12-07 17:08:41 -08:00
lantz 73f530b569 Merge pull request #841 from gabay/master
replaces isinstance(str) with isinstance(BaseString)
(note we define BaseString as str for python 3)
2018-11-15 06:40:28 -08:00
gabay c7a27b8876 replaces isinstance(str) with isinstance(BaseString) 2018-11-15 16:15:58 +02:00
Bob Lantz b7a6b8137f Fix undefined variable and code check. 2018-10-19 13:38:06 -07:00
lantz d072e531c2 Merge pull request #837 from theojepsen/master
intf.rename() also renames the intf name in the host
2018-10-11 15:51:48 -07:00
Bob Lantz 8139d7d1b4 Better IP address selection for controllers
Previously we looked for 'eth0', but linux has renamed interfaces.

Instead of looking by name, we now look for an interface which
is the appropriate gateway to a remote server address.

fixes #831
2018-10-11 15:46:57 -07:00
Theo 273b14b771 intf.rename() also renames the intf name in the host 2018-10-11 09:31:26 +02:00
Bob Lantz bfda33544a code check 2018-08-28 18:48:50 -07:00
Bob Lantz 1f4525ed0a Test for Node()/Host() pty leakage 2018-08-28 18:48:45 -07:00
Bob Lantz 1969669f51 pass code check 2018-08-23 13:06:16 -07:00
Bob Lantz c639f342e5 2.3.0d4 2018-08-23 11:43:45 -07:00
Bob Lantz 6e887d0788 Close pty to avoid leak 2018-08-23 11:43:43 -07:00
Bob Lantz 092863f113 2.3.0d3 2018-08-15 23:13:20 -07:00
Bob Lantz 5100a38fcd pmonitor() fixes
pmonitor() is hard to get right, but this seems like
a reasonable improvement:

1. non-blocking I/O so we can drain buffer with impunity
2. drain buffer after poll
3. unregister fd on POLLHUP

Note we may have a unicode splitting problem since we're
decoding chunks of bytes, but that is a problem for another
day I think.
2018-08-02 12:45:35 -07:00
Bob Lantz 3bcecd8e08 Fix popen() to work properly with shell=True 2018-08-02 06:56:18 -07:00
Bob Lantz 486676d617 2.3.0d2 2018-07-26 14:04:08 -07:00
lantz 9e5280b4d1 Python 3 Compatibility (Merge pull request #817 from lantz/py3-compat)
Changes for compatibility with Python 3.

The approach is to make as few changes as possible to maintain compatibility with both Python 2 and Python 3.

We use whatever python is installed, and also support a PYTHON environment variable for installing another version.

For simplicity, we provide mininet.util.pexpect which works out of the box with Python 3 utf-8 strings.

Thanks to @cuihantao for looking at this as well and also for changes to MiniEdit.

Closes #794
2018-07-26 14:03:34 -07:00
Bob Lantz b70eed69d3 Fix typo (50s -> 50ms) 2018-07-26 13:52:15 -07:00
Bob Lantz d96b35694a Use appropriate python version 2018-07-26 13:40:12 -07:00
Bob Lantz c7deeae11c Add sanity check for python mn version 2018-07-26 13:40:12 -07:00
Bob Lantz 323989953b Try to fix pylint errors ;-p 2018-07-26 13:40:12 -07:00
Bob Lantz 17f9756e5c sudo interferes with travis python 3 environment 2018-07-26 13:40:12 -07:00
Bob Lantz 71f3931f2f Remove redundancies in miniedit; pass code check 2018-07-26 13:40:12 -07:00
Bob Lantz 1e9ca5f7fc Fix missing quote in .travis.yml 2018-07-26 13:40:12 -07:00
Bob Lantz 8933c996cb Add python-tk/python3-tk 2018-07-26 13:40:06 -07:00
Bob Lantz afa83cd5ae Restore python 2 compatibility 2018-07-26 13:40:03 -07:00
Hantao Cui 3e81ea7455 miniedit.py changes for Python 3 2018-07-26 13:39:49 -07:00
Bob Lantz 8102704726 Pass code check (14.04) 2018-07-25 21:30:05 -07:00
Bob Lantz f4490069ca Add PYTHON and python2/python3 support
Perhaps we should have an option for py2/p3, but for
now we detect the default python version and it can
also be specified via the PYTHON env var.
2018-07-25 19:44:46 -07:00
Bob Lantz 2ac4f92af3 Add PYTHON variable for python2/python3 make install 2018-07-25 19:44:46 -07:00
Bob Lantz f98154a323 Decode subprocess output for python3 2018-07-25 19:44:46 -07:00
Bob Lantz 7b48460e9e Change pexpect hackery to use a custom class
So the original pexpect is unperturbed
2018-07-25 19:44:46 -07:00
Bob Lantz 3a0ef258d1 grep cgroup /proc/mounts in mountCgroup 2018-07-25 19:44:45 -07:00
Bob Lantz 0d9a6796df Add Python 2.7 and Python 3.6 2018-07-25 19:44:45 -07:00
Bob Lantz 08a59783f4 Decode readline() and call cleanup() for python 3
Python 3.6 crashes if there are leftover processes, even
if they shut down later...
2018-07-25 19:44:45 -07:00
Bob Lantz e37afe996b First crack at Python 3 compatibility
Mostly we just use mininet.util.pexpect so that we can use
unicode strings without changing calls to pexpect.spawn()
2018-07-25 19:44:45 -07:00
Bob Lantz 6a387faa04 Fix whitespace error 2018-07-25 19:44:45 -07:00
Bob Lantz 1a134cb4d2 80 columns 2018-07-25 19:44:45 -07:00
Bob Lantz af4921adc5 Minor changes for Python 3
items() vs. iteritems() and decode() bytes in monitorFiles

Probably not strictly correct if bytes are split - we still
need to deal with this properly.
2018-07-25 19:44:45 -07:00
Bob Lantz f94ee8ec97 Redesign pmonitor()
This is tricky to get right, but I think it is correct
to drain the buffers like this.
2018-07-25 19:44:45 -07:00
Bob Lantz 356b024d6b Wrap reads in pmonitor() for python3 2018-07-25 19:44:45 -07:00
Bob Lantz 2283bb01e6 Python3 compat and add timeout for connect 2018-07-25 19:44:45 -07:00
Bob Lantz 3eef584c6b Python 3 compatibility
And some Ubuntu 18 compat:

- ifconfig's output has changed
- relaxing timing as it seems first ping is slower
2018-07-25 19:44:45 -07:00
Bob Lantz 2e00a7de97 Python 3 compatibility 2018-07-25 19:44:45 -07:00
Bob Lantz e28348f6cd Indent error. 2018-07-25 19:44:44 -07:00
Bob Lantz 4b744d1fb0 Fix dynamic Python items() in deleteIntfs() 2018-07-25 19:44:44 -07:00
Bob Lantz 2822998cad A couple of missed py3 items: execfile, iteritems 2018-07-25 19:44:44 -07:00
Bob Lantz 853815500d Create util.pexpect which is compatible with Python 3 strings
Unfortunately pexpect isn't compatible with Python 3 strings
unless you specify unicode encoding, which isn't the default.

With this helper code, you can import pexpect from mininet.util
and get default pexpect.spawn() behavior that works with Python 3
strings.
2018-07-25 19:44:44 -07:00
Bob Lantz 1a2925923f translate() -> replace() for Python 3 compatibility
str.translate() works differently in Python 3
2018-07-25 19:44:44 -07:00
Bob Lantz cac884a85e Wait for Node() shell to exit on Python 3
I'm not a fan of this, but Python 3's subprocess module
complains otherwise.

For now we're only doing this on Python 3. We should probably
quantify the slowdown however.
2018-07-25 19:44:44 -07:00
Bob Lantz f314a6626a Missed one instance of basestring 2018-07-25 19:44:44 -07:00
Bob Lantz bf83f4f343 Don't bother checking for negative delay/jitter
It's kind of pointless. However technically the argument should
be a number and not a string - it's always in ms.
2018-07-25 19:44:44 -07:00
Bob Lantz 1ef12e450a Apparently Python 3 types aren't ordered 2018-07-25 19:44:44 -07:00
Bob Lantz 56fc6c3955 Close popen stdout and stderr
They should get cleaned up anyway, but Python 3 gives
us an error message. This silences it.
2018-07-25 19:44:44 -07:00
Bob Lantz 88cbf4ec42 Kinder, gentler Python 3 compatibility
Looking at trying to minimize code changes for Python 3.

For now we are keeping byte str() in py2 and unicode str()
in py3. It's a pain to rewrite all string code to usee
u'' for py2 compatibility. However we may need to revisit
this for pexpect().

Created compatibility wrappers in util.py:

BaseString (replacement for basestring)
decode() (optionally decode utf-8 for py3)
encode() (optionally encode utf-8 for py3)

Also we've switched from the .iter*() flavors to their
static alternatives for py2 (note they are iterators in py3!)

Changed util.errRun and clean.sh to call decode()
2018-07-25 19:44:44 -07:00
Bob Lantz 26b3959968 Fix keys parameter in edges_iter
Fixes #813
2018-07-11 23:36:11 +00:00
Bob Lantz 966dd20b4b waitConnected: don't compare timeout if it is None 2018-07-11 23:29:19 +00:00
Bob Lantz 82998cac8b waitConnected and debug output for testMultiPing
This test fails periodically on Travis (#714)

Hopefully waiting for connection will make it more reliable,
and if not then the debug output will help.
2018-07-10 07:41:19 -07:00
lantz 34b1f4161a Merge pull request #803 from teto/nixos_clean
Merge in nixos changes:
1. More flexible makefile
2. Fix problems from setting vi-ins-mode-string in .inputrc (fixes #799)
2018-06-25 19:49:22 -07:00
lantz dfb297901f Update copyright date in LICENSE
It's 2018. ;-)
2018-06-18 09:59:11 -07:00
Bob Lantz 67236e9db3 Update ofsoftswitch build
Changed netbee URL.
Changed libxerces package for Ubuntu 18.04
2018-06-03 17:20:18 -07:00
Matthieu Coudron 8a00c3abf8 bash: run with --noediting
Having "set editing-mode vi" in ~/.inputrc can output characters that
may induce mininet into (wrongly) thinking a process has failed.
2018-06-03 09:54:50 +09:00
Matthieu Coudron 037f7f5921 Makefile: make it more flexible
- let the user choose where to install (via PREFIX)
- added targets install-mnexec/install-manpages
2018-06-03 09:54:13 +09:00
56 changed files with 396 additions and 231 deletions
+15 -11
View File
@@ -4,28 +4,32 @@ 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
- sudo util/install.sh -n
- PYTHON=`which python` util/install.sh -n
install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- sudo util/install.sh -fnvw
- pip install pexpect || pip3 install pexpect
- util/install.sh -nfvw
script:
- sudo mn --test pingall
- sudo python mininet/test/runner.py -v -quick
- sudo python examples/test/runner.py -v -quick
- 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
notifications:
email:
on_success: never
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
# More details: https://docs.travis-ci.com/user/notifications
+1 -1
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0d1
Mininet 2.3.0d4
---
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.3.0d4 License
Copyright (c) 2013-2016 Open Networking Laboratory
Copyright (c) 2013-2018 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+14 -8
View File
@@ -2,14 +2,16 @@ MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
PYMN = python -B bin/mn
PYTHON ?= python
PYMN = $(PYTHON) -B bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
PREFIX ?= /usr
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man/man1
DOCDIRS = doc/html doc/latex
PDF = doc/latex/refman.pdf
@@ -46,16 +48,20 @@ slowtest: $(MININET)
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py install
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
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.0d1
Mininet 2.3.0d4
[![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
+6 -4
View File
@@ -186,8 +186,10 @@ class MininetRunner( object ):
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
# pylint: disable=exec-used
exec( compile( open( fileName ).read(), fileName, 'exec' ),
customs, customs )
for name, val in customs.items():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
@@ -256,7 +258,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=LEVELS.keys(), default = 'info',
choices=list( LEVELS.keys() ), default = 'info',
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
@@ -286,7 +288,7 @@ class MininetRunner( object ):
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
opts.add_option( '--placement', type='choice',
choices=PLACEMENT.keys(), default='block',
choices=list( PLACEMENT.keys() ), default='block',
metavar='block|random',
help=( 'node placement for --cluster '
'(experimental!) ' ) )
+1 -4
View File
@@ -13,12 +13,9 @@ from mininet.topo import Topo
class MyTopo( Topo ):
"Simple topology example."
def __init__( self ):
def build( self ):
"Create custom topo."
# Initialize topology
Topo.__init__( self )
# Add hosts and switches
leftHost = self.addHost( 'h1' )
rightHost = self.addHost( 'h2' )
+21 -6
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.iteritems():
for server, user in cls.serveruser.items():
if server == 'localhost':
# Handled by mininet.clean.cleanup()
continue
@@ -843,11 +843,26 @@ class MininetCluster( Mininet ):
def addController( self, *args, **kwargs ):
"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()
loopback = '127.0.0.1'
if ( not isinstance( controller, Controller ) or
controller.IP() != loopback ):
return
# Find route to a different server IP address
serverIPs = [ ip for ip in self.serverIP.values()
if ip is not controller.IP() ]
if not serverIPs:
return # no remote servers - loopback is fine
remoteIP = serverIPs[ 0 ]
# Route should contain 'dev <intfname>'
route = controller.cmd( 'ip route get', remoteIP,
r'| egrep -o "dev\s[^[:space:]]+"' )
if not route:
raise Exception('addController: no route from', controller,
'to', remoteIP )
intf = route.split()[ 1 ].strip()
debug( 'adding', intf, 'to', controller )
Intf( intf, node=controller ).updateIP()
debug( controller, 'IP address updated to', controller.IP() )
return controller
def buildFromTopo( self, *args, **kwargs ):
+2 -2
View File
@@ -30,7 +30,7 @@ class ClusterCLI( CLI ):
global nx, plt, graphviz_layout
if not nx:
try:
# pylint: disable=import-error
# pylint: disable=import-error,no-member
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
@@ -42,7 +42,7 @@ class ClusterCLI( CLI ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error
# pylint: enable=import-error,no-member
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
+2 -3
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 ) + kwargs.values()
self.nets = [ net ] + list( args ) + list( kwargs.values() )
self.nameToNet = kwargs
self.nameToNet['net'] = net
@@ -100,10 +100,9 @@ class MininetFacade( object ):
class ControlNetwork( Topo ):
"Control Network Topology"
def __init__( self, n, dataController=DataController, **kwargs ):
def build( self, n, dataController=DataController, **_kwargs ):
"""n: number of data network controller nodes
dataController: class for data network controllers"""
Topo.__init__( self, **kwargs )
# Connect everything to a single switch
cs0 = self.addSwitch( 'cs0' )
# Add hosts which will serve as data network controllers
+5 -4
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
from mininet.util import custom, waitListening, decode
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,7 +55,8 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except
except:
info( '*** Skipping scheduler %s\n' % sched )
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
cleanup()
break
net.start()
net.pingAll()
@@ -70,7 +71,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 = popen.stdout.readline().split( ',' )
result = decode( popen.stdout.readline() ).split( ',' )
bps = float( result[ -1 ] )
popen.terminate()
net.stop()
+1 -5
View File
@@ -38,11 +38,7 @@ flush = sys.stdout.flush
class LinearTestTopo( Topo ):
"Topology for a string of N hosts and N-1 switches."
def __init__( self, N, **params ):
# Initialize topology
Topo.__init__( self, **params )
def build( self, N, **params ):
# Create switches and hosts
hosts = [ self.addHost( 'h%s' % h )
for h in irange( 1, N ) ]
+31 -16
View File
@@ -20,24 +20,39 @@ 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
import tkFont
import tkFileDialog
import tkSimpleDialog
# 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 re
import json
from distutils.version import StrictVersion
import os
import sys
from functools import partial
if 'PYTHONPATH' in os.environ:
@@ -1408,7 +1423,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.iteritems()}
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
elif isinstance(text, list):
return [self.convertJsonUnicode(element) for element in text]
elif isinstance(text, unicode):
@@ -1835,7 +1850,7 @@ class MiniEdit( Frame ):
# Save Links
f.write(" info( '*** Add links\\n')\n")
for key,linkDetail in self.links.iteritems():
for key,linkDetail in self.links.items():
tags = self.canvas.gettags(key)
if 'data' in tags:
optsExist = False
@@ -2875,7 +2890,7 @@ class MiniEdit( Frame ):
def buildLinks( self, net):
# Make links
info( "Getting Links.\n" )
for key,link in self.links.iteritems():
for key,link in self.links.items():
tags = self.canvas.gettags(key)
if 'data' in tags:
src=link['src']
@@ -3211,7 +3226,7 @@ class MiniEdit( Frame ):
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
for name, val in customs.items():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
+1 -3
View File
@@ -21,9 +21,7 @@ def runMultiLink():
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
def __init__( self, n, **kwargs ):
Topo.__init__( self, **kwargs )
def build( self, n, **_kwargs ):
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
s1 = self.addSwitch( 's1' )
+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 )
net = Mininet( topo=topo, waitConnected=True )
net.start()
hosts = net.hosts
subnets = chunks( hosts, chunksize )
+3 -2
View File
@@ -9,6 +9,7 @@ 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
@@ -19,7 +20,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.iteritems():
for h, outfile in outfiles.items():
tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno()
@@ -40,7 +41,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
host = fdToHost[ fd ]
# Wait for a line of output
line = f.readline().strip()
yield host, line
yield host, decode( line )
else:
# If we timed out, return nothing
yield None, ''
+1 -3
View File
@@ -27,9 +27,7 @@ from mininet.util import irange
class InternetTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
def build(self, n=2, **_kwargs ):
# set up inet switch
inetSwitch = self.addSwitch('s0')
# add inet host
+7 -3
View File
@@ -5,8 +5,9 @@ Tests for baresshd.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from mininet.clean import cleanup, sh
from sys import stdout
class testBareSSHD( unittest.TestCase ):
@@ -14,7 +15,9 @@ class testBareSSHD( unittest.TestCase ):
def connected( self ):
"Log into ssh server, check banner, then exit"
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
'-o StrictHostKeyChecking=no '
'-i /tmp/ssh/test_rsa exit' )
while True:
index = p.expect( self.opts )
if index == 0:
@@ -22,6 +25,7 @@ class testBareSSHD( unittest.TestCase ):
else:
return False
def setUp( self ):
# verify that sshd is not running
self.assertFalse( self.connected() )
@@ -55,7 +59,7 @@ class testBareSSHD( unittest.TestCase ):
def tearDown( self ):
# kill the ssh process
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
sh( "ps aux | grep ssh |grep 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
import pexpect
from mininet.util import pexpect
class testBind( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ A simple sanity check test for cluster edition
'''
import unittest
import pexpect
from mininet.util import pexpect
class clusterSanityCheck( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testControllers( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for controlnet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testControlNet( unittest.TestCase ):
+1 -1
View File
@@ -15,7 +15,7 @@ cfs 10% 1.29e+09
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testCPU( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for emptynet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testEmptyNet( unittest.TestCase ):
+1 -1
View File
@@ -7,7 +7,7 @@ Test for hwintf.py
import unittest
import re
import pexpect
from mininet.util 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
import pexpect
from mininet.util import pexpect
import sys
class testIntfOptions( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for limit.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testLimit( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testLinearBandwidth( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linuxrouter.py
"""
import unittest
import pexpect
from mininet.util 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
import pexpect
from mininet.util import pexpect
class testMultiLink( unittest.TestCase ):
+7 -5
View File
@@ -5,7 +5,7 @@ Test for multiping.py
"""
import unittest
import pexpect
from mininet.util import pexpect
from collections import defaultdict
class testMultiPing( unittest.TestCase ):
@@ -31,18 +31,20 @@ 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 )
self.assertEqual( received, 0, p.match.group(0) + '\n' +
target + ' received %d != 0 packets' % received )
else:
self.assertEqual( received, 1 )
self.assertEqual( received, 1, p.match.group(0) + '\n' +
target + ' received %d != 1 packets' % received )
try:
pings[ name ].remove( target )
except:
pass
else:
break
self.assertTrue( len( pings ) > 0 )
self.assertTrue( len( pings ) > 0, 'too few pings' )
for t in pings.values():
self.assertEqual( len( t ), 0 )
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multipoll.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testMultiPoll( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multitest.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testMultiTest( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for nat.py
"""
import unittest
import pexpect
from mininet.util 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
import pexpect
from mininet.util 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
import pexpect
from mininet.util 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
import pexpect
from mininet.util import pexpect
class testPopen( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for scratchnet.py
"""
import unittest
import pexpect
from mininet.util import pexpect
class testScratchNet( unittest.TestCase ):
+2 -2
View File
@@ -5,7 +5,7 @@ Test for simpleperf.py
"""
import unittest
import pexpect
from mininet.util 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
import pexpect
from mininet.util 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
import pexpect
from mininet.util import pexpect
import sys
class testTree1024( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for treeping64.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
class testTreePing64( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for vlanhost.py
"""
import unittest
import pexpect
from mininet.util import pexpect
import sys
from mininet.util import quietRun
+3 -3
View File
@@ -16,12 +16,13 @@ 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' )
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
result = Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
return decode( result )
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
@@ -76,7 +77,6 @@ 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:
+9
View File
@@ -29,6 +29,8 @@ from subprocess import call
from cmd import Cmd
from os import isatty
from select import poll, POLLIN
import select
import errno
import sys
import time
import os
@@ -459,6 +461,13 @@ class CLI( Cmd ):
# it's possible to interrupt ourselves after we've
# read data but before it has been printed.
node.sendInt()
except select.error as e:
# pylint: disable=unpacking-non-sequence
errno_, errmsg = e.args
# pylint: enable=unpacking-non-sequence
if errno_ != errno.EINTR:
error( "select.error: %d, %s" % (errno_, errmsg) )
node.sendInt()
def precmd( self, line ):
"allow for comments in the cli"
+5 -6
View File
@@ -146,6 +146,9 @@ class Intf( object ):
def rename( self, newname ):
"Rename interface"
if self.node and self.name in self.node.nameToIntf:
# rename intf in node's nameToIntf
self.node.nameToIntf[newname] = self.node.nameToIntf.pop(self.name)
self.ifconfig( 'down' )
result = self.cmd( 'ip link set', self.name, 'name', newname )
self.name = newname
@@ -164,7 +167,7 @@ class Intf( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
name, value = list( param.items() )[ 0 ]
f = getattr( self, method, None )
if not f or value is None:
return
@@ -285,11 +288,7 @@ class TCIntf( Intf ):
loss=None, max_queue_size=None ):
"Internal method: return tc commands for delay and loss"
cmds = []
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 ):
if loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' )
else:
# Delay/jitter/loss/max queue size
+3 -3
View File
@@ -1,6 +1,6 @@
"Module dependency utility functions for Mininet."
from mininet.util import quietRun
from mininet.util import quietRun, BaseString
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 -7
View File
@@ -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 )
waitListening, BaseString )
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.0d1"
VERSION = "2.3.0d4"
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 time > timeout and timeout is not None:
if timeout is not None and time > timeout:
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:
@@ -549,7 +549,8 @@ class Mininet( object ):
switch.start( self.controllers )
started = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches )
@@ -576,7 +577,8 @@ class Mininet( object ):
info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches )
+43 -24
View File
@@ -62,7 +62,8 @@ from time import sleep
from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
numCores, retry, mountCgroups )
numCores, retry, mountCgroups, BaseString, decode,
encode, Python3 )
from mininet.moduledeps import moduleDeps, pathCheck, TUN
from mininet.link import Link, Intf, TCIntf, OVSIntf
from re import findall
@@ -87,6 +88,9 @@ class Node( object ):
self.privateDirs = params.get( 'privateDirs', [] )
self.inNamespace = params.get( 'inNamespace', inNamespace )
# Python 3 complains if we don't wait for shell exit
self.waitExited = params.get( 'waitExited', Python3 )
# Stash configuration parameters for future reference
self.params = params
@@ -103,6 +107,7 @@ class Node( object ):
self.readbuf = ''
# Start command interpreter shell
self.master, self.slave = None, None # pylint
self.startShell()
self.mountPrivateDirs()
@@ -135,14 +140,18 @@ class Node( object ):
# -s: pass $* to shell, and make process easy to find in ps
# prompt is set to sentinel chr( 127 )
cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
'bash', '--norc', '-is', 'mininet:' + self.name ]
'bash', '--norc', '--noediting',
'-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 )
self.stdin = os.fdopen( master, 'rw' )
self.master, self.slave = pty.openpty()
self.shell = self._popen( cmd, stdin=self.slave, stdout=self.slave,
stderr=self.slave, close_fds=False )
# XXX BL: This doesn't seem right, and we should also probably
# close our files when we exit...
self.stdin = os.fdopen( self.master, 'r' )
self.stdout = self.stdin
self.pid = self.shell.pid
self.pollOut = select.poll()
@@ -169,7 +178,7 @@ class Node( object ):
def mountPrivateDirs( self ):
"mount private directories"
# Avoid expanding a string into a list of chars
assert not isinstance( self.privateDirs, basestring )
assert not isinstance( self.privateDirs, BaseString )
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
@@ -198,7 +207,9 @@ class Node( object ):
params: parameters to Popen()"""
# Leave this is as an instance method for now
assert self
return Popen( cmd, **params )
popen = Popen( cmd, **params )
debug( '_popen', cmd, popen.pid )
return popen
def cleanup( self ):
"Help python collect its garbage."
@@ -207,6 +218,13 @@ class Node( object ):
# for intfName in self.intfNames():
# if self.name in intfName:
# quietRun( 'ip link del ' + intfName )
if self.shell:
# Close ptys
self.stdin.close()
os.close(self.slave)
if self.waitExited:
debug( 'waiting for', self.pid, 'to terminate\n' )
self.shell.wait()
self.shell = None
# Subshell I/O, commands and control
@@ -216,7 +234,7 @@ class Node( object ):
maxbytes: maximum number of bytes to return"""
count = len( self.readbuf )
if count < maxbytes:
data = os.read( self.stdout.fileno(), maxbytes - count )
data = decode( os.read( self.stdout.fileno(), maxbytes - count ) )
self.readbuf += data
if maxbytes >= len( self.readbuf ):
result = self.readbuf
@@ -240,7 +258,7 @@ class Node( object ):
def write( self, data ):
"""Write data to node.
data: string"""
os.write( self.stdin.fileno(), data )
os.write( self.stdin.fileno(), encode( data ) )
def terminate( self ):
"Send kill signal to Node and clean up after it."
@@ -370,23 +388,23 @@ class Node( object ):
'mncmd':
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
shell = defaults.pop( 'shell', False )
if len( args ) == 1:
if isinstance( args[ 0 ], list ):
# popen([cmd, arg1, arg2...])
cmd = args[ 0 ]
elif isinstance( args[ 0 ], basestring ):
elif isinstance( args[ 0 ], BaseString ):
# popen("cmd arg1 arg2...")
cmd = args[ 0 ].split()
cmd = [ args[ 0 ] ] if shell else args[ 0 ].split()
else:
raise Exception( 'popen() requires a string or list' )
elif len( args ) > 0:
# popen( cmd, arg1, arg2... )
cmd = list( args )
if shell:
cmd = [ os.environ[ 'SHELL' ], '-c' ] + [ ' '.join( cmd ) ]
# Attach to our namespace using mnexec -a
cmd = defaults.pop( 'mncmd' ) + cmd
# Shell requires a string, not a list!
if defaults.get( 'shell', False ):
cmd = ' '.join( cmd )
popen = self._popen( cmd, **defaults )
return popen
@@ -398,7 +416,7 @@ class Node( object ):
# Warning: this can fail with large numbers of fds!
out, err = popen.communicate()
exitcode = popen.wait()
return out, err, exitcode
return decode( out ), decode( err ), exitcode
# Interface management, configuration, and routing
@@ -460,7 +478,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
@@ -487,7 +505,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 self.intfs.values():
for intf in list( self.intfs.values() ):
# Protect against deleting hardware interfaces
if ( self.name in intf.name ) or ( not checkName ):
intf.delete()
@@ -512,7 +530,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
@@ -559,7 +577,7 @@ class Node( object ):
method: config method name
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
name, value = list( param.items() )[ 0 ]
if value is None:
return
f = getattr( self, method, None )
@@ -608,7 +626,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.iterkeys() ) ]
return [ self.intfs[ p ] for p in sorted( self.intfs.keys() ) ]
def intfNames( self ):
"The names of our interfaces sorted by port number"
@@ -879,7 +897,7 @@ class Switch( Node ):
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
if dpid:
# Remove any colons and make sure it's a good hex number
dpid = dpid.translate( None, ':' )
dpid = dpid.replace( ':', '' )
assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
else:
# Use hex of the first number in the switch name
@@ -887,6 +905,7 @@ 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.' )
@@ -1230,7 +1249,7 @@ class OVSSwitch( Switch ):
run( cmds, shell=True )
# Reapply link config if necessary...
for switch in switches:
for intf in switch.intfs.itervalues():
for intf in switch.intfs.values():
if isinstance( intf, TCIntf ):
intf.config( **intf.params )
return switches
@@ -1256,7 +1275,7 @@ class OVSSwitch( Switch ):
pids = ' '.join( str( switch.pid ) for switch in switches )
run( 'kill -HUP ' + pids )
for switch in switches:
switch.shell = None
switch.terminate()
return switches
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
"""
Regression test for pty leak in Node()
"""
import unittest
from mininet.net import Mininet
from mininet.clean import cleanup
from mininet.topo import SingleSwitchTopo
class TestPtyLeak( unittest.TestCase ):
"Verify that there is no pty leakage"
@staticmethod
def testPtyLeak():
"Test for pty leakage"
net = Mininet( SingleSwitchTopo() )
net.start()
host = net[ 'h1' ]
for _ in range( 0, 10 ):
oldptys = host.slave, host.master
net.delHost( host )
host = net.addHost( 'h1' )
assert ( host.slave, host.master ) == oldptys
net.stop()
if __name__ == '__main__':
unittest.main()
cleanup()
+17 -13
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."""
switch = Mininet( Topo(),
self.switchClass,
Host, Controller ).addSwitch( 's1' )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
net.stop()
def dpidFrom( self, num ):
"Compute default dpid from number"
@@ -44,31 +44,34 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation."""
dpid = self.dpidFrom( 0xABCD )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch(
's1', dpid=dpid )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.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:
Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 'A' )
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
net.addSwitch( 'A' )
self.assertTrue( 'Unable to derive '
'default datapath ID - please either specify a dpid '
'or use a canonical switch name such as s23.')
'or use a canonical switch name such as s23.'
in str( raises_cm.exception ) )
net.stop()
def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch
name) 0's followed by hex of first string of contiguous digits passed
in switch name."""
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 's123' )
net = Mininet( Topo(), self.switchClass, Host, Controller )
switch = net.addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
net.stop()
class OVSUser( OVSSwitch):
"OVS User Switch convenience class"
@@ -95,3 +98,4 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
cleanup()
+16 -9
View File
@@ -7,13 +7,13 @@ TODO: missing xterm test
"""
import unittest
import pexpect
import os
import re
from mininet.util import quietRun
from mininet.util import quietRun, pexpect
from distutils.version import StrictVersion
from time import sleep
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
@@ -95,7 +95,8 @@ class testWalkthrough( unittest.TestCase ):
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ 'h1-eth0', 's1-eth1', r'[^-](eth|en)\w*\d', 'lo',
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
@@ -122,7 +123,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 )
@@ -156,9 +157,13 @@ 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 SimpleHTTPServer 80 &' )
p.sendline( 'h1 python -m %s 80 &' % httpserver )
# 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
@@ -222,8 +227,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 < 45, 'Delay > 40ms' )
self.assertTrue( delay >= 40, 'Delay < 40ms' )
self.assertTrue( delay <= 50, 'Delay > 50ms' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@@ -260,7 +265,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
for i in range( 1, 3 ):
p.sendline( 'h%d ifconfig' % i )
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
p.expect( r'\s00:00:00:00:00:0%d\s' % i )
p.expect( self.prompt )
p.sendline( 'exit' )
p.expect( pexpect.EOF )
@@ -286,7 +291,9 @@ class testWalkthrough( unittest.TestCase ):
"Test running user switch in its own namespace"
p = pexpect.spawn( 'mn --innamespace --switch user' )
p.expect( self.prompt )
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
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"
for src, entry in self.edge.iteritems():
for dst, keys in entry.iteritems():
"Iterator: return graph edges, optionally with data and keys"
for src, entry in self.edge.items():
for dst, entrykeys in entry.items():
if src > dst:
# Skip duplicate edges
continue
for k, attrs in keys.iteritems():
for k, attrs in entrykeys.items():
if data:
if keys:
yield( src, dst, k, attrs )
+60 -21
View File
@@ -12,6 +12,39 @@ 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
@@ -33,7 +66,7 @@ def oldQuietRun( *cmd ):
cmd: list of command params"""
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, str ):
if isinstance( cmd, BaseString ):
cmd = cmd.split( ' ' )
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
# We can't use Popen.communicate() because it uses
@@ -57,7 +90,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
# pylint: disable=too-many-branches,too-many-statements
def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code
cmd: string or list of command and args
@@ -74,7 +107,7 @@ def errRun( *cmd, **kwargs ):
if len( cmd ) == 1:
cmd = cmd[ 0 ]
# Allow passing in a list or a string
if isinstance( cmd, str ) and not shell:
if isinstance( cmd, BaseString ) and not shell:
cmd = cmd.split( ' ' )
cmd = [ str( arg ) for arg in cmd ]
elif isinstance( cmd, list ) and shell:
@@ -98,6 +131,8 @@ 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:
@@ -116,6 +151,10 @@ 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
@@ -374,30 +413,30 @@ def pmonitor(popens, timeoutms=500, readline=True,
terminates: when all EOFs received"""
poller = poll()
fdToHost = {}
for host, popen in popens.iteritems():
for host, popen in popens.items():
fd = popen.stdout.fileno()
fdToHost[ fd ] = host
poller.register( fd, POLLIN )
if not readline:
# Use non-blocking reads
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
poller.register( fd, POLLIN | POLLHUP )
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:
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:
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:
poller.unregister( fd )
del popens[ host ]
else:
@@ -460,7 +499,7 @@ def fixLimits():
def mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'cat /proc/mounts' )
mounts = quietRun( 'grep cgroup /proc/mounts' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if ('cgroup %s' % cgdir not in mounts and
@@ -600,7 +639,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 )
+40 -28
View File
@@ -102,6 +102,14 @@ 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
@@ -145,19 +153,21 @@ 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 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 \
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8 python-pexpect
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
ethtool help2man python-pyflakes python3-pylint \
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
else # Debian/Ubuntu
$install gcc make socat psmisc xterm ssh iperf telnet \
cgroup-bin ethtool help2man pyflakes pylint pep8 \
${PYPKG}-setuptools ${PYPKG}-pexpect ${PYPKG}-tk
$install iproute2 || $install iproute
fi
echo "Installing Mininet core"
pushd $MININET_DIR/mininet
sudo make install
sudo PYTHON=${PYTHON} make install
popd
}
@@ -204,10 +214,14 @@ 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 libxerces-c2-dev \
$install git-core autoconf automake autotools-dev pkg-config \
make gcc g++ libtool libc6-dev cmake libpcap-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
@@ -218,24 +232,17 @@ function of13 {
fi
# Install netbee
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
NBEESRC="nbeesrc-feb-24-2015"
NBEEDIR="netbee"
else
NBEESRC="nbeesrc-jan-10-2013"
NBEEDIR="nbeesrc-jan-10-2013"
if [ ! -d "netbee" ]; then
git clone https://github.com/netgroup-polito/netbee.git
fi
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
wget -nc ${NBEEURL}${NBEESRC}.zip
unzip ${NBEESRC}.zip
cd ${NBEEDIR}/src
cd netbee/src
cmake .
make
cd $BUILD_DIR/
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
cd $BUILD_DIR
sudo cp netbee/bin/libn*.so /usr/local/lib
sudo ldconfig
sudo cp -R ${NBEEDIR}/include/ /usr/
sudo cp -R netbee/include/ /usr/
# Resume the install:
cd $BUILD_DIR/ofsoftswitch13
@@ -340,15 +347,20 @@ function ubuntuOvs {
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
DEB_BUILD_OPTIONS='parallel=$parallel nocheck' fakeroot debian/rules binary
cd ..
# Install packages
if [ -e libopenvswitch_$OVS_RELEASE*.deb ]; then
$pkginst libopenvswitch_$OVS_RELEASE*.deb 2>/dev/null
fi
for pkg in common datapath-dkms pki switch; do
pkg=openvswitch-${pkg}_$OVS_RELEASE*.deb
echo "Installing $pkg"
$pkginst $pkg
done
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb 2>/dev/null; then
echo "Ignoring error installing openvswitch-controller"
if [ -e openvswitch-controller_$OVS_RELEASE*.deb ]; then
$pkginst openvswitch-controller_$OVS_RELEASE*.deb 2>/dev/null
fi
# Ubuntu/Debian will mask a service if you uninstall it
sudo systemctl unmask openvswitch-switch || true
/sbin/modinfo openvswitch
sudo ovs-vsctl show
# Switch can run on its own, but
+8 -3
View File
@@ -1,14 +1,19 @@
#!/usr/bin/python
from subprocess import check_output as co
from sys import exit
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
# Actually run bin/mn rather than importing via python path
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = 'Mininet ' + run( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = version.strip()
# Find all Mininet path references
lines = co( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
lines = run( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
error = False