Compare commits

...
107 Commits
Author SHA1 Message Date
Brian O'Connor ab97dfa19c fixing no --nat issue 2014-11-24 18:36:57 -08:00
Brian O'Connor af1ccf93a5 Updating NAT class to use gateway interface
Also, passing CLI args to NAT constructor

fixes #437
2014-11-24 18:16:57 -08:00
lantz 015cd9e776 Merge pull request #443 from cdburkard/devel/cluster
use rcmd instead of quietRun when shutting down remote nodes
2014-11-24 12:30:52 -08:00
Bob Lantz 3d44bcdcc4 MiniNet -> Mininet 2014-11-23 17:32:11 -08:00
Bob Lantz 1817cbc3a4 Pass pyflakes 2014-11-23 17:31:43 -08:00
Bob Lantz e0bf8ece3c Minor code cleanup 2014-11-23 17:17:21 -08:00
Bob Lantz 37bdf14b49 Rename examples.{intfOptions,multiLink} -> {intfoptions,multilink} 2014-11-23 17:11:06 -08:00
Bob Lantz 292e69f89a Renamed to intfoptions.py for consistency 2014-11-23 17:07:32 -08:00
Bob Lantz dd876e69c8 DemoCLI -> ClusterCLI 2014-11-23 17:04:07 -08:00
lantz 4e644d7465 Merge pull request #450 from mininet/sw-cmd
adding deleteIntfs option to switches and corresponding CLI command
2014-11-24 11:27:59 -08:00
lantz 596fd9d0c6 Merge pull request #449 from cdburkard/devel/cluster_controlPaths
use ControlPersist ssh option to fix ControlPath shutdown
2014-11-23 14:59:11 -08:00
Bob Lantz 474f68600e Make pylint happier for no particular reason 2014-11-23 11:13:54 -08:00
Bob Lantz 50774e407c Remove unused imports 2014-11-23 11:11:03 -08:00
Bob Lantz 8e63e2c540 Fix undefined sleep() 2014-11-23 11:10:43 -08:00
Bob Lantz 273c4e9403 Fix typo. ;-p 2014-11-23 11:09:21 -08:00
Bob Lantz c273f49077 type( foo ) is bar -> isinstance( foo, bar ) 2014-11-23 11:06:12 -08:00
Bob Lantz 9a8bdfd765 use isinstance( obj, basestring) to allow unicode strings
fixes #448
2014-11-23 10:59:08 -08:00
Brian O'Connor 23dfbe4ae2 explicit param call in cli command 2014-11-21 18:35:37 -08:00
Brian O'Connor b57e5d93b8 adding deleteIntfs option to switches and corresponding CLI command 2014-11-21 18:33:06 -08:00
Bob Lantz f0f9907b9d Add setup() and dpctl() methods for LinuxBridge
Also adds docstrings.
Fixes #422
2014-11-19 09:32:56 -08:00
Bob Lantz c4fc630413 Correctly call cli() rather than CLI() 2014-11-19 09:18:05 -08:00
Bob Lantz aabbf29542 Revert to old "Adding controller" message 2014-11-19 08:58:17 -08:00
backb1 b7898befef raise exception when no prefixLen is set 2014-11-19 08:58:17 -08:00
backb1 6721f0655e Some clarifications 2014-11-19 08:58:17 -08:00
lantz d37d6ecd99 Merge pull request #241 from moijes12/fix44
(pending) Create test_switchdpidassignment.py
2014-11-20 14:14:14 -08:00
Bob Lantz e4db698184 Fix indentation error so we don't wait forever. 2014-11-19 05:58:36 -08:00
lantz e661a4b1c3 Merge pull request #447 from rlane/ivs-verbose-off
IVSSwitch: turn off verbose logging by default
2014-11-20 11:56:42 -08:00
Rich Lane f5164f86b7 IVSSwitch: turn off verbose logging by default
Most users don't need this much logging and it slows down the switch.
2014-11-19 18:24:49 -08:00
Bob Lantz 9e0c1549f4 Only uninstall ntpd on COW disk. 2014-11-19 04:59:23 -08:00
Bob Lantz 55b455e9ae Fix typo in depend() 2014-11-19 03:20:48 -08:00
cody burkard 34933ef741 use ControlPersist ssh option to create a ControlMaster connection that will not die when a node dies 2014-11-18 17:22:06 -08:00
cody burkard bbf94cdb63 use rcmd instead of quietRun when shutting down remote nodes 2014-11-17 17:55:06 -08:00
Brian O'Connor 593fc365d0 fixing --custom in mn (there was one two many selfs) 2014-11-17 17:46:24 -08:00
Brian O'Connor 1edf3515e3 Merge pull request #439 from cdburkard/patches/sudoers
fix sudoers file to allow -u option
2014-11-17 17:01:50 -08:00
cody burkard 0d271f9477 fix sudoers file to allow -u option 2014-11-17 16:19:28 -08:00
Bob Lantz 3534f77761 Remove extra git clone line 2014-11-12 18:31:10 -08:00
Bob Lantz 64bbaeccc4 Add doxygen-latex if needed (14.04+) 2014-11-12 17:45:36 -08:00
Bob Lantz de002b0d9a Remove ^S which was in this file (thanks emacs bindings) 2014-11-12 17:39:50 -08:00
Bob Lantz 307302ed32 Clarify dependencies 2014-11-12 15:52:53 -08:00
Bob Lantz a60f77ad36 Clarify checking out a version, and add Debian/Fedora 2014-11-12 15:50:41 -08:00
Bob Lantz 383c3fb3e4 in-line documentation link 2014-11-12 15:50:24 -08:00
Bob Lantz a64f8c28bd Don't blow away parameters that aren't specified in node.config() 2014-11-12 15:17:17 -08:00
Bob Lantz 377d1b1cd8 2.2.0b0 -> 2.2.0b1 2014-11-12 13:33:34 -08:00
Bob Lantz dde2263fe7 Disable shared SSH connections by default.
Note that we do still provide a default if you specify
ControlPath=True
2014-11-12 13:29:58 -08:00
Bob Lantz 434619053a Satisfy pyflakes by making a local cli variable 2014-11-12 13:12:22 -08:00
Bob Lantz b739cd11c8 Remove obsolete util.custom(), and make custom() a method 2014-11-12 13:08:36 -08:00
lantz bc6ef0dad8 Merge pull request #435 from mininet/devel/custom
Adding support for multiple custom files
2014-11-12 12:25:46 -08:00
Brian O'Connor 3ac0dd7093 Adding support for multiple custom files
Works in a few ways:
 * a single file: --custom foo.py (as before)
 * comma-separated list: --custom foo.py,bar.py
 * multiple custom args: --custom foo.py --custom bar.py
2014-11-11 03:14:46 -08:00
Bob Lantz 0f0fe82350 Fix typo, ugh. 2014-11-11 00:14:11 -08:00
Bob Lantz 6be4bfd026 Avoid mirrors.kernel.org for now 2014-11-10 23:31:08 -08:00
Bob Lantz d9d209f34d Update from official archive since mirror seems to be failing 2014-11-10 23:00:56 -08:00
Bob Lantz 2059786f7b Use sudo -n when talking to VM 2014-11-10 22:45:42 -08:00
lantz 6008f987d3 Merge pull request #436 from cdburkard/patches/plot
fix plot command to work when standard classes are present
2014-11-11 11:54:56 -08:00
Bob Lantz 635e8f11f3 Add -q option to apt-get for quieter logging 2014-11-10 19:37:11 -08:00
Bob Lantz 9b5fa1d7ed Always chdir() to current working directory. 2014-11-10 16:57:12 -08:00
Bob Lantz 1955e90493 Minor cleanup. 2014-11-10 16:50:24 -08:00
Bob Lantz 222e87daeb Rearrange init code slightly. 2014-11-10 16:48:20 -08:00
Bob Lantz a89ccb789e Fix problem of ssh'ing into "localhost" on a remote node. 2014-11-10 16:46:43 -08:00
lantz 2013b7ae81 Merge pull request #428 from cdburkard/patches/cluster_servers
standardize on localhost for local server's name
2014-11-10 16:23:59 -08:00
Bob Lantz 3e1100b71a Clarify MultiTopo docstrs and copy addLInk opts
Note: it's a bit confusing, but we need to copy the link
parameter dicts (since we update them with node info), but we
can share the node dicts. Perhaps we should copy the node
dicts as well...
2014-11-10 15:18:05 -08:00
lantz 086afe852b Merge pull request #434 from cdburkard/patches/cluster_x11
wrap the title string in quotes so that bash interprets it correctly
2014-11-10 14:12:39 -08:00
Bob Lantz abcdf18547 Uninstall ntpd to disable it more reliably 2014-11-10 14:09:35 -08:00
Bob Lantz 4a304688f4 Select TCP Reno and run iperf for a longer time interval
The hope is that this will make the results a bit more consistent
when running in a VM environment.
2014-11-10 12:39:55 -08:00
Bob Lantz d3377bf911 Add seconds option to iperf() 2014-11-10 12:37:54 -08:00
Bob Lantz 481cbea10c Update mininet docs ref to markdown format 2014-11-07 15:23:18 -08:00
Bob Lantz b817cbc0ed Update documentation link 2014-11-07 15:12:12 -08:00
lantz 15275048b6 Merge pull request #433 from mininet/devel/update-version-2.2b0
Initial update of README, text files and versions for 2.2.0b0
2014-11-07 16:58:12 -08:00
lantz 3baccfee3a Merge pull request #416 from mininet/devel/multitopo
Multi-link topology support
2014-11-07 16:16:25 -08:00
Bob Lantz bb76c21275 Use 2.2.0b0 for consistency with earlier Mininet releases 2014-11-07 15:08:15 -08:00
Bob Lantz c92c4efb66 Add a few clarifying comments 2014-11-07 13:47:03 -08:00
Bob Lantz 39203484a6 Make port1, port2 truly optional and don't pass them to Link() 2014-11-07 13:42:50 -08:00
Bob Lantz 2a2d605074 Get rid of paramDict and simplify things a bit 2014-11-07 13:40:00 -08:00
cody burkard 0676346aeb fix plot command to work when standard classes are present 2014-11-07 05:10:06 -08:00
cody burkard 93fdb69ee3 standardize on localhost for local server's name 2014-11-07 02:32:42 -08:00
Brian O'Connor c7921fe402 Merge pull request #432 from mininet/devel/fallback
Fall back to OVSBridge if no controller is available for default switch
2014-11-07 01:29:06 -08:00
cody burkard 3660e6d02f wrap the title string in quotes so that bash interprets it correctly 2014-11-07 01:11:40 -08:00
Bob Lantz 083322a217 Draft update for Mininet 2.2b0 2014-11-06 12:05:55 -08:00
Bob Lantz 8225105cf2 Fix to allow more flexible version numbers 2014-11-06 12:05:33 -08:00
Bob Lantz ccd3276dcf Raise exception if DefaultController cannot find a controller 2014-11-05 18:43:58 -08:00
Bob Lantz f51eddef6d Return controller correctly. 2014-11-05 18:41:00 -08:00
Bob Lantz 4f8aa1d8a0 Don't check rt_runtime_us for CFS scheduler 2014-11-05 16:49:28 -08:00
Bob Lantz 060d46a282 Set VM date based on host date.
This should fix #398 for real; note that if we try to shut down ntpd
right at boot, it doesn't work! ;-(

However, setting the Unix time in the traditional manner using seconds
since 1970 should do the trick!
2014-11-05 16:18:13 -08:00
Bob Lantz 820c3be7df Reorganize CFS and RT default/error conditions. 2014-11-04 17:20:35 -08:00
Bob Lantz a562ca1be3 Move RT check into its own method, and save value. 2014-11-04 16:27:34 -08:00
lantz 658761d953 Merge pull request #419 from cdburkard/patches/rt_failure_output
fix silent failures when rt cannot be assigned - will follow up on this
2014-11-04 16:09:25 -08:00
Bob Lantz 1b69ea13f5 Merge branch 'fallback' of https://github.com/thinred/mininet into thinred-fallback
Conflicts:
	bin/mn
	mininet/node.py
2014-11-04 03:24:16 -08:00
Bob Lantz 6e5ac34bc2 Update module comment. 2014-11-03 20:01:22 -08:00
lantz ec9f02c7ab Merge pull request #424 from cdburkard/devel/mergePrivate
merge HostWithPrivateDirs into Node
2014-11-04 15:09:20 -08:00
lantz f75bee62b5 Merge pull request #429 from cdburkard/patches/cluster_m
ensure we retrieve a single PID when run on a cluster node
2014-11-04 12:49:08 -08:00
Bob Lantz e77123cf0e Remove unnecessary 0 2014-11-03 14:25:09 -08:00
Bob Lantz 8dea57d271 Ignore link info when sorting links. 2014-11-03 14:20:01 -08:00
Bob Lantz 634761b8a7 Fix edges() and add convertTo() to Topo() (with keys option) 2014-11-03 12:59:53 -08:00
Bob Lantz 01aac350fa Remove unused edgeinfo 2014-11-03 12:43:52 -08:00
Cody Burkard 08d611f49b fix silent failures when rt cannot be assigned 2014-10-31 20:30:36 -07:00
cody burkard f66904ab90 ensure we retrieve a single PID when run on a cluster node 2014-10-31 08:23:00 -07:00
cody burkard 6a363f65e3 unmount private directories after use 2014-10-31 04:59:35 -07:00
cody burkard 736db20c9f merge HostWithPrivateDirs into Host 2014-10-31 04:43:10 -07:00
Bob Lantz ba8ea8f0cc Return (src, dst) in original order, and allow keys + data 2014-10-28 20:43:10 -07:00
Bob Lantz eab4ea3fb9 Minor fixes 2014-10-28 17:05:00 -07:00
cody burkard 06d9e4bba7 add example and test for multiple links 2014-10-28 16:10:46 -07:00
Bob Lantz 38ce329e7e Allow Mininet() to accept multi-link topos w/correct params. 2014-10-28 16:06:53 -07:00
Bob Lantz 94f088d7e8 Allow natural sort to accept non-strings. 2014-10-28 16:06:53 -07:00
Bob Lantz 89fb081983 First crack at fixing multiple links
* Makes MultiGraph more like networkx.multigraph
* Adds converTo method
* Synchronizes node1 with xxx1 in link options
2014-10-28 15:50:06 -07:00
Tomasz Buchert 39a3b73f85 fallback to ovsb when no OF controller is unavailable 2014-08-13 17:04:24 +02:00
Tomasz Buchert e8623fdc91 introducing OVSBridge 2014-08-13 16:55:00 +02:00
moijes12 2fc5e46f1b Update test_switchdpidassignment.py 2014-01-30 23:41:46 +05:30
moijes12 09b06509a8 Create test_switchdpidassignment.py
Regression tests to verify switch datapath ID assignment.
2013-11-07 09:49:36 -08:00
38 changed files with 918 additions and 478 deletions

No files matched your search

+12 -1
View File
@@ -7,32 +7,43 @@ or send a pull request.
Contributors include:
Mininet Core Team
Mininet Core Team (and alumni)
Bob Lantz
Brandon Heller
Nikhil Handigol
Vimal Jeyakumar
Brian O'Connor
Cody Burkard
Additional Mininet Contributors
Tomasz Buchert
Gustavo Pantuza Coelho Pinto
Fernando Cappi
Ryan Cox
Shaun Crampton
David Erickson
Glen Gibb
Andrew Ferguson
Eder Leao Fernandes
Gregory Gee
Jon Hall
Vitaly Ivanov
Rich Lane
Zi Shen Lim
Murphy McCauley
José Pedro Oliveira
James Page
Rich Lane
Rémy Léone
Angad Singh
Piyush Srivastava
Ed Swierk
Darshan Thaker
Andreas Wundsam
Isaku Yamahata
Baohua Yang
Thanks also to everyone who has submitted issues and pull
requests on github, and to our friendly mininet-discuss
+17 -11
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.1.0+
Mininet 2.2.0b1
---
The supported installation methods for Mininet are 1) using a
@@ -53,19 +53,27 @@ like to contribute an installation script, we would welcome it!)
Note that the above git command will check out the latest and greatest
Mininet (which we recommend!) If you want to run the last tagged/released
version of Mininet, use:
version of Mininet, you can look at the release tags using
git clone git://github.com/mininet/mininet
git checkout -b 2.1.0 2.1.0
cd mininet
git tag
If you are running Ubuntu, you may be able to use our handy
`install.sh` script, which is in `mininet/util`.
and then
git checkout <release tag>
where <release tag> is the release you want to check out.
If you are running Ubuntu, Debian, or Fedora, you may be able to use
our handy `install.sh` script, which is in `mininet/util`.
*WARNING: USE AT YOUR OWN RISK!*
`install.sh` is a bit intrusive and may possibly damage your OS
and/or home directory, by creating/modifying several directories
such as `mininet`, `openflow`, `oftest`, `pox`, etc..
such as `mininet`, `openflow`, `oftest`, `pox`, etc.. We recommend
trying it in a VM before trying it on a system you use from day to day.
Although we hope it won't do anything completely terrible, you may
want to look at the script before you run it, and you should make
sure your system and home directory are backed up just in case!
@@ -153,10 +161,8 @@ like to contribute an installation script, we would welcome it!)
* A Linux kernel compiled with network namespace support enabled
* An OpenFlow implementation (either the reference user or kernel
space implementations, or Open vSwitch.) Appropriate kernel
modules (e.g. tun and ofdatapath for the reference kernel
implementation) must be loaded.
* An compatible software switch such as Open vSwitch or
the Linux bridge.
* Python, `bash`, `ping`, `iperf`, etc.
+1 -1
View File
@@ -1,4 +1,4 @@
Mininet 2.1.0+ License
Mininet 2.2.0b1 License
Copyright (c) 2013 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
+35 -24
View File
@@ -3,7 +3,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
*The best way to emulate almost any network on your laptop!*
Version 2.1.0+
Mininet 2.2.0b1
### What is Mininet?
@@ -66,28 +66,33 @@ Mininet includes:
`mn -c`
### New features in 2.1.0+
### New features in this release
Mininet 2.1.0+ provides a number of bug fixes as well as
This release provides a number of bug fixes as well as
several new features, including:
* Convenient access to `Mininet()` as a dict of nodes
* X11 tunneling (wireshark in Mininet hosts, finally!)
* Accurate reflection of the `Mininet()` object in the CLI
* Automatically detecting and adjusting resource limits
* Automatic cleanup on failure of the `mn` command
* Preliminary support for running OVS in user space mode
* Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch
* support for installing the OpenFlow 1.3 versions of the reference
user switch and NOX from CPqD
* The ability to import modules from `mininet.examples`
* Improved OpenFlow 1.3 support
We have provided several new examples (which can easily be
imported to provide useful functionality) including:
- `mn --switch ovs,protocols=openflow13` starts OVS in 1.3 mode
- `install.sh -w` installs 1.3-compatible Wireshark dissector using
Loxigen
- `install.sh -y` installs Ryu 1.3-compatible controller
* Modeling separate control and data networks: `mininet.examples.controlnet`
* Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat`
* Creating per-host custom directories using bind mounts: `mininet.examples.bind`
* A new `nodelib.py` node library, and new `Node` types including
`LinuxBridge`, `OVSBridge`, `LinuxRouter` and `NAT`
* An improved MiniEdit GUI (`examples/miniedit.py`) - thanks to
Gregory Gee
* Support for multiple `--custom` arguments to `mn`
* Experimental cluster support - consult the
[documentation](http://docs.mininet.org) for details -
as well as `examples/cluster.py` and an experimental `--cluster`
option for topologies built with the default `Host` and `OVSSwitch`
classes:
`mn --cluster localhost,server1,server2`
Note that examples contain experimental features which might
"graduate" into mainline Mininet in the future, but they should
@@ -113,21 +118,27 @@ Mininet mailing list, `mininet-discuss` at:
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
### Contributing
### Join Us
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.
Thanks to everyone who has contributed to the project
(see CONTRIBUTORS for more info!)
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!
### Credits
The Mininet 2.1.0+ Team:
The Mininet Core Team:
* Bob Lantz
* Brian O'Connor
* Cody Burkard
Thanks again to all of the Mininet contributors, particularly Gregory
Gee for his work on MiniEdit.
+77 -39
View File
@@ -25,14 +25,14 @@ from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, warn, error
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
RYU, NOX, RemoteController, DefaultController,
UserSwitch, OVSSwitch,
RYU, NOX, RemoteController, findController, DefaultController,
UserSwitch, OVSSwitch, OVSBridge,
OVSLegacyKernelSwitch, IVSSwitch )
from mininet.nodelib import LinuxBridge
from mininet.link import Link, TCLink
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo, TorusTopo
from mininet.util import custom, customConstructor
from mininet.util import customConstructor, splitArgs
from mininet.util import buildTopo
from functools import partial
@@ -41,7 +41,7 @@ from functools import partial
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
RemoteOVSSwitch, RemoteLink,
SwitchBinPlacer, RandomPlacer )
from mininet.examples.clustercli import DemoCLI as ClusterCLI
from mininet.examples.clustercli import ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
@@ -54,27 +54,29 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'tree': TreeTopo,
'torus': TorusTopo }
SWITCHDEF = 'ovsk'
SWITCHDEF = 'default'
SWITCHES = { 'user': UserSwitch,
'ovs': OVSSwitch,
'ovsbr' : OVSBridge,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch,
'lxbr': LinuxBridge }
'lxbr': LinuxBridge,
'default': OVSSwitch }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
'rt': partial( CPULimitedHost, sched='rt' ),
'cfs': partial( CPULimitedHost, sched='cfs' ) }
CONTROLLERDEF = 'default'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'default': DefaultController,
'ryu': RYU,
'default': DefaultController, # Note: replaced below
'none': lambda name: None }
LINKDEF = 'default'
@@ -116,6 +118,7 @@ def version( *_args ):
print "%s" % VERSION
exit()
class MininetRunner( object ):
"Build, setup, and run Mininet."
@@ -129,6 +132,29 @@ class MininetRunner( object ):
self.setup()
self.begin()
def custom( self, option, opt_str, value, parser ):
"""Parse custom file and add params.
option: option e.g. --custom
opt_str: option string e.g. --custom
value: the value the follows the option
parser: option parser instance"""
files = []
if os.path.isfile( value ):
# Accept any single file (including those with commas)
files.append( value )
else:
# Accept a comma-separated list of filenames
files += value.split(',')
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
def setCustom( self, name, value ):
"Set custom parameters for MininetRunner."
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
@@ -142,26 +168,20 @@ class MininetRunner( object ):
# Add or modify global variable or class
globals()[ name ] = value
def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options."
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
def setNat( self, option, opt_str, value, parser ):
parser.values.nat = True
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-': #first arg, first char != '-'
value = parser.rargs.pop( 0 )
_, args, kwargs = splitArgs( opt_str + ',' + value )
parser.values.nat_args = args
parser.values.nat_kwargs = kwargs
else:
raise Exception( 'could not find custom file: %s' % fileName )
parser.values.nat_args = []
parser.values.nat_kwargs = {}
def parseArgs( self ):
"""Parse command-line args and return options object.
returns: opts parse options dict"""
if '--custom' in sys.argv:
index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1:
filename = sys.argv[ index + 1 ]
self.parseCustomFile( filename )
else:
raise Exception( 'Custom file name not found' )
desc = ( "The %prog utility creates Mininet network from the\n"
"command line. It can create parametrized topologies,\n"
@@ -179,9 +199,9 @@ class MininetRunner( object ):
opts.add_option( '--clean', '-c', action='store_true',
default=False, help='clean and exit' )
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py' +
'file' )
opts.add_option( '--custom', action='callback',
callback=self.custom,
type='string', help='read custom classes or params from .py file(s)' )
opts.add_option( '--test', type='choice', choices=TESTS,
default=TESTS[ 0 ],
help='|'.join( TESTS ) )
@@ -210,10 +230,14 @@ class MininetRunner( object ):
opts.add_option( '--pin', action='store_true',
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
opts.add_option( '--nat', action='store_true',
default=False, help="adds a NAT to the topology "
"that connects Mininet to the physical network" )
opts.add_option( '--version', action='callback', callback=version )
opts.add_option( '--nat', action='callback', callback=self.setNat,
help="adds a NAT to the topology that connects Mininet hosts"
" to the physical network."
" Warning: This may route any traffic on the machine that uses Mininet's"
" IP subnet into the Mininet network. If you need to change"
" Mininet's IP subnet, see the --ipbase option." )
opts.add_option( '--version', action='callback', callback=version,
help='prints the version and exits' )
opts.add_option( '--cluster', type='string', default=None,
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
@@ -244,14 +268,28 @@ class MininetRunner( object ):
def begin( self ):
"Create and run mininet."
global CLI
if self.options.clean:
cleanup()
exit()
start = time.time()
if self.options.controller == 'default':
# Update default based on available controllers
CONTROLLERS[ 'default' ] = findController()
if CONTROLLERS[ 'default' ] is None:
if self.options.switch == 'default':
# Fall back to OVS Bridge, which does not use an OF controller
info( '*** No default OpenFlow controller found for default switch!\n' )
info( '*** Falling back to OVS Bridge\n' )
self.options.switch = 'ovsbr'
self.options.controller = 'none'
elif self.options.switch in ( 'ovsbr', 'lxbr' ):
self.options.controller = 'none'
else:
raise Exception( "Could not find a default controller for switch %s" %
self.options.switch )
topo = buildTopo( TOPOS, self.options.topo )
switch = customConstructor( SWITCHES, self.options.switch )
host = customConstructor( HOSTS, self.options.host )
@@ -277,11 +315,11 @@ class MininetRunner( object ):
print "Please specify --innamespace OR --cluster"
exit()
Net = MininetWithControlNet if inNamespace else Mininet
cli = ClusterCLI if cluster else CLI
if cluster:
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
CLI = ClusterCLI
Net = partial( MininetCluster, servers=cluster.split( ',' ),
placement=PLACEMENT[ self.options.placement ] )
@@ -294,12 +332,12 @@ class MininetRunner( object ):
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
if self.options.nat:
nat = mn.addNAT()
if self.options.ensure_value( 'nat', False ):
nat = mn.addNAT( *self.options.nat_args, **self.options.nat_kwargs )
nat.configDefault()
if self.options.pre:
CLI( mn, script=self.options.pre )
cli( mn, script=self.options.pre )
test = self.options.test
test = ALTSPELLING.get( test, test )
@@ -314,13 +352,13 @@ class MininetRunner( object ):
mn.ping()
mn.iperf()
elif test == 'cli':
CLI( mn )
cli( mn )
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
if self.options.post:
CLI( mn, script=self.options.post )
cli( mn, script=self.options.post )
mn.stop()
+5 -8
View File
@@ -35,10 +35,10 @@ on '/var/mn'
"""
from mininet.net import Mininet
from mininet.node import Host, HostWithPrivateDirs
from mininet.node import Host
from mininet.cli import CLI
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel, info, debug
from mininet.log import setLogLevel, info
from functools import partial
@@ -51,15 +51,12 @@ def testHostWithPrivateDirs():
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/var/run', '/tmp/%(name)s/var/run' ),
'/var/mn' ]
host = partial( HostWithPrivateDirs,
host = partial( Host,
privateDirs=privateDirs )
net = Mininet( topo=topo, host=host )
net.start()
directories = []
for directory in privateDirs:
directories.append( directory[ 0 ]
if isinstance( directory, tuple )
else directory )
directories = [ directory[ 0 ] if isinstance( directory, tuple )
else directory for directory in privateDirs ]
info( 'Private Directories:', directories, '\n' )
CLI( net )
net.stop()
+51 -37
View File
@@ -83,7 +83,7 @@ from mininet.util import quietRun, makeIntfPair, errRun, retry
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from signal import signal, SIGINT, SIGHUP, SIG_IGN
from signal import signal, SIGINT, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
import os
from random import randrange
@@ -112,34 +112,37 @@ class RemoteMixin( object ):
'-o', 'BatchMode=yes',
'-o', 'ForwardAgent=yes', '-tt' ]
def __init__( self, name, server=None, user=None, serverIP=None,
controlPath='/tmp/mn-%r@%h:%p', splitInit=False, **kwargs):
def __init__( self, name, server='localhost', user=None, serverIP=None,
controlPath=False, splitInit=False, **kwargs):
"""Instantiate a remote node
name: name of remote node
server: remote server (optional)
user: user on remote server (optional)
controlPath: ssh control path template (optional)
controlPath: specify shared ssh control path (optional)
splitInit: split initialization?
**kwargs: see Node()"""
# We connect to servers by IP address
if server == 'localhost':
server = None
self.server = server
self.serverIP = serverIP if serverIP else self.findServerIP( server )
self.server = server if server else 'localhost'
self.serverIP = serverIP if serverIP else self.findServerIP( self.server )
self.user = user if user else self.findUser()
if self.user and self.server:
self.dest = '%s@%s' % ( self.user, self.serverIP )
else:
self.dest = None
if controlPath is True:
# Set a default control path for shared SSH connections
controlPath = '/tmp/mn-%r@%h:%p'
self.controlPath = controlPath
self.sshcmd = []
if self.dest:
self.splitInit = splitInit
if self.user and self.server != 'localhost':
self.dest = '%s@%s' % ( self.user, self.serverIP )
self.sshcmd = [ 'sudo', '-E', '-u', self.user ] + self.sshbase
if self.controlPath:
self.sshcmd += [ '-o', 'ControlPath=' + self.controlPath,
'-o', 'ControlMaster=auto' ]
'-o', 'ControlMaster=auto',
'-o', 'ControlPersist=' + '1' ]
self.sshcmd = self.sshcmd + [ self.dest ]
self.splitInit = splitInit
self.isRemote = True
else:
self.dest = None
self.sshcmd = []
self.isRemote = False
super( RemoteMixin, self ).__init__( name, **kwargs )
@staticmethod
@@ -160,20 +163,14 @@ class RemoteMixin( object ):
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
@classmethod
def findServerIP( cls, server, intf='eth0' ):
def findServerIP( cls, server ):
"Return our server's IP address"
# Check for this server
if not server:
output = quietRun( 'ifconfig %s' % intf )
# Otherwise, handle remote server
else:
# First, check for an IP address
if server:
ipmatch = cls._ipMatchRegex.findall( server )
if ipmatch:
return ipmatch[ 0 ]
# Otherwise, look up remote server
output = quietRun( 'getent ahostsv4 %s' % server )
# First, check for an IP address
ipmatch = cls._ipMatchRegex.findall( server )
if ipmatch:
return ipmatch[ 0 ]
# Otherwise, look up remote server
output = quietRun( 'getent ahostsv4 %s' % server )
ips = cls._ipMatchRegex.findall( output )
ip = ips[ 0 ] if ips else None
return ip
@@ -181,7 +178,7 @@ class RemoteMixin( object ):
# Command support via shell process in namespace
def startShell( self, *args, **kwargs ):
"Start a shell process for running commands"
if self.dest:
if self.isRemote:
kwargs.update( mnopts='-c' )
super( RemoteMixin, self ).startShell( *args, **kwargs )
if self.splitInit:
@@ -231,7 +228,7 @@ class RemoteMixin( object ):
returns: Popen() object"""
if type( cmd ) is str:
cmd = cmd.split()
if self.dest:
if self.isRemote:
if sudo:
cmd = [ 'sudo', '-E' ] + cmd
if tt:
@@ -259,6 +256,13 @@ class RemoteMixin( object ):
return super( RemoteMixin, self).addIntf( *args,
moveIntfFn=RemoteLink.moveIntf, **kwargs )
def cleanup( self ):
"Help python collect its garbage."
# Intfs may end up in root NS
for intfName in self.intfNames():
if self.name in intfName:
self.rcmd( 'ip link del ' + intfName )
self.shell = None
class RemoteNode( RemoteMixin, Node ):
"A node on a remote server"
@@ -312,9 +316,9 @@ class RemoteLink( Link ):
(override this method [and possibly delete()]
to change link type)"""
node1, node2 = self.node1, self.node2
server1 = getattr( node1, 'server', None )
server2 = getattr( node2, 'server', None )
if not server1 and not server2:
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == 'localhost' and server2 == 'localhost':
# Local link
return makeIntfPair( intfname1, intfname2, addr1, addr2 )
elif server1 == server2:
@@ -346,6 +350,13 @@ class RemoteLink( Link ):
def makeTunnel( self, node1, node2, intfname1, intfname2,
addr1=None, addr2=None ):
"Make a tunnel across switches on different servers"
# We should never try to create a tunnel to ourselves!
assert node1.server != 'localhost' or node2.server != 'localhost'
# And we can't ssh into this server remotely as 'localhost',
# so try again swappping node1 and node2
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
# 1. Create tap interfaces
for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename
@@ -596,8 +607,8 @@ class MininetCluster( Mininet ):
'link': RemoteLink,
'precheck': True }
params.update( kwargs )
servers = params.pop( 'servers', [ None ] )
servers = [ s if s != 'localhost' else None for s in servers ]
servers = params.pop( 'servers', [ 'localhost' ] )
servers = [ s if s else 'localhost' for s in servers ]
self.servers = servers
self.serverIP = params.pop( 'serverIP', {} )
if not self.serverIP:
@@ -671,7 +682,10 @@ class MininetCluster( Mininet ):
switches=self.topo.switches(),
links=self.topo.links() )
for node in nodes:
config = self.topo.node_info[ node ]
config = self.topo.nodeInfo( node )
# keep local server name consistent accross nodes
if 'server' in config.keys() and config[ 'server' ] == None:
config[ 'server' ] = 'localhost'
server = config.setdefault( 'server', placer.place( node ) )
if server:
config.setdefault( 'serverIP', self.serverIP[ server ] )
+2 -2
View File
@@ -5,8 +5,8 @@ A sanity check for cluster edition
'''
from mininet.examples.cluster import MininetCluster
from mininet.log import info, setLogLevel
from mininet.examples.clustercli import DemoCLI as CLI
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
from mininet.topo import SingleSwitchTopo
def clusterSanity():
+4 -4
View File
@@ -8,7 +8,7 @@ from mininet.log import output, error
nx, graphviz_layout, plt = None, None, None # Will be imported on demand
class DemoCLI( CLI ):
class ClusterCLI( CLI ):
"CLI with additional commands for Cluster Edition demo"
@staticmethod
@@ -32,6 +32,7 @@ class DemoCLI( CLI ):
import networkx as nx
import matplotlib.pyplot as plt
import pygraphviz
assert pygraphviz # silence pyflakes
except:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
@@ -40,7 +41,6 @@ class DemoCLI( CLI ):
g = nx.Graph()
mn = self.mn
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
hlen, slen = len( hosts ), len( switches )
nodes = hosts + switches
g.add_nodes_from( nodes )
links = [ ( link.intf1.node, link.intf2.node )
@@ -53,8 +53,8 @@ class DemoCLI( CLI ):
pos = nx.graphviz_layout( g )
opts = { 'ax': None, 'font_weight': 'bold',
'width': 2, 'edge_color': 'darkblue' }
hcolors = [ color[ h.server ] for h in hosts ]
scolors = [ color[ s.server ] for s in switches ]
hcolors = [ color[ getattr( h, 'server', 'localhost' ) ] for h in hosts ]
scolors = [ color[ getattr( s, 'server', 'localhost' ) ] for s in switches ]
nx.draw_networkx( g, pos=pos, nodelist=hosts, node_size=800, label='host',
node_color=hcolors, node_shape='s', **opts )
nx.draw_networkx( g, pos=pos, nodelist=switches, node_size=1000,
+1 -1
View File
@@ -5,7 +5,7 @@
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.examples.clustercli import DemoCLI as CLI
from mininet.examples.clustercli import ClusterCLI as CLI
def demo():
"Simple Demo of Cluster Mode"
+1 -2
View File
@@ -8,9 +8,8 @@ from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo
from mininet.util import custom, waitListening
from mininet.log import setLogLevel, output, info
from mininet.log import setLogLevel, info
from time import sleep
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
"""Example/test of link and CPU bandwidth limits
@@ -19,7 +19,7 @@ def intfOptions():
h2 = net.addHost( 'h2' )
s1 = net.addSwitch( 's1' )
link1 = net.addLink( h1, s1, cls=TCLink )
link2 = net.addLink( h2, s1 )
net.addLink( h2, s1 )
net.start()
# flush out latency from reactive forwarding delay
+6 -2
View File
@@ -27,7 +27,7 @@ from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
from mininet.log import lg
from mininet.util import irange
from mininet.util import irange, quietRun
from mininet.link import TCLink
from functools import partial
@@ -78,6 +78,10 @@ def linearBandwidthTest( lengths ):
topo = LinearTestTopo( hostCount )
# Select TCP Reno
output = quietRun( 'sysctl -w net.ipv4.tcp_congestion_control=reno' )
assert 'reno' in output
for datapath in switches.keys():
print "*** testing", datapath, "datapath"
Switch = switches[ datapath ]
@@ -97,7 +101,7 @@ def linearBandwidthTest( lengths ):
# since the reference controller is reactive
src.cmd( 'telnet', dst.IP(), '5001' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ] )
bandwidth = net.iperf( [ src, dst ], seconds=10 )
print bandwidth
flush()
results[ datapath ] += [ ( n, bandwidth ) ]
-1
View File
@@ -27,7 +27,6 @@ from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
from mininet.util import irange
class LinuxRouter( Node ):
"A Node with IP forwarding enabled."
+23 -46
View File
@@ -16,12 +16,16 @@ OpenFlow icon from https://www.opennetworking.org/
MINIEDIT_VERSION = '2.2.0.1'
from optparse import OptionParser
from Tkinter import *
# from Tkinter import *
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton,
Menu, Toplevel, Button, BitmapImage, PhotoImage, Canvas,
Scrollbar, Wm, TclError, StringVar, IntVar,
E, W, EW, NW, Y, VERTICAL, SOLID, CENTER,
RIGHT, LEFT, BOTH, TRUE, FALSE )
from ttk import Notebook
from tkMessageBox import showinfo, showerror, showwarning
from tkMessageBox import showerror
from subprocess import call
import tkFont
import csv
import tkFileDialog
import tkSimpleDialog
import re
@@ -36,22 +40,22 @@ if 'PYTHONPATH' in os.environ:
# someday: from ttk import *
from mininet.log import info, error, debug, output, setLogLevel
from mininet.log import info, setLogLevel
from mininet.net import Mininet, VERSION
from mininet.util import ipStr, netParse, ipAdd, quietRun
from mininet.util import netParse, ipAdd, quietRun
from mininet.util import buildTopo
from mininet.util import custom, customConstructor
from mininet.term import makeTerm, cleanUpScreens
from mininet.node import Controller, RemoteController, NOX, OVSController
from mininet.node import CPULimitedHost, Host, Node, HostWithPrivateDirs
from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
from mininet.node import CPULimitedHost, Host, Node
from mininet.node import OVSSwitch, UserSwitch
from mininet.link import TCLink, Intf, Link
from mininet.cli import CLI
from mininet.moduledeps import moduleDeps, pathCheck
from mininet.moduledeps import moduleDeps
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
print 'MiniEdit running against MiniNet '+VERSION
print 'MiniEdit running against Mininet '+VERSION
MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
from mininet.node import IVSSwitch
@@ -78,32 +82,6 @@ HOSTS = { 'proc': Host,
'cfs': custom( CPULimitedHost, sched='cfs' ) }
class CPULimitedHostWithPrivateDirs( CPULimitedHost ):
"Host with private directories"
def __init__( self, name, sched='cfs', **kwargs ):
"privateDirs: list of private directory strings or tuples"
self.name = name
self.privateDirs = kwargs.pop( 'privateDirs', [] )
CPULimitedHost.__init__( self, name, sched, **kwargs )
self.mountPrivateDirs()
def mountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
privateDir = directory[ 1 ] % self.__dict__
mountPoint = directory[ 0 ]
self.cmd( 'mkdir -p %s' % privateDir )
self.cmd( 'mkdir -p %s' % mountPoint )
self.cmd( 'mount --bind %s %s' %
( privateDir, mountPoint ) )
else:
# mount temporary filesystem on directory
self.cmd( 'mkdir -p %s' % directory )
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
class InbandController( RemoteController ):
def checkListening( self ):
@@ -1400,7 +1378,6 @@ class MiniEdit( Frame ):
def addNamedNode( self, node, name, x, y):
"Add a new node to our canvas."
c = self.canvas
icon = self.nodeIcon( node, name )
item = self.canvas.create_window( x, y, anchor='c', window=icon,
tags=node )
@@ -1701,7 +1678,6 @@ class MiniEdit( Frame ):
f.write("from subprocess import call\n")
inBandCtrl = False
hasLegacySwitch = False
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
@@ -1846,16 +1822,16 @@ class MiniEdit( Frame ):
linkopts = linkDetail['linkOpts']
srcName, dstName = src[ 'text' ], dst[ 'text' ]
bw = ''
delay = ''
loss = ''
max_queue_size = ''
# delay = ''
# loss = ''
# max_queue_size = ''
linkOpts = "{"
if 'bw' in linkopts:
bw = linkopts['bw']
linkOpts = linkOpts + "'bw':"+str(bw)
optsExist = True
if 'delay' in linkopts:
delay = linkopts['delay']
# delay = linkopts['delay']
if optsExist:
linkOpts = linkOpts + ","
linkOpts = linkOpts + "'delay':'"+linkopts['delay']+"'"
@@ -2561,8 +2537,8 @@ class MiniEdit( Frame ):
link = self.selection
linkDetail = self.links[link]
src = linkDetail['src']
dest = linkDetail['dest']
# src = linkDetail['src']
# dest = linkDetail['dest']
linkopts = linkDetail['linkOpts']
linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
if linkBox.result is not None:
@@ -2653,7 +2629,7 @@ class MiniEdit( Frame ):
del source.links[ dest ]
del dest.links[ source ]
stags = self.canvas.gettags( self.widgetToItem[ source ] )
dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
# dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
ltags = self.canvas.gettags( link )
if 'control' in ltags:
@@ -2784,16 +2760,17 @@ class MiniEdit( Frame ):
# Create the correct host class
if 'cores' in opts or 'cpu' in opts:
if ('privateDirectory' in opts):
hostCls = partial( CPULimitedHostWithPrivateDirs,
hostCls = partial( CPULimitedHost,
privateDirs=opts['privateDirectory'] )
else:
hostCls=CPULimitedHost
else:
if ('privateDirectory' in opts):
hostCls = partial( HostWithPrivateDirs,
hostCls = partial( Host,
privateDirs=opts['privateDirectory'] )
else:
hostCls=Host
print hostCls
newHost = net.addHost( name,
cls=hostCls,
ip=ip,
-2
View File
@@ -22,11 +22,9 @@ to-do:
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.util import quietRun
from mininet.log import output, warn
from random import randint
from re import findall
class MobilitySwitch( OVSSwitch ):
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/python
"""
This is a simple example that demonstrates multiple links
between nodes.
"""
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.topo import Topo
def runMultiLink():
topo = simpleMultiLinkTopo( n=2 )
net = Mininet( topo=topo )
net.start()
CLI( net )
net.stop()
class simpleMultiLinkTopo( Topo ):
def __init__( self, n, **kwargs ):
Topo.__init__( self, **kwargs )
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
s1 = self.addSwitch( 's1' )
for _ in range( n ):
self.addLink( s1, h1 )
self.addLink( s1, h2 )
if __name__ == '__main__':
setLogLevel( 'info' )
runMultiLink()
-2
View File
@@ -9,7 +9,6 @@ and that the ovs ports match the mininet ports.
from mininet.net import Mininet
from mininet.node import Controller
from mininet.log import setLogLevel, info, warn
from mininet.node import Node
def validatePort( switch, intf ):
"Validate intf's OF port number"
@@ -48,7 +47,6 @@ def net():
net.addLink( h4, s1 )
net.addLink( h5, s1, port1 = 1, port2 = 9 ) # specify a different port to connect host 5 to on the switch.
root = Node( 'root', inNamespace=False )
info( '*** Starting network\n' )
net.start()
+1 -1
View File
@@ -40,7 +40,7 @@ def perfTest():
dumpNodeConnections(net.hosts)
print "Testing bandwidth between h1 and h4"
h1, h4 = net.getNodeByName('h1', 'h4')
results = net.iperf( ( h1, h4 ), l4Type='UDP' )
net.iperf( ( h1, h4 ), l4Type='UDP' )
net.stop()
if __name__ == '__main__':
@@ -12,7 +12,7 @@ class testIntfOptions( unittest.TestCase ):
def testIntfOptions( self ):
"verify that intf.config is correctly limiting traffic"
p = pexpect.spawn( 'python -m mininet.examples.intfOptions ' )
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
tolerance = .8
opts = [ "Results: \['([\d\.]+) .bits/sec",
"Results: \['10M', '([\d\.]+) .bits/sec",
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
'''
Test for multiple links between nodes
validates mininet interfaces against systems interfaces
'''
import unittest
import pexpect
class testMultiLink( unittest.TestCase ):
prompt = 'mininet>'
def testMultiLink(self):
p = pexpect.spawn( 'python -m mininet.examples.multilink' )
p.expect( self.prompt )
p.sendline( 'intfs' )
p.expect( 's(\d): lo' )
intfsOutput = p.before
# parse interfaces from mininet intfs, and store them in a list
hostToIntfs = intfsOutput.split( '\r\n' )[ 1:3 ]
intfList = []
for hostToIntf in hostToIntfs:
intfList += [ intf for intf in
hostToIntf.split()[1].split(',') ]
# get interfaces from system by running ifconfig on every host
sysIntfList = []
opts = [ 'h(\d)-eth(\d)', self.prompt ]
p.expect( self.prompt )
p.sendline( 'h1 ifconfig' )
while True:
p.expect( opts )
if p.after == self.prompt:
break
sysIntfList.append( p.after )
p.sendline( 'h2 ifconfig' )
while True:
p.expect( opts )
if p.after == self.prompt:
break
sysIntfList.append( p.after )
failMsg = ( 'The systems interfaces and mininet interfaces\n'
'are not the same' )
self.assertEqual( sysIntfList, intfList, msg=failMsg )
p.sendline( 'exit' )
p.wait()
if __name__ == '__main__':
unittest.main()
+3 -2
View File
@@ -16,6 +16,7 @@ import time
from mininet.log import info
from mininet.term import cleanUpScreens
def sh( cmd ):
"Print a command and send it to the shell"
info( cmd + '\n' )
@@ -32,7 +33,7 @@ def killprocs( pattern ):
pids = ''
if pids:
sh( 'pkill -f 9 mininet:' )
sleep( .5 )
time.sleep( .5 )
else:
break
@@ -49,7 +50,7 @@ def cleanup():
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep(1)
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
# And kill off sudo mnexec
+21 -1
View File
@@ -36,7 +36,7 @@ import atexit
from mininet.log import info, output, error
from mininet.term import makeTerms, runX11
from mininet.util import ( quietRun, isShellBuiltin, dumpNodeConnections,
from mininet.util import ( quietRun, dumpNodeConnections,
dumpPorts )
class CLI( Cmd ):
@@ -351,6 +351,26 @@ class CLI( Cmd ):
for link in self.mn.links:
print link, link.status()
def do_switch( self, line ):
"Starts or stops a switch"
args = line.split()
if len(args) != 2:
error( 'invalid number of args: switch <switch name> {start, stop}\n' )
return
sw = args[ 0 ]
command = args[ 1 ]
if sw not in self.mn or self.mn.get( sw ) not in self.mn.switches :
error( 'invalid switch: %s\n' % args[ 1 ] )
else:
sw = args[ 0 ]
command = args[ 1 ]
if command == 'start':
self.mn.get( sw ).start( self.mn.controllers )
elif command == 'stop':
self.mn.get( sw ).stop( deleteIntfs=False )
else:
error( 'invalid command: switch <switch name> {start, stop}\n' )
def default( self, line ):
"""Called on an input line when the command prefix is not recognized.
Overridden to run shell commands when a node is the first CLI argument.
+5 -3
View File
@@ -70,6 +70,8 @@ class Intf( object ):
self.ip, self.prefixLen = ipstr.split( '/' )
return self.ifconfig( ipstr, 'up' )
else:
if prefixLen is None:
raise Exception( 'No prefix length set for IP address %s' % ( ipstr, ) )
self.ip, self.prefixLen = ipstr, prefixLen
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
@@ -158,9 +160,9 @@ class Intf( object ):
f = getattr( self, method, None )
if not f or value is None:
return
if type( value ) is list:
if isinstance( value, list ):
result = f( *value )
elif type( value ) is dict:
elif isinstance( value, dict ):
result = f( **value )
else:
result = f( value )
@@ -415,7 +417,7 @@ class Link( object ):
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
def intfName( _self, node, n ):
def intfName( self, node, n ):
"Construct a canonical interface name node-ethN for interface n."
return node.name + '-eth' + repr( n )
+2 -2
View File
@@ -28,9 +28,9 @@ def moduleDeps( subtract=None, add=None ):
add: string or list of module names to add, if not already loaded"""
subtract = subtract if subtract is not None else []
add = add if add is not None else []
if type( subtract ) is str:
if isinstance( subtract, basestring ):
subtract = [ subtract ]
if type( add ) is str:
if isinstance( add, basestring ):
add = [ add ]
for mod in subtract:
if mod in lsmod():
+46 -31
View File
@@ -91,7 +91,7 @@ import re
import select
import signal
import random
import copy
from time import sleep
from itertools import chain, groupby
from math import ceil
@@ -106,7 +106,7 @@ from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.1.0+"
VERSION = "2.2.0b1"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -265,7 +265,7 @@ class Mininet( object ):
return controller_new
def addNAT( self, name='nat0', connect=True, inNamespace=False, **params ):
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
subnet=self.ipBase, **params )
# find first switch and create link
if connect:
@@ -321,25 +321,36 @@ class Mininet( object ):
"return (key,value) tuple list for every node in net"
return zip( self.keys(), self.values() )
@staticmethod
def randMac():
"Return a random, non-multicast MAC address"
return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
0x020000000000 )
def addLink( self, node1, node2, port1=None, port2=None,
cls=None, **params ):
""""Add a link from node1 to node2
node1: source node
node2: dest node
port1: source port
port2: dest port
node1: source node (or name)
node2: dest node (or name)
port1: source port (optional)
port2: dest port (optional)
cls: link class (optional)
params: additional link params (optional)
returns: link object"""
mac1 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 )
mac2 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 )
defaults = { 'port1': port1,
'port2': port2,
'addr1': mac1,
'addr2': mac2,
'intf': self.intf }
defaults.update( params )
if not cls:
cls = self.link
link = cls( node1, node2, **defaults )
# Accept node objects or names
node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ]
node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ]
options = dict( params )
# Port is optional
if port1 is not None:
options.setdefault( 'port1', port1 )
if port2 is not None:
options.setdefault( 'port2', port2 )
# Set default MAC - this should probably be in Link
options.setdefault( 'addr1', self.randMac() )
options.setdefault( 'addr2', self.randMac() )
cls = self.link if cls is None else cls
link = cls( node1, node2, **options )
self.links.append( link )
return link
@@ -377,7 +388,7 @@ class Mininet( object ):
# Add a default controller
info( '*** Adding controller\n' )
classes = self.controller
if type( classes ) is not list:
if not isinstance( classes, list ):
classes = [ classes ]
for i, cls in enumerate( classes ):
# Allow Controller objects because nobody understands currying
@@ -397,12 +408,10 @@ class Mininet( object ):
info( switchName + ' ' )
info( '\n*** Adding links:\n' )
for srcName, dstName in topo.links(sort=True):
src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
params = topo.linkInfo( srcName, dstName )
srcPort, dstPort = topo.port( srcName, dstName )
self.addLink( src, dst, srcPort, dstPort, **params )
info( '(%s, %s) ' % ( src.name, dst.name ) )
for srcName, dstName, params in topo.links(
sort=True, withInfo=True ):
self.addLink( **params )
info( '(%s, %s) ' % ( srcName, dstName ) )
info( '\n' )
@@ -455,7 +464,9 @@ class Mininet( object ):
self.build()
info( '*** Starting controller\n' )
for controller in self.controllers:
info( controller.name + ' ')
controller.start()
info( '\n' )
info( '*** Starting %s switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ')
@@ -688,11 +699,15 @@ class Mininet( object ):
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ):
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None,
seconds=5):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses opposite hosts
l4Type: string, one of [ TCP, UDP ]
returns: results two-element array of [ server, client ] speeds
udpBw: bandwidth target for UDP test
format: iperf format argument if any
seconds: iperf time to transmit
returns: two-element array of [ server, client ] speeds
note: send() is buffered, so client rate can be much higher than
the actual transmission rate; on an unloaded system, server
rate should be much closer to the actual receive rate"""
@@ -725,8 +740,8 @@ class Mininet( object ):
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
info( 'Waiting for iperf to start up...' )
sleep(.5)
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
bwArgs )
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
server.IP() + ' ' + bwArgs )
debug( 'Client output: %s\n' % cliout )
server.sendInt()
servout += server.waitOutput()
@@ -793,9 +808,9 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
if type( src ) is str:
if isinstance( src, basestring ):
src = self.nameToNode[ src ]
if type( dst ) is str:
if isinstance( dst, basestring ):
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
+129 -85
View File
@@ -11,16 +11,13 @@ Host: a virtual host. By default, a host is simply a shell; commands
may be sent using Cmd (which waits for output), or using sendCmd(),
which returns immediately, allowing subsequent monitoring using
monitor(). Examples of how to run experiments using this
functionality are provided in the examples/ directory.
functionality are provided in the examples/ directory. By default,
hosts share the root file system, but they may also specify private
directories.
CPULimitedHost: a virtual host whose CPU bandwidth is limited by
RT or CFS bandwidth limiting.
HostWithPrivateDirs: a virtual host that has user-specified private
directories. These may be temporary directories stored as a tmpfs,
or persistent directories that are mounted from another directory in
the root filesystem.
Switch: superclass for switch nodes.
UserSwitch: a switch using the user-space switch from the OpenFlow
@@ -54,7 +51,7 @@ import pty
import re
import signal
import select
from subprocess import Popen, PIPE, STDOUT
from subprocess import Popen, PIPE
from operator import or_
from time import sleep
@@ -75,12 +72,14 @@ class Node( object ):
def __init__( self, name, inNamespace=True, **params ):
"""name: name of node
inNamespace: in network namespace?
privateDirs: list of private directory strings or tuples
params: Node parameters (see config() for details)"""
# Make sure class actually works
self.checkSetup()
self.name = params.get( 'name', name )
self.privateDirs = params.get( 'privateDirs', [] )
self.inNamespace = params.get( 'inNamespace', inNamespace )
# Stash configuration parameters for future reference
@@ -100,6 +99,7 @@ class Node( object ):
# Start command interpreter shell
self.startShell()
self.mountPrivateDirs()
# File descriptor to node mapping support
# Class variables and methods
@@ -161,6 +161,30 @@ class Node( object ):
self.cmd( 'stty -echo' )
self.cmd( 'set +m' )
def mountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
privateDir = directory[ 1 ] % self.__dict__
mountPoint = directory[ 0 ]
self.cmd( 'mkdir -p %s' % privateDir )
self.cmd( 'mkdir -p %s' % mountPoint )
self.cmd( 'mount --bind %s %s' %
( privateDir, mountPoint ) )
else:
# mount temporary filesystem on directory
self.cmd( 'mkdir -p %s' % directory )
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
def unmountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
self.cmd( 'umount ', directory[ 0 ] )
else:
self.cmd( 'umount ', directory )
def _popen( self, cmd, **params ):
"""Internal method: spawn and return a process
cmd: command to run (list)
@@ -210,6 +234,7 @@ class Node( object ):
def terminate( self ):
"Send kill signal to Node and clean up after it."
self.unmountPrivateDirs()
if self.shell:
if self.shell.poll() is None:
os.killpg( self.shell.pid, signal.SIGHUP )
@@ -233,7 +258,7 @@ class Node( object ):
assert not self.waiting
printPid = kwargs.get( 'printPid', True )
# Allow sendCmd( [ list ] )
if len( args ) == 1 and type( args[ 0 ] ) is list:
if len( args ) == 1 and isinstance( args[ 0 ], list ):
cmd = args[ 0 ]
# Allow sendCmd( cmd, arg1, arg2... )
elif len( args ) > 0:
@@ -327,10 +352,10 @@ class Node( object ):
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
if len( args ) == 1:
if type( args[ 0 ] ) is list:
if isinstance( args[ 0 ], list ):
# popen([cmd, arg1, arg2...])
cmd = args[ 0 ]
elif type( args[ 0 ] ) is str:
elif isinstance( args[ 0 ], basestring ):
# popen("cmd arg1 arg2...")
cmd = args[ 0 ].split()
else:
@@ -407,7 +432,7 @@ class Node( object ):
"""
if not intf:
return self.defaultIntf()
elif type( intf ) is str:
elif isinstance( intf, basestring):
return self.nameToIntf[ intf ]
else:
return intf
@@ -459,7 +484,7 @@ class Node( object ):
"""Set the default route to go through intf.
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
# Note setParam won't call us if intf is none
if type( intf ) is str and ' ' in intf:
if isinstance( intf, basestring ) and ' ' in intf:
params = intf
else:
params = 'dev %s' % intf
@@ -509,12 +534,14 @@ class Node( object ):
param: arg=value (ignore if value=None)
value may also be list or dict"""
name, value = param.items()[ 0 ]
f = getattr( self, method, None )
if not f or value is None:
if value is None:
return
if type( value ) is list:
f = getattr( self, method, None )
if not f:
return
if isinstance( value, list ):
result = f( *value )
elif type( value ) is dict:
elif isinstance( value, dict ):
result = f( **value )
else:
result = f( value )
@@ -590,12 +617,10 @@ class Node( object ):
"Make sure our class dependencies are available"
pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
class Host( Node ):
"A host is simply a Node"
pass
class CPULimitedHost( Host ):
"CPU limited host"
@@ -617,13 +642,9 @@ class CPULimitedHost( Host ):
# still does better with larger period values.
self.period_us = kwargs.get( 'period_us', 100000 )
self.sched = sched
if self.sched == 'rt':
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
error( '\n*** error: please enable RT_GROUP_SCHED in your kernel\n' )
exit( 1 )
self.rtprio = 20
if sched == 'rt':
self.checkRtGroupSched()
self.rtprio = 20
def cgroupSet( self, param, value, resource='cpu' ):
"Set a cgroup parameter and return its value"
@@ -655,8 +676,14 @@ class CPULimitedHost( Host ):
# Tell mnexec to execute command in our cgroup
mncmd = [ 'mnexec', '-g', self.name,
'-da', str( self.pid ) ]
# if our cgroup is not given any cpu time,
# we cannot assign the RR Scheduler.
if self.sched == 'rt':
mncmd += [ '-r', str( self.rtprio ) ]
if int( self.cgroupGet( 'rt_runtime_us', 'cpu' ) ) <= 0:
mncmd += [ '-r', str( self.rtprio ) ]
else:
debug( '*** error: not enough cpu time available for %s.' % self.name,
'Using cfs scheduler for subprocess\n' )
return Host.popen( self, *args, mncmd=mncmd, **kwargs )
def cleanup( self ):
@@ -664,6 +691,19 @@ class CPULimitedHost( Host ):
super( CPULimitedHost, self ).cleanup()
retry( retries=3, delaySecs=1, fn=self.cgroupDel )
_rtGroupSched = False # internal class var: Is CONFIG_RT_GROUP_SCHED set?
@classmethod
def checkRtGroupSched( cls ):
"Check (Ubuntu,Debian) kernel config for CONFIG_RT_GROUP_SCHED for RT"
if not cls._rtGroupSched:
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
error( '\n*** error: please enable RT_GROUP_SCHED in your kernel\n' )
exit( 1 )
cls._rtGroupSched = True
def chrt( self ):
"Set RT scheduling priority"
quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
@@ -681,7 +721,7 @@ class CPULimitedHost( Host ):
quota = int( self.period_us * f )
return pstr, qstr, self.period_us, quota
def cfsInfo( self, f):
def cfsInfo( self, f ):
"Internal method: return parameters for CFS bandwidth"
pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
# CFS uses wall clock time for period and CPU time for quota.
@@ -691,6 +731,9 @@ class CPULimitedHost( Host ):
debug( '(cfsInfo: increasing default period) ' )
quota = 1000
period = int( quota / f / numCores() )
# Reset to unlimited on negative quota
if quota < 0:
quota = -1
return pstr, qstr, period, quota
# BL comment:
@@ -702,35 +745,36 @@ class CPULimitedHost( Host ):
# to CPU seconds per second, essentially assuming that
# all CPUs are the same.
def setCPUFrac( self, f=-1, sched=None):
def setCPUFrac( self, f, sched=None ):
"""Set overall CPU fraction for this host
f: CPU bandwidth limit (fraction)
f: CPU bandwidth limit (positive fraction, or -1 for cfs unlimited)
sched: 'rt' or 'cfs'
Note 'cfs' requires CONFIG_CFS_BANDWIDTH"""
if not f:
return
Note 'cfs' requires CONFIG_CFS_BANDWIDTH,
and 'rt' requires CONFIG_RT_GROUP_SCHED"""
if not sched:
sched = self.sched
if sched == 'rt':
if not f or f < 0:
raise Exception( 'Please set a positive CPU fraction for sched=rt\n' )
return
pstr, qstr, period, quota = self.rtInfo( f )
elif sched == 'cfs':
pstr, qstr, period, quota = self.cfsInfo( f )
else:
return
if quota < 0:
# Reset to unlimited
quota = -1
# Set cgroup's period and quota
self.cgroupSet( pstr, period )
self.cgroupSet( qstr, quota )
setPeriod = self.cgroupSet( pstr, period )
setQuota = self.cgroupSet( qstr, quota )
if sched == 'rt':
# Set RT priority if necessary
self.chrt()
info( '(%s %d/%dus) ' % ( sched, quota, period ) )
sched = self.chrt()
info( '(%s %d/%dus) ' % ( sched, setQuota, setPeriod ) )
def setCPUs( self, cores, mems=0 ):
"Specify (real) cores that our cgroup can run on"
if type( cores ) is list:
if not cores:
return
if isinstance( cores, list ):
cores = ','.join( [ str( c ) for c in cores ] )
self.cgroupSet( resource='cpuset', param='cpus',
value=cores )
@@ -743,7 +787,7 @@ class CPULimitedHost( Host ):
errFail( 'cgclassify -g cpuset:/%s %s' % (
self.name, self.pid ) )
def config( self, cpu=None, cores=None, **params ):
def config( self, cpu=-1, cores=None, **params ):
"""cpu: desired overall system CPU fraction
cores: (real) core(s) this host can run on
params: parameters for Node.config()"""
@@ -762,33 +806,6 @@ class CPULimitedHost( Host ):
mountCgroups()
cls.inited = True
class HostWithPrivateDirs( Host ):
"Host with private directories"
def __init__( self, name, *args, **kwargs ):
"privateDirs: list of private directory strings or tuples"
self.name = name
self.privateDirs = kwargs.pop( 'privateDirs', [] )
Host.__init__( self, name, *args, **kwargs )
self.mountPrivateDirs()
def mountPrivateDirs( self ):
"mount private directories"
for directory in self.privateDirs:
if isinstance( directory, tuple ):
# mount given private directory
privateDir = directory[ 1 ] % self.__dict__
mountPoint = directory[ 0 ]
self.cmd( 'mkdir -p %s' % privateDir )
self.cmd( 'mkdir -p %s' % mountPoint )
self.cmd( 'mount --bind %s %s' %
( privateDir, mountPoint ) )
else:
# mount temporary filesystem on directory
self.cmd( 'mkdir -p %s' % directory )
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
# Some important things to note:
#
@@ -920,7 +937,7 @@ class UserSwitch( Switch ):
over tc queuing disciplines. To resolve the conflict,
we re-create the user switch's configuration, but as a
leaf of the TCIntf-created configuration."""
if type( intf ) is TCIntf:
if isinstance( intf, TCIntf ):
ifspeed = 10000000000 # 10 Gbps
minspeed = ifspeed * 0.001
@@ -964,11 +981,12 @@ class UserSwitch( Switch ):
if not intf.IP():
self.TCReapply( intf )
def stop( self ):
def stop( self, deleteIntfs=True ):
"Stop OpenFlow reference user datapath."
self.cmd( 'kill %ofdatapath' )
self.cmd( 'kill %ofprotocol' )
self.deleteIntfs()
if deleteIntfs:
self.deleteIntfs()
class OVSLegacyKernelSwitch( Switch ):
@@ -1014,11 +1032,12 @@ class OVSLegacyKernelSwitch( Switch ):
' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False
def stop( self ):
def stop( self, deleteIntfs=True ):
"Terminate kernel datapath."
quietRun( 'ovs-dpctl del-dp ' + self.dp )
self.cmd( 'kill %ovs-openflowd' )
self.deleteIntfs()
if deleteIntfs:
self.deleteIntfs()
class OVSSwitch( Switch ):
@@ -1080,7 +1099,7 @@ class OVSSwitch( Switch ):
"""Unfortunately OVS and Mininet are fighting
over tc queuing disciplines. As a quick hack/
workaround, we clear OVS's and reapply our own."""
if type( intf ) is TCIntf:
if isinstance( intf, TCIntf ):
intf.config( **intf.params )
def attach( self, intf ):
@@ -1166,20 +1185,33 @@ class OVSSwitch( Switch ):
self.TCReapply( intf )
def stop( self ):
def stop( self, deleteIntfs=True ):
"Terminate OVS switch."
self.cmd( 'ovs-vsctl del-br', self )
if self.datapath == 'user':
self.cmd( 'ip link del', self )
self.deleteIntfs()
if deleteIntfs:
self.deleteIntfs()
OVSKernelSwitch = OVSSwitch
class IVSSwitch(Switch):
class OVSBridge( OVSSwitch ):
"OVSBridge is an OVSSwitch in standalone/bridge mode"
def __init__( self, args, **kwargs ):
kwargs.update( failMode='standalone' )
OVSSwitch.__init__( self, args, **kwargs )
def start( self, controllers ):
OVSSwitch.start( self, controllers=[] )
class IVSSwitch( Switch ):
"""IVS virtual switch"""
def __init__( self, name, verbose=True, **kwargs ):
def __init__( self, name, verbose=False, **kwargs ):
Switch.__init__( self, name, **kwargs )
self.verbose = verbose
@@ -1222,11 +1254,12 @@ class IVSSwitch(Switch):
self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
def stop( self ):
def stop( self, deleteIntfs=True ):
"Terminate IVS switch."
self.cmd( 'kill %ivs' )
self.cmd( 'wait' )
self.deleteIntfs()
if deleteIntfs:
self.deleteIntfs()
def attach( self, intf ):
"Connect a data port"
@@ -1365,7 +1398,7 @@ class RYU( Controller ):
Controller.__init__( self, name,
command='ryu-manager',
cargs='--ofp-tcp-listen-port %s ' +
cargs='--ofp-tcp-listen-port %s ' +
' '.join( ryuArgs ),
cdir=ryuCoreDir,
**kwargs )
@@ -1398,8 +1431,19 @@ class RemoteController( Controller ):
warn( "Unable to contact the remote controller"
" at %s:%d\n" % ( self.ip, self.port ) )
def DefaultController( name, order=[ Controller, OVSController ], **kwargs ):
"find any controller that is available and run it"
for controller in order:
DefaultControllers = [ Controller, OVSController ]
def findController( controllers=DefaultControllers ):
"Return first available controller from list, if any"
for controller in controllers:
if controller.isAvailable():
return controller( name, **kwargs )
return controller
def DefaultController( name, controllers=DefaultControllers, **kwargs ):
"Find a controller that is available and instantiate it"
controller = findController( controllers )
if not controller:
raise Exception( 'Could not find a default OpenFlow controller' )
return controller( name, **kwargs )
+29 -5
View File
@@ -5,7 +5,10 @@ This contains additional Node types which you may find to be useful.
"""
from mininet.node import Node, Switch
from mininet.log import setLogLevel, info
from mininet.log import info, warn
from mininet.moduledeps import pathCheck
import re
class LinuxBridge( Switch ):
"Linux Bridge (with optional spanning tree)"
@@ -31,6 +34,7 @@ class LinuxBridge( Switch ):
return True
def start( self, controllers ):
"Start Linux bridge"
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
self.cmd( 'brctl addbr', self )
@@ -43,19 +47,30 @@ class LinuxBridge( Switch ):
self.cmd( 'ifconfig', self, 'up' )
def stop( self ):
"Stop Linux bridge"
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
def dpctl( self, *args ):
"Run brctl command"
return self.cmd( 'brctl', *args )
@classmethod
def setup( cls ):
"Make sure our class dependencies are available"
pathCheck( 'brctl', moduleName='bridge-utils' )
class NAT( Node ):
"""NAT: Provides connectivity to external network"""
def __init__( self, name, inetIntf='eth0', subnet='10.0/8', localIntf=None, **params):
def __init__( self, name, inetIntf=None, subnet='10.0/8', localIntf=None, **params):
super( NAT, self ).__init__( name, **params )
"""Start NAT/forwarding between Mininet and external network
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
self.inetIntf = inetIntf
self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
self.subnet = subnet
self.localIntf = localIntf
@@ -82,7 +97,7 @@ class NAT( Node ):
self.cmd( 'iptables -I FORWARD -i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -A FORWARD -i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -A FORWARD -i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING -o ', self.inetIntf, '-j MASQUERADE' )
self.cmd( 'iptables -t nat -A POSTROUTING -o ', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
@@ -94,13 +109,22 @@ class NAT( Node ):
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if ( line ) not in config:
info( '*** Adding "' + line.strip() + '" to ' + cfile )
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
self.cmd( 'service network-manager restart' )
def getGatewayIntf( self ):
routes = self.cmd( 'ip route show' )
match = re.search('default via \S+ dev (\S+)', routes )
if match:
return match.group( 1 )
else:
warn( 'There is no default route set. Using eth0 as gateway interface...\n' )
return 'eth0'
def terminate( self ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
+1 -1
View File
@@ -41,7 +41,7 @@ def makeTerm( node, title='Node', term='xterm', display=None ):
title: base title
term: 'xterm' or 'gterm'
returns: two Popen objects, tunnel and terminal"""
title += ': ' + node.name
title = '"%s: %s"' % ( title, node.name )
if not node.inNamespace:
title += ' (root)'
cmds = {
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python
"""Package: mininet
Regression tests for switch dpid assignment."""
import unittest
from functools import partial
import re
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import UserSwitch, OVSSwitch, OVSLegacyKernelSwitch, IVSSwitch
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
class testSwitchDpidAssignmentCommon ( object ):
"""Verify Switch dpid assignment."""
switchClass = None # overridden in subclasses
def testDefaultDpid ( self ):
"""Verify that the default dpid is assigned using a valid provided
canonical switchname if no dpid is passed in switch creation."""
switch = Mininet( Topo(), self.switchClass, Host, Controller ).addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
def testActualDpidAssignment( self ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation."""
switch = Mininet( Topo(), self.switchClass, Host, Controller ).addSwitch( 'A', dpid = '000000000000ABCD' )
self.assertEqual( switch.dpid, '000000000000ABCD' )
def testDefaultDpidAssignmentFailure( self ):
"""Verify that Default dpid assignment raises an Exception if the
name of the switch does not contin a digit. Also verify the
exception message."""
with self.assertRaises( Exception ) as raises_cm:
Mininet( Topo(), self.switchClass, Host, Controller ).addSwitch( 'A' )
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
'default datapath ID - please either specify a dpid '
'or use a canonical switch name such as s23.')
def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch
name) 0's followed by hex of first string of contiguous digits passed
in switch name."""
switch = Mininet( Topo(), self.switchClass, Host, Controller ).addSwitch( 's123' )
dpid = hex( int(re.findall( r'\d+', switch.name ) [0]) ) [ 2: ]
try:
if issubclass(UserSwitch, self.switchClass):
# Dpid lenght of UserSwitch = 12
self.assertEqual( switch.dpid, '0' * (12 - len(dpid)) + str(dpid) )
else:
self.assertEqual( switch.dpid, '0' * (16 - len(dpid)) + str(dpid) )
except TypeError:
# Switch is OVS User Switch
self.assertEqual( switch.dpid, '0' * (16 - len(dpid)) + str(dpid) )
class testSwitchOVSKernel( testSwitchDpidAssignmentCommon, unittest.TestCase ):
"""Test dpid assignnment of OVS Kernel Switch."""
switchClass = OVSSwitch
class testSwitchOVSUser( testSwitchDpidAssignmentCommon, unittest.TestCase ):
"""Test dpid assignnment of OVS User Switch."""
switchClass = partial(OVSSwitch, datapath = 'user')
@unittest.skipUnless( quietRun( 'which ovs-openflowd' ), 'OVS Legacy Kernel switch is not installed' )
class testSwitchOVSLegacyKernel( testSwitchDpidAssignmentCommon, unittest.TestCase ):
"""Test dpid assignnment of OVS Legacy Kernel Switch."""
switchClass = OVSLegacyKernelSwitch
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS switch is not installed' )
class testSwitchIVS( testSwitchDpidAssignmentCommon, unittest.TestCase ):
"""Test dpid assignment of IVS switch."""
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ), 'Reference user switch is not installed' )
class testSwitchUserspace( testSwitchDpidAssignmentCommon, unittest.TestCase ):
"""Test dpid assignment of Userspace switch."""
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+193 -107
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
'''@package topo
"""@package topo
Network topology creation.
@@ -9,46 +9,98 @@ This package includes code to represent network topologies.
A Topo object can be a topology database for NOX, can represent a physical
setup for testing, and can even be emulated with the Mininet package.
'''
"""
from mininet.util import irange, natural, naturalSeq
class MultiGraph( object ):
"Utility class to track nodes and edges - replaces networkx.Graph"
"Utility class to track nodes and edges - replaces networkx.MultiGraph"
def __init__( self ):
self.data = {}
self.node = {}
self.edge = {}
def add_node( self, node ):
"Add node to graph"
self.data.setdefault( node, [] )
def add_node( self, node, attr_dict=None, **attrs):
"""Add node to graph
attr_dict: attribute dict (optional)
attrs: more attributes (optional)
warning: updates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node[ node ] = attr_dict
def add_edge( self, src, dest ):
"Add edge to graph"
src, dest = sorted( ( src, dest ) )
self.add_node( src )
self.add_node( dest )
self.data[ src ].append( dest )
def add_edge( self, src, dst, key=None, attr_dict=None, **attrs ):
"""Add edge to graph
key: optional key
attr_dict: optional attribute dict
attrs: more attributes
warning: udpates attr_dict with attrs"""
attr_dict = {} if attr_dict is None else attr_dict
attr_dict.update( attrs )
self.node.setdefault( src, {} )
self.node.setdefault( dst, {} )
self.edge.setdefault( src, {} )
self.edge.setdefault( dst, {} )
self.edge[ src ].setdefault( dst, {} )
entry = self.edge[ dst ][ src ] = self.edge[ src ][ dst ]
# If no key, pick next ordinal number
if key is None:
keys = [ k for k in entry.keys() if isinstance( k, int ) ]
key = max( [ 0 ] + keys ) + 1
entry[ key ] = attr_dict
return key
def nodes( self ):
"Return list of graph nodes"
return self.data.keys()
def nodes( self, data=False):
"""Return list of graph nodes
data: return list of ( node, attrs)"""
return self.node.items() if data else self.node.keys()
def edges( self ):
def edges_iter( self, data=False, keys=False ):
"Iterator: return graph edges"
for src in self.data.keys():
for dest in self.data[ src ]:
yield ( src, dest )
for src, entry in self.edge.iteritems():
for dst, keys in entry.iteritems():
if src > dst:
# Skip duplicate edges
continue
for k, attrs in keys.iteritems():
if data:
if keys:
yield( src, dst, k, attrs )
else:
yield( src, dst, attrs )
else:
if keys:
yield( src, dst, k )
else:
yield( src, dst )
def edges( self, data=False, keys=False ):
"Return list of graph edges"
return list( self.edges_iter( data=data, keys=keys ) )
def __getitem__( self, node ):
"Return link dict for the given node"
return self.data[node]
"Return link dict for given src node"
return self.edge[ node ]
def __len__( self ):
"Return the number of nodes"
return len( self.node )
def convertTo( self, cls, data=False, keys=False ):
"""Convert to a new object of networkx.MultiGraph-like class cls
data: include node and edge data
keys: include edge keys as well as edge data"""
g = cls()
g.add_nodes_from( self.nodes( data=data ) )
g.add_edges_from( self.edges( data=( data or keys ), keys=keys ) )
return g
class Topo(object):
class Topo( object ):
"Data center network representation for structured multi-trees."
def __init__(self, *args, **params):
def __init__( self, *args, **params ):
"""Topo object.
Optional named parameters:
hinfo: default host options
@@ -56,150 +108,183 @@ class Topo(object):
lopts: default link options
calls build()"""
self.g = MultiGraph()
self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = params.pop( 'hopts', {} )
self.sopts = params.pop( 'sopts', {} )
self.lopts = params.pop( 'lopts', {} )
self.ports = {} # ports[src][dst] is port on src that connects to dst
self.ports = {} # ports[src][dst][sport] is port on dst that connects to src
self.build( *args, **params )
def build( self, *args, **params ):
"Override this method to build your topology."
pass
def addNode(self, name, **opts):
def addNode( self, name, **opts ):
"""Add Node to graph.
name: name
opts: node options
returns: node name"""
self.g.add_node(name)
self.node_info[name] = opts
self.g.add_node( name, **opts )
return name
def addHost(self, name, **opts):
def addHost( self, name, **opts ):
"""Convenience method: Add host to graph.
name: host name
opts: host options
returns: host name"""
if not opts and self.hopts:
opts = self.hopts
return self.addNode(name, **opts)
return self.addNode( name, **opts )
def addSwitch(self, name, **opts):
def addSwitch( self, name, **opts ):
"""Convenience method: Add switch to graph.
name: switch name
opts: switch options
returns: switch name"""
if not opts and self.sopts:
opts = self.sopts
result = self.addNode(name, isSwitch=True, **opts)
result = self.addNode( name, isSwitch=True, **opts )
return result
def addLink(self, node1, node2, port1=None, port2=None,
**opts):
def addLink( self, node1, node2, port1=None, port2=None,
key=None, **opts ):
"""node1, node2: nodes to link together
port1, port2: ports (optional)
opts: link options (optional)
returns: link info key"""
if not opts and self.lopts:
opts = self.lopts
self.addPort(node1, node2, port1, port2)
key = tuple(self.sorted([node1, node2]))
self.link_info[key] = opts
self.g.add_edge(*key)
port1, port2 = self.addPort( node1, node2, port1, port2 )
opts = dict( opts )
opts.update( node1=node1, node2=node2, port1=port1, port2=port2 )
self.g.add_edge(node1, node2, key, opts )
return key
def addPort(self, src, dst, sport=None, dport=None):
'''Generate port mapping for new edge.
@param src source switch name
@param dst destination switch name
'''
self.ports.setdefault(src, {})
self.ports.setdefault(dst, {})
# New port: number of outlinks + base
src_base = 1 if self.isSwitch(src) else 0
dst_base = 1 if self.isSwitch(dst) else 0
if sport is None:
sport = len(self.ports[src]) + src_base
if dport is None:
dport = len(self.ports[dst]) + dst_base
self.ports[src][dst] = sport
self.ports[dst][src] = dport
def nodes(self, sort=True):
def nodes( self, sort=True ):
"Return nodes in graph"
if sort:
return self.sorted( self.g.nodes() )
else:
return self.g.nodes()
def isSwitch(self, n):
'''Returns true if node is a switch.'''
info = self.node_info[n]
return info and info.get('isSwitch', False)
def isSwitch( self, n ):
"Returns true if node is a switch."
return self.g.node[ n ].get( 'isSwitch', False )
def switches(self, sort=True):
'''Return switches.
sort: sort switches alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if self.isSwitch(n)]
def switches( self, sort=True ):
"""Return switches.
sort: sort switches alphabetically
returns: dpids list of dpids"""
return [ n for n in self.nodes( sort ) if self.isSwitch( n ) ]
def hosts(self, sort=True):
'''Return hosts.
sort: sort hosts alphabetically
@return dpids list of dpids
'''
return [n for n in self.nodes(sort) if not self.isSwitch(n)]
def hosts( self, sort=True ):
"""Return hosts.
sort: sort hosts alphabetically
returns: list of hosts"""
return [ n for n in self.nodes( sort ) if not self.isSwitch( n ) ]
def links(self, sort=True):
'''Return links.
sort: sort links alphabetically
@return links list of name pairs
'''
if not sort:
return self.g.edges()
else:
links = [tuple(self.sorted(e)) for e in self.g.edges()]
return sorted( links, key=naturalSeq )
def iterLinks( self, withKeys=False, withInfo=False ):
"""Return links (iterator)
withKeys: return link keys
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
for src, dst, key, info in self.g.edges_iter( data=True, keys=True ):
node1, node2 = info[ 'node1' ], info[ 'node2' ]
if withKeys:
if withInfo:
yield( node1, node2, key, info )
else:
yield( node1, node2, key )
else:
if withInfo:
yield( node1, node2, info )
else:
yield( node1, node2 )
def port(self, src, dst):
'''Get port number.
def links( self, sort=False, withKeys=False, withInfo=False ):
"""Return links
sort: sort links alphabetically, preserving (src, dst) order
withKeys: return link keys
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
links = list( self.iterLinks( withKeys, withInfo ) )
if not sorted:
return links
# Ignore info when sorting
tupleSize = 3 if withKeys else 2
return sorted( links, key=( lambda l: naturalSeq( l[ :tupleSize ] ) ) )
@param src source switch name
@param dst destination switch name
@return tuple (src_port, dst_port):
src_port: port on source switch leading to the destination switch
dst_port: port on destination switch leading to the source switch
'''
if src in self.ports and dst in self.ports[src]:
assert dst in self.ports and src in self.ports[dst]
return self.ports[src][dst], self.ports[dst][src]
# This legacy port management mechanism is clunky and will probably
# be removed at some point.
def linkInfo( self, src, dst ):
"Return link metadata"
src, dst = self.sorted([src, dst])
return self.link_info[(src, dst)]
def addPort( self, src, dst, sport=None, dport=None ):
"""Generate port mapping for new edge.
src: source switch name
dst: destination switch name"""
# Initialize if necessary
ports = self.ports
ports.setdefault( src, {} )
ports.setdefault( dst, {} )
# New port: number of outlinks + base
if sport is None:
src_base = 1 if self.isSwitch( src ) else 0
sport = len( ports[ src ] ) + src_base
if dport is None:
dst_base = 1 if self.isSwitch( dst ) else 0
dport = len( ports[ dst ] ) + dst_base
ports[ src ][ sport ] = ( dst, dport )
ports[ dst ][ dport ] = ( src, sport )
return sport, dport
def setlinkInfo( self, src, dst, info ):
"Set link metadata"
src, dst = self.sorted([src, dst])
self.link_info[(src, dst)] = info
def port( self, src, dst ):
"""Get port numbers.
src: source switch name
dst: destination switch name
sport: optional source port (otherwise use lowest src port)
returns: tuple (sport, dport), where
sport = port on source switch leading to the destination switch
dport = port on destination switch leading to the source switch
Note that you can also look up ports using linkInfo()"""
# A bit ugly and slow vs. single-link implementation ;-(
ports = [ ( sport, entry[ 1 ] )
for sport, entry in self.ports[ src ].items()
if entry[ 0 ] == dst ]
return ports if len( ports ) != 1 else ports[ 0 ]
def _linkEntry( self, src, dst, key=None ):
"Helper function: return link entry and key"
entry = self.g[ src ][ dst ]
if key is None:
key = min( entry )
return entry, key
def linkInfo( self, src, dst, key=None ):
"Return link metadata dict"
entry, key = self._linkEntry( src, dst, key )
return entry[ key ]
def setlinkInfo( self, src, dst, info, key=None ):
"Set link metadata dict"
entry, key = self._linkEntry( src, dst, key )
entry[ key ] = info
def nodeInfo( self, name ):
"Return metadata (dict) for node"
info = self.node_info[ name ]
return info if info is not None else {}
return self.g.node[ name ]
def setNodeInfo( self, name, info ):
"Set metadata (dict) for node"
self.node_info[ name ] = info
self.g.node[ name ] = info
def convertTo( self, cls, data=True, keys=True ):
"""Convert to a new object of networkx.MultiGraph-like class cls
data: include node and edge data (default True)
keys: include edge keys as well as edge data (default True)"""
return self.g.convertTo( cls, data=data, keys=keys )
@staticmethod
def sorted( items ):
"Items sorted in natural (i.e. alphabetical) order"
return sorted(items, key=natural)
return sorted( items, key=natural )
class SingleSwitchTopo( Topo ):
@@ -228,6 +313,7 @@ class SingleSwitchReversedTopo( Topo ):
self.addLink( host, switch,
port1=0, port2=( k - h + 1 ) )
class LinearTopo( Topo ):
"Linear topology of k switches, with n hosts per switch."
+3 -3
View File
@@ -388,7 +388,7 @@ def sysctlTestAndSet( name, limit ):
#read limit
with open( name, 'r' ) as readFile:
oldLimit = readFile.readline()
if type( limit ) is int:
if isinstance( limit, int ):
#compare integer limits before overriding
if int( oldLimit ) < limit:
with open( name, 'w' ) as writeFile:
@@ -448,7 +448,7 @@ def natural( text ):
def num( s ):
"Convert text segment to int if necessary"
return int( s ) if s.isdigit() else s
return [ num( s ) for s in re.split( r'(\d+)', text ) ]
return [ num( s ) for s in re.split( r'(\d+)', str( text ) ) ]
def naturalSeq( t ):
"Natural sort key function for sequences"
@@ -549,7 +549,7 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
partial( quietRun, shell=True ) )
if not run( 'which telnet' ):
raise Exception('Could not find telnet' )
serverIP = server if type( server ) is str else server.IP()
serverIP = server if isinstance( server, basestring ) else server.IP()
cmd = ( 'sh -c "echo A | telnet -e A %s %s"' %
( serverIP, port ) )
time = 0
+6 -6
View File
@@ -99,6 +99,7 @@ int main(int argc, char *argv[])
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)
@@ -157,18 +158,17 @@ int main(int argc, char *argv[])
sprintf(path, "/proc/%d/ns/mnt", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0 || setns(nsid, 0) != 0) {
char *cwd = get_current_dir_name();
/* Plan B: chroot/chdir into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
return 1;
}
/* need to chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
}
}
/* chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
}
break;
case 'g':
+3
View File
@@ -140,6 +140,9 @@ function mn_deps {
function mn_dev {
echo "Installing Mininet developer dependencies"
$install doxygen doxypy texlive-fonts-recommended
if ! $install doxygen-latex; then
echo "doxygen-latex not needed"
fi
}
# The following will cause a full OF install, covering:
+1 -1
View File
@@ -9,7 +9,7 @@ else
host=$1
fi
pid=`ps ax | grep "mininet:$host$" | grep bash | awk '{print $1};'`
pid=`ps ax | grep "mininet:$host$" | grep bash | grep -v mnexec | awk '{print $1};'`
if echo $pid | grep -q ' '; then
echo "Error: found multiple mininet:$host processes"
+1 -1
View File
@@ -8,7 +8,7 @@ version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
version = version.strip()
# Find all Mininet path references
lines = co( "grep -or 'Mininet \w\+\.\w\+\.\w\+[+]*' *", shell=True )
lines = co( "egrep -or 'Mininet [0-9\.]+\w*' *", shell=True )
error = False
+50 -37
View File
@@ -162,8 +162,8 @@ def srun( cmd, **kwargs ):
def depend():
"Install package dependencies"
log( '* Installing package dependencies' )
run( 'sudo apt-get -y update' )
run( 'sudo apt-get install -y'
run( 'sudo apt-get -qy update' )
run( 'sudo apt-get -qy install'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs dnsmasq curl'
' python-setuptools mtools zip' )
@@ -333,9 +333,11 @@ skipx
# Tell the Ubuntu/Debian installer to stop asking stupid questions
PreseedText = """
d-i mirror/country string manual
d-i mirror/http/hostname string mirrors.kernel.org
PreseedText = ( """
"""
#d-i mirror/country string manual
#d-i mirror/http/hostname string mirrors.kernel.org
"""
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string
d-i partman/confirm_write_new_label boolean true
@@ -345,7 +347,7 @@ d-i partman/confirm_nooverwrite boolean true
d-i user-setup/allow-password-weak boolean true
d-i finish-install/reboot_in_progress note
d-i debian-installer/exit/poweroff boolean true
"""
""" )
def makeKickstartFloppy():
"Create and return kickstart floppy, kickstart, preseed"
@@ -487,22 +489,24 @@ def login( vm, user='mininet', password='mininet' ):
log( '* Waiting for login...' )
def disableNtpd( vm, prompt=Prompt ):
"Turn off ntpd and set clock immediately"
log( '* Turning off ntpd' )
vm.sendline( 'sudo -n service ntp stop' )
def removeNtpd( vm, prompt=Prompt, ntpPackage='ntp' ):
"Remove ntpd and set clock immediately"
log( '* Removing ntpd' )
vm.sendline( 'sudo -n apt-get -qy remove ' + ntpPackage )
vm.expect( prompt )
log( '* Setting clock' )
# -gq: set and quit immediately
vm.sendline( 'sudo -n ntpd -gq' )
# Try to make sure that it isn't still running
vm.sendline( 'sudo -n pkill ntpd' )
vm.expect( prompt )
log( '* Waiting one second and running date command' )
vm.sendline( 'sleep 1 && date ' )
log( '* Getting seconds since epoch from this server' )
# Note r'date +%s' specifies a format for 'date', not python!
seconds = int( run( r'date +%s' ) )
log( '* Setting VM clock' )
vm.sendline( 'sudo -n date -s @%d' % seconds )
def sanityTest( vm ):
"Run Mininet sanity test (pingall) in vm"
vm.sendline( 'sudo mn --test pingall' )
vm.sendline( 'sudo -n mn --test pingall' )
if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0:
log( '* Sanity check OK' )
else:
@@ -514,9 +518,9 @@ def sanityTest( vm ):
def coreTest( vm, prompt=Prompt ):
"Run core tests (make test) in VM"
log( '* Making sure cgroups are mounted' )
vm.sendline( 'sudo service cgroup-lite restart' )
vm.sendline( 'sudo -n service cgroup-lite restart' )
vm.expect( prompt )
vm.sendline( 'sudo cgroups-mount' )
vm.sendline( 'sudo -n cgroups-mount' )
vm.expect( prompt )
log( '* Running make test' )
vm.sendline( 'cd ~/mininet; sudo make test' )
@@ -532,29 +536,35 @@ def coreTest( vm, prompt=Prompt ):
log( '* Test', test, 'output:' )
log( vm.before )
def noneTest( vm ):
def installPexpect( vm, prompt=Prompt ):
"install pexpect"
vm.sendline( 'sudo -n apt-get -qy install python-pexpect' )
vm.expect( prompt )
def noneTest( vm, prompt=Prompt ):
"This test does nothing"
installPexpect( vm, prompt )
vm.sendline( 'echo' )
def examplesquickTest( vm, prompt=Prompt ):
"Quick test of mininet examples"
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v -quick' )
installPexpect( vm, prompt )
vm.sendline( 'sudo -n python ~/mininet/examples/test/runner.py -v -quick' )
def examplesfullTest( vm, prompt=Prompt ):
"Full (slow) test of mininet examples"
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v' )
installPexpect( vm, prompt )
vm.sendline( 'sudo -n python ~/mininet/examples/test/runner.py -v' )
def walkthroughTest( vm, prompt=Prompt ):
"Test mininet walkthrough"
vm.sendline( 'sudo apt-get install python-pexpect' )
vm.expect( prompt )
vm.sendline( 'sudo python ~/mininet/mininet/test/test_walkthrough.py -v' )
installPexpect( vm, prompt )
vm.sendline( 'sudo -n python ~/mininet/mininet/test/test_walkthrough.py -v' )
def useTest( vm, prompt=Prompt ):
@@ -580,7 +590,7 @@ def checkOutBranch( vm, branch, prompt=Prompt ):
vm.sendline( 'cd ~/mininet; git fetch --all; git checkout '
+ branch + '; git pull --rebase origin ' + branch )
vm.expect( prompt )
vm.sendline( 'sudo make install' )
vm.sendline( 'sudo -n make install' )
def interact( vm, tests, pre='', post='', prompt=Prompt ):
@@ -599,7 +609,7 @@ def interact( vm, tests, pre='', post='', prompt=Prompt ):
'install-mininet-vm.sh' % branch )
vm.expect( prompt )
log( '* Running VM install script' )
installcmd = 'bash install-mininet-vm.sh'
installcmd = 'bash -v install-mininet-vm.sh'
if Branch:
installcmd += ' ' + Branch
vm.sendline( installcmd )
@@ -830,12 +840,14 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
os.chdir( '..' )
def runTests( vm, tests=None, pre='', post='', prompt=Prompt ):
def runTests( vm, tests=None, pre='', post='', prompt=Prompt, uninstallNtpd=False ):
"Run tests (list) in vm (pexpect object)"
# We disable ntpd and set the time so that it won't be
# messing with the time during tests
disableNtpd( vm )
vm.expect( prompt )
# We disable ntpd and set the time so that ntpd won't be
# messing with the time during tests. Set to true for a COW
# disk and False for a non-COW disk.
if uninstallNtpd:
removeNtpd( vm )
vm.expect( prompt )
if Branch:
checkOutBranch( vm, branch=Branch )
vm.expect( prompt )
@@ -902,7 +914,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
if runFunction:
runFunction( vm, **runArgs )
log( '* Shutting down' )
vm.sendline( 'sudo shutdown -h now ' )
vm.sendline( 'sudo -n shutdown -h now ' )
log( '* Waiting for shutdown' )
vm.wait()
if outputFile:
@@ -1011,7 +1023,8 @@ def parseArgs():
exit( 1 )
for image in args.image:
bootAndRun( image, runFunction=runTests, tests=args.test, pre=args.run,
post=args.post, memory=args.memory, outputFile=args.out )
post=args.post, memory=args.memory, outputFile=args.out,
uninstallNtpd=True )
if not ( args.depend or args.list or args.clean or args.flavor
or args.image ):
parser.print_help()
+8 -7
View File
@@ -6,20 +6,21 @@
#
# optional argument: Mininet branch to install
set -e
echo `whoami` ALL=NOPASSWD: ALL | sudo tee -a /etc/sudoers > /dev/null
echo "$(whoami) ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers > /dev/null
sudo sed -i -e 's/Default/#Default/' /etc/sudoers
echo mininet-vm | sudo tee /etc/hostname > /dev/null
sudo sed -i -e 's/ubuntu/mininet-vm/g' /etc/hosts
sudo hostname `cat /etc/hostname`
sudo sed -i -e 's/quiet splash/text/' /etc/default/grub
sudo update-grub
# 12.10 and earlier
sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
/etc/apt/sources.list
# 13.04 and later
sudo sed -i -e 's/\/archive.ubuntu.com/\/mirrors.kernel.org/' \
/etc/apt/sources.list
# Update from official archive
sudo apt-get update
# 12.10 and earlier
#sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \
# /etc/apt/sources.list
# 13.04 and later
#sudo sed -i -e 's/\/archive.ubuntu.com/\/mirrors.kernel.org/' \
# /etc/apt/sources.list
# Clean up vmware easy install junk if present
if [ -e /etc/issue.backup ]; then
sudo mv /etc/issue.backup /etc/issue