Compare commits
52 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 | |||
| d7f399d7a1 | |||
| 1c42632978 | |||
| 5ef6e1dedc | |||
| f8e54cad47 | |||
| fc3152d724 | |||
| 9a3a3edf75 | |||
| 377a4b5af5 | |||
| 4a1dbac09b | |||
| 92fe3cc120 | |||
| 39ed456de2 | |||
| 5d4ec1ab9e | |||
| 6b90434b9c | |||
| c2fb4d2e8c | |||
| 7b240ce5b8 | |||
| c7de350a4e | |||
| 06dae1adc1 | |||
| f0c726a42f | |||
| a882d68a5f | |||
| 9b69c99f4e | |||
| 904796436d |
@@ -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.0b2
|
||||
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`
|
||||
@@ -137,9 +137,7 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
This takes about 4 minutes on our test system.
|
||||
|
||||
3.2. Native installation from source on Fedora 18+.
|
||||
|
||||
*This may be out of date.*
|
||||
3.2. (Experimental) Native installation from source on Fedora:
|
||||
|
||||
As root execute the following operations:
|
||||
|
||||
@@ -147,21 +145,9 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
yum install git
|
||||
|
||||
* create an user account (e.g. mininet) and add it to the wheel group
|
||||
|
||||
useradd [...] mininet
|
||||
usermod -a -G wheel mininet
|
||||
|
||||
* change the SElinux setting to permissive. It can be done
|
||||
temporarily with:
|
||||
|
||||
setenforce 0
|
||||
|
||||
then login with the new account (e.g. mininet) and do the following:
|
||||
|
||||
* 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
|
||||
@@ -177,7 +163,10 @@ like to contribute an installation script, we would welcome it!)
|
||||
|
||||
sudo mn --test pingall
|
||||
|
||||
4. Creating your own Mininet/OpenFlow tutorial VM
|
||||
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
|
||||
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
|
||||
@@ -211,7 +200,10 @@ like to contribute an installation script, we would welcome it!)
|
||||
support other Linux distributions.
|
||||
|
||||
|
||||
Good luck!
|
||||
As always, please feel free to submit issues or pull requests for
|
||||
installation-related features.
|
||||
|
||||
Good luck, and have fun!
|
||||
|
||||
Mininet Developers
|
||||
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
Mininet 2.3.0b2 License
|
||||
BSD 3-Clause License (original Mininet authors: Bob Lantz and Brandon Heller)
|
||||
|
||||
Copyright (c) 2013-2020 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)
|
||||
@@ -56,12 +57,15 @@ install-manpages: $(MANPAGES)
|
||||
install -D -t $(MANDIR) $(MANPAGES)
|
||||
|
||||
install: install-mnexec install-manpages
|
||||
# This seems to work on all pip versions
|
||||
$(PYTHON) -m pip uninstall -y mininet || true
|
||||
$(PYTHON) -m pip install .
|
||||
|
||||
develop: $(MNEXEC) $(MANPAGES)
|
||||
# Perhaps we should link these as well
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
$(PYTHON) -m pip uninstall -y mininet || true
|
||||
$(PYTHON) -m pip install -e . --no-binary :all:
|
||||
|
||||
man: $(MANPAGES)
|
||||
|
||||
@@ -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.0b2
|
||||
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.0b2 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
|
||||
@@ -82,12 +82,19 @@ determine which Python version is used by default by `mn`.
|
||||
|
||||
$ sudo python2 `which mn`
|
||||
|
||||
- More information regarding Python 3 and Python 2 support
|
||||
may be found in the release notes on http://docs.mininet.org.
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -114,22 +121,22 @@ Mininet mailing list, `mininet-discuss` at:
|
||||
|
||||
### Join Us
|
||||
|
||||
Thanks again to all of the Mininet contributors!
|
||||
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.
|
||||
|
||||
### Enjoy Mininet
|
||||
|
||||
Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
Have fun! We look forward to seeing what you will do with Mininet
|
||||
to change the networking world.
|
||||
|
||||
Bob Lantz
|
||||
Bob Lantz,
|
||||
on behalf of the Mininet Contributors
|
||||
|
||||
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Mininet runner
|
||||
@@ -101,7 +101,6 @@ CLI = None # Set below if needed
|
||||
# Locally defined tests
|
||||
def allTest( net ):
|
||||
"Run ping and iperf tests"
|
||||
net.waitConnected()
|
||||
net.start()
|
||||
net.ping()
|
||||
net.iperf()
|
||||
@@ -131,7 +130,6 @@ def runTests( mn, options ):
|
||||
if callable( testfn ):
|
||||
testfn( mn, *args, **kwargs )
|
||||
elif hasattr( mn, test ):
|
||||
mn.waitConnected()
|
||||
getattr( mn, test )( *args, **kwargs )
|
||||
else:
|
||||
raise Exception( 'Test %s is unknown - please specify one of '
|
||||
@@ -191,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:
|
||||
@@ -290,6 +289,9 @@ class MininetRunner( object ):
|
||||
help='prints the version and exits' )
|
||||
opts.add_option( '--wait', '-w', action='store_true',
|
||||
default=False, help='wait for switches to connect' )
|
||||
opts.add_option( '--twait', '-t', action='store', type='int',
|
||||
dest='wait',
|
||||
help='timed wait (s) for switches to connect' )
|
||||
opts.add_option( '--cluster', type='string', default=None,
|
||||
metavar='server1,server2...',
|
||||
help=( 'run on multiple servers (experimental!)' ) )
|
||||
@@ -386,6 +388,11 @@ class MininetRunner( object ):
|
||||
placement=PLACEMENT[ opts.placement ] )
|
||||
mininet.cli.CLI = ClusterCLI
|
||||
|
||||
# Wait for controllers to connect unless we're running null test
|
||||
if ( opts.test and opts.test != [ 'none' ] and
|
||||
isinstance( opts.wait, bool ) ):
|
||||
opts.wait = True
|
||||
|
||||
mn = Net( topo=topo,
|
||||
switch=switch, host=host, controller=controller, link=link,
|
||||
ipBase=opts.ipbase, inNamespace=opts.innamespace,
|
||||
|
||||
+1927
-821
File diff suppressed because it is too large
Load Diff
+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.)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
bind.py: Bind mount example
|
||||
|
||||
+64
-36
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
cluster.py: prototyping/experimentation for distributed Mininet,
|
||||
@@ -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
|
||||
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
|
||||
@@ -247,7 +246,7 @@ class RemoteMixin( object ):
|
||||
result = ''
|
||||
while True:
|
||||
poll = popen.poll()
|
||||
result += popen.stdout.read()
|
||||
result += decode( popen.stdout.read() )
|
||||
if poll is not None:
|
||||
break
|
||||
return result
|
||||
@@ -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
|
||||
@@ -440,13 +440,16 @@ class RemoteLink( Link ):
|
||||
# When we receive the character '@', it means that our
|
||||
# tunnel should be set up
|
||||
debug( 'Waiting for tunnel to come up...\n' )
|
||||
ch = tunnel.stdout.read( 1 )
|
||||
ch = decode( tunnel.stdout.read( 1 ) )
|
||||
if ch != '@':
|
||||
raise Exception( 'makeTunnel:\n',
|
||||
'Tunnel setup failed for',
|
||||
'%s:%s' % ( node1, node1.dest ), 'to',
|
||||
'%s:%s\n' % ( node2, node2.dest ),
|
||||
'command was:', cmd, '\n' )
|
||||
ch += decode( tunnel.stdout.read() )
|
||||
cmd = ' '.join( cmd )
|
||||
raise Exception( 'makeTunnel:\n'
|
||||
'Tunnel setup failed for '
|
||||
'%s:%s' % ( node1, node1.dest ) + ' to '
|
||||
'%s:%s\n' % ( node2, node2.dest ) +
|
||||
'command was: %s' % cmd + '\n' +
|
||||
'result was: ' + ch )
|
||||
# 3. Move interfaces if necessary
|
||||
for node in node1, node2:
|
||||
if not self.moveIntf( 'tap9', node ):
|
||||
@@ -778,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
|
||||
|
||||
@@ -848,27 +853,37 @@ class MininetCluster( Mininet ):
|
||||
if cfile:
|
||||
config.setdefault( 'controlPath', cfile )
|
||||
|
||||
@staticmethod
|
||||
def isLoopback( ipaddr ):
|
||||
"Is ipaddr an IPv4 loopback address?"
|
||||
return ipaddr.startswith( '127.' )
|
||||
|
||||
# pylint: disable=arguments-differ,signature-differs
|
||||
def addController( self, *args, **kwargs ):
|
||||
"Patch to update IP address to global IP address"
|
||||
controller = Mininet.addController( self, *args, **kwargs )
|
||||
loopback = '127.0.0.1'
|
||||
controllerIP = controller.IP()
|
||||
if ( not isinstance( controller, Controller ) or
|
||||
controller.IP() != loopback ):
|
||||
return None
|
||||
not self.isLoopback( controller.IP() ) ):
|
||||
return controller
|
||||
# Find route to a different server IP address
|
||||
serverIPs = [ ip for ip in self.serverIP.values()
|
||||
if ip is not controller.IP() ]
|
||||
if ip != controllerIP ]
|
||||
if not serverIPs:
|
||||
return None # 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()
|
||||
for remoteIP in serverIPs:
|
||||
# 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()
|
||||
if intf != 'lo':
|
||||
break
|
||||
if intf == 'lo':
|
||||
raise Exception( 'addController: could not find external '
|
||||
'interface/IP for %s' % controller )
|
||||
debug( 'adding', intf, 'to', controller )
|
||||
Intf( intf, node=controller ).updateIP()
|
||||
debug( controller, 'IP address updated to', controller.IP() )
|
||||
@@ -883,7 +898,10 @@ class MininetCluster( Mininet ):
|
||||
Mininet.buildFromTopo( self, *args, **kwargs )
|
||||
|
||||
|
||||
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
|
||||
# Default remote server for tests
|
||||
remoteServer = 'ubuntu2'
|
||||
|
||||
def testNsTunnels( remote=remoteServer, link=RemoteGRELink ):
|
||||
"Test tunnels between nodes in namespaces"
|
||||
net = Mininet( host=RemoteHost, link=link, waitConnected=True )
|
||||
h1 = net.addHost( 'h1')
|
||||
@@ -898,14 +916,14 @@ def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
|
||||
# This shows how node options may be used to manage
|
||||
# cluster placement using the net.add*() API
|
||||
|
||||
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testRemoteNet( remote=remoteServer, link=RemoteGRELink ):
|
||||
"Test remote Node classes"
|
||||
info( '*** Remote Node Test\n' )
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link,
|
||||
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
|
||||
link=link, controller=ClusterController,
|
||||
waitConnected=True )
|
||||
c0 = net.addController( 'c0' )
|
||||
# Make sure controller knows its non-loopback address
|
||||
Intf( 'eth0', node=c0 ).updateIP()
|
||||
info( "*** Creating local h1\n" )
|
||||
h1 = net.addHost( 'h1' )
|
||||
info( "*** Creating remote h2\n" )
|
||||
@@ -937,7 +955,7 @@ def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
|
||||
|
||||
remoteHosts = [ 'h2' ]
|
||||
remoteSwitches = [ 's2' ]
|
||||
remoteServer = 'ubuntu2'
|
||||
|
||||
|
||||
def HostPlacer( name, *args, **params ):
|
||||
"Custom Host() constructor which places hosts on servers"
|
||||
@@ -954,10 +972,21 @@ def SwitchPlacer( name, *args, **params ):
|
||||
return RemoteOVSSwitch( name, *args, **params )
|
||||
|
||||
def ClusterController( *args, **kwargs):
|
||||
"Custom Controller() constructor which updates its eth0 IP address"
|
||||
"Custom Controller() constructor which updates its intf IP address"
|
||||
intf = kwargs.pop( 'intf', '' )
|
||||
controller = Controller( *args, **kwargs )
|
||||
# Find out its IP address so that cluster switches can connect
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
if not intf:
|
||||
output = controller.cmd(
|
||||
r"ip a | egrep -o '\w+:\s\w+'" ).split( '\n' )
|
||||
for line in output:
|
||||
intf = line.split()[ -1 ]
|
||||
if intf != 'lo':
|
||||
break
|
||||
if intf == 'lo':
|
||||
raise Exception( 'Could not find non-loopback interface'
|
||||
'for %s' % controller )
|
||||
Intf( intf, node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
def testRemoteTopo( link=RemoteGRELink ):
|
||||
@@ -974,7 +1003,7 @@ def testRemoteTopo( link=RemoteGRELink ):
|
||||
# do random switch placement rather than completely random
|
||||
# host placement.
|
||||
|
||||
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testRemoteSwitches( remote=remoteServer, link=RemoteGRELink ):
|
||||
"Test with local hosts and remote switches"
|
||||
servers = [ 'localhost', remote]
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
@@ -992,7 +1021,7 @@ def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
|
||||
# functions, for maximum ease of use. MininetCluster() also
|
||||
# pre-flights and multiplexes server connections.
|
||||
|
||||
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
|
||||
def testMininetCluster( remote=remoteServer, link=RemoteGRELink ):
|
||||
"Test MininetCluster()"
|
||||
servers = [ 'localhost', remote ]
|
||||
topo = TreeTopo( depth=3, fanout=3 )
|
||||
@@ -1002,7 +1031,7 @@ def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
|
||||
net.pingAll()
|
||||
net.stop()
|
||||
|
||||
def signalTest( remote='ubuntu2'):
|
||||
def signalTest( remote=remoteServer):
|
||||
"Make sure hosts are robust to signals"
|
||||
h = RemoteHost( 'h0', server=remote )
|
||||
h.shell.send_signal( SIGINT )
|
||||
@@ -1017,7 +1046,6 @@ def signalTest( remote='ubuntu2'):
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
remoteServer = 'ubuntu2'
|
||||
remoteLink = RemoteSSHLink
|
||||
testRemoteTopo(link=remoteLink)
|
||||
testNsTunnels( remote=remoteServer, link=remoteLink )
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"CLI for Mininet Cluster Edition prototype demo"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
consoles.py: bring up a bunch of miniature consoles on a virtual network
|
||||
@@ -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' )
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Create a network where different switches are connected to
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This example creates a multi-controller network from semi-scratch by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
controlnet.py: Mininet with a custom control network
|
||||
|
||||
+10
-5
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
cpu.py: test iperf bandwidth for varying cpu limits
|
||||
@@ -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, [] )
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This example shows how to create an empty Mininet object
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This example shows how to add an interface (for example a real
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
example of using various TCIntf options.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
limit.py: example of using link and CPU limits
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test bandwidth (using iperf) on linear networks of varying size,
|
||||
@@ -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,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
linuxrouter.py: Example network with Linux IP router
|
||||
|
||||
+58
-63
@@ -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,9 +1697,9 @@ 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/python\n")
|
||||
f.write("#!/usr/bin/env python\n")
|
||||
f.write("\n")
|
||||
f.write("from mininet.net import Mininet\n")
|
||||
f.write("from mininet.node import Controller, RemoteController, OVSController\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,14 +2699,13 @@ class MiniEdit( Frame ):
|
||||
tags = self.canvas.gettags(item)
|
||||
if 'Controller' in tags:
|
||||
# remove from switch controller lists
|
||||
for serachwidget in self.widgetToItem:
|
||||
name = serachwidget[ 'text' ]
|
||||
tags = self.canvas.gettags( self.widgetToItem[ serachwidget ] )
|
||||
for searchwidget, searchitem in self.widgetToItem.items():
|
||||
name = searchwidget[ 'text' ]
|
||||
tags = self.canvas.gettags( searchitem )
|
||||
if 'Switch' in tags:
|
||||
if widget['text'] in self.switchOpts[name]['controllers']:
|
||||
self.switchOpts[name]['controllers'].remove(widget['text'])
|
||||
|
||||
for link in widget.links.values():
|
||||
for link in tuple( widget.links.values() ):
|
||||
# Delete from view and model
|
||||
self.deleteItem( link )
|
||||
del self.itemToWidget[ item ]
|
||||
@@ -2719,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:
|
||||
@@ -2935,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]
|
||||
@@ -2962,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]
|
||||
@@ -2992,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]
|
||||
@@ -3037,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 = []
|
||||
@@ -3059,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]
|
||||
@@ -3204,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()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple example of Mobility with Mininet
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This is a simple example that demonstrates multiple links
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
multiping.py: monitor multiple sets of hosts using ping
|
||||
@@ -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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple example of sending output to multiple files and
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This example shows how to create a network and run multiple tests.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Example to create a Mininet topology and connect it to the internet via NAT
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
natnet.py: Example network with NATs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Create a network with 5 hosts, numbered 1-4 and 9.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This example monitors a number of hosts using host.popen() and
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"Monitor multiple hosts using popen()/pmonitor()"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Build a simple network from scratch, using mininet primitives.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Build a simple network from scratch, using mininet primitives.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple example of setting network and CPU parameters
|
||||
@@ -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 ) )
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Create a network and start sshd(8) on each host.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Create a 1024-host network, and run the CLI on it.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
"Create a 64-node tree network, and test connectivity using ping."
|
||||
|
||||
@@ -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.
|
||||
|
||||
+20
-10
@@ -89,7 +89,15 @@ class CLI( Cmd ):
|
||||
if os.path.isfile( history_path ):
|
||||
read_history_file( history_path )
|
||||
set_history_length( 1000 )
|
||||
atexit.register( lambda: write_history_file( history_path ) )
|
||||
|
||||
def writeHistory():
|
||||
"Write out history file"
|
||||
try:
|
||||
write_history_file( history_path )
|
||||
except IOError:
|
||||
# Ignore probably spurious IOError
|
||||
pass
|
||||
atexit.register( writeHistory )
|
||||
|
||||
def run( self ):
|
||||
"Run our cmdloop(), catching KeyboardInterrupt"
|
||||
@@ -142,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 == '':
|
||||
@@ -176,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' )
|
||||
@@ -344,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()
|
||||
@@ -448,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:
|
||||
|
||||
+59
-42
@@ -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.0b2"
|
||||
VERSION = "2.3.1b4"
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
@@ -137,7 +137,9 @@ class Mininet( object ):
|
||||
autoStaticArp: set all-pairs static MAC addrs?
|
||||
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
|
||||
listenPort: base listening port to open; will be incremented for
|
||||
each additional switch in the net if inNamespace=False"""
|
||||
each additional switch in the net if inNamespace=False
|
||||
waitConnected: wait for switches to Connect?
|
||||
(False; True/None=wait indefinitely; time(s)=timed wait)"""
|
||||
self.topo = topo
|
||||
self.switch = switch
|
||||
self.host = host
|
||||
@@ -175,15 +177,17 @@ class Mininet( object ):
|
||||
if topo and build:
|
||||
self.build()
|
||||
|
||||
def waitConnected( self, timeout=5, delay=.5 ):
|
||||
"""wait for each switch to connect to a controller,
|
||||
up to 5 seconds
|
||||
timeout: time to wait, or None to wait indefinitely
|
||||
def waitConnected( self, timeout=None, delay=.5 ):
|
||||
"""wait for each switch to connect to a controller
|
||||
timeout: time to wait, or None or True to wait indefinitely
|
||||
delay: seconds to sleep per iteration
|
||||
returns: True if all switches are connected"""
|
||||
info( '*** Waiting for switches to connect\n' )
|
||||
time = 0
|
||||
time = 0.0
|
||||
remaining = list( self.switches )
|
||||
# False: 0s timeout; None: wait forever (preserve 2.2 behavior)
|
||||
if isinstance( timeout, bool ):
|
||||
timeout = None if timeout else 0
|
||||
while True:
|
||||
for switch in tuple( remaining ):
|
||||
if switch.connected():
|
||||
@@ -192,12 +196,12 @@ class Mininet( object ):
|
||||
if not remaining:
|
||||
info( '\n' )
|
||||
return True
|
||||
if timeout is not None and time > timeout:
|
||||
if timeout is not None and time >= timeout:
|
||||
break
|
||||
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 )
|
||||
@@ -559,7 +563,7 @@ class Mininet( object ):
|
||||
started.update( { s: s for s in success } )
|
||||
info( '\n' )
|
||||
if self.waitConn:
|
||||
self.waitConnected()
|
||||
self.waitConnected( self.waitConn )
|
||||
|
||||
def stop( self ):
|
||||
"Stop the controller(s), switches and hosts"
|
||||
@@ -568,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()
|
||||
@@ -667,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:
|
||||
@@ -778,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
|
||||
|
||||
@@ -799,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
|
||||
@@ -812,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 )
|
||||
|
||||
+59
-45
@@ -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:
|
||||
#
|
||||
@@ -1505,32 +1525,26 @@ class NOX( Controller ):
|
||||
|
||||
Controller.__init__( self, name,
|
||||
command=noxCoreDir + '/nox_core',
|
||||
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
|
||||
cargs='--libdir=/usr/local/lib -v '
|
||||
'-i ptcp:%s ' +
|
||||
' '.join( noxArgs ),
|
||||
cdir=noxCoreDir,
|
||||
**kwargs )
|
||||
|
||||
class Ryu( Controller ):
|
||||
"Controller to run Ryu application"
|
||||
def __init__( self, name, *ryuArgs, **kwargs ):
|
||||
"Ryu OpenFlow Controller"
|
||||
def __init__( self, name, ryuArgs='ryu.app.simple_switch',
|
||||
command='ryu run', **kwargs ):
|
||||
"""Init.
|
||||
name: name to give controller.
|
||||
ryuArgs: arguments and modules to pass to Ryu"""
|
||||
homeDir = quietRun( 'printenv HOME' ).strip( '\r\n' )
|
||||
ryuCoreDir = '%s/ryu/ryu/app/' % homeDir
|
||||
if not ryuArgs:
|
||||
warn( 'warning: no Ryu modules specified; '
|
||||
'running simple_switch only\n' )
|
||||
ryuArgs = [ ryuCoreDir + 'simple_switch.py' ]
|
||||
elif not isinstance( ryuArgs, ( list, tuple ) ):
|
||||
ryuArgs = [ ryuArgs ]
|
||||
|
||||
Controller.__init__( self, name,
|
||||
command='ryu-manager',
|
||||
cargs='--ofp-tcp-listen-port %s ' +
|
||||
' '.join( ryuArgs ),
|
||||
cdir=ryuCoreDir,
|
||||
**kwargs )
|
||||
name: name to give controller.
|
||||
ryuArgs: modules to pass to Ryu (ryu.app.simple_switch)
|
||||
command: command to run Ryu ('ryu run')"""
|
||||
if isinstance( ryuArgs, ( list, tuple ) ):
|
||||
ryuArgs = ' '.join( ryuArgs )
|
||||
cargs = kwargs.pop(
|
||||
'cargs', ryuArgs + ' --ofp-tcp-listen-port %s' )
|
||||
Controller.__init__( self, name, command=command,
|
||||
cargs=cargs, **kwargs )
|
||||
|
||||
|
||||
class RemoteController( Controller ):
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+1
-5
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python2
|
||||
|
||||
"""
|
||||
Convert simple documentation to epydoc/pydoctor-compatible markup
|
||||
@@ -83,7 +83,3 @@ if __name__ == '__main__':
|
||||
infile.close()
|
||||
os.close( outfid )
|
||||
call( [ 'doxypy', outname ] )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+31
-14
@@ -164,7 +164,9 @@ function mn_deps {
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
$install gcc make socat psmisc xterm openssh-clients iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man pyflakes pylint python-pep8 python-pexpect
|
||||
ethtool help2man net-tools
|
||||
$install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming
|
||||
${PYPKG}-pexpect
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install gcc make socat psmisc xterm openssh iperf \
|
||||
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
|
||||
@@ -172,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"
|
||||
@@ -205,10 +218,11 @@ function mn_deps {
|
||||
# Install Mininet documentation dependencies
|
||||
function mn_doc {
|
||||
echo "Installing Mininet documentation dependencies"
|
||||
$install doxygen doxypy texlive-fonts-recommended
|
||||
$install doxygen texlive-fonts-recommended
|
||||
if ! $install doxygen-latex; then
|
||||
echo "doxygen-latex not needed"
|
||||
fi
|
||||
sudo pip2 install doxypy
|
||||
}
|
||||
|
||||
# The following will cause a full OF install, covering:
|
||||
@@ -228,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
|
||||
@@ -407,7 +421,10 @@ function ovs {
|
||||
echo "Installing Open vSwitch..."
|
||||
|
||||
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
|
||||
$install openvswitch openvswitch-controller
|
||||
$install openvswitch
|
||||
if ! $install openvswitch-controller; then
|
||||
echo "openvswitch-controller not installed"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -492,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
|
||||
@@ -512,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
|
||||
@@ -623,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
|
||||
@@ -640,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
|
||||
@@ -791,7 +808,7 @@ function vm_clean {
|
||||
echo 'Removing SSH keys from /etc/ssh/'
|
||||
sudo rm -f /etc/ssh/*key*
|
||||
if [ ! -e /etc/rc.local ]; then
|
||||
echo '#!/usr/bin/bash' | sudo tee /etc/rc.local
|
||||
echo '#!/bin/bash' | sudo tee /etc/rc.local
|
||||
sudo chmod +x /etc/rc.local
|
||||
fi
|
||||
if ! grep mininet /etc/rc.local >& /dev/null; then
|
||||
|
||||
+15
-15
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python2
|
||||
|
||||
"""
|
||||
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
|
||||
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
|
||||
Python style
|
||||
|
||||
usage: unpep8 < old.py > new.py
|
||||
@@ -51,7 +51,7 @@ def fixUnderscoreTriplet( match ):
|
||||
def reinstateCapWords( text ):
|
||||
underscoreTriplet = re.compile( r'[A-Za-z0-9]_[A-Za-z0-9]' )
|
||||
return underscoreTriplet.sub( fixUnderscoreTriplet, text )
|
||||
|
||||
|
||||
def replaceTripleApostrophes( text ):
|
||||
"Replace triple apostrophes with triple quotes."
|
||||
return text.replace( "'''", '"""')
|
||||
@@ -60,7 +60,7 @@ def simplifyTripleQuotes( text ):
|
||||
"Fix single-line doc strings."
|
||||
r = re.compile( r'"""([^\"\n]+)"""' )
|
||||
return r.sub( r'"\1"', text )
|
||||
|
||||
|
||||
def insertExtraSpaces( text ):
|
||||
"Insert extra spaces inside of parentheses and brackets/curly braces."
|
||||
lparen = re.compile( r'\((?![\s\)])' )
|
||||
@@ -76,9 +76,9 @@ def insertExtraSpaces( text ):
|
||||
lcurly = re.compile( r'\{(?![\s\}])' )
|
||||
text = lcurly.sub( r'{ ', text )
|
||||
rcurly = re.compile( r'([^\s\{])(?=\})' )
|
||||
text = rcurly.sub( r'\1 ', text)
|
||||
text = rcurly.sub( r'\1 ', text)
|
||||
return text
|
||||
|
||||
|
||||
def fixDoxygen( text ):
|
||||
"""Translate @param foo to foo:, @return bar to returns: bar, and
|
||||
@author me to author: me"""
|
||||
@@ -96,11 +96,11 @@ def removeCommentFirstBlankLine( text ):
|
||||
"Remove annoying blank lines after first line in comments."
|
||||
line = re.compile( r'("""[^\n]*\n)\s*\n', re.MULTILINE )
|
||||
return line.sub( r'\1', text )
|
||||
|
||||
|
||||
def fixArgs( match, kwarg = re.compile( r'(\w+) = ' ) ):
|
||||
"Replace foo = bar with foo=bar."
|
||||
return kwarg.sub( r'\1=', match.group() )
|
||||
|
||||
|
||||
def fixKeywords( text ):
|
||||
"Change keyword argumentsfrom foo = bar to foo=bar."
|
||||
args = re.compile( r'\(([^\)]+)\)', re.MULTILINE )
|
||||
@@ -115,7 +115,7 @@ def fixKeywords( text ):
|
||||
def lineIter( text ):
|
||||
"Simple iterator over lines in text."
|
||||
for line in text.splitlines(): yield line
|
||||
|
||||
|
||||
def stringIter( strList ):
|
||||
"Yield strings in strList."
|
||||
for s in strList: yield s
|
||||
@@ -134,7 +134,7 @@ def restoreRegex( regex, old, new ):
|
||||
# This is a cheap hack, and it may not work 100%, since
|
||||
# it doesn't handle multiline strings.
|
||||
# However, it should be mostly harmless...
|
||||
|
||||
|
||||
def restoreStrings( oldText, newText ):
|
||||
"Restore strings from oldText into newText, returning result."
|
||||
oldLines, newLines = lineIter( oldText ), lineIter( newText )
|
||||
@@ -149,13 +149,13 @@ def restoreStrings( oldText, newText ):
|
||||
newLine = restoreRegex( tickStrings, oldLine, newLine )
|
||||
result += newLine + '\n'
|
||||
return result
|
||||
|
||||
|
||||
# This might be slightly controversial, since it uses
|
||||
# three spaces to line up multiline comments. However,
|
||||
# I much prefer it. Limitations: if you have deeper
|
||||
# indents in comments, they will be eliminated. ;-(
|
||||
|
||||
def fixComment( match,
|
||||
def fixComment( match,
|
||||
indentExp=re.compile( r'\n([ ]*)(?=[^/s])', re.MULTILINE ),
|
||||
trailingQuotes=re.compile( r'\s+"""' ) ):
|
||||
"Re-indent comment, and join trailing quotes."
|
||||
@@ -166,17 +166,17 @@ def fixComment( match,
|
||||
if len( originalIndent ) is not 0: indent += ' '
|
||||
comment = indentExp.sub( indent, comment )
|
||||
return originalIndent + trailingQuotes.sub( '"""', comment )
|
||||
|
||||
|
||||
def fixCommentIndents( text ):
|
||||
"Fix multiline comment indentation."
|
||||
comments = re.compile( r'^([ ]*)("""[^"]*""")$', re.MULTILINE )
|
||||
return comments.sub( fixComment, text )
|
||||
|
||||
|
||||
def removeBogusLinefeeds( text ):
|
||||
"Remove extra linefeeds at the end of single-line comments."
|
||||
bogusLfs = re.compile( r'"([^"\n]*)\n"', re.MULTILINE )
|
||||
return bogusLfs.sub( '"\1"', text)
|
||||
|
||||
|
||||
def convertFromPep8( program ):
|
||||
oldProgram = program
|
||||
# Program text transforms
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
from subprocess import check_output as co
|
||||
from sys import exit, version_info
|
||||
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2.7
|
||||
#!/usr/bin/python2
|
||||
|
||||
"""
|
||||
build.py: build a Mininet VM
|
||||
@@ -954,7 +954,8 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
|
||||
log( '* Creating COW disk', cow )
|
||||
run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) )
|
||||
log( '* Extracting kernel and initrd' )
|
||||
kernel, initrd, part = extractKernel( image, flavor=basename, imageDir=tmpdir )
|
||||
kernel, initrd, partnum = extractKernel(
|
||||
image, flavor=basename, imageDir=tmpdir )
|
||||
if LogToConsole:
|
||||
logfile = stdout
|
||||
else:
|
||||
@@ -962,7 +963,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
|
||||
suffix='.testlog', delete=False )
|
||||
log( '* Logging VM output to', logfile.name )
|
||||
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
|
||||
memory=memory, cpuCores=cpuCores, part=part )
|
||||
memory=memory, cpuCores=cpuCores, partnum=partnum )
|
||||
login( vm )
|
||||
log( '* Waiting for prompt after login' )
|
||||
vm.expect( prompt )
|
||||
|
||||
@@ -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