Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88f14e946a | |||
| 0cefa0b8de | |||
| 1b4c7d706d | |||
| 5e909c6e4f | |||
| 740acfb350 | |||
| a978ce9484 | |||
| 1e546cb73b | |||
| a2cb3e4051 | |||
| 4cfe97ee49 | |||
| b762d0bf96 | |||
| 4840bc20c5 | |||
| 30a5fd0df4 | |||
| 4707e90028 | |||
| cf5675876c | |||
| 1169982c0a | |||
| 32a7126d2b | |||
| 678add6202 | |||
| 936d303f5d | |||
| c5413a74f5 | |||
| 3f5503d773 | |||
| aa0176fce6 | |||
| bdd2f8d87f | |||
| 1a47699f21 | |||
| 270a6ba333 | |||
| c3ba039a97 | |||
| 8a50d3867c | |||
| d1b0b32c8c | |||
| 71d3e58d8c | |||
| 9edbb3b4a3 | |||
| 57294d013e | |||
| bfb6b9f4c4 | |||
| 1b55f09c4e |
@@ -1,4 +1,5 @@
|
||||
**Mininet uses GitHub issues for bug reports and feature requests only.**
|
||||
<!--
|
||||
Mininet uses GitHub issues for bug reports and feature requests only.
|
||||
These issues can be viewed at bugs.mininet.org
|
||||
|
||||
If you have a question that is not a **bug report** or a **feature request**,
|
||||
@@ -8,7 +9,7 @@ at faq.mininet.org, and the mininet-discuss mailing list.
|
||||
For bug reports, please fill in the following information in detail,
|
||||
and also feel free to include additional information such as debug
|
||||
output from mn -v debug, etc.
|
||||
--- Cut Here ---
|
||||
-->
|
||||
### Expected/Desired Behavior:
|
||||
|
||||
### Actual Behavior:
|
||||
|
||||
@@ -17,6 +17,6 @@ jobs:
|
||||
- name: Install Mininet code check dependencies
|
||||
run: |
|
||||
PYTHON=`which python` util/install.sh -n
|
||||
python -m pip install pylint==2.4.4
|
||||
python -m pip install pylint==2.15.7
|
||||
- name: Run code check
|
||||
run: make codecheck
|
||||
|
||||
@@ -9,43 +9,43 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
|
||||
python-version: [3.x, 2.x]
|
||||
os: [ubuntu-22.04, ubuntu-20.04]
|
||||
py: [python3, python2]
|
||||
steps:
|
||||
- name: Check out Mininet source
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Python ${{ matrix.py }}
|
||||
run: sudo apt install ${{ matrix.py }}
|
||||
- name: Install Mininet and base dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
# This seems too slow unfortunately:
|
||||
# sudo apt-get upgrade -y -qq
|
||||
PYTHON=`which python` util/install.sh -nv
|
||||
PYTHON=${{ matrix.py }} util/install.sh -nv
|
||||
- name: Disable slow udevd
|
||||
run: sudo systemctl stop systemd-udevd
|
||||
systemd-udevd-kernel.socket
|
||||
systemd-udevd-control.socket
|
||||
|| echo "couldn't disable udevd"
|
||||
- name: Sanity test
|
||||
run: |
|
||||
export sudo="sudo env PATH=$PATH"
|
||||
# Verify major python-version number matches python and mininet
|
||||
export PYVER=`echo ${{ matrix.python-version }} | cut -d . -f 1`
|
||||
export CHKVER='px import platform; print(platform.python_version())'
|
||||
python --version |& grep " $PYVER\."
|
||||
echo $CHKVER | $sudo mn -v output | grep " $PYVER\."
|
||||
export PYTHON=${{ matrix.py }}
|
||||
# Newer OvS tries OpenFlow15 which crashes ovsc on ubuntu-20.04
|
||||
$sudo mn --switch ovs,protocols=OpenFlow13 --test pingall
|
||||
- name: Install test dependencies
|
||||
run: |
|
||||
sudo apt-get install -qq vlan
|
||||
pip install pexpect
|
||||
export PYTHON=${{ matrix.py }}
|
||||
sudo $PYTHON -m pip install pexpect
|
||||
util/install.sh -fw
|
||||
- name: Run core tests
|
||||
run: |
|
||||
export sudo="sudo env PATH=$PATH"
|
||||
export PYTHON=`which python`
|
||||
export PYTHON=${{ matrix.py }}
|
||||
$sudo $PYTHON mininet/test/runner.py -v
|
||||
- name: Run examples tests (quick)
|
||||
- name: Run examples tests
|
||||
run: |
|
||||
export sudo="sudo env PATH=$PATH"
|
||||
export PYTHON=`which python`
|
||||
$sudo $PYTHON examples/test/runner.py -v -quick
|
||||
export PYTHON=${{ matrix.py }}
|
||||
$sudo $PYTHON examples/test/runner.py -v
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#init-hook=
|
||||
|
||||
# Profiled execution.
|
||||
profile=no
|
||||
#profile=no
|
||||
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
@@ -45,12 +45,13 @@ load-plugins=
|
||||
# Note: we may want to re-enable some of these at some point, but many of them
|
||||
# are just style issues rather than errors.
|
||||
#
|
||||
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
|
||||
disable=invalid-name, super-init-not-called, fixme,
|
||||
too-many-instance-attributes, too-few-public-methods,
|
||||
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
|
||||
locally-disabled, locally-enabled, bad-continuation,
|
||||
too-many-locals, too-many-public-methods, duplicate-code,
|
||||
locally-disabled,
|
||||
useless-object-inheritance, unnecessary-pass, no-else-return,
|
||||
no-else-raise, no-else-continue, super-with-arguments
|
||||
no-else-raise, no-else-continue, super-with-arguments,
|
||||
consider-using-f-string, unspecified-encoding
|
||||
|
||||
# bad-continuation, wrong-import-order
|
||||
|
||||
@@ -62,12 +63,12 @@ output-format=colorized
|
||||
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
|
||||
|
||||
# Include message's id in output
|
||||
include-ids=yes
|
||||
# include-ids=yes
|
||||
|
||||
# Put messages in a separate file for each module / package specified on the
|
||||
# command line instead of printing them on stdout. Reports (if any) will be
|
||||
# written in a file name "pylint_global.[txt|html]".
|
||||
files-output=no
|
||||
# files-output=no
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
||||
@@ -81,7 +82,7 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme
|
||||
|
||||
# Add a comment according to your evaluation note. This is used by the global
|
||||
# evaluation report (R0004).
|
||||
comment=no
|
||||
#comment=no
|
||||
|
||||
# Enable the report(s) with the given id(s).
|
||||
#enable-report=
|
||||
@@ -103,7 +104,7 @@ comment=no
|
||||
[BASIC]
|
||||
|
||||
# Required attributes for module, separated by a comma
|
||||
required-attributes=
|
||||
#required-attributes=
|
||||
|
||||
# Regular expression which should only match functions or classes name which do
|
||||
# not require a docstring
|
||||
@@ -144,7 +145,7 @@ good-names=i,j,k,ex,Run,_
|
||||
bad-names=foo,bar,baz,toto,tutu,tata
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma
|
||||
bad-functions=map,filter,apply,inpu
|
||||
#bad-functions=map,filter,apply,inpu
|
||||
|
||||
|
||||
# try to find bugs in the code using type inference
|
||||
@@ -161,7 +162,7 @@ ignored-classes=SQLObjec
|
||||
|
||||
# When zope mode is activated, add a predefined set of Zope acquired attributes
|
||||
# to generated-members.
|
||||
zope=no
|
||||
#zope=no
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E0201 when accessed.
|
||||
@@ -199,7 +200,7 @@ additional-builtins=
|
||||
|
||||
# List of interface methods to ignore, separated by a comma. This is used for
|
||||
# instance to not check methods defines in Zope's Interface base class.
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
#ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp,build
|
||||
@@ -221,7 +222,7 @@ max-locals=15
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
max-branchs=12
|
||||
#max-branchs=12
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
max-statements=50
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.3.0
|
||||
Mininet 2.3.1b4
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
@@ -51,7 +51,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
If you're reading this, you've probably already done so, but the
|
||||
command to download the Mininet source code is:
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
git clone https://github.com/mininet/mininet.git
|
||||
|
||||
Note that the above git command will check out the latest and greatest
|
||||
Mininet (which we recommend!) If you want to run the last
|
||||
@@ -124,7 +124,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
Whichever version was installed last will be the default for `mn`.
|
||||
As long as Mininet is installed for the appropriate version of
|
||||
Python, you can run it using that versinon of Python:
|
||||
Python, you can run it using that version of Python:
|
||||
|
||||
python3 `which mn`
|
||||
python2 `which mn`
|
||||
@@ -147,7 +147,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
* clone the Mininet repository
|
||||
|
||||
git clone git://github.com/mininet/mininet.git
|
||||
git clone https://github.com/mininet/mininet.git
|
||||
|
||||
* install Mininet, the OpenFlow reference implementation, and
|
||||
Open vSwitch
|
||||
@@ -165,7 +165,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
Note that `install.sh -fnv `may not install all dependencies on Fedora,
|
||||
and many tests may still fail.
|
||||
|
||||
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM on Ubuntu/Debian
|
||||
|
||||
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
Mininet 2.3.0 License
|
||||
BSD 3-Clause License (original Mininet authors: Bob Lantz and Brandon Heller)
|
||||
|
||||
Copyright (c) 2013-2021 Open Networking Foundation
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
|
||||
The Leland Stanford Junior University
|
||||
Copyright (c) 2013-2022 Open Networking Foundation
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of The Leland Stanford Junior University
|
||||
|
||||
Original authors: Bob Lantz and Brandon Heller
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
We are making Mininet available for public use and benefit with the
|
||||
expectation that others will use, modify and enhance the Software and
|
||||
contribute those enhancements back to the community. However, since we
|
||||
would like to make the Software available for broadest use, with as few
|
||||
restrictions as possible permission is hereby granted, free of charge, to
|
||||
any person obtaining a copy of this Software to deal in the Software
|
||||
under the copyrights without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
The name and trademarks of copyright holder(s) may NOT be used in
|
||||
advertising or publicity pertaining to the Software or any derivatives
|
||||
without specific, written prior permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -26,14 +26,14 @@ clean:
|
||||
codecheck: $(PYSRC)
|
||||
-echo "Running code check"
|
||||
util/versioncheck.py
|
||||
pyflakes $(PYSRC)
|
||||
pyflakes3 $(PYSRC) || pyflakes $(PYSRC)
|
||||
pylint --rcfile=.pylint $(PYSRC)
|
||||
# Exclude miniedit from pep8 checking for now
|
||||
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
|
||||
|
||||
errcheck: $(PYSRC)
|
||||
-echo "Running check for errors only"
|
||||
pyflakes $(PYSRC)
|
||||
pyflakes3 $(PYSRC) || pyflakes $(PYSRC)
|
||||
pylint -E --rcfile=.pylint $(PYSRC)
|
||||
|
||||
test: $(MININET) $(TEST)
|
||||
@@ -47,7 +47,8 @@ slowtest: $(MININET)
|
||||
mininet/examples/test/runner.py -v
|
||||
|
||||
mnexec: mnexec.c $(MN) mininet/net.py
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) \
|
||||
-DVERSION=\"`PYTHONPATH=. $(PYMN) --version 2>&1`\" $< -o $@
|
||||
|
||||
install-mnexec: $(MNEXEC)
|
||||
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
|
||||
|
||||
@@ -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.0
|
||||
Mininet 2.3.1b4
|
||||
|
||||
[![Build Status][1]](https://github.com/mininet/mininet/actions)
|
||||
|
||||
@@ -70,7 +70,7 @@ Mininet includes:
|
||||
|
||||
### Python 3 Support
|
||||
|
||||
- Mininet 2.3.0 supports Python 3 and Python 2!
|
||||
- Mininet 2.3.1b4 supports Python 3 and Python 2
|
||||
|
||||
- You can install both the Python 3 and Python 2 versions of
|
||||
Mininet side by side, but the most recent installation will
|
||||
@@ -87,10 +87,14 @@ determine which Python version is used by default by `mn`.
|
||||
|
||||
### Other Enhancements and Information
|
||||
|
||||
- Support for Ubuntu 20.04 LTS (and 18.04 and 16.04)
|
||||
- Support for Ubuntu 22.04 LTS (and 20.04)
|
||||
|
||||
- More reliable testing and CI via github actions
|
||||
|
||||
- Preliminary support for cgroups v2 (and v1)
|
||||
|
||||
- Minor bug fixes (2.3.1)
|
||||
|
||||
- Additional information about this release and previous releases
|
||||
may be found in the release notes on http://docs.mininet.org.
|
||||
|
||||
@@ -120,9 +124,9 @@ Mininet mailing list, `mininet-discuss` at:
|
||||
Thanks again to all of the Mininet contributors and users!
|
||||
|
||||
Mininet is an open source project and is currently hosted
|
||||
at <https://github.com/mininet>. You are encouraged to download
|
||||
the code, examine it, modify it, and submit bug reports, bug fixes,
|
||||
feature requests, new features and other issues and pull requests.
|
||||
at <https://github.com/mininet>. You are encouraged to download,
|
||||
examine, and modify the code, and to submit bug reports, bug fixes,
|
||||
feature requests, new features, and other issues and pull requests.
|
||||
Thanks to everyone who has contributed code to the Mininet project
|
||||
(see CONTRIBUTORS for more info!) It is because of everyone's
|
||||
hard work that Mininet continues to grow and improve.
|
||||
|
||||
@@ -189,8 +189,9 @@ class MininetRunner( object ):
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
# pylint: disable=exec-used
|
||||
exec( compile( open( fileName ).read(), fileName, 'exec' ),
|
||||
customs, customs )
|
||||
with open( fileName ) as f:
|
||||
exec( compile( f.read(), fileName, 'exec' ),
|
||||
customs, customs )
|
||||
for name, val in customs.items():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ A simple example of configuring network and CPU bandwidth limits.
|
||||
|
||||
This example shows how to run an `sshd` process in each host, allowing
|
||||
you to log in via `ssh`. This requires connecting the Mininet data network
|
||||
to an interface in the root namespace (generaly the control network
|
||||
to an interface in the root namespace (generally the control network
|
||||
already lives in the root namespace, so it does not need to be explicitly
|
||||
connected.)
|
||||
|
||||
|
||||
@@ -27,9 +27,8 @@ h1.setIP( '10.0.0.1', 8 )
|
||||
root.setIP( '10.0.0.2', 8 )
|
||||
|
||||
info( "*** Creating banner file\n" )
|
||||
f = open( '/tmp/%s.banner' % h1.name, 'w' )
|
||||
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
|
||||
f.close()
|
||||
with open( '/tmp/%s.banner' % h1.name, 'w' ) as f:
|
||||
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
|
||||
|
||||
info( "*** Running sshd\n" )
|
||||
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
|
||||
|
||||
+7
-5
@@ -82,14 +82,13 @@ import sys
|
||||
import re
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
from mininet.node import Node, Host, OVSSwitch, Controller
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import LinearTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import quietRun, errRun, decode
|
||||
from mininet.util import quietRun, errRun, decode, StrictVersion
|
||||
from mininet.examples.clustercli import CLI
|
||||
from mininet.log import setLogLevel, debug, info, error
|
||||
from mininet.clean import addCleanupCallback
|
||||
@@ -314,7 +313,7 @@ class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
|
||||
kwargs.update( batch=True )
|
||||
super( RemoteOVSSwitch, self ).__init__( *args, **kwargs )
|
||||
|
||||
def isOldOVS( self ):
|
||||
def isOldOVS( self ): # pylint: disable=arguments-differ
|
||||
"Is remote switch using an old OVS version?"
|
||||
cls = type( self )
|
||||
if self.server not in cls.OVSVersions:
|
||||
@@ -376,8 +375,9 @@ class RemoteLink( Link ):
|
||||
Link.stop( self )
|
||||
self.tunnel = None
|
||||
|
||||
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
|
||||
node1=None, node2=None, deleteIntfs=True ):
|
||||
def makeIntfPair( self, # pylint: disable=arguments-renamed
|
||||
intfname1, intfname2, addr1=None, addr2=None,
|
||||
node1=None, node2=None, deleteIntfs=True ):
|
||||
"""Create pair of interfaces
|
||||
intfname1: name of interface 1
|
||||
intfname2: name of interface 2
|
||||
@@ -781,7 +781,9 @@ class MininetCluster( Mininet ):
|
||||
"Popen() for server connections"
|
||||
assert self # please pylint
|
||||
old = signal( SIGINT, SIG_IGN )
|
||||
# pylint: disable=consider-using-with
|
||||
conn = Popen( cmd, stdin=PIPE, stdout=PIPE, close_fds=True )
|
||||
# pylint: enable=consider-using-with
|
||||
signal( SIGINT, old )
|
||||
return conn
|
||||
|
||||
|
||||
@@ -69,7 +69,10 @@ class Console( Frame ):
|
||||
self.bindEvents()
|
||||
self.sendCmd( 'export TERM=dumb' )
|
||||
|
||||
self.outputHook = None
|
||||
def outputHook( _obj, _text):
|
||||
return True
|
||||
|
||||
self.outputHook = outputHook
|
||||
|
||||
def makeWidgets( self ):
|
||||
"Make a label, a text area, and a scroll bar."
|
||||
@@ -111,10 +114,8 @@ class Console( Frame ):
|
||||
self.text.insert( 'end', text )
|
||||
self.text.mark_set( 'insert', 'end' )
|
||||
self.text.see( 'insert' )
|
||||
outputHook = lambda x, y: True # make pylint happier
|
||||
if self.outputHook:
|
||||
outputHook = self.outputHook
|
||||
outputHook( self, text )
|
||||
if callable( self.outputHook ):
|
||||
self.outputHook( self, text )
|
||||
|
||||
def handleKey( self, event ):
|
||||
"If it's an interactive command, send it to the node."
|
||||
@@ -294,10 +295,10 @@ class ConsoleApp( Frame ):
|
||||
'switches': 'Switch',
|
||||
'controllers': 'Controller'
|
||||
}
|
||||
for name in titles:
|
||||
for name, title in titles.items():
|
||||
nodes = getattr( net, name )
|
||||
frame, consoles = self.createConsoles(
|
||||
cframe, nodes, width, titles[ name ] )
|
||||
cframe, nodes, width, title )
|
||||
self.consoles[ name ] = Object( frame=frame, consoles=consoles )
|
||||
self.selected = None
|
||||
self.select( 'hosts' )
|
||||
|
||||
+9
-4
@@ -68,11 +68,16 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
# the client's buffer fill rate
|
||||
popen = server.popen( 'iperf -yc -s -p 5001' )
|
||||
waitListening( client, server, 5001 )
|
||||
# ignore empty result from waitListening/telnet
|
||||
popen.stdout.readline()
|
||||
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
|
||||
result = decode( popen.stdout.readline() ).split( ',' )
|
||||
bps = float( result[ -1 ] )
|
||||
# ignore empty result from waitListening/telnet for old iperf
|
||||
svals = {}
|
||||
while not svals or int( svals[ 'rate' ] ) == 0:
|
||||
line = decode( popen.stdout.readline() )
|
||||
# Probably shouldn't depend on an internal method, but
|
||||
# this is the easiest way
|
||||
svals = Mininet._iperfVals( # pylint: disable=protected-access
|
||||
line, server.IP() )
|
||||
bps = float( svals[ 'rate' ] )
|
||||
popen.terminate()
|
||||
net.stop()
|
||||
updated = results.get( sched, [] )
|
||||
|
||||
@@ -83,9 +83,8 @@ def linearBandwidthTest( lengths ):
|
||||
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
|
||||
assert 'reno' in output
|
||||
|
||||
for datapath in switches:
|
||||
for datapath, Switch in switches.items():
|
||||
info( "*** testing", datapath, "datapath\n" )
|
||||
Switch = switches[ datapath ]
|
||||
results[ datapath ] = []
|
||||
link = partial( TCLink, delay='30ms', bw=100 )
|
||||
net = Mininet( topo=topo, switch=Switch,
|
||||
|
||||
+55
-59
@@ -18,16 +18,15 @@ import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from distutils.version import StrictVersion
|
||||
from functools import partial
|
||||
from optparse import OptionParser
|
||||
from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
from subprocess import call
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
|
||||
from mininet.log import info, debug, warn, setLogLevel
|
||||
from mininet.net import Mininet, VERSION
|
||||
from mininet.util import (netParse, ipAdd, quietRun,
|
||||
buildTopo, custom, customClass )
|
||||
buildTopo, custom, customClass, StrictVersion )
|
||||
from mininet.term import makeTerm, cleanUpScreens
|
||||
from mininet.node import (Controller, RemoteController, NOX, OVSController,
|
||||
CPULimitedHost, Host, Node,
|
||||
@@ -1363,7 +1362,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
# Tools
|
||||
for tool in self.tools:
|
||||
cmd = ( lambda t=tool: self.activate( t ) )
|
||||
cmd = partial( self.activate, tool )
|
||||
b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
|
||||
if tool in self.images:
|
||||
b.config( height=35, image=self.images[ tool ] )
|
||||
@@ -1421,10 +1420,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
def convertJsonUnicode(self, text):
|
||||
"Some part of Mininet don't like Unicode"
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
return text
|
||||
unicode = globals.get( 'unicode', str )
|
||||
if isinstance(text, dict):
|
||||
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
|
||||
if isinstance(text, list):
|
||||
@@ -1449,7 +1445,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
# Load application preferences
|
||||
if 'application' in loadedTopology:
|
||||
self.appPrefs = dict(self.appPrefs.items() + loadedTopology['application'].items())
|
||||
self.appPrefs.update(loadedTopology['application'])
|
||||
if "ovsOf10" not in self.appPrefs["openFlowVersions"]:
|
||||
self.appPrefs["openFlowVersions"]["ovsOf10"] = '0'
|
||||
if "ovsOf11" not in self.appPrefs["openFlowVersions"]:
|
||||
@@ -1607,7 +1603,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
def newTopology( self ):
|
||||
"New command."
|
||||
for widget in self.widgetToItem:
|
||||
for widget in tuple( self.widgetToItem ):
|
||||
self.deleteItem( self.widgetToItem[ widget ] )
|
||||
self.hostCount = 0
|
||||
self.switchCount = 0
|
||||
@@ -1635,10 +1631,10 @@ class MiniEdit( Frame ):
|
||||
hostsToSave = []
|
||||
switchesToSave = []
|
||||
controllersToSave = []
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
x1, y1 = self.canvas.coords( item )
|
||||
if 'Switch' in tags or 'LegacySwitch' in tags or 'LegacyRouter' in tags:
|
||||
nodeNum = self.switchOpts[name]['nodeNum']
|
||||
nodeToSave = {'number':str(nodeNum),
|
||||
@@ -1683,14 +1679,13 @@ class MiniEdit( Frame ):
|
||||
savingDictionary['application'] = self.appPrefs
|
||||
|
||||
try:
|
||||
f = open(fileName, 'wb')
|
||||
f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
|
||||
# pylint: disable=broad-except
|
||||
except Exception as er:
|
||||
with open(fileName, 'w') as f:
|
||||
f.write(
|
||||
json.dumps(savingDictionary,
|
||||
sort_keys=True,
|
||||
indent=4, separators=(',', ': ')))
|
||||
except Exception as er: # pylint: disable=broad-except
|
||||
warn( er, '\n' )
|
||||
# pylint: enable=broad-except
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def exportScript( self ):
|
||||
"Export command."
|
||||
@@ -1702,7 +1697,7 @@ class MiniEdit( Frame ):
|
||||
fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
|
||||
if len(fileName ) > 0:
|
||||
# debug( "Now saving under %s\n" % fileName )
|
||||
f = open(fileName, 'wb')
|
||||
f = open(fileName, 'w') # pylint: disable=consider-using-with
|
||||
|
||||
f.write("#!/usr/bin/env python\n")
|
||||
f.write("\n")
|
||||
@@ -1718,9 +1713,9 @@ class MiniEdit( Frame ):
|
||||
f.write("from subprocess import call\n")
|
||||
|
||||
inBandCtrl = False
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
|
||||
if 'Controller' in tags:
|
||||
opts = self.controllers[name]
|
||||
@@ -1746,9 +1741,9 @@ class MiniEdit( Frame ):
|
||||
f.write(" ipBase='"+self.appPrefs['ipBase']+"')\n")
|
||||
f.write("\n")
|
||||
f.write(" info( '*** Adding controller\\n' )\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
|
||||
if 'Controller' in tags:
|
||||
opts = self.controllers[name]
|
||||
@@ -1780,9 +1775,9 @@ class MiniEdit( Frame ):
|
||||
|
||||
# Save Switches and Hosts
|
||||
f.write(" info( '*** Add switches\\n')\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'LegacyRouter' in tags:
|
||||
f.write(" "+name+" = net.addHost('"+name+"', cls=Node, ip='0.0.0.0')\n")
|
||||
f.write(" "+name+".cmd('sysctl -w net.ipv4.ip_forward=1')\n")
|
||||
@@ -1820,9 +1815,9 @@ class MiniEdit( Frame ):
|
||||
|
||||
f.write("\n")
|
||||
f.write(" info( '*** Add hosts\\n')\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Host' in tags:
|
||||
opts = self.hostOpts[name]
|
||||
ip = None
|
||||
@@ -1915,9 +1910,9 @@ class MiniEdit( Frame ):
|
||||
f.write("\n")
|
||||
|
||||
f.write(" info( '*** Starting switches\\n')\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Switch' in tags or 'LegacySwitch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
ctrlList = ",".join(opts['controllers'])
|
||||
@@ -1926,9 +1921,9 @@ class MiniEdit( Frame ):
|
||||
f.write("\n")
|
||||
|
||||
f.write(" info( '*** Post configure switches and hosts\\n')\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
if opts['switchType'] == 'default':
|
||||
@@ -1956,9 +1951,9 @@ class MiniEdit( Frame ):
|
||||
if 'switchIP' in opts:
|
||||
if len(opts['switchIP']) > 0:
|
||||
f.write(" "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Host' in tags:
|
||||
opts = self.hostOpts[name]
|
||||
# Attach vlan interfaces
|
||||
@@ -1980,9 +1975,9 @@ class MiniEdit( Frame ):
|
||||
if len(nflowValues['nflowTarget']) > 0:
|
||||
nflowEnabled = False
|
||||
nflowSwitches = ''
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
@@ -2004,9 +1999,9 @@ class MiniEdit( Frame ):
|
||||
if len(sflowValues['sflowTarget']) > 0:
|
||||
sflowEnabled = False
|
||||
sflowSwitches = ''
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
@@ -2021,9 +2016,9 @@ class MiniEdit( Frame ):
|
||||
|
||||
f.write("\n")
|
||||
f.write(" CLI(net)\n")
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem:
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Host' in tags:
|
||||
opts = self.hostOpts[name]
|
||||
# Run User Defined Stop Command
|
||||
@@ -2445,7 +2440,8 @@ class MiniEdit( Frame ):
|
||||
line3.pack(pady=10 )
|
||||
line4.pack(pady=10 )
|
||||
line5.pack(pady=10 )
|
||||
hide = ( lambda about=about: about.withdraw() )
|
||||
def hide():
|
||||
about.withdraw()
|
||||
self.aboutBox = about
|
||||
# Hide on close rather than destroying window
|
||||
Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
|
||||
@@ -2620,9 +2616,9 @@ class MiniEdit( Frame ):
|
||||
info( 'New controller details for ' + name + ' = ' + str(self.controllers[name]), '\n' )
|
||||
# Find references to controller and change name
|
||||
if oldName != name:
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
switchName = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Switch' in tags:
|
||||
switch = self.switchOpts[switchName]
|
||||
if oldName in switch['controllers']:
|
||||
@@ -2703,9 +2699,9 @@ class MiniEdit( Frame ):
|
||||
tags = self.canvas.gettags(item)
|
||||
if 'Controller' in tags:
|
||||
# remove from switch controller lists
|
||||
for searchwidget in self.widgetToItem:
|
||||
for searchwidget, searchitem in self.widgetToItem.items():
|
||||
name = searchwidget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ searchwidget ] )
|
||||
tags = self.canvas.gettags( searchitem )
|
||||
if 'Switch' in tags:
|
||||
if widget['text'] in self.switchOpts[name]['controllers']:
|
||||
self.switchOpts[name]['controllers'].remove(widget['text'])
|
||||
@@ -2718,9 +2714,9 @@ class MiniEdit( Frame ):
|
||||
def buildNodes( self, net):
|
||||
# Make nodes
|
||||
info( "Getting Hosts and Switches.\n" )
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
# debug( name+' has '+str(tags), '\n' )
|
||||
|
||||
if 'Switch' in tags:
|
||||
@@ -2934,9 +2930,9 @@ class MiniEdit( Frame ):
|
||||
def postStartSetup( self ):
|
||||
|
||||
# Setup host details
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Host' in tags:
|
||||
newHost = self.net.get(name)
|
||||
opts = self.hostOpts[name]
|
||||
@@ -2961,9 +2957,9 @@ class MiniEdit( Frame ):
|
||||
if len(nflowValues['nflowTarget']) > 0:
|
||||
nflowEnabled = False
|
||||
nflowSwitches = ''
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
@@ -2991,9 +2987,9 @@ class MiniEdit( Frame ):
|
||||
if len(sflowValues['sflowTarget']) > 0:
|
||||
sflowEnabled = False
|
||||
sflowSwitches = ''
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
@@ -3036,9 +3032,9 @@ class MiniEdit( Frame ):
|
||||
#for switch in self.net.switches:
|
||||
# info( switch.name + ' ')
|
||||
# switch.start( self.net.controllers )
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Switch' in tags:
|
||||
opts = self.switchOpts[name]
|
||||
switchControllers = []
|
||||
@@ -3058,9 +3054,9 @@ class MiniEdit( Frame ):
|
||||
"Stop network."
|
||||
if self.net is not None:
|
||||
# Stop host details
|
||||
for widget in self.widgetToItem:
|
||||
for widget, item in self.widgetToItem.items():
|
||||
name = widget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
|
||||
tags = self.canvas.gettags( item )
|
||||
if 'Host' in tags:
|
||||
newHost = self.net.get(name)
|
||||
opts = self.hostOpts[name]
|
||||
@@ -3203,7 +3199,7 @@ class MiniEdit( Frame ):
|
||||
addDictOption( opts, LINKS, LINKDEF, 'link' )
|
||||
|
||||
opts.add_option( '--custom', type='string', default=None,
|
||||
help='read custom topo and node params from .py' +
|
||||
help='read custom topo and node params from .py ' +
|
||||
'file' )
|
||||
|
||||
self.options, self.args = opts.parse_args()
|
||||
|
||||
@@ -17,9 +17,9 @@ from mininet.topo import SingleSwitchTopo
|
||||
from mininet.log import info, setLogLevel
|
||||
|
||||
|
||||
def chunks( l, n ):
|
||||
def chunks( items, n ):
|
||||
"Divide list l into chunks of size n - thanks Stackoverflow"
|
||||
return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ]
|
||||
return [ items[ i: i + n ] for i in range( 0, len( items ), n ) ]
|
||||
|
||||
def startpings( host, targetips ):
|
||||
"Tell host to repeatedly ping targets"
|
||||
|
||||
@@ -18,11 +18,12 @@ from mininet.util import decode
|
||||
|
||||
def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
"Monitor set of files and return [(host, line)...]"
|
||||
devnull = open( '/dev/null', 'w' )
|
||||
devnull = open( '/dev/null', 'w' ) # pylint: disable=consider-using-with
|
||||
tails, fdToFile, fdToHost = {}, {}, {}
|
||||
for h, outfile in outfiles.items():
|
||||
tail = Popen( [ 'tail', '-f', outfile ],
|
||||
stdout=PIPE, stderr=devnull )
|
||||
tail = Popen( # pylint: disable=consider-using-with
|
||||
[ 'tail', '-f', outfile ],
|
||||
stdout=PIPE, stderr=devnull )
|
||||
fd = tail.stdout.fileno()
|
||||
tails[ h ] = tail
|
||||
fdToFile[ fd ] = tail.stdout
|
||||
|
||||
@@ -49,13 +49,19 @@ def perfTest( lossy=True ):
|
||||
net.start()
|
||||
info( "Dumping host connections\n" )
|
||||
dumpNodeConnections(net.hosts)
|
||||
info( "Testing bandwidth between h1 and h4\n" )
|
||||
info( "Testing bandwidth between h1 and h4 (lossy=%s)\n" % lossy )
|
||||
h1, h4 = net.getNodeByName('h1', 'h4')
|
||||
net.iperf( ( h1, h4 ), l4Type='UDP' )
|
||||
# Debugging
|
||||
h1.cmd('jobs')
|
||||
h4.cmd('jobs')
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
# Debug for now
|
||||
if 'testmode' in argv:
|
||||
setLogLevel( 'debug' )
|
||||
# Prevent test_simpleperf from failing due to packet loss
|
||||
perfTest( lossy=( 'testmode' not in argv ) )
|
||||
|
||||
@@ -19,9 +19,12 @@ class testSimplePerf( unittest.TestCase ):
|
||||
# 10 Mb/s, plus or minus 20% tolerance
|
||||
BW = 10
|
||||
TOLERANCE = .2
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
|
||||
p = pexpect.spawn(
|
||||
'python -m mininet.examples.simpleperf testmode' )
|
||||
# Log since this seems to be failing intermittently
|
||||
p.logfile = sys.stdout
|
||||
# check iperf results
|
||||
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
|
||||
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=90 )
|
||||
measuredBw = float( p.match.group( 1 ) )
|
||||
lowerBound = BW * ( 1 - TOLERANCE )
|
||||
upperBound = BW + ( 1 + TOLERANCE )
|
||||
@@ -30,5 +33,5 @@ class testSimplePerf( unittest.TestCase ):
|
||||
p.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
setLogLevel( 'debug' )
|
||||
unittest.main()
|
||||
|
||||
@@ -25,9 +25,8 @@ def treePing64():
|
||||
switches = { 'reference user': UserSwitch,
|
||||
'Open vSwitch kernel': OVSKernelSwitch }
|
||||
|
||||
for name in switches:
|
||||
for name, switch in switches.items():
|
||||
info( "*** Testing", name, "datapath\n" )
|
||||
switch = switches[ name ]
|
||||
network = TreeNet( depth=2, fanout=8, switch=switch,
|
||||
waitConnected=True )
|
||||
result = network.run( network.pingAll )
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../bin/mn
|
||||
+6
-4
@@ -21,7 +21,9 @@ 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 ]
|
||||
p = Popen( # pylint: disable=consider-using-with
|
||||
[ '/bin/sh', '-c', cmd ], stdout=PIPE )
|
||||
result = p.communicate()[ 0 ]
|
||||
return decode( result )
|
||||
|
||||
def killprocs( pattern ):
|
||||
@@ -51,9 +53,9 @@ class Cleanup( object ):
|
||||
|
||||
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
|
||||
"pings/noxes\n" )
|
||||
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
|
||||
'lt-nox_core ovs-openflowd ovs-controller'
|
||||
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
|
||||
zombies = ( 'controller ofprotocol ofdatapath ping nox_core '
|
||||
'lt-nox_core ovs-openflowd ovs-controller '
|
||||
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager ' )
|
||||
# Note: real zombie processes can't actually be killed, since they
|
||||
# are already (un)dead. Then again,
|
||||
# you can't connect to them either, so they're mostly harmless.
|
||||
|
||||
+11
-9
@@ -150,7 +150,7 @@ class CLI( Cmd ):
|
||||
' mininet> xterm h2\n\n'
|
||||
)
|
||||
|
||||
def do_help( self, line ): # pylint: disable=arguments-differ
|
||||
def do_help( self, line ): # pylint: disable=arguments-renamed
|
||||
"Describe available CLI commands."
|
||||
Cmd.do_help( self, line )
|
||||
if line == '':
|
||||
@@ -184,7 +184,7 @@ class CLI( Cmd ):
|
||||
try:
|
||||
# pylint: disable=eval-used
|
||||
result = eval( line, globals(), self.getLocals() )
|
||||
if not result:
|
||||
if result is None:
|
||||
return
|
||||
elif isinstance( result, str ):
|
||||
output( result + '\n' )
|
||||
@@ -352,13 +352,13 @@ class CLI( Cmd ):
|
||||
error( 'usage: source <file>\n' )
|
||||
return
|
||||
try:
|
||||
self.inputFile = open( args[ 0 ] )
|
||||
while True:
|
||||
line = self.inputFile.readline()
|
||||
if len( line ) > 0:
|
||||
self.onecmd( line )
|
||||
else:
|
||||
break
|
||||
with open( args[ 0 ] ) as self.inputFile:
|
||||
while True:
|
||||
line = self.inputFile.readline()
|
||||
if len( line ) > 0:
|
||||
self.onecmd( line )
|
||||
else:
|
||||
break
|
||||
except IOError:
|
||||
error( 'error reading file %s\n' % args[ 0 ] )
|
||||
self.inputFile.close()
|
||||
@@ -456,12 +456,14 @@ class CLI( Cmd ):
|
||||
try:
|
||||
bothPoller.poll()
|
||||
# XXX BL: this doesn't quite do what we want.
|
||||
# pylint: disable=condition-evals-to-constant
|
||||
if False and self.inputFile:
|
||||
key = self.inputFile.read( 1 )
|
||||
if key != '':
|
||||
node.write( key )
|
||||
else:
|
||||
self.inputFile = None
|
||||
# pylint: enable=condition-evals-to-constant
|
||||
if isReadable( self.inPoller ):
|
||||
key = self.stdin.read( 1 )
|
||||
node.write( key )
|
||||
|
||||
+5
-6
@@ -316,8 +316,9 @@ class TCIntf( Intf ):
|
||||
debug(" *** executing command: %s\n" % c)
|
||||
return self.cmd( c )
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
def config( self, bw=None, delay=None, jitter=None, loss=None,
|
||||
def config( # pylint: disable=arguments-renamed,arguments-differ
|
||||
self,
|
||||
bw=None, delay=None, jitter=None, loss=None,
|
||||
gro=False, txo=True, rxo=True,
|
||||
speedup=0, use_hfsc=False, use_tbf=False,
|
||||
latency_ms=None, enable_ecn=False, enable_red=False,
|
||||
@@ -539,9 +540,7 @@ class OVSLink( Link ):
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"See Link.__init__() for options"
|
||||
try:
|
||||
OVSSwitch
|
||||
except NameError:
|
||||
if 'OVSSwitch' not in globals():
|
||||
# pylint: disable=import-outside-toplevel,cyclic-import
|
||||
from mininet.node import OVSSwitch
|
||||
self.isPatchLink = False
|
||||
@@ -551,7 +550,7 @@ class OVSLink( Link ):
|
||||
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
# pylint: disable=arguments-differ, signature-differs
|
||||
# pylint: disable=arguments-renamed, arguments-differ, signature-differs
|
||||
def makeIntfPair( self, *args, **kwargs ):
|
||||
"Usually delegated to OVSSwitch"
|
||||
if self.isPatchLink:
|
||||
|
||||
+47
-34
@@ -98,18 +98,18 @@ from itertools import chain, groupby
|
||||
from math import ceil
|
||||
|
||||
from mininet.cli import CLI
|
||||
from mininet.log import info, error, debug, output, warn
|
||||
from mininet.log import info, error, output, warn
|
||||
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
|
||||
Controller )
|
||||
from mininet.nodelib import NAT
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
|
||||
macColonHex, ipStr, ipParse, netParse, ipAdd,
|
||||
waitListening, BaseString )
|
||||
waitListening, BaseString, fmtBps )
|
||||
from mininet.term import cleanUpScreens, makeTerms
|
||||
|
||||
# Mininet version: should be consistent with README and LICENSE
|
||||
VERSION = "2.3.0"
|
||||
VERSION = "2.3.1b4"
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
@@ -201,7 +201,7 @@ class Mininet( object ):
|
||||
sleep( delay )
|
||||
time += delay
|
||||
warn( 'Timed out after %d seconds\n' % time )
|
||||
for switch in remaining:
|
||||
for switch in remaining.copy():
|
||||
if not switch.connected():
|
||||
warn( 'Warning: %s is not connected to a controller\n'
|
||||
% switch.name )
|
||||
@@ -572,6 +572,10 @@ class Mininet( object ):
|
||||
info( controller.name + ' ' )
|
||||
controller.stop()
|
||||
info( '\n' )
|
||||
# Unlimit cfs hosts to speed up shutdown
|
||||
for h in self.hosts:
|
||||
if hasattr( h, 'unlimit' ):
|
||||
h.unlimit()
|
||||
if self.terms:
|
||||
info( '*** Stopping %i terms\n' % len( self.terms ) )
|
||||
self.stopXterms()
|
||||
@@ -671,7 +675,7 @@ class Mininet( object ):
|
||||
if timeout:
|
||||
opts = '-W %s' % timeout
|
||||
if dest.intfs:
|
||||
result = node.cmd( 'ping -c1 %s %s' %
|
||||
result = node.cmd( 'LANG=C ping -c1 %s %s' %
|
||||
(opts, dest.IP()) )
|
||||
sent, received = self._parsePing( result )
|
||||
else:
|
||||
@@ -782,18 +786,26 @@ class Mininet( object ):
|
||||
return self.pingFull( hosts=hosts )
|
||||
|
||||
@staticmethod
|
||||
def _parseIperf( iperfOutput ):
|
||||
"""Parse iperf output and return bandwidth.
|
||||
iperfOutput: string
|
||||
returns: result string"""
|
||||
r = r'([\d\.]+ \w+/sec)'
|
||||
m = re.findall( r, iperfOutput )
|
||||
if m:
|
||||
return m[-1]
|
||||
else:
|
||||
# was: raise Exception(...)
|
||||
error( 'could not parse iperf output: ' + iperfOutput )
|
||||
return ''
|
||||
def _iperfVals( iperfcsv, serverip ):
|
||||
"""Return iperf CSV as dict
|
||||
iperfcsv: iperf -y C output
|
||||
serverip: iperf server IP address
|
||||
"""
|
||||
fields = 'date cip cport sip sport ipver interval sent rate'
|
||||
lines = iperfcsv.strip().split('\n')
|
||||
svals = {}
|
||||
for line in lines:
|
||||
if ',' not in line:
|
||||
continue
|
||||
line = line.split( ',' )
|
||||
svals = dict( zip( fields.split(), line ) )
|
||||
# Return client in cip:cport, server in sip:sport
|
||||
if svals[ 'cip' ] == serverip:
|
||||
svals[ 'cip' ], svals[ 'sip' ] = (
|
||||
svals[ 'sip' ], svals[ 'cip' ] )
|
||||
svals[ 'cport' ], svals[ 'sport' ] = (
|
||||
svals[ 'sport' ], svals[ 'cport' ] )
|
||||
return svals
|
||||
|
||||
# XXX This should be cleaned up
|
||||
|
||||
@@ -803,7 +815,7 @@ class Mininet( object ):
|
||||
hosts: list of hosts; if None, uses first and last hosts
|
||||
l4Type: string, one of [ TCP, UDP ]
|
||||
udpBw: bandwidth target for UDP test
|
||||
fmt: iperf format argument if any
|
||||
fmt: scale/format argument (e.g. m/M for Mbps)
|
||||
seconds: iperf time to transmit
|
||||
port: iperf port
|
||||
returns: two-element array of [ server, client ] speeds
|
||||
@@ -816,33 +828,34 @@ class Mininet( object ):
|
||||
output( '*** Iperf: testing', l4Type, 'bandwidth between',
|
||||
client, 'and', server, '\n' )
|
||||
server.cmd( 'killall -9 iperf' )
|
||||
iperfArgs = 'iperf -p %d ' % port
|
||||
# Note: CSV mode
|
||||
iperfArgs = 'iperf -y C -p %d ' % port
|
||||
bwArgs = ''
|
||||
if l4Type == 'UDP':
|
||||
iperfArgs += '-u '
|
||||
bwArgs = '-b ' + udpBw + ' '
|
||||
elif l4Type != 'TCP':
|
||||
raise Exception( 'Unexpected l4 type: %s' % l4Type )
|
||||
if fmt:
|
||||
iperfArgs += '-f %s ' % fmt
|
||||
server.sendCmd( iperfArgs + '-s' )
|
||||
serverip = server.IP()
|
||||
if l4Type == 'TCP':
|
||||
if not waitListening( client, server.IP(), port ):
|
||||
if not waitListening( client, serverip, port ):
|
||||
raise Exception( 'Could not connect to iperf on port %d'
|
||||
% port )
|
||||
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
|
||||
server.IP() + ' ' + bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
servout = ''
|
||||
# We want the last *b/sec from the iperf server output
|
||||
# for TCP, there are two of them because of waitListening
|
||||
count = 2 if l4Type == 'TCP' else 1
|
||||
while len( re.findall( '/sec', servout ) ) < count:
|
||||
servout += server.monitor( timeoutms=5000 )
|
||||
cvals = self._iperfVals( cliout, serverip )
|
||||
serverout = ''
|
||||
# Wait for output from the client session
|
||||
while True:
|
||||
serverout += server.monitor( timeoutms=5000 )
|
||||
svals = self._iperfVals( serverout, serverip )
|
||||
# Check for the client's source/output port
|
||||
if ( svals and cvals[ 'sport' ] == svals[ 'sport' ]
|
||||
and int( svals[ 'rate' ] ) > 0 ):
|
||||
break
|
||||
server.sendInt()
|
||||
servout += server.waitOutput()
|
||||
debug( 'Server output: %s\n' % servout )
|
||||
result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
|
||||
serverout += server.waitOutput()
|
||||
result = [ fmtBps( svals[ 'rate'], fmt ),
|
||||
fmtBps( cvals[ 'rate' ], fmt ) ]
|
||||
if l4Type == 'UDP':
|
||||
result.insert( 0, udpBw )
|
||||
output( '*** Results: %s\n' % result )
|
||||
|
||||
+46
-26
@@ -57,7 +57,6 @@ import pty
|
||||
import re
|
||||
import signal
|
||||
import select
|
||||
from distutils.version import StrictVersion
|
||||
from re import findall
|
||||
from subprocess import Popen, PIPE
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
@@ -66,7 +65,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, BaseString, decode,
|
||||
encode, getincrementaldecoder, Python3, which )
|
||||
encode, getincrementaldecoder, Python3, which,
|
||||
StrictVersion )
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, TUN
|
||||
from mininet.link import Link, Intf, TCIntf, OVSIntf
|
||||
|
||||
@@ -219,7 +219,7 @@ class Node( object ):
|
||||
params: parameters to Popen()"""
|
||||
# Leave this is as an instance method for now
|
||||
assert self
|
||||
popen = Popen( cmd, **params )
|
||||
popen = Popen( cmd, **params ) # pylint: disable=consider-using-with
|
||||
debug( '_popen', cmd, popen.pid )
|
||||
return popen
|
||||
|
||||
@@ -685,8 +685,21 @@ class CPULimitedHost( Host ):
|
||||
|
||||
"CPU limited host"
|
||||
|
||||
def __init__( self, name, sched='cfs', **kwargs ):
|
||||
Host.__init__( self, name, **kwargs )
|
||||
def __init__( self, name, sched='cfs', **params ):
|
||||
Host.__init__( self, name, **params )
|
||||
# BL: Setting the correct period/quota is tricky, particularly
|
||||
# for RT. RT allows very small quotas, but the overhead
|
||||
# seems to be high. CFS has a mininimum quota of 1 ms, but
|
||||
# still does better with larger period values.
|
||||
self.period_us = params.get( 'period_us', 100000 )
|
||||
self.sched = sched
|
||||
self.cgroupsInited = False
|
||||
self.cgroup, self.rtprio = None, None
|
||||
|
||||
def initCgroups( self ):
|
||||
"Deferred cgroup initialization"
|
||||
if self.cgroupsInited:
|
||||
return
|
||||
# Initialize class if necessary
|
||||
if not CPULimitedHost.inited:
|
||||
CPULimitedHost.init()
|
||||
@@ -696,32 +709,26 @@ class CPULimitedHost( Host ):
|
||||
# We don't add ourselves to a cpuset because you must
|
||||
# specify the cpu and memory placement first
|
||||
errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
|
||||
# BL: Setting the correct period/quota is tricky, particularly
|
||||
# for RT. RT allows very small quotas, but the overhead
|
||||
# seems to be high. CFS has a mininimum quota of 1 ms, but
|
||||
# still does better with larger period values.
|
||||
self.period_us = kwargs.get( 'period_us', 100000 )
|
||||
self.sched = sched
|
||||
if sched == 'rt':
|
||||
if self.sched == 'rt':
|
||||
self.checkRtGroupSched()
|
||||
self.rtprio = 20
|
||||
|
||||
def cgroupSet( self, param, value, resource='cpu' ):
|
||||
"Set a cgroup parameter and return its value"
|
||||
cmd = 'cgset -r %s.%s=%s /%s' % (
|
||||
resource, param, value, self.name )
|
||||
quietRun( cmd )
|
||||
nvalue = int( self.cgroupGet( param, resource ) )
|
||||
if nvalue != value:
|
||||
cmd = [ 'cgset', '-r', "%s.%s=%s" % (
|
||||
resource, param, value), '/' + self.name ]
|
||||
errFail( cmd )
|
||||
nvalue = self.cgroupGet( param, resource )
|
||||
if nvalue != str( value ):
|
||||
error( '*** error: cgroupSet: %s set to %s instead of %s\n'
|
||||
% ( param, nvalue, value ) )
|
||||
return nvalue
|
||||
|
||||
def cgroupGet( self, param, resource='cpu' ):
|
||||
"Return value of cgroup parameter"
|
||||
cmd = 'cgget -r %s.%s /%s' % (
|
||||
resource, param, self.name )
|
||||
return int( quietRun( cmd ).split()[ -1 ] )
|
||||
pname = '%s.%s' % ( resource, param )
|
||||
cmd = 'cgget -n -r %s /%s' % ( pname, self.name )
|
||||
return quietRun( cmd )[len(pname)+1:].strip()
|
||||
|
||||
def cgroupDel( self ):
|
||||
"Clean up our cgroup"
|
||||
@@ -788,6 +795,8 @@ class CPULimitedHost( Host ):
|
||||
def cfsInfo( self, f ):
|
||||
"Internal method: return parameters for CFS bandwidth"
|
||||
pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
|
||||
if self.cgversion == 'cgroup2':
|
||||
pstr, qstr = 'max', ''
|
||||
# CFS uses wall clock time for period and CPU time for quota.
|
||||
quota = int( self.period_us * f * numCores() )
|
||||
period = self.period_us
|
||||
@@ -797,7 +806,7 @@ class CPULimitedHost( Host ):
|
||||
period = int( quota / f / numCores() )
|
||||
# Reset to unlimited on negative quota
|
||||
if quota < 0:
|
||||
quota = -1
|
||||
quota = 'max' if self.cgversion == 'cgroup2' else -1
|
||||
return pstr, qstr, period, quota
|
||||
|
||||
# BL comment:
|
||||
@@ -827,12 +836,16 @@ class CPULimitedHost( Host ):
|
||||
else:
|
||||
return
|
||||
# Set cgroup's period and quota
|
||||
setPeriod = self.cgroupSet( pstr, period )
|
||||
setQuota = self.cgroupSet( qstr, quota )
|
||||
if self.cgversion == 'cgroup':
|
||||
setPeriod = self.cgroupSet( pstr, period )
|
||||
setQuota = self.cgroupSet( qstr, quota )
|
||||
else:
|
||||
setQuota, setPeriod = self.cgroupSet(
|
||||
pstr, '%s %s' % (quota, period) ).split()
|
||||
if sched == 'rt':
|
||||
# Set RT priority if necessary
|
||||
sched = self.chrt()
|
||||
info( '(%s %d/%dus) ' % ( sched, setQuota, setPeriod ) )
|
||||
info( '(%s %s/%dus) ' % ( sched, setQuota, int( setPeriod ) ) )
|
||||
|
||||
def setCPUs( self, cores, mems=0 ):
|
||||
"Specify (real) cores that our cgroup can run on"
|
||||
@@ -857,6 +870,7 @@ class CPULimitedHost( Host ):
|
||||
cores: (real) core(s) this host can run on
|
||||
params: parameters for Node.config()"""
|
||||
r = Node.config( self, **params )
|
||||
self.initCgroups()
|
||||
# Was considering cpu={'cpu': cpu , 'sched': sched}, but
|
||||
# that seems redundant
|
||||
self.setParam( r, 'setCPUFrac', cpu=cpu )
|
||||
@@ -864,13 +878,19 @@ class CPULimitedHost( Host ):
|
||||
return r
|
||||
|
||||
inited = False
|
||||
cgversion = 'cgroup2'
|
||||
|
||||
@classmethod
|
||||
def init( cls ):
|
||||
"Initialization for CPULimitedHost class"
|
||||
mountCgroups()
|
||||
cls.cgversion = mountCgroups()
|
||||
cls.inited = True
|
||||
|
||||
def unlimit( self ):
|
||||
"Unlimit cpu for cfs"
|
||||
if self.sched == 'cfs' and self.params.get( 'cpu', -1 ) != -1:
|
||||
self.setCPUFrac( -1, sched=self.sched )
|
||||
|
||||
|
||||
# Some important things to note:
|
||||
#
|
||||
@@ -1518,7 +1538,7 @@ class Ryu( Controller ):
|
||||
"""Init.
|
||||
name: name to give controller.
|
||||
ryuArgs: modules to pass to Ryu (ryu.app.simple_switch)
|
||||
command: comand to run Ryu ('ryu run')"""
|
||||
command: command to run Ryu ('ryu run')"""
|
||||
if isinstance( ryuArgs, ( list, tuple ) ):
|
||||
ryuArgs = ' '.join( ryuArgs )
|
||||
cargs = kwargs.pop(
|
||||
|
||||
@@ -10,10 +10,9 @@ import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from distutils.version import StrictVersion
|
||||
from sys import stdout
|
||||
|
||||
from mininet.util import quietRun, pexpect
|
||||
from mininet.util import quietRun, pexpect, StrictVersion
|
||||
from mininet.clean import cleanup
|
||||
|
||||
|
||||
@@ -132,7 +131,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 )
|
||||
|
||||
+4
-2
@@ -336,9 +336,11 @@ class LinearTopo( Topo ):
|
||||
self.n = n
|
||||
|
||||
if n == 1:
|
||||
genHostName = lambda i, j: 'h%s' % i
|
||||
def genHostName( i, _j ):
|
||||
return 'h%s' % i
|
||||
else:
|
||||
genHostName = lambda i, j: 'h%ss%d' % ( j, i )
|
||||
def genHostName( i, j ):
|
||||
return 'h%ss%d' % ( j, i )
|
||||
|
||||
lastSwitch = None
|
||||
for i in irange( 1, k ):
|
||||
|
||||
+4
-2
@@ -53,9 +53,11 @@ class TorusTopo( Topo ):
|
||||
raise Exception( 'Please use 3x3 or greater for compatibility '
|
||||
'with 2.1' )
|
||||
if n == 1:
|
||||
genHostName = lambda loc, k: 'h%s' % ( loc )
|
||||
def genHostName( loc, _k ):
|
||||
return 'h%s' % ( loc )
|
||||
else:
|
||||
genHostName = lambda loc, k: 'h%sx%d' % ( loc, k )
|
||||
def genHostName( loc, k ):
|
||||
return 'h%sx%d' % ( loc, k )
|
||||
|
||||
hosts, switches, dpid = {}, {}, 0
|
||||
# Create and wire interior
|
||||
|
||||
+63
-21
@@ -5,6 +5,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from collections import namedtuple
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from functools import partial
|
||||
from os import O_NONBLOCK
|
||||
@@ -46,6 +47,7 @@ if Python3:
|
||||
"Encode buffer for Python 3"
|
||||
return buf.encode( Encoding )
|
||||
getincrementaldecoder = codecs.getincrementaldecoder( Encoding )
|
||||
|
||||
else:
|
||||
decode, encode = NullCodec.decode, NullCodec.encode
|
||||
|
||||
@@ -54,11 +56,16 @@ else:
|
||||
return NullCodec
|
||||
|
||||
try:
|
||||
# pylint: disable=import-error
|
||||
oldpexpect = None
|
||||
import pexpect as oldpexpect
|
||||
import packaging.version # replacement for distutils.version
|
||||
StrictVersion = packaging.version.parse
|
||||
except ImportError: # python2.7 lacks ModuleNotFoundError
|
||||
import distutils.version # pylint: disable=deprecated-module
|
||||
StrictVersion = distutils.version.StrictVersion
|
||||
|
||||
try:
|
||||
oldpexpect = None
|
||||
import pexpect as oldpexpect # pylint: disable=import-error
|
||||
|
||||
# pylint: enable=import-error
|
||||
class Pexpect( object ):
|
||||
"Custom pexpect that is compatible with str"
|
||||
@staticmethod
|
||||
@@ -97,11 +104,12 @@ def oldQuietRun( *cmd ):
|
||||
cmd = cmd[ 0 ]
|
||||
if isinstance( cmd, BaseString ):
|
||||
cmd = cmd.split( ' ' )
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
|
||||
out = ''
|
||||
popen = Popen( # pylint: disable=consider-using-with
|
||||
cmd, stdout=PIPE, stderr=STDOUT )
|
||||
# We can't use Popen.communicate() because it uses
|
||||
# select(), which can't handle
|
||||
# high file descriptor numbers! poll() can, however.
|
||||
out = ''
|
||||
readable = poll()
|
||||
readable.register( popen.stdout )
|
||||
while True:
|
||||
@@ -119,6 +127,8 @@ def oldQuietRun( *cmd ):
|
||||
# This is a bit complicated, but it enables us to
|
||||
# monitor command output as it is happening
|
||||
|
||||
CmdResult = namedtuple( 'CmdResult', 'out err ret' )
|
||||
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
def errRun( *cmd, **kwargs ):
|
||||
"""Run a command and return stdout, stderr and return code
|
||||
@@ -142,6 +152,7 @@ def errRun( *cmd, **kwargs ):
|
||||
elif isinstance( cmd, list ) and shell:
|
||||
cmd = " ".join( arg for arg in cmd )
|
||||
debug( '*** errRun:', cmd, '\n' )
|
||||
# pylint: disable=consider-using-with
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
|
||||
# We use poll() because select() doesn't work with large fd numbers,
|
||||
# and thus communicate() doesn't work either
|
||||
@@ -186,7 +197,8 @@ def errRun( *cmd, **kwargs ):
|
||||
if stderr == PIPE:
|
||||
popen.stderr.close()
|
||||
debug( out, err, returncode )
|
||||
return out, err, returncode
|
||||
return CmdResult( out, err, returncode )
|
||||
|
||||
# pylint: enable=too-many-branches
|
||||
|
||||
def errFail( *cmd, **kwargs ):
|
||||
@@ -195,11 +207,11 @@ def errFail( *cmd, **kwargs ):
|
||||
if ret:
|
||||
raise Exception( "errFail: %s failed with return code %s: %s"
|
||||
% ( cmd, ret, err ) )
|
||||
return out, err, ret
|
||||
return CmdResult( out, err, ret )
|
||||
|
||||
def quietRun( cmd, **kwargs ):
|
||||
"Run a command and return merged stdout and stderr"
|
||||
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
|
||||
return errRun( cmd, stderr=STDOUT, **kwargs ).out
|
||||
|
||||
def which(cmd, **kwargs ):
|
||||
"Run a command and return merged stdout and stderr"
|
||||
@@ -537,18 +549,25 @@ def fixLimits():
|
||||
"Mininet's performance may be affected.\n" )
|
||||
# pylint: enable=broad-except
|
||||
|
||||
|
||||
def mountCgroups():
|
||||
"Make sure cgroups file system is mounted"
|
||||
mounts = quietRun( 'grep cgroup /proc/mounts' )
|
||||
cgdir = '/sys/fs/cgroup'
|
||||
csdir = cgdir + '/cpuset'
|
||||
if ('cgroup %s' % cgdir not in mounts and
|
||||
'cgroups %s' % cgdir not in mounts):
|
||||
raise Exception( "cgroups not mounted on " + cgdir )
|
||||
if 'cpuset %s' % csdir not in mounts:
|
||||
errRun( 'mkdir -p ' + csdir )
|
||||
errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
|
||||
def mountCgroups( cgcontrol='cpu cpuacct cpuset' ):
|
||||
"""Mount cgroupfs if needed and return cgroup version
|
||||
cgcontrol: cgroup controllers to check ('cpu cpuacct cpuset')
|
||||
Returns: 'cgroup' | 'cgroup2' """
|
||||
# Try to read the cgroup controllers in cgcontrol
|
||||
cglist = cgcontrol.split()
|
||||
paths = ' '.join( '-g ' + c for c in cglist )
|
||||
cmd = 'cgget -n %s /' % paths
|
||||
result = errRun( cmd )
|
||||
# If it failed, mount cgroupfs and retry
|
||||
if result.ret or result.err or any(
|
||||
c not in result.out for c in cglist ):
|
||||
errFail( 'cgroupfs-mount' )
|
||||
result = errRun( cmd )
|
||||
errFail( cmd )
|
||||
# cpu.cfs_period_us is used for cgroup but not cgroup2
|
||||
if 'cpu.cfs_period_us' in result.out:
|
||||
return 'cgroup'
|
||||
return 'cgroup2'
|
||||
|
||||
def natural( text ):
|
||||
"To sort sanely/alphabetically: sorted( l, key=natural )"
|
||||
@@ -697,3 +716,26 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
|
||||
time += .5
|
||||
result = runCmd( cmd )
|
||||
return True
|
||||
|
||||
def unitScale( num, prefix='' ):
|
||||
"Return unit scale prefix and factor"
|
||||
scale = 'kMGTP'
|
||||
if prefix:
|
||||
pos = scale.lower().index( prefix.lower() )
|
||||
return prefix, float( 10**(3*(pos+1)) )
|
||||
num, prefix, factor = float( num ), '', 1
|
||||
for i, c in enumerate(scale, start=1):
|
||||
f = 10**(3*i)
|
||||
if num < f:
|
||||
break
|
||||
prefix, factor = c, f
|
||||
return prefix, float( factor )
|
||||
|
||||
def fmtBps( bps, prefix='', fmt='%.1f %sbits/sec' ):
|
||||
"""Return bps as iperf-style formatted rate string
|
||||
prefix: lock to specific prefix (k, M, G, ...)
|
||||
fmt: default format string for bps, prefix"""
|
||||
bps = float( bps )
|
||||
prefix, factor = unitScale( bps, prefix )
|
||||
bps /= factor
|
||||
return fmt % ( bps, prefix)
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <sched.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#if !defined(VERSION)
|
||||
#define VERSION "(devel)"
|
||||
@@ -96,18 +99,26 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int fd;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
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)
|
||||
switch(c) {
|
||||
case 'c':
|
||||
/* close file descriptors except stdin/out/error */
|
||||
for (fd = getdtablesize(); fd > 2; fd--)
|
||||
close(fd);
|
||||
if ((dir = opendir("/proc/self/fd"))) {
|
||||
while ((de = readdir(dir)))
|
||||
if ((fd = atoi(de->d_name)) > 2)
|
||||
close(fd);
|
||||
}
|
||||
/* fall back to old method if needed */
|
||||
else for (fd = getdtablesize(); fd > 2; fd--)
|
||||
close(fd);
|
||||
break;
|
||||
case 'd':
|
||||
/* detach from tty */
|
||||
|
||||
+23
-12
@@ -165,7 +165,7 @@ function mn_deps {
|
||||
$install gcc make socat psmisc xterm openssh-clients iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man net-tools
|
||||
$install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming \
|
||||
$install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming
|
||||
${PYPKG}-pexpect
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install gcc make socat psmisc xterm openssh iperf \
|
||||
@@ -174,28 +174,39 @@ function mn_deps {
|
||||
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
|
||||
else # Debian/Ubuntu
|
||||
pf=pyflakes
|
||||
pep8=pep8
|
||||
# Starting around 20.04, installing pyflakes instead of pyflakes3
|
||||
# causes Python 2 to be installed, which is exactly NOT what we want.
|
||||
if [ `expr $RELEASE '>=' 20.04` = "1" ]; then
|
||||
if [ "$DIST" = "Ubuntu" -a `expr $RELEASE '>=' 20.04` = "1" ]; then
|
||||
pf=pyflakes3
|
||||
fi
|
||||
# Debian 11 "bullseye" renamed
|
||||
# * pep8 to python3-pep8
|
||||
# * pyflakes to pyflakes3
|
||||
if [ "$DIST" = "Debian" -a `expr $RELEASE '>=' 11` = "1" ]; then
|
||||
pf=pyflakes3
|
||||
pep8=python3-pep8
|
||||
fi
|
||||
|
||||
$install gcc make socat psmisc xterm ssh iperf telnet \
|
||||
ethtool help2man $pf pylint pep8 \
|
||||
net-tools \
|
||||
${PYPKG}-pexpect ${PYPKG}-tk
|
||||
ethtool help2man $pf pylint $pep8 \
|
||||
net-tools ${PYPKG}-tk
|
||||
|
||||
# Install pip
|
||||
$install ${PYPKG}-pip || $install ${PYPKG}-pip-whl
|
||||
if ! ${PYTHON} -m pip -V; then
|
||||
if [ $PYTHON_VERSION == 2 ]; then
|
||||
wget https://bootstrap.pypa.io/2.6/get-pip.py
|
||||
wget https://bootstrap.pypa.io/pip/2.7/get-pip.py
|
||||
else
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
fi
|
||||
sudo ${PYTHON} get-pip.py
|
||||
rm get-pip.py
|
||||
fi
|
||||
${python} -m pip install pexpect
|
||||
$install iproute2 || $install iproute
|
||||
$install cgroup-tools || $install cgroup-bin
|
||||
$install cgroupfs-mount
|
||||
fi
|
||||
|
||||
echo "Installing Mininet core"
|
||||
@@ -211,7 +222,7 @@ function mn_doc {
|
||||
if ! $install doxygen-latex; then
|
||||
echo "doxygen-latex not needed"
|
||||
fi
|
||||
sudo pip install doxypy
|
||||
sudo pip2 install doxypy
|
||||
}
|
||||
|
||||
# The following will cause a full OF install, covering:
|
||||
@@ -231,7 +242,7 @@ function of {
|
||||
fi
|
||||
# was: git clone git://openflowswitch.org/openflow.git
|
||||
# Use our own fork on github for now:
|
||||
git clone git://github.com/mininet/openflow
|
||||
git clone https://github.com/mininet/openflow
|
||||
cd $BUILD_DIR/openflow
|
||||
|
||||
# Patch controller to handle more than 16 switches
|
||||
@@ -498,7 +509,7 @@ function ivs {
|
||||
|
||||
# Install IVS from source
|
||||
cd $BUILD_DIR
|
||||
git clone git://github.com/floodlight/ivs $IVS_SRC
|
||||
git clone https://github.com/floodlight/ivs $IVS_SRC
|
||||
cd $IVS_SRC
|
||||
git submodule update --init
|
||||
make
|
||||
@@ -518,7 +529,7 @@ function ryu {
|
||||
|
||||
# fetch RYU
|
||||
cd $BUILD_DIR/
|
||||
git clone git://github.com/osrg/ryu.git ryu
|
||||
git clone https://github.com/osrg/ryu.git ryu
|
||||
cd ryu
|
||||
|
||||
# install ryu
|
||||
@@ -629,7 +640,7 @@ function oftest {
|
||||
|
||||
# Install oftest:
|
||||
cd $BUILD_DIR/
|
||||
git clone git://github.com/floodlight/oftest
|
||||
git clone https://github.com/floodlight/oftest
|
||||
}
|
||||
|
||||
# Install cbench
|
||||
@@ -646,7 +657,7 @@ function cbench {
|
||||
cd $BUILD_DIR/
|
||||
# was: git clone git://gitosis.stanford.edu/oflops.git
|
||||
# Use our own fork on github for now:
|
||||
git clone git://github.com/mininet/oflops
|
||||
git clone https://github.com/mininet/oflops
|
||||
cd oflops
|
||||
sh boot.sh || true # possible error in autoreconf, so run twice
|
||||
sh boot.sh
|
||||
|
||||
@@ -25,7 +25,7 @@ if [ -e /etc/rc.local.backup ]; then
|
||||
fi
|
||||
# Fetch Mininet
|
||||
sudo apt-get -y -qq install git-core openssh-server
|
||||
git clone git://github.com/mininet/mininet
|
||||
git clone https://github.com/mininet/mininet
|
||||
# Optionally check out branch
|
||||
if [ "$1" != "" ]; then
|
||||
pushd mininet
|
||||
|
||||
Reference in New Issue
Block a user