Compare commits

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

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

inNamespace is set to true if you're in a network namespace.
2018-05-12 00:17:26 +00:00
Bob Lantz a114eb78a0 Fix to use self.ns 2018-05-12 00:17:26 +00:00
Bob Lantz 7924b15331 Change to ns=['net','mnt'] and deprecate inNamespace 2018-05-12 00:17:26 +00:00
Bob Lantz 48131b13b8 Check for /var/log and /var/run in overlay dirs 2018-05-12 00:17:26 +00:00
Bob Lantz 277e4d2e09 Move Server to nodelib and add --host server option 2018-05-12 00:17:25 +00:00
Bob Lantz 81da901b6b Enable control path 2018-05-12 00:16:45 +00:00
Bob Lantz 5916830268 Delete junk at end of file 2018-05-12 00:16:45 +00:00
Bob Lantz 6f00369dff 8 servers + LinuxBridge 2018-05-12 00:16:45 +00:00
Bob Lantz 7c83f036df debug -> info 2018-05-12 00:16:45 +00:00
Bob Lantz b340488fdc Add /etc/openvswitch to private dirs 2018-05-12 00:16:45 +00:00
Bob Lantz fc462e3bdd Specify runCmd, and update controller IP correctly 2018-05-12 00:16:45 +00:00
Bob Lantz 76f3db36a2 Fix ^c on cluster edition; still broken for pid ns 2018-05-12 00:16:45 +00:00
Bob Lantz 160b665d09 still working on this... 2018-05-12 00:16:45 +00:00
Bob Lantz 29f15487ea Revert a bit to node.py from master 2018-05-12 00:14:53 +00:00
Bob Lantz a9c3844830 use runCmd 2018-05-12 00:14:53 +00:00
Bob Lantz eea5d0b602 Allow runCmd 2018-05-12 00:14:53 +00:00
Bob Lantz 1687605cf0 Remove debug print 2018-05-12 00:14:53 +00:00
Bob Lantz 0a5c1f6026 Create new pgrp so that we can use killpg 2018-05-12 00:14:53 +00:00
Bob Lantz c46608710f Remove debug print 2018-05-12 00:14:53 +00:00
Bob Lantz 020f276d10 Respect runCmd in makeIntfPair() 2018-05-12 00:14:53 +00:00
Bob Lantz 43aa659e08 Still working on it. 2018-05-12 00:14:53 +00:00
Bob Lantz 034c16d95a Change to sudo -E to preserve ssh forwarding 2018-05-12 00:14:52 +00:00
Bob Lantz d30f7bd931 Add API and mn support for --host proc,ns=[net,mnt,pid] 2018-05-12 00:14:52 +00:00
Bob Lantz 90281b1d44 First attempt fixing x11 to work with pid namespace 2018-05-12 00:14:52 +00:00
Bob Lantz b141594a29 Fix pid namespace by making sure we fork on attach 2018-05-12 00:14:52 +00:00
Bob Lantz 9b86b5b7de First crack at a pid namespace-capable mnexec
The detach is a bit ugly because we fork twice, which shouldn't be
necessary!!
2018-05-12 00:14:52 +00:00
Bob Lantz 1d2ce39a01 Experiments with pid ns 2018-05-12 00:14:52 +00:00
Bob Lantz 7dee9ce563 Avoid expanding a string into a list of chars 2018-05-12 00:14:52 +00:00
Bob Lantz 24dd285b1a Use ['/etc/foo'] vs. ('/etc/foo',) to avoid deleted comma problem 2018-05-12 00:14:52 +00:00
Bob Lantz 2b7eb2ed99 Use motd instead of ssh banner message 2018-05-12 00:14:52 +00:00
Bob Lantz 7dae4e4b44 Clarify comment 2018-05-12 00:14:52 +00:00
Bob Lantz 5960e4ef89 Add docstring for terminate() 2018-05-12 00:14:52 +00:00
Bob Lantz 4d4d4719ff Document config params 2018-05-12 00:14:52 +00:00
Bob Lantz e67c9d9ea7 add private btmp 2018-05-12 00:14:52 +00:00
Bob Lantz 4c70429587 Comment changes 2018-05-12 00:14:52 +00:00
Bob Lantz d830b2331b Add documentation for overlayDirs 2018-05-12 00:14:52 +00:00
Bob Lantz c616476c33 Change to super() call in __init__ 2018-05-12 00:14:52 +00:00
Bob Lantz 1c1fa3af96 Simplify startup/shutdown, and a few comments 2018-05-12 00:14:52 +00:00
Bob Lantz 0a9a5af67b Private utmp and wtmp for w and last 2018-05-12 00:14:52 +00:00
Bob Lantz 57ba8bcd2e Minor changes 2018-05-12 00:14:52 +00:00
Bob Lantz c447d17362 Clarify parameter processing for overlayDirs 2018-05-12 00:14:52 +00:00
Bob Lantz 7ff615acea Fake cluster example 2018-05-12 00:14:52 +00:00
Bob Lantz 7263c16e89 Add overlayDirs 2018-05-12 00:14:52 +00:00
Bob Lantz 8af3f28b67 iproute -> iproute2 for Ubuntu 18.04
This should also be backward compatible to 14.04.
2018-04-28 01:48:36 +00:00
lantz c5b2efd839 Merge pull request #780 from nemethf/set_dp_desc
Set the datapath desciption (dp-desc) of OVS switches
2018-02-28 12:20:20 -08:00
Felician Nemeth a638e0b085 Set the datapath desciption (dp-desc) of OVS switches 2017-12-07 12:17:50 +01:00
lantz f65e1ab936 Merge pull request #767 from juangascon/travis_yml
Remove '- dist: xenial' until Travis gets their act together and supports the current LTS
2017-10-10 16:12:50 -07:00
juan 70d14db881 Remove '- dist: xenial' because both jobs are run in the same Ubuntu version (14.04) as I have seen in the build logs.
The travis-CI docs confirm the OS environment is 'Ubuntu 14.04 LTS trusty'.
Travis-CI only proposes 14.04 LTS Trusty and there is no plan to update to 16.04 xenial
(c.f. https://github.com/travis-ci/travis-ci/issues/5821)
It is useless to add a second job because it will run in the same environment.
2017-10-10 21:36:29 +02:00
Bob Lantz b142cf32ec Pass code check (14.04 LTS: pyflakes 0.8.1, pylint 1.1.0)
Was no longer passing due to pyflakes.
2017-09-25 16:07:47 -07:00
Bob Lantz 87e26ef931 Restore CLI=MyCLI customization in --custom files
fixes #746
2017-06-02 14:11:36 -07:00
Bob Lantz 7366645c81 Fix plot() command for newer networkx
Networkx moved graphviz_layout, so we look for it in two different
places.

Also plot() should be fixed for regular Mininet(), which doesn't have
a .servers list.
2017-05-30 15:37:10 -07:00
Bob Lantz 2437aba984 Change to if to avoid breaking travis build on 16.04 2017-05-24 17:17:25 -07:00
Bob Lantz a6627cd928 Don't call node.addIntf() if node is None
fixes #716
2017-05-24 15:11:03 -07:00
IWASE Yusuke aa8901268d Fix Ryu installation
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
2017-05-22 15:56:46 -07:00
Bob Lantz 9d306ad53a Allow passing mncmd to CPULimitedHost.popen()
This is not not really intended as an external API (and
it currently isn't documented) but this makes it consistent.

Fixes #743
2017-05-22 15:36:23 -07:00
lantz 69b6c05970 Merge pull request #736 from hwchiu/FIX_TBF_LINK
Modify the default latency type from int to float if tbf is enabled.
Fixes #729
2017-05-09 15:10:39 -07:00
Bob Lantz e55e18e4ce Treat Raspbian as Debian; add $DISTS.
Fixes #713
2017-05-09 14:41:10 -07:00
hwchiu f86db75b79 Modify the default latency from int to float if tbf is enabled.
- the original version will caluse the latency to 0 when bandwidth
  is bigger than 200;
- the TC command doesn't allow use the 0.00ms as input for latency field

Signed-off-by: hwchiu <sppsorrg@gmail.com>
2017-04-28 09:29:17 +08:00
lantz 5728fcbf40 Merge pull request #727 from leonardodavila/master
Fix ryu installation
2017-04-27 16:31:37 -07:00
lantz 23f4f4a998 Merge pull request #734 from akoshibe/upstream
Allow match on 'packets received' in ping results
2017-04-23 23:59:05 -07:00
akoshibe ba2d91b489 Allow match on 'packets received' in ping results
Fix typo and remove redundant call to nproc
2017-04-22 16:17:35 -08:00
Leonardo D'Avila 40e2c374d0 Merge pull request #1 from leonardodavila/fix-ryu-install
update install.sh fixing ryu install
2017-03-28 23:43:48 -03:00
Leonardo D'Avila ce8feb7bfa update install.sh fixing ryu install 2017-03-28 23:43:27 -03:00
lantz e823c4f93c Fix line break
Beware of emacs eating trailing spaces!
2017-03-22 14:34:23 -07:00
lantz 497a342c27 Update README.md 2017-03-22 14:32:44 -07:00
Bob Lantz 32565765c1 For now, install dependencies before make codecheck 2017-03-15 16:19:31 -07:00
Bob Lantz c1d07835bd Rearrange code check 2017-03-15 15:49:26 -07:00
Bob Lantz 65acef533a Run bash explicitly in codecheck conditional 2017-03-15 15:09:06 -07:00
Bob Lantz bf2bb4c88c Sadly pylint isn't backward compatible; comment out newer options
Also slightly adjust some valid name conventions
2017-03-15 15:03:47 -07:00
Bob Lantz bfc00c567c Add more ignores for plint 1.5.2 2017-03-15 14:50:01 -07:00
Bob Lantz 92d57db0d0 Run codecheck on 14.04 only and after install (for deps) 2017-03-14 15:47:46 -07:00
Bob Lantz 13331d0ffd Add "make codecheck" 2017-03-14 15:43:47 -07:00
Bob Lantz 359b1feb5a code check 2017-03-14 15:43:41 -07:00
Bob Lantz a77ddba607 code check 2017-03-14 15:43:28 -07:00
Bob Lantz caf23f078e code check 2017-03-13 19:09:24 -07:00
Bob Lantz d5cb865aaa code check 2017-03-13 19:08:39 -07:00
Bob Lantz 1c0aeb699f code check 2017-03-13 19:08:26 -07:00
Bob Lantz c458c9e2b4 code check 2017-03-13 18:56:21 -07:00
Bob Lantz 26a0e351f3 satisfy code check 2017-03-13 17:11:05 -07:00
Bob Lantz 742f5b9af9 satisfy code check 2017-03-13 17:10:14 -07:00
Bob Lantz 40e4546c48 satisfy code check 2017-03-13 17:09:19 -07:00
Bob Lantz 5479117584 pass code check 2017-03-13 17:06:53 -07:00
Bob Lantz 02453b02b0 Remove relative import 2017-03-13 17:05:50 -07:00
Bob Lantz d282c55831 pass code check; make GRE_KEY a class var 2017-03-13 17:03:11 -07:00
Bob Lantz 6977633de1 pass code check 2017-03-13 16:36:50 -07:00
Bob Lantz bc0a72e2f9 pass code check 2017-03-13 16:36:41 -07:00
Bob Lantz ef5c80abb3 pass code check 2017-03-13 16:34:38 -07:00
Bob Lantz 35a33fc617 print() -> error() 2017-03-13 16:31:17 -07:00
Bob Lantz 494840c908 satisfy codecheck 2017-03-13 13:57:41 -07:00
Bob Lantz b7d9fdd5bd Satisfy codecheck 2017-03-13 13:56:46 -07:00
Bob Lantz 32299a6808 Restore incorrectly placed docstring 2017-03-13 13:48:50 -07:00
Bob Lantz d7cfb729c3 Pass code check and add ovs-testcontroller to zombies 2017-03-13 13:46:51 -07:00
Bob Lantz 348b526212 Satisfy pylint 2017-02-15 18:35:45 -08:00
Bob Lantz 18bfa1d6b3 warn() was undefined 2017-02-15 18:30:09 -08:00
Bob Lantz 5d2dfa9f40 Handle version string sent to stderr
An unfortunate side effect of switching from print to output() is
that all output() goes to stderr. We should probably carefully
consider whether this is the right thing to do.
2017-02-15 18:26:18 -08:00
Bob Lantz 0b5d24148b default(): add CR to error(); also clean up docstring 2017-02-08 16:30:36 -08:00
Bob Lantz dfb2594793 testLinkChange: report bandwidth on error 2017-02-07 15:01:05 -08:00
lantz b5b6beacb0 Multiple builds: trusty and xenial 2017-01-31 15:34:27 -08:00
Brian O'Connor 213563583d Updating .travis.yml to suppress success notifications 2017-01-12 18:44:45 -08:00
Brian O'Connor 9c7638a015 Updating README
Moving Travis logo down and using image reference to declutter.
2017-01-12 17:01:26 -08:00
Brian O'Connor fef7e0521c Merge pull request #710 from bocon13/travisci
Setting up .travis.yml for automated builds on Travis CI
2017-01-12 16:29:54 -08:00
Brian O'Connor 335cb1bdf3 Adding TravisCI build static icon to README 2017-01-12 16:13:20 -08:00
Brian O'Connor 0b2d8461fd Updating hwintf test to call host.stop()
host.terminate() does not remove the interfaces
2017-01-12 16:11:24 -08:00
Brian O'Connor 9376ccbedd Updating runner to exit 1 on failure 2017-01-12 16:11:24 -08:00
Brian O'Connor 03b1cbb347 Update example runner to exit 1 on failure 2017-01-12 16:11:24 -08:00
Brian O'Connor 51f50e8b8f Adding .travis.yml for Travis CI 2017-01-12 16:11:13 -08:00
lantz 2e9a79a375 Merge pull request #618 from BhattiMarry/master
UPDATE command made generic. Obsolete kernel() function was breaking Fedora/RHEL install.
2017-01-11 15:50:53 -08:00
lantz 86a26e0baf Merge pull request #602 from youf3/patch-1
Added ryu to be clean up in clean.py
2017-01-10 14:08:16 -08:00
lantz 6f2981134d Merge pull request #606 from oliviertilmans/patch-2
link: Fix circular import
2017-01-06 15:26:47 -08:00
lantz e58cc37c49 Merge pull request #699 from NvanAdrichem/MiniEdit-OVSVersion
Changed verifying the OVS version to not require root in MiniEdit.
2017-01-06 14:57:59 -08:00
lantz baebce8b05 Merge pull request #672 from nirmoy/opensuse_installation
RFC: add installation support for SUSE based distros
2017-01-06 14:55:11 -08:00
lantz 2bfaec75e0 Merge pull request #685 from maufl/fix-ping-parse-regex
Fix regex for ping result parsing, supporting optional 'packets' word in ping output
2017-01-06 14:53:42 -08:00
lantz 36c2e7cd79 Added "Additional Information" section. 2017-01-06 14:37:11 -08:00
lantz 3284b04f2b Merge pull request #704 from mininet/devel/ipBase
Updating Mininet to start with host IP request in ipBase
2017-01-03 16:55:37 -08:00
Brian O'Connor 8c662d63bf Updating Mininet to start with host IP request in ipBase 2017-01-03 15:11:38 -08:00
Niels van Adrichem 3cb387ebd0 Changed verifying the OVS version to not require root in MiniEdit. 2016-12-21 11:59:55 +01:00
lantz e90a4a74cb Update ISSUE_TEMPLATE 2016-12-09 16:02:29 -08:00
Bob Lantz d338b4425b Added GitHub issue template. 2016-12-09 15:55:48 -08:00
Felix Konstantin Maurer c128712d4d Fix regex for ping result parsing 2016-11-14 14:16:07 +01:00
Bob Lantz 8b2881b0c2 Changes for OVS 2.6.0 + Ubuntu 16.04 + Debian 8.6 2016-10-28 18:16:25 -07:00
Bob Lantz 3a9081b5b1 Add small wait before wget in web test.
Although the walkthrough doesn't specify waiting, it is a race
condition that we occasionally hit in tests. Adding a 2 second
delay seems realistic (for when a human is doing the walkthrough)
and should reduce the likelihood of hitting the race condition.
2016-10-21 15:31:33 -07:00
Bob Lantz 54bd4e4d3a For 10Mb/s links, we should certainly be network-limited rather
than CPU-limited, which should expose the feature we are trying
to demonstrate (TCP data rate slowing down as latency increases
due to congestion control.)
2016-10-21 13:25:40 -07:00
lantz cc1cd9619f Merge pull request #671 from vik-y/master
Changes override method from __init__ to build
2016-09-28 20:45:35 -07:00
Nirmoy Das 007261d19e add installation support for SUSE based distros 2016-09-28 12:08:32 +02:00
vik_y dc22b0a877 Changes override method from __init__ to build 2016-09-27 13:19:08 +05:30
Bob Lantz 4178333174 Wait for exit in testWireshark()
If mn doesn't shut down cleanly, the subsequent test may fail.
2016-09-23 16:07:12 -07:00
Bob Lantz f012dac2cb Clean shutdown for testStaticMAC
Without waiting for mn to exit, OVS keeps listening on the
static listening port, which prevents UserSwitch from listening
on it (and starting up) in the next test.
2016-09-21 13:25:04 -07:00
Bob Lantz 8def6752f3 Add TCULink and make it the default for --switch user
For some time, UserSwitch has suffered from abysmal performance due
to rx and tx offload settings. In the latest Ubuntu 16.04/Linux 4.4
kernel, it simply doesn't work with the default settings, since
packets with bad TCP checksums are generated at one end, passed through
the user switch, and dropped at the other. It doesn't seem to be
something we can fix in UserSwitch itself although it may be possible
to do something in the C code of the Stanford reference switch and
CPqD switch.
2016-09-20 12:43:27 -07:00
Bob Lantz 0f9c1b33c7 Change default --listenport to 6654
Previously the standard OpenFlow poort (6653) that Controller()
may be listening at would conflict with the default listenPort
for switch 20 (usually UserSwitch 20), breaking waitConnected()
and disconnecting that switch from the network in general.

Fixes #668
2016-09-14 00:38:35 -07:00
Bob Lantz f530c99415 testHostCommands: accept both ethX and en.*X interfaces
systemd (Ubuntu 16) names interfaces like this:
enp1s3 for "ethernet, PCI bus 1, slot 3"

We now accept both in testHostCommands

Fixes #665
2016-09-09 14:54:38 -07:00
Bob Lantz 5470415bfc Fix mn -v by using output() rather than info()
info() doesn't produce any output if it's called at
this point; bug was introduced when we tried to eliminate
print().
2016-09-01 23:25:14 -07:00
Bob Lantz 7e5324c80e Add TESTS parameters, multiple tests with +
It's very useful to be able to pass parameters to tests.
Unfortunately, our previous --test argument syntax didn't
permit it, because , was used as a delimiter for multiple
tests. With this change, we change , to become a delimiter
for arguments, as it is with other options to mn.  For now,
we introduce + as a separator, as it is legal in filenames
and therefore shouldn't conflict with special characters
used by Unix shells.

We also reorganize mn a bit to make it hopefully slightly
clearer.
2016-08-30 02:44:38 -07:00
Bob Lantz e893fc9da4 Try specifying timeout in pexpect.spawn() + adjust error msg
Unfortunately pexpect() seems to be timing out with a 30 second
timeout rather than the 600 seconds we are passing in. How
could this even be working normally? It is puzzling. We are
going to try specifying the timeout in the spawn() call.

Also if the test fails, we use %e format for readability.
2016-08-29 23:57:17 -07:00
Bob Lantz b2e1907c3a Increase link delay to 2ms for more robust effect
With a 1 ms delay, the performance tests periodically fails
under nested virtualization, which is how we are testing it.
Increasing the delay should make the effect (lower TCP
data rates as latency increases, due to TCP's congestion
control algorithm) more pronounced and robust.
2016-08-25 03:55:28 -07:00
Bob Lantz 21086cd79e Reduce CPU so that iperf client is CPU bound
If we want to observe a monotonic affect, we should
make sure that we are in fact CPU limited where it
matters. In this case, we are CPU limiting the hosts,
and the iperf client uses a lot of CPU. We need to
reduce the CPU allocation so that iperf is in fact
CPU bound.

We also correct the CPU allocation so that the client
and server each receive 50% of the total. Previously
we were specifying the per-host CPU allocation, so
45% meant we were allocating 90% of the overall CPU,
which seems a bit confusing. On the other hand, now
at 40% each host gets 20% of the CPU, which could also
be considered slightly confusing!

Although the client transmit rate is going to be the
limiting factor, we still measure the received data
rate at the server, because that is more interesting
than the initial burst of buffering at the client.
Measuring at the server becomes more important as
we reduce the iperf time.

The output is also changed slightly, and the test has
been updated appropriately.
2016-08-24 23:59:02 -07:00
Bob Lantz ee15ce2243 print -> info; end the horror of print vs. print()
Although we could use print() from __future__, this messes
up scripts which use examples as modules.

The simple, if not nicest for 2.7, solution is to use
info(), output() and other mininet.log functions. The disadvantage
is that we may have to adjust things if we change info() to
add automatic newlines, but we can burn that bridge in Mininet
3.x.
2016-08-23 01:54:54 -07:00
Bob Lantz 1eb6f65e40 Increase scratchnet timeout to see if it's just slow. 2016-08-22 23:18:58 -07:00
lantz 238ffe64a7 Merge pull request #659 from jhall11/links_output
Restore links output format
2016-08-23 12:28:58 -07:00
Jon Hall cea176fa9f Restore links output format
In the change from print -> print() -> output() we lost the new lines
which makes it hard to read the output
2016-08-23 10:49:36 -07:00
Bob Lantz 3276e3fef5 Allow some diffs in h1 ps vs. h2 ps
Presumably daemons, race conditions, or ephemeral processes
can cause the ps output to vary slightly. We allow up to
two differing lines to account for this.

Fixes #651
2016-08-22 15:59:49 -07:00
Bob Lantz e40d9b8c0d print -> info; also clarify signalTest output 2016-08-15 22:54:21 -07:00
lantz 30d08cfbb2 Merge pull request #641 from xiaozhou/master
add a new type of RemoteLink in cluster edition that uses GRE tunnels
2016-08-16 15:05:59 -07:00
Bob Lantz 29d902918d Change tshark version check for compatibility
The version string changed in tshark 2.0.2 in Ubuntu 16.
Perhaps we should just check for the version string itself
to be more robust but this is probably OK for now.
2016-08-11 14:33:41 -07:00
Bob Lantz f89a5bcf70 Use output()/error() instead of print
We shouldn't be using print in the core API, with
the possible (?) exception of error/abort messages.
2016-08-08 15:32:31 -07:00
Bob Lantz 8ac077a721 Remove "from __future__ import print"
This was well-intentioned, but it causes more trouble
than it's worth.

Fixes #652
2016-08-08 15:28:52 -07:00
lantz 666635c969 Merge pull request #636 from mininet/devel/add-del
Add net.delNode(), net.delLink() and associated methods
2016-08-04 17:10:08 -07:00
Bob Lantz 4c1cfcf6f3 14.04.3 -> 14.04.4
We should probably do this in a more automatic way.
2016-08-03 15:17:08 -07:00
Bob Lantz 76c8afde8a Fixes for Unbuntu 16.04/Xenial
- add xenial
- partx returns an error (warn rather than fail)
- remove dnsmasq build.py dependency (not really needed, side effects)
2016-08-02 15:27:53 -07:00
lantz 5714495f8c Merge pull request #603 from oliviertilmans/patch-1
TCIntf: Don't delete a non-existing root qdisc
2016-08-01 15:24:20 -07:00
Xiaozhou Li d8371615ce fix typo 2016-07-29 09:57:30 -07:00
Xiaozhou Li 7630861fde fix typo 2016-07-11 21:15:04 -07:00
Xiaozhou Li da594dcecd add a new type of RemoteLink in cluster edition that uses GRE tunnels 2016-07-11 21:05:01 -07:00
lantz 4fe5a19221 Merge pull request #598 from croft/master
Set limit on command history length
2016-06-21 16:40:55 -07:00
lantz 330a466503 Merge pull request #611 from jue-jiang/master
modify loss type of tclink to simulate loss rate under 1%
2016-06-21 16:37:38 -07:00
lantz c0b8eddadb Merge pull request #623 from bradmwalker/ivs-install
Fix/speed up IVS install and build on Fedora/RH
2016-06-21 16:36:39 -07:00
Bob Lantz 76b54e3e44 Merge branch 'bradmwalker-py3-part1'
py3 low-hanging fruit

closes #621
2016-06-21 16:34:12 -07:00
Bob Lantz 36489c7470 Change xrange to range for py3 compatibility 2016-06-21 16:31:46 -07:00
Brad Walker dd8adda3ff Use "Exception as _" instead of "Exception, _"
Requires Python 2.6+
2016-06-21 16:27:04 -07:00
Brad Walker fb1f0dca2a Make some tuples explicit to pass parsing in py3 2016-06-21 16:27:04 -07:00
Brad Walker 70fcc45893 Use print function 2016-06-21 16:27:04 -07:00
Bob Lantz 511d71a110 Add net.delNode(), net.delLink() and associated methods
This makes the mid-level net.add*() API symmetric.
You can now delete hosts, switches, controllers and links using
a new net.del*() API, as well as del net[ 'nodename' ].
2016-06-03 23:59:27 -07:00
Bob Lantz 57abd9baef Use correct command name in OVSController 2016-05-24 17:40:00 -07:00
Bob Lantz e6b0430a87 Enable TESTS={'mytest':test} in --custom files 2016-05-11 19:30:18 -07:00
Brad Walker 6f286033c5 Checkout only first level of IVS submodules
The submodules have submodules with authenticated URLs which breaks anonymous,
automated builds. Excluding them also decreases build time.

git -C is not available in git 1.8 (CentOS 7)
2016-04-30 19:13:27 -04:00
Brad Walker 1600b10131 Specify RPM dependencies for IVS 2016-04-30 19:13:27 -04:00
Bob Lantz 475bb4d911 2.3.0d1 2016-04-26 15:13:14 -07:00
Bob Lantz c5517f7872 2.2.1 -> 2.2.2b1 2016-04-26 15:10:06 -07:00
Bob Lantz 40353c9150 Split lines to make pylint happy 2016-04-26 14:48:06 -07:00
lantz c7e23ccedf Merge pull request #617 from oliviertilmans/patch-3
link: Set prefixLen for loopback address
2016-04-22 13:30:16 -07:00
Muhammad Umair Bhatti 361beab0b0 UPDATE command made generic.
Update command was hard-coded for debian or ubuntu based systems which was 'apt-get'. Setting an update variable accordingly solves this problem.
2016-04-18 16:45:44 +05:00
Olivier Tilmans 07fbc894e7 link: Set prefixLen for loopback address
The prefixLen for a loopback address is well-defined (/8),
and as such the variable is set accordingly.

Signed-off-by: Olivier Tilmans <olivier.tilmans@uclouvain.be>
2016-04-17 23:07:34 +02:00
zhuo 098b39d07a modify loss type of tclink to simulate loss rate under 1% 2016-04-09 19:27:52 +08:00
Olivier Tilmans 60e537f9bc link: Fix circular import
Having an 'import mininet.node' in the link module causes
a circular import as the node module also has a top-level
import for the link module.

In order to break the cycle, the import for the node module
is delayed and executed only if using the sole class that
actually needs it in the link module.
2016-03-25 00:32:52 +01:00
Olivier Tilmans 5cfc9f84b8 TCIntf: Don't delete a non-existing root qdisc
In recent kernels, virtual interfaces come without any associated
qdisc, resulting in errors when spawning the network.
Checking for "noqueue" in the tc output, enables to detect that
case and thus avoid deleting the non-existent qdisc.
2016-03-11 13:36:56 +01:00
youf3 6ddce0ac92 Added ryu to be clean up in clean.py
'ryu-manager' is included in zombies list so Ryu controller is cleaned up properly.
2016-03-11 16:47:35 +13:00
Jason Croft e87ee10f8b Set limit on command history length 2016-02-25 03:50:22 -06:00
lantz e171aba8b5 Merge pull request #597 from nemethf/doc_fix
Fix documentation of failMode in OVSSwitch
2016-02-19 14:42:38 -08:00
Felician Nemeth 1bd0e927d2 Fix documentation of failMode in OVSSwitch 2016-02-19 01:26:00 -08:00
Bob Lantz e113f8ed12 Add error message to shed light on why this sometimes fails 2016-02-02 23:32:03 -08:00
Bob Lantz 336958352a Only use 80 hosts for linearbandwidth.py for now
In the long run, we should debug the performance issues
with kvm and Ubuntu 15. For now, however, we're relaxing
the constraints.

Closes #594
2016-01-26 15:40:59 -08:00
Bob Lantz 5dc15aeaaf Tolerate slow startup/lost pings for now
In the long run we should troubleshoot the performance issue
on kvm/Ubuntu15, but for now we are relaxing the constraint.

Closes #593
2016-01-26 15:40:56 -08:00
Bob Lantz 9a22e2b737 Use ifconfig for interface verification.
Previously we were using both ip link and ifconfig - not only is
this inconsistent and redundant, but it also broke when newer
ip link changed the reported names of certain interfacs to
"h1-eth0@36:".

Fixes #592
2016-01-25 14:27:34 -08:00
Bob Lantz 76d3252cd0 Fix error exit from dd/block zeroing 2016-01-22 16:47:23 -08:00
Bob Lantz 9756c9a392 vm_clean: delete keys from from /etc/ssh/ before shipping vm
It's a bad idea for all Mininet VMs to share the same SSH keys.
Certainly users can regenerate their own keys, but it's better
if we don't ship a key and simply regenerate it on boot.
2016-01-22 16:02:55 -08:00
Bob Lantz 0fac568a19 Rewrite tolerance to be saner (plus or minus 20%) 2016-01-22 13:52:15 -08:00
Bob Lantz 04897513cd Collect all server output in iperf()
This still isn't ideal - this was breaking UDP iperf, which
can take a bit of time to print its output after you control-c it.
2016-01-22 13:33:33 -08:00
Bob Lantz 5365831de8 Use 0% loss when testing examples/simpleperf.py
Also clarified the code in test_simpleperf.py.

Fixes #590
2016-01-21 17:07:39 -08:00
Bob Lantz 6a69c3c743 Fix UDP iperf. 2016-01-21 16:59:18 -08:00
Bob Lantz c0793cb58b Add "run" alias for "use" and integrate build/test options
Keeping "use" for now even though it seems harder to remember
than "run". And the build/test options are probably much
clearer being inline in the help.
2016-01-20 17:33:00 -08:00
Bob Lantz 327af97ccc Try to fix iperf race condition
This is more complicated than it should be. We are also relying on
the fact that waitOutput should eat extra prompts most of the time.
Still not perfect - it's hard to get this exactly right, and we
should try to make it easier!

Fixes #589
2016-01-20 17:32:49 -08:00
Bob Lantz b78b99b695 monitor() should return on timeout; docstring changes
It appears that read() has been blocking for some time,
so for now it makes sense to change the documentation to
match the functionality!

It's not entirely clear if monitor() expects this functionality.
However, with the current blocking read() semantics, it should
definitely return (and not call read()) on a poll timeout.

So, we now return the poll() result from waitReadable() (which
should have done this already probably) and check it. In the
fullness of time, we still need to revisit the whole I/O API
and make sure that it is consistent, sane, correctly documented,
and used correctly in the examples.

See #588 for more comments.
2016-01-20 13:33:54 -08:00
Bob Lantz a1bff4b035 Add python-pexpect to dependencies
It's used by tests and isn't large.
Fixes #587
2016-01-19 13:24:36 -08:00
Bob Lantz 0b673d7cb6 Update Ryu dependencies. 2016-01-19 13:23:43 -08:00
Bob Lantz db134f36ae Allow RemoteController to connect to correct port.
Fixes #584
2016-01-13 22:40:41 -08:00
Bob Lantz f873068a2c Add chown argument to change build dir owner
This allows Jenkins to delete a sudo build.
2016-01-13 17:16:07 -08:00
Bob Lantz 38a4000a97 Update to recent LTS and ubuntu releases.
Perhaps we should make this more algorithmic...
2016-01-12 01:16:26 -08:00
Bob Lantz 0f5e05c088 Handle openvswitch-testcontroller in ubuntu 15 2016-01-12 01:00:34 -08:00
Bob Lantz 65000d3245 Non-interactive installation on debian (respect -y flag!) 2016-01-11 19:18:15 -08:00
Bob Lantz 312ed1a4a5 Use our github forks of openflow, oflops for now
This enables us to test our changes for Ubuntu 15 (new gcc/c99)
before pushing upstream if desired.
2016-01-11 17:24:17 -08:00
lantz ce5738b4a6 Merge pull request #533 from pichuang/node
Check for ovs-testcontroller in OVSController
2016-01-11 16:58:40 -08:00
lantz 447db4c77c Merge pull request #526 from msvbhat/master
Fixing the install.sh in INSTALL file
2016-01-11 16:56:48 -08:00
lantz b47aa5dadc Merge pull request #558 from bregman-arie/master
Update install.sh to support RedHat distrubtion
2015-11-30 16:09:15 -08:00
lantz af0215fb9f Merge pull request #536 from jonohart/buildcpu
Add parameter for number of CPU cores to use in VM builds
2015-11-23 15:40:46 -08:00
lantz 2791333c70 Merge pull request #566 from moz/master
addNAT always use first switch
2015-11-23 15:33:12 -08:00
Tomasz Buchert 96ea5367db mnexec: properly setup the mount namespace
Systemd's default is to mark the root mount as shared and it is
inherited as such by the new mount namespace. This means that any
mounts performed inthe new namespace will be visible by the rest of
the system, breaking privateDirs.

To restore a more sane behaviour, we explicitly mark all mounts
recursively as private, meaning that we will no longer see new mounts
from the root namespace, and our mounts will also not propagate to the
rest of the system.

Fixes #565
2015-11-18 11:47:50 -08:00
Rahman Pujianto 0298e9be7d addNAT always use first switch
addNAT always use first switch even though another switch specified
2015-11-09 11:48:12 +07:00
Bob Lantz 7c6d645a0a Workaround for cgdelete deleting cgroup but returning error
fixes #513
2015-10-22 16:34:05 -07:00
Arie Bregman 0c2fbaf187 Update install.sh to support RedHat distrubtion
Mininet installation will also work on RedHat distribution
2015-10-18 10:40:11 +03:00
Brian O'Connor d254d7496c Removing unnecessary braces in RemoteController 2015-09-23 16:02:55 -07:00
lantz 2c5d86f197 Merge pull request #555 from mininet/devel/of-port
Updating OpenFlow default port to 6653 (in Controller and RemoteController)
2015-09-23 15:36:04 -07:00
Brian O'Connor 8df24304d8 Updating OpenFlow default port to 6653
- Pass 6653 to controllers that Mininet starts
- Try to connect first on 6653 for RemoteController, then fallback to 6633

fixes #545
2015-09-22 17:29:50 -07:00
lantz 93b123761b Merge pull request #541 from thinred/master
don't generate .pyc files on some makefile targets
2015-07-29 13:25:32 -07:00
Tomasz Buchert cdbbb5b7a6 don't generate .pyc files on some makefile targets 2015-07-29 17:13:01 +02:00
Jonathan Hart 86af067e42 Add option for number of CPU cores to use for VM builds 2015-07-15 10:40:45 -07:00
Roan Huang db3bffa971 Check for ovs-testcontroller in OVSController
The programe name changed from test-controller to ovs-testcontroller

Reference:
openvswitch/ovs 0bc1b46a38cca06023fdfa5d500c738ccdfa94e7
2015-07-07 00:11:57 +08:00
Bob Lantz 2cb17590c2 Remove blank line
I'm not sure whether we really need to turn off ip_forward before
flushing iptables, but it might possibly be useful to avoid certain
unexpected behavior.
2015-06-25 15:42:22 -07:00
Bob Lantz f7601da006 Change quoting for '!' 2015-06-25 15:38:50 -07:00
lantz 65a0e5f3b6 Merge pull request #528 from pichuang/cluster
Fixed `whoami` output problem in cluster.py
2015-06-25 13:36:21 -07:00
Roan Huang d5d66f1276 Fixed whoami output problem in cluster.py
Remove the last character when use `whoami`
2015-06-25 05:37:20 +00:00
Bob Lantz 9ed14fa0f4 Remove explicit NAT code and use built-in NAT functionality.
This API isn't great - we should try to improve it in the future.
2015-06-23 15:17:58 -07:00
lantz f24ebc438e Merge pull request #530 from jonohart/master
Enable NAT to use all interfaces.
2015-06-23 15:08:47 -07:00
Jonathan Hart 90d50dcbc4 Enable NAT to use all interfaces.
Also put the net.ipv4.ip_forward option back to what it was before on exit,
rather than always setting back to 0.
2015-06-23 13:57:19 -07:00
M S Vishwanath Bhat ce2458c1df INSTALL: Another trivial install.sh path fix
Signed-off-by: M S Vishwanath Bhat <msvbhat@gmail.com>
2015-06-14 14:12:23 +05:30
M S Vishwanath Bhat 8871893993 INSTALL: Fixing the path of install.sh
Signed-off-by: M S Vishwanath Bhat <msvbhat@gmail.com>
2015-06-14 14:02:31 +05:30
Bob Lantz 5f68be2273 Remove debug print 2015-06-11 00:16:55 -07:00
Bob Lantz 68ae67dc58 Support custom links in custom file 2015-06-10 20:35:53 -07:00
Bob Lantz c4a85ab1d1 Add flush option to disable flushing iptables 2015-06-10 20:09:29 -07:00
Bob Lantz ef3c885630 Add help regarding --nat [option=val...] 2015-06-10 20:08:44 -07:00
Bob Lantz bb35d04102 Fix TorusTopo for UserSwitch
UserSwitch requires a 12-digit dpid.
2015-06-07 22:10:18 -07:00
Bob Lantz ba05fd363b Warn if bridge netfilter (firewall) is enabled
Newer linux kernels enable filtering on the linux bridge;
this can prevent it from working in mininet!
2015-06-05 16:10:21 -07:00
Brian O'Connor e988b0f660 Merge pull request #522 from mininet/devel/torus
Adding number of hosts per switch option to torus topo
2015-06-05 01:45:01 -07:00
Brian O'Connor b27cce08af Adding number of hosts per switch option to torus topo 2015-05-27 23:45:19 -07:00
Bob Lantz a38896c254 Avoid expanding a string into a list of chars 2015-05-16 08:24:15 -07:00
Bob Lantz 63ae13fcf9 Fix #520 2015-05-12 23:50:37 -07:00
Bob Lantz c5f6d0ff17 Restore use of self.intf (if present) in addLink
fixes #515
2015-05-02 13:16:48 -07:00
Bob Lantz ab8c4e9198 2.2.1rc1 -> 2.2.1 2015-04-16 16:05:30 -07:00
Bob Lantz 8daa4193c7 Fix typo 2015-04-15 16:21:55 -07:00
Bob Lantz 5c895eaad8 Added note on Raspberry Pi ;-) 2015-04-14 14:40:27 -07:00
Bob Lantz 435d0d68b3 -> 2.2.1rc1 2015-04-14 14:33:08 -07:00
Bob Lantz 66e9845f05 Add babisk 2015-04-13 17:58:47 -07:00
Bob Lantz fe3340077c Add dmahler and pichuang 2015-04-13 17:56:11 -07:00
lantz c589660e17 Merge pull request #494 from pichuang/master
Modify directory path in clustersetup.sh
2015-04-13 17:52:06 -07:00
Bob Lantz d9117c77e1 Merge branch 'dmahler-master' 2015-04-13 17:35:52 -07:00
Bob Lantz 6cb68f26c9 Simply use class names as option help
The justification for this is that 1) the options are
already documented in the API documentation and 2) the
class names are fairly self-explanatory, so adding short
descriptions doesn't add much.

In the future, however, we could add a short description
by simply using the first line or sentence of the docstring;
note that docstrings would have to be canonicalized, which
they aren't now.
2015-04-13 17:30:55 -07:00
David Mahler c60764c34f Update to mn --help to provide details on options 2015-04-11 06:20:42 +00:00
lantz ba723826b7 Merge pull request #489 from BabisK/master
New netbee lib, fixes need for very old bison
2015-04-10 17:06:05 -07:00
Bob Lantz 0165d7bdff Implement Switch.stop(), which doesn't call terminate
When I changed the switch implementations to call super().stop(),
I neglected to include a Switch.stop() method which didn't call
terminate(). This broke switch s1 stop and s1.stop().

Fixes #497
2015-04-09 23:19:40 -07:00
Bob Lantz 1df1f9a1c5 Fix sorting & remove duplicate 2015-04-09 17:56:37 -07:00
Roan Huang 2c916acc06 Modify cluster help information 2015-04-07 03:37:53 +00:00
Roan Huang 0a810b226c Modify SSHDIR and USERDIR path 2015-04-06 11:19:26 +00:00
Roan Huang 57c8f5934f Fixed code indentation 2015-04-06 11:12:27 +00:00
Bob Lantz 125e669723 Support multiple --controller arguments 2015-03-30 20:40:07 -07:00
Bob Lantz a2486a6d66 Allow node.setIP() to pass extra arguments to intf.setIP() 2015-03-30 19:43:51 -07:00
Bob Lantz 15f2898f26 Add a space after ipv6.disable=1 so as not to break text boot 2015-03-30 16:26:05 -07:00
Bob Lantz 1c4adde1b6 Remove redundant processing for '/' 2015-03-24 15:22:55 -07:00
Bob Lantz 4d22991245 Allow install.sh -V on debian 2015-03-23 18:48:30 -07:00
Bob Lantz 90ea6c6bd1 Usually kernel install is unnecessary, so don't quit if it fails 2015-03-23 17:42:51 -07:00
Bob Lantz 269cecd3ee openvswitch-datapath-dkms is no longer needed for 14.04+ 2015-03-23 17:15:59 -07:00
Bob Lantz c2be20f09e Rename node.RYU to node.Ryu
Perhaps it should be RyuController, but just Ryu for now.
RYU made no sense because it's not an acronym.
2015-03-18 16:00:28 -07:00
Bob Lantz df56fa2716 2.2.1d1 -> 2.2.1d2 2015-03-18 15:47:53 -07:00
Bob Lantz f49e2539b7 Minor formatting, pass code check 2015-03-18 15:32:21 -07:00
Bob Lantz 4da7b3b7f0 Fix exception for unknown class 2015-03-18 15:31:40 -07:00
Bob Lantz e5a5cd0070 customConstructor -> customClass 2015-03-18 15:18:02 -07:00
Bob Lantz c5779deeb6 Pass code check 2015-03-18 15:17:33 -07:00
Bob Lantz 79c944aef4 Remove obsolete OVSLegacyKernelSwitch 2015-03-18 15:12:09 -07:00
Bob Lantz a23c6a2871 Fix undefined constructors in customClass() 2015-03-18 15:10:20 -07:00
Babis Kaidos 7acc693277 New netbee library is installed for Ubuntu 14.04 and newer. For older releases or for other OSs the old netbee is installed 2015-03-18 17:59:29 +02:00
Babis Kaidos 889698f7bd New netbee lib, fixes need for very old bison 2015-03-16 15:28:20 +02:00
Bob Lantz f6f6d9282b CustomConstructor -> CustomClass, which calls specialClass
specialClass is an analog of functools.partial but for classes.
We can now use it instead of partial() in mn, so that Mininet
can introspect on the actual base class.

Fixes #488
2015-03-13 21:17:43 -07:00
lantz 5224884e5e Merge pull request #473 from joerango/master
Support for running arbitrary command when starting terminal
2015-03-13 15:09:11 -07:00
lantz f063b023bd Merge pull request #478 from rlane/fix-readline-history
cli: don't read/write readline history more than once
2015-03-02 14:22:19 -08:00
Rich Lane a1edb167b1 Merge pull request #1 from lantz/devel/fix-readline-history
Move cmdloop() wrapper into a new run() method
2015-03-02 14:20:11 -08:00
Bob Lantz 613fac4bab Move cmdloop() wrapper into a new run() method
I also added another try/catch block so that interrupting
the 'Interrupt' message should no longer occur.
2015-02-20 16:46:18 -08:00
Rich Lane e0cd11ab21 cli: don't read/write readline history more than once
Previously, when creating multiple CLI objects, each one would append the
~/.mininet_history file to readline's internal list. When writing the file back
it would be duplicated for each CLI object created. So, over a few mininet runs
the history file would grow exponentially.

This change moves the readline setup code out of the CLI constructor into a
class method and adds a flag to prevent it from being executed more than once.
2015-02-20 10:47:43 -08:00
Joseph Beshay 5b818ad75c Support for running arbitrary command when starting terminal (other than bash) 2015-02-09 12:55:25 -06:00
Bob Lantz 17ba6a7c4d Add ip:port shorthand to Controller
Fixes #475
2015-02-04 11:16:42 -08:00
Bob Lantz f77a8b9e17 Fix customConstructor to work correctly with newargs 2015-02-04 10:56:32 -08:00
Bob Lantz 3dd8c2cda6 A controller should delete its interfaces if necessary
This was causing controlnet.py to not clean up its interfaces, since
those interfaces were linked from the controller to the switch
rather than vice-versa!!

deleteIntfs already will only delete interfaces whose names match
our name, which should usually prevent us from accidentally deleting
a hardware interface from the root namespace!
2015-02-04 10:30:26 -08:00
Bob Lantz 48a8ed857e 2.2.0+ -> 2.2.1d1 2015-02-04 05:27:51 -08:00
Bob Lantz 74c3511d5c pass code check 2015-02-04 05:27:14 -08:00
Bob Lantz 5ac113cfaa Batch link delete commands (and minor edits) 2015-02-04 05:21:15 -08:00
Bob Lantz cd02954c91 pass code check 2015-02-04 04:45:46 -08:00
Bob Lantz 93be1d0401 Fix OVS protocols option 2015-02-04 04:44:55 -08:00
Bob Lantz 6a38811f1a Clarify makeIntfPair behavior and pass code check 2015-02-04 04:44:14 -08:00
Bob Lantz 4ac45a3967 Fix super() arg in DataController 2015-02-04 04:43:23 -08:00
Bob Lantz d7e01bb821 Pass code check 2015-02-04 04:43:02 -08:00
Bob Lantz 340bf3cb7a Remove now-unused quietRun import 2015-02-04 04:29:08 -08:00
Bob Lantz 5f8547a5e0 Remove now-unused Link import 2015-02-04 04:28:57 -08:00
Bob Lantz 09e9c0550a Indent one line for consistency 2015-02-04 04:27:31 -08:00
Bob Lantz c1dc80571a Correctly group switches for batch operations 2015-02-04 04:26:58 -08:00
Bob Lantz ec9b23ba9c Delete tap9 for CE
In the future, we should probably not hardwire this,
or should at least make it a constant!!
2015-02-04 04:15:19 -08:00
Bob Lantz 7c0b56f9ba Delete both tunnel interfaces
We also clean things up a bit and check for error messages,
which now can cause exceptions which should invoke cleanup.
2015-02-04 04:14:40 -08:00
Bob Lantz d90a45514f node shell: remove unnecessary -m and unset HISTFILE
Since we already disable job notification with +m, it doesn't
make sense to set it in the original invocation!

It's also annoying if all of the host commands end up overwriting
your regular bash_history!!
2015-02-03 22:02:29 -08:00
Bob Lantz b2fe0778dc Change iperf() to use waitListening() 2015-02-03 21:44:21 -08:00
lantz 3e4f254573 Merge pull request #474 from mininet/devel/ovsbatch
Batch startup support for OVS

Currently, every ovs-vsctl command requires reading the entire OVS
configuration database. This means that its performance gets linearly
slower as more switches and ports are added. To mitigate this, we
batch multiple configuration operations into individual, long,
ovs-vsctl commands.

This patch set makes a couple of other notable changes, including
setting printPid=False by default (avoids using mnexec unnecessarily)
and running certain commands using errRun rather than quietRun.
Additionally we no longer look for leftover links in the root
namespace, so code relying on that functionality may have to change
slightly (as in controlnet.py and sshd.py for example.) It also adds
cluster support to mn -c.

The performance result is that mn --topo linear,200 --test none now
completes in 60 seconds rather than 95 seconds (on my laptop) without
the patch (vs. 101 seconds in 2.2.0).

This is still slower than I would like - we should be able to make
some additional improvements.
2015-02-03 19:24:03 -08:00
Bob Lantz 2e4dd13482 Turn off printPid by default to avoid mnexec fork/exec 2015-02-03 17:40:20 -08:00
Bob Lantz 19331ca287 use net.addLink() so that link is cleaned up 2015-02-03 16:02:07 -08:00
Bob Lantz 9483f6378f Make sure DataController's interfaces are deleted 2015-02-03 15:30:16 -08:00
Bob Lantz a4e933688a Set batch=False in OVSSwitch for low-level API
If you try to use the low-level API, you are probably
not going to call batchStartup()! So, we set batch=False
by default. This means that buildFromTopo() needs to set
it to True, so we add a bit of irritatingly complex machinery
to allow this to happen. The good fallout of this is that
now customConstructor() returns a real subclass, not simply
a constructor function! We also detect errors where people
are incorrectly attempting to give parameters to a lambda
function - since none of our lambdas accept parameters!!

Note that this is a bit like functools.partial for classes -
it would be nice if functools had a true subclassing function.
2015-01-29 00:26:15 -08:00
Bob Lantz c11e9f3316 Fix OVS user switch (remove unnecessary % parameter) 2015-01-28 17:04:58 -08:00
Bob Lantz acdcf9b6ae cluster: add batchStartup/Shutdown, cleanup 2015-01-27 15:27:26 -08:00
Bob Lantz c702840a0a Remove debug print lines 2015-01-27 15:24:29 -08:00
Bob Lantz 254fae2dc9 Clarify which intf pair failed and raise exception 2015-01-27 15:23:48 -08:00
Bob Lantz bdad3e8c8e Merge OVSBatch into OVSSwitch
Note that we are changing the interface of batchStartup/Shutdown
slightly so that the method can choose not to start some of the
switches. We might wish to refine this a bit...
2015-01-26 18:01:20 -08:00
Bob Lantz 574d634fc2 Don't clean up links that may have been dumped into root NS.
This should rarely happen - in the usual case, either the
links will be shut down by Mininet.stop(), or the interfaces
will be deleted by node.stop( deleteIntfs=True ), or the
links or interfaces will be explicitly deleted or stopped
using the low-level API.

Cases that are relying on links being automatically deleted
in cleanup() will potentially find that they are now no longer
deleted, but these cases should be rare.
2015-01-26 14:06:23 -08:00
Bob Lantz eafbd2a597 Change to OVSSwitch 2015-01-26 14:06:23 -08:00
Bob Lantz 7485b035af make 'ovs-vsctl' string symmetric 2015-01-26 14:06:23 -08:00
Bob Lantz 8014a7023c Fix super() typo 2015-01-26 14:06:23 -08:00
Bob Lantz bec34e7227 Clean up - TCReapply still broken! 2015-01-26 14:06:23 -08:00
Bob Lantz 9ca6322603 Remove shared reconnectms, improve self.started
We still need to set it in batchShutdown()
2015-01-26 14:06:23 -08:00
Bob Lantz 957fe1db93 Refactor for compatibility with isOldOVS() == True 2015-01-26 14:06:23 -08:00
Bob Lantz 3b4738c2ca First crack at setting controller backoff in single command 2015-01-26 14:06:23 -08:00
Bob Lantz 30ebb852a3 errRun: add debug( results ) 2015-01-26 14:06:22 -08:00
Bob Lantz 959586bc8f Add debug(cmd) to errRun() 2015-01-26 14:06:22 -08:00
Bob Lantz 9bda98486d Add OVSBatch class (experimental)
This implements batch startup for OVS switches.
2015-01-26 14:06:22 -08:00
Bob Lantz c68e4e76f4 Clarify bandwidth limit for TCIntfs
In the future, we would like to support higher bandwidth
limits for TCIntfs, but we'll probably need to adjust some
of the parameters dynamically.
2015-01-26 13:55:38 -08:00
Bob Lantz 98a8231cef Exit poll loop on POLLHUP or anything unexpected
In my opinion, we really shouldn't even get POLLHUP, but
we do. In case we get anything else odd, quite polling on
that fd.
2015-01-23 16:43:49 -08:00
Bob Lantz 28ce13d18e Fix polling in errRun
It's tricky to get this right, but basically we want to read
if there is something to read; if not, we want to check for
EOF.
2015-01-23 16:41:29 -08:00
Bob Lantz ef59cd88dc Return correct success condition in cgroupDel
Without this, we end up retrying until we fail, instead
of returning immediately on success!
2015-01-23 16:39:50 -08:00
Bob Lantz 9db6cdc261 Call delete() in link.stop() ; warn on exited node.cmd()
We should think a bit about the semantics that we want here.
The comments say "stop and clean up link" so perhaps that's
what we want. However, we could also imagine stop stopping
forwarding on the link (and possibly allowing restarts).

We warn on exited node.cmd() because we terminate the controller
before stopping/deleting the links. This makes sense to avoid a
storm of link/port down events, but since the controller's
shell has exited we cannot call link.stop() on any of its
links. We may want to simply stop the controller and not
terminate it, but at least it doesn't hang for now.
2015-01-22 03:22:43 -08:00
Bob Lantz f7b29333f5 2.2.0 -> 2.2.0+ 2015-01-18 23:35:54 -08:00
Bob Lantz 24520fc982 Allow + in version number 2015-01-18 23:35:29 -08:00
Bob Lantz b93cc989f4 Pass code check 2015-01-18 23:35:12 -08:00
Bob Lantz a8cc243aa7 Add stp param to OVS and connected() to OVSBridge
This allows --switch ovsbr,stp=True to work correctly
2015-01-18 22:59:21 -08:00
Bob Lantz e65dc4c6d6 OVSLink: use isinstance() to detect OVSSwitch subclasses 2015-01-18 21:56:38 -08:00
Bob Lantz d4be92713a Merge del-br into cmd, and add reconnectms param
With newer versions of OVS, this allows us to set up a switch
with a single OVS command (if reconnectms is zero.) If reconnectms
is specified, then it slows things down slightly (but not much.)
2015-01-18 16:09:02 -08:00
Bob Lantz 79f5d39db5 Comment edits and pass code check 2015-01-17 13:56:37 -08:00
Bob Lantz 6da3fcdef1 Add bridges s1-s3 to topology and explain some details 2015-01-17 13:38:55 -08:00
Bob Lantz 026130bd5f pass code check 2015-01-17 13:33:59 -08:00
Bob Lantz c1b48fb5c8 Stub out RemoteOVSSwitch.batchShutdown()
Eventually we should implement true batch shutdown.
In the mean time, we just ignore it. Note there's no good
way that I know of for a subclass to remove a superclass
method, so we changed the protocol a bit to require a return
value of True.
2015-01-15 02:43:38 -08:00
Bob Lantz b1983548aa Fix indentation error 2015-01-15 02:36:34 -08:00
Bob Lantz c62812a944 Update cluster.py for new makeIntfPair 2015-01-15 02:29:51 -08:00
Bob Lantz d66b96260a Don't stop switches that we've already stopped.
Note that this also changes the way that links are deleted;
the reason is that the batch shutdown doesn't currently delete
the links, but OVSSwitch.stop() does. We may wish to revisit
this in the future.
2015-01-15 02:07:00 -08:00
Bob Lantz d7e9c3bbfd Add comment about redundant intf.delete() 2015-01-15 02:07:00 -08:00
Bob Lantz 7a4a865bdb customize makeIntfPair to eliminate fastIntfPair 2015-01-15 02:07:00 -08:00
Bob Lantz da4dcf3753 Add addresses to fastIntfPair() and fix codecheck 2015-01-15 02:07:00 -08:00
Bob Lantz 5383b0e603 Update comment to reflect OVS patch link limits 2015-01-15 02:07:00 -08:00
Bob Lantz 9d2e6404b3 add fastIntfPair to speed up link creation 2015-01-15 02:07:00 -08:00
Bob Lantz 91a73bd191 use isinstance( intf, OVSIntf ) 2015-01-15 02:07:00 -08:00
Bob Lantz c069542c5c Add OVSLink/--link ovs, which uses OVS patch links when possible 2015-01-15 02:07:00 -08:00
Bob Lantz 127f35a9bc Revert to using OVS default OpenFlow versions.
It makes sense to follow Open vSwitch's lead here.
OVS 2.3 enables OpenFlow 1.0 through 1.3 by default.
OVS 2.0 has incomplete support for 1.3, but you can enable
it with protocols=OpenFlow13
2015-01-14 14:18:53 -08:00
Bob Lantz 171e815122 Set OVSSwitch default protocols to OpenFlow10,OpenFlow13
For OVS versions 1.9 or earlier, this setting is ignored.
2015-01-14 14:15:14 -08:00
Bob Lantz 3ac5cafe53 Fix code minor code check errors 2015-01-06 16:26:33 -08:00
Bob Lantz a7ad739036 Disable IPv6 via grub command line
Unfortunately disabling IPv6 via sysctl doesn't actually
disable it on all of the interfaces by default. Disabling
it via grub disables it entirely in the VM.

Helps with #454
2014-12-17 13:49:12 -08:00
Bob Lantz a84bec9709 Disable splash and quiet individually (more robust) 2014-12-17 13:48:43 -08:00
Bob Lantz 05dbf82edb Correctly set controller backoff for OVS.
Also report connected in standalone/bridge mode

Fixes #460

Conflicts:
	mininet/node.py
2014-12-11 17:03:49 -08:00
Bob Lantz c75eb47158 2.2.0rc1 -> 2.2.0 2014-12-09 13:37:43 -08:00
Bob Lantz 9945864a32 2.2.0rc1 -> 2.2.0rc2
Basically no changes except for whitespace and satisfying pep8.
2014-12-08 15:37:50 -08:00
Bob Lantz beeea4b292 Exclude miniedit from pep8 checking for now
(Also untabbed comment lines, flagged by emacs et al.)
2014-12-08 15:27:13 -08:00
Bob Lantz 7a3159c9af Spacing tweaks for pep8 checker 2014-12-08 15:10:32 -08:00
Bob Lantz ccd6b5cd7d version -> 2.2.0rc1 and update copyright date 2014-12-04 09:28:19 -08:00
Bob Lantz 908e85d9f9 Remove PLFMT since options are moved to .pylint 2014-12-04 09:27:53 -08:00
Bob Lantz e341526f46 Raise line limit to 4000 for miniedit.py (see note)
Future versions of pylint will allow this to be disabled
in the file itself, so we can drop the limit back to
1500 or something more reasonable!!
2014-12-04 09:26:11 -08:00
Bob Lantz 03461ce908 Add 'slowtest' make target to test walkthrough, examples 2014-12-04 09:17:13 -08:00
Bob Lantz 8c37975d44 Remove erroneous self.cmd = None 2014-12-04 09:03:46 -08:00
Bob Lantz 4d55ef1132 Restore missing space in RT_GROUP_SCHED message 2014-12-04 08:57:28 -08:00
Bob Lantz 342d47cfb5 call to run() should be to runCmd() 2014-12-04 08:56:08 -08:00
Bob Lantz 554abdd57a warn -> debug in connected; change IVS class comment 2014-12-04 08:38:54 -08:00
Bob Lantz 061598f011 Change from numeric to symbolic pylint error codes 2014-12-04 08:21:53 -08:00
Bob Lantz d754a7ceea Call super(deleteIntfs)
Maybe this is better - maybe not. ;-p
2014-12-04 07:36:53 -08:00
Bob Lantz 643c9f912f More pylint changes... 2014-12-04 05:55:57 -08:00
Bob Lantz 4965421215 More pylint fixes... 2014-12-04 02:57:36 -08:00
Bob Lantz 18aab5b786 More pylint changes 2014-12-04 00:51:05 -08:00
Bob Lantz b905dddf19 Reorganize and pass pylint 2014-12-02 20:26:34 -08:00
Bob Lantz 11a9c46904 Fix missing imports 2014-12-02 23:22:56 -08:00
Bob Lantz b1ec912db5 Fixing pylint errors 2014-12-02 23:00:50 -08:00
Bob Lantz db45b7c644 Parseable output format for newer pylint 2014-12-02 23:00:14 -08:00
Bob Lantz 2256a53830 Fix pylint error 2014-12-02 22:59:32 -08:00
Bob Lantz 03ef55672d Add cleanup and fix pylint errors 2014-12-02 22:58:57 -08:00
Bob Lantz c45bfab318 Add cleanup 2014-12-02 22:58:38 -08:00
Bob Lantz b2fcab827d Add cleanup and fix pylint errors 2014-12-02 22:58:10 -08:00
Bob Lantz 1471da95a9 Fix pylint errors 2014-12-02 22:57:32 -08:00
Bob Lantz 5a530af189 Remove trailing whitespace. ;-/ 2014-12-01 15:39:44 -08:00
Bob Lantz 3c9f5ad56e Disable pep8 whitespace errors 2014-12-01 15:38:38 -08:00
Bob Lantz c5d9e0e03c Set default route in one cmd line to avoid dc'ing root NS
Usually you won't want to create a node in the root namespace,
and usually you won't want to use the Mininet API to set your
(real) default route, but if you do then you will probably want
to use a single command line to avoid disconnecting an SSH
session while we wait for the reulst of the 'ip route del default
command.
2014-12-01 15:10:44 -08:00
Brian O'Connor 0094997aa1 fixing install-mininet-vm.sh 2014-11-25 17:18:49 -08:00
lantz 7a411b6bb5 Merge pull request #453 from cdburkard/master
update examples README with new examples
2014-11-25 13:01:31 -08:00
cody burkard c2341cd47a update examples README with new examples 2014-11-24 19:43:15 -08:00
Bob Lantz 4219b22978 2.2.0b2 2014-11-24 19:35:34 -08:00
lantz 08ab7e8de7 Merge pull request #452 from mininet/nat-cmd
Updating NAT class to use gateway interface
2014-11-24 19:13:33 -08:00
lantz 3ef6bcface Additional info about --nat and LinuxRouter 2014-11-24 19:04:15 -08:00
88 changed files with 4014 additions and 2600 deletions

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
Mininet uses GitHub issues for bug reports and feature requests only.
These issues can be viewed at bugs.mininet.org
If you have a question that is not a bug report or a feature request,
please use the documentation at docs.mininet.org, the FAQ
at faq.mininet.org, and the mininet-discuss mailing list.
For bug reports, please fill in the following information in detail,
and also feel free to include additional information such as debug
output from mn -v debug, etc.
--- Cut Here ---
### Expected/Desired Behavior:
### Actual Behavior:
### Detailed Steps to Reproduce the Behavior:
### Additional Information:
+14 -8
View File
@@ -41,16 +41,21 @@ load-plugins=
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011
disable=pointless-except, invalid-name, super-init-not-called, fixme, star-args,
too-many-instance-attributes, too-few-public-methods, too-many-arguments,
too-many-locals, too-many-public-methods, duplicate-code, bad-whitespace,
locally-disabled, locally-enabled
# bad-continuation, wrong-import-order
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=colorized
msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
# Include message's id in outpu
# Include message's id in output
include-ids=yes
# Put messages in a separate file for each module / package specified on the
@@ -58,7 +63,7 @@ include-ids=yes
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells wether to display a full report or only the messages
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highes
@@ -108,10 +113,10 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
function-rgx=[a-z_][a-z0-9]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
method-rgx=[a-z_][a-z0-9]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
@@ -120,7 +125,7 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
variable-rgx=[a-z_][a-z0-9]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
@@ -191,7 +196,7 @@ additional-builtins=
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
defining-attr-methods=__init__,__new__,setUp,build
# checks for sign of poor/misdesign:
@@ -264,7 +269,8 @@ int-import-graph=
max-line-length=80
# Maximum number of lines in a module
max-module-lines=1500
# XXX 1500 -> 4000 for miniedit.py
max-module-lines=4000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
+31
View File
@@ -0,0 +1,31 @@
language: python
sudo: required
matrix:
include:
- dist: trusty
env: dist="14.04 LTS trusty"
# - dist: xenial
# env: dist="16.04 LTS xenial"
# Travis-CI only proposes 14.04 LTS Trusty and there is no plan to update to 16.04 xenial
# (c.f. https://github.com/travis-ci/travis-ci/issues/5821)
# It is useless to add a second job because it will run in the same Ubuntu version (14.04)
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq vlan
- sudo util/install.sh -n
install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- sudo util/install.sh -fnvw
script:
- sudo mn --test pingall
- sudo python mininet/test/runner.py -v -quick
- sudo python examples/test/runner.py -v -quick
notifications:
email:
on_success: never
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
+4 -2
View File
@@ -29,14 +29,16 @@ Andrew Ferguson
Eder Leao Fernandes
Gregory Gee
Jon Hall
Roan Huang
Vitaly Ivanov
Babis Kaidos
Rich Lane
Rémy Léone
Zi Shen Lim
David Mahler
Murphy McCauley
José Pedro Oliveira
James Page
Rich Lane
Rémy Léone
Angad Singh
Piyush Srivastava
Ed Swierk
+6 -6
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.2.0b1
Mininet 2.3.0d1
---
The supported installation methods for Mininet are 1) using a
@@ -65,7 +65,7 @@ like to contribute an installation script, we would welcome it!)
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`.
our handy `install.sh` script, which is in `util/`.
*WARNING: USE AT YOUR OWN RISK!*
@@ -81,7 +81,7 @@ like to contribute an installation script, we would welcome it!)
To install Mininet itself, the OpenFlow reference implementation, and
Open vSwitch, you may use:
mininet/util/install.sh -fnv
util/install.sh -fnv
This should be reasonably quick, and the following command should
work after the installation:
@@ -92,14 +92,14 @@ like to contribute an installation script, we would welcome it!)
including POX, the OpenFlow WireShark dissector, the `oftest`
framework, and other potentially useful software, you may use:
mininet/util/install.sh -a
util/install.sh -a
This takes about 4 minutes on our test system.
You can change the directory where the dependencies are installed using
the -s <directory> flag.
mininet/util/install.sh -s <directory> -a
util/install.sh -s <directory> -a
3.2. Native installation from source on Fedora 18+.
@@ -128,7 +128,7 @@ like to contribute an installation script, we would welcome it!)
* install Mininet, the OpenFlow reference implementation, and
Open vSwitch
mininet/util/install.sh -fnv
util/install.sh -fnv
* enable and start openvswitch
+2 -2
View File
@@ -1,6 +1,6 @@
Mininet 2.2.0b1 License
Mininet 2.3.0d1 License
Copyright (c) 2013 Open Networking Laboratory
Copyright (c) 2013-2016 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
The Leland Stanford Junior University
+13 -6
View File
@@ -2,11 +2,12 @@ MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py
MN = bin/mn
PYMN = python -B bin/mn
BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec
MANPAGES = mn.1 mnexec.1
P8IGN = E251,E201,E302,E202
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
BINDIR = /usr/bin
MANDIR = /usr/share/man/man1
DOCDIRS = doc/html doc/latex
@@ -24,7 +25,8 @@ codecheck: $(PYSRC)
util/versioncheck.py
pyflakes $(PYSRC)
pylint --rcfile=.pylint $(PYSRC)
pep8 --repeat --ignore=$(P8IGN) $(PYSRC)
# Exclude miniedit from pep8 checking for now
pep8 --repeat --ignore=$(P8IGN) `ls $(PYSRC) | grep -v miniedit.py`
errcheck: $(PYSRC)
-echo "Running check for errors only"
@@ -36,8 +38,13 @@ test: $(MININET) $(TEST)
mininet/test/test_nets.py
mininet/test/test_hifi.py
slowtest: $(MININET)
-echo "Running slower tests (walkthrough, examples)"
mininet/test/test_walkthrough.py -v
mininet/examples/test/runner.py -v
mnexec: mnexec.c $(MN) mininet/net.py
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
install: $(MNEXEC) $(MANPAGES)
install $(MNEXEC) $(BINDIR)
@@ -45,7 +52,7 @@ install: $(MNEXEC) $(MANPAGES)
python setup.py install
develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well
# Perhaps we should link these as well
install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR)
python setup.py develop
@@ -54,11 +61,11 @@ man: $(MANPAGES)
mn.1: $(MN)
PYTHONPATH=. help2man -N -n "create a Mininet network." \
--no-discard-stderr $< -o $@
--no-discard-stderr "$(PYMN)" -o $@
mnexec.1: mnexec
help2man -N -n "execution utility for Mininet." \
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
-h "-h" -v "-v" --no-discard-stderr ./$< -o $@
.PHONY: doc
+22 -34
View File
@@ -1,9 +1,10 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.2.0b1
Mininet 2.3.0d1
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
### What is Mininet?
@@ -68,35 +69,25 @@ Mininet includes:
### New features in this release
This release provides a number of bug fixes as well as
several new features, including:
This is primarily a performance improvement and bug fix release.
* Improved OpenFlow 1.3 support
- Batch startup has been implemented for Open vSwitch, improving
startup performance.
- `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
- OVS patch links have been implemented via OVSLink and --link ovs
* A new `nodelib.py` node library, and new `Node` types including
`LinuxBridge`, `OVSBridge`, `LinuxRouter` and `NAT`
Warning! These links have *serious limitations* compared to
virtual Ethernet pairs: they are not attached to real Linux
interfaces so you cannot use tcpdump or wireshark with them;
they also cannot be used in long chains - we don't recommend more
than 64 OVSLinks, for example --linear,64. However, they can offer
significantly better performance than veth pairs, for certain
configurations.
* An improved MiniEdit GUI (`examples/miniedit.py`) - thanks to
Gregory Gee
- You can now easily install Mininet on a Raspberry Pi ;-)
* 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
not be considered a stable part of the Mininet API!
- Additional information for this release and previous releases
may be found in the release notes on docs.mininet.org
### Installation
@@ -120,6 +111,8 @@ Mininet mailing list, `mininet-discuss` at:
### Join Us
Thanks again to all of the Mininet contributors!
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,
@@ -133,12 +126,7 @@ hard work that Mininet continues to grow and improve.
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
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.
Bob Lantz
Mininet Core Team
[1]: https://travis-ci.org/mininet/mininet.svg?branch=master
+175 -131
View File
@@ -21,18 +21,20 @@ if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
from mininet.clean import cleanup
from mininet.cli import CLI
from mininet.log import lg, LEVELS, info, debug, warn, error
import mininet.cli
from mininet.log import lg, LEVELS, info, debug, warn, error, output
from mininet.net import Mininet, MininetWithControlNet, VERSION
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
RYU, NOX, RemoteController, findController, DefaultController,
Ryu, NOX, RemoteController, findController,
DefaultController, NullController,
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 customConstructor, splitArgs
IVSSwitch )
from mininet.nodelib import LinuxBridge, Server
from mininet.link import Link, TCLink, TCULink, OVSLink
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
SingleSwitchReversedTopo, MinimalTopo )
import mininet.topolib
from mininet.util import customClass, specialClass, splitArgs
from mininet.util import buildTopo
from functools import partial
@@ -40,82 +42,117 @@ from functools import partial
# Experimental! cluster edition prototype
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
RemoteOVSSwitch, RemoteLink,
SwitchBinPlacer, RandomPlacer )
SwitchBinPlacer, RandomPlacer,
ClusterCleanup )
from mininet.examples.clustercli import ClusterCLI
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
# built in topologies, created only when run
TOPODEF = 'minimal'
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
TOPOS = { 'minimal': MinimalTopo,
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'tree': TreeTopo,
'torus': TorusTopo }
'single': SingleSwitchTopo }
TOPOS.update( mininet.topolib.topos)
SWITCHDEF = 'default'
SWITCHES = { 'user': UserSwitch,
'ovs': OVSSwitch,
'ovs': OVSSwitch,
'ovsbr' : OVSBridge,
# Keep ovsk for compatibility with 2.0
'ovsk': OVSSwitch,
'ovsl': OVSLegacyKernelSwitch,
'ivs': IVSSwitch,
'lxbr': LinuxBridge,
'default': OVSSwitch }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': partial( CPULimitedHost, sched='rt' ),
'cfs': partial( CPULimitedHost, sched='cfs' ) }
'server': Server,
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
CONTROLLERDEF = 'default'
CONTROLLERS = { 'ref': Controller,
'ovsc': OVSController,
'nox': NOX,
'remote': RemoteController,
'ryu': RYU,
'default': DefaultController, # Note: replaced below
'none': lambda name: None }
'ryu': Ryu,
'default': DefaultController, # Note: overridden below
'none': NullController }
LINKDEF = 'default'
LINKS = { 'default': Link,
'tc': TCLink }
LINKS = { 'default': Link, # Note: overridden below
'tc': TCLink,
'tcu': TCULink,
'ovs': OVSLink }
# TESTS dict can contain functions and/or Mininet() method names
# XXX: it would be nice if we could specify a default test, but
# this may be tricky
TESTS = { name: True
for name in ( 'pingall', 'pingpair', 'iperf', 'iperfudp' ) }
CLI = None # Set below if needed
# Locally defined tests
def allTest( net ):
"Run ping and iperf tests"
net.waitConnected()
net.start()
net.ping()
net.iperf()
def nullTest( _net ):
"Null 'test' (does nothing)"
pass
TESTS.update( all=allTest, none=nullTest, build=nullTest )
# Map to alternate spellings of Mininet() methods
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
'iperfudp': 'iperfUdp' }
def runTests( mn, options ):
"""Run tests
mn: Mininet object
option: list of test optinos """
# Split option into test name and parameters
for option in options:
# Multiple tests may be separated by '+' for now
for test in option.split( '+' ):
test, args, kwargs = splitArgs( test )
test = ALTSPELLING.get( test.lower(), test )
testfn = TESTS.get( test, test )
if callable( testfn ):
testfn( mn, *args, **kwargs )
elif hasattr( mn, test ):
mn.waitConnected()
getattr( mn, test )( *args, **kwargs )
else:
raise Exception( 'Test %s is unknown - please specify one of '
'%s ' % ( test, TESTS.keys() ) )
# optional tests to run
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
'none' ]
ALTSPELLING = { 'pingall': 'pingAll',
'pingpair': 'pingPair',
'iperfudp': 'iperfUdp',
'iperfUDP': 'iperfUdp' }
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
def addDictOption( opts, choicesDict, default, name, **kwargs ):
"""Convenience function to add choices dicts to OptionParser.
opts: OptionParser instance
choicesDict: dictionary of valid choices, must include default
default: default choice key
name: long option name
help: string"""
if default not in choicesDict:
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
if not helpStr:
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
help = helpStr )
kwargs: additional arguments to add_option"""
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
helpList = [ '%s=%s' % ( k, v.__name__ )
for k, v in choicesDict.items() ]
helpStr += ' ' + ( ' '.join( helpList ) )
params = dict( type='string', default=default, help=helpStr )
params.update( **kwargs )
opts.add_option( '--' + name, **params )
def version( *_args ):
"Print Mininet version and exit"
print "%s" % VERSION
output( "%s\n" % VERSION )
exit()
@@ -132,7 +169,7 @@ class MininetRunner( object ):
self.setup()
self.begin()
def custom( self, option, opt_str, value, parser ):
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
@@ -145,7 +182,7 @@ class MininetRunner( object ):
else:
# Accept a comma-separated list of filenames
files += value.split(',')
for fileName in files:
customs = {}
if os.path.isfile( fileName ):
@@ -157,7 +194,8 @@ class MininetRunner( object ):
def setCustom( self, name, value ):
"Set custom parameters for MininetRunner."
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links'
'testnames', 'tests' ):
# Update dictionaries
param = name.upper()
globals()[ param ].update( value )
@@ -168,9 +206,12 @@ class MininetRunner( object ):
# Add or modify global variable or class
globals()[ name ] = value
def setNat( self, option, opt_str, value, parser ):
def setNat( self, _option, opt_str, value, parser ):
"Set NAT option(s)"
assert self # satisfy pylint
parser.values.nat = True
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-': #first arg, first char != '-'
# first arg, first char != '-'
if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-':
value = parser.rargs.pop( 0 )
_, args, kwargs = splitArgs( opt_str + ',' + value )
parser.values.nat_args = args
@@ -193,7 +234,7 @@ class MininetRunner( object ):
opts = OptionParser( description=desc, usage=usage )
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
addDictOption( opts, CONTROLLERS, [], 'controller', action='append' )
addDictOption( opts, LINKS, LINKDEF, 'link' )
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
@@ -201,10 +242,11 @@ class MininetRunner( object ):
default=False, help='clean and exit' )
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 ) )
type='string',
help='read custom classes or params from .py file(s)'
)
opts.add_option( '--test', default=[], action='append',
dest='test', help='|'.join( TESTS.keys() ) )
opts.add_option( '--xterms', '-x', action='store_true',
default=False, help='spawn xterms for each node' )
opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
@@ -218,7 +260,7 @@ class MininetRunner( object ):
help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' )
opts.add_option( '--listenport', type='int', default=6634,
opts.add_option( '--listenport', type='int', default=6654,
help='base port for passive switch listening' )
opts.add_option( '--nolistenport', action='store_true',
default=False, help="don't use passive listening " +
@@ -231,10 +273,12 @@ class MininetRunner( object ):
default=False, help="pin hosts to CPU cores "
"(requires --host cfs or --host rt)" )
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"
help="[option=val...] 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' )
@@ -259,106 +303,106 @@ class MininetRunner( object ):
# set logging verbosity
if LEVELS[self.options.verbosity] > LEVELS['output']:
print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
warn( '*** WARNING: selected verbosity level (%s) will hide CLI '
'output!\n'
'Please restart Mininet with -v [debug, info, output].'
'Please restart Mininet with -v [debug, info, output].\n'
% self.options.verbosity )
lg.setLogLevel( self.options.verbosity )
# Maybe we'll reorganize this someday...
# pylint: disable=too-many-branches,too-many-statements,global-statement
def begin( self ):
"Create and run mininet."
if self.options.clean:
global CLI
opts = self.options
if opts.cluster:
servers = opts.cluster.split( ',' )
for server in servers:
ClusterCleanup.add( server )
if opts.clean:
cleanup()
exit()
start = time.time()
if self.options.controller == 'default':
if not opts.controller:
# 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' )
opts.controller = [ 'default' ]
if not CONTROLLERS[ 'default' ]:
opts.controller = [ 'none' ]
if opts.switch == 'default':
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 )
controller = customConstructor( CONTROLLERS, self.options.controller )
link = customConstructor( LINKS, self.options.link )
opts.switch = 'ovsbr'
elif opts.switch not in ( 'ovsbr', 'lxbr' ):
raise Exception( "Could not find a default controller "
"for switch %s" %
opts.switch )
topo = buildTopo( TOPOS, opts.topo )
switch = customClass( SWITCHES, opts.switch )
host = customClass( HOSTS, opts.host )
controller = [ customClass( CONTROLLERS, c )
for c in opts.controller ]
if opts.switch == 'user' and opts.link == 'default':
debug( '*** Using TCULink with UserSwitch\n' )
# Use link configured correctly for UserSwitch
opts.link = 'tcu'
link = customClass( LINKS, opts.link )
if self.validate:
self.validate( self.options )
self.validate( opts )
ipBase = self.options.ipbase
xterms = self.options.xterms
mac = self.options.mac
arp = self.options.arp
pin = self.options.pin
listenPort = None
if not self.options.nolistenport:
listenPort = self.options.listenport
if opts.nolistenport:
opts.listenport = None
# Handle inNamespace, cluster options
inNamespace = self.options.innamespace
cluster = self.options.cluster
if inNamespace and cluster:
print "Please specify --innamespace OR --cluster"
# Handle innamespace, cluster options
if opts.innamespace and opts.cluster:
error( "Please specify --innamespace OR --cluster\n" )
exit()
Net = MininetWithControlNet if inNamespace else Mininet
cli = ClusterCLI if cluster else CLI
if cluster:
Net = MininetWithControlNet if opts.innamespace else Mininet
if opts.cluster:
warn( '*** WARNING: Experimental cluster mode!\n'
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
'*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
Net = partial( MininetCluster, servers=cluster.split( ',' ),
placement=PLACEMENT[ self.options.placement ] )
Net = partial( MininetCluster, servers=servers,
placement=PLACEMENT[ opts.placement ] )
mininet.cli.CLI = ClusterCLI
mn = Net( topo=topo,
switch=switch, host=host, controller=controller,
link=link,
ipBase=ipBase,
inNamespace=inNamespace,
xterms=xterms, autoSetMacs=mac,
autoStaticArp=arp, autoPinCpus=pin,
listenPort=listenPort )
switch=switch, host=host, controller=controller, link=link,
ipBase=opts.ipbase, inNamespace=opts.innamespace,
xterms=opts.xterms, autoSetMacs=opts.mac,
autoStaticArp=opts.arp, autoPinCpus=opts.pin,
listenPort=opts.listenport )
if self.options.ensure_value( 'nat', False ):
nat = mn.addNAT( *self.options.nat_args, **self.options.nat_kwargs )
nat.configDefault()
if opts.ensure_value( 'nat', False ):
mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
if self.options.pre:
cli( mn, script=self.options.pre )
# --custom files can set CLI or change mininet.cli.CLI
CLI = mininet.cli.CLI if CLI is None else CLI
test = self.options.test
test = ALTSPELLING.get( test, test )
if opts.pre:
CLI( mn, script=opts.pre )
mn.start()
if test == 'none':
pass
elif test == 'all':
mn.waitConnected()
mn.start()
mn.ping()
mn.iperf()
elif test == 'cli':
cli( mn )
elif test != 'build':
mn.waitConnected()
getattr( mn, test )()
if opts.test:
runTests( mn, opts.test )
else:
CLI( mn )
if self.options.post:
cli( mn, script=self.options.post )
if opts.post:
CLI( mn, script=opts.post )
mn.stop()
+43 -3
View File
@@ -11,6 +11,31 @@ Mininet's Python API.
This example uses Mininet's medium-level API to create an sshd
process running in a namespace. Doesn't use OpenFlow.
#### bind.py:
This example shows how you can create private directories for each
node in a Mininet topology.
#### cluster.py:
This example contains all of the code for experimental cluster
edition. Remote classes and MininetCluster can be imported from
here to create a topology with nodes on remote machines.
#### clusterSanity.py:
This example runs cluster edition locally as a sanity check to test
basic functionality.
#### clustercli.py:
This example contains a CLI for experimental cluster edition.
#### clusterdemo.py:
This example is a basic demo of cluster edition on 3 servers with
a tree topology of depth 3 and fanout 3.
#### consoles.py:
This example creates a grid of console windows, one for each node,
@@ -47,6 +72,11 @@ topology object) and adding nodes to it.
This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
#### intfoptions.py:
This example reconfigures a TCIntf during runtime with different
traffic control commands to test bandwidth, loss, and delay.
#### limit.py:
This example shows how to use link and CPU limits.
@@ -56,7 +86,7 @@ This example shows how to use link and CPU limits.
This example shows how to create a custom topology programatically
by subclassing Topo, and how to run a series of tests on it.
### linuxrouter.py:
#### linuxrouter.py:
This example shows how to create and configure a router in Mininet
that uses Linux IP forwarding.
@@ -65,11 +95,16 @@ that uses Linux IP forwarding.
This example demonstrates creating a network via a graphical editor.
#### mobility.py
#### mobility.py:
This example demonstrates detaching an interface from one switch and
attaching it another as a basic way to move a host around a network.
#### multiLink.py:
This example demonstrates the creation of multiple links between
nodes using a custom Topology class.
#### multiping.py:
This example demonstrates one method for
@@ -89,7 +124,12 @@ This example shows how to connect a Mininet network to the Internet
using NAT. It also answers the eternal question "why can't I ping
`google.com`?"
#### numberedports.py
#### natnet.py:
This example demonstrates how to create a network using a NAT node
to connect hosts to the internet.
#### numberedports.py:
This example verifies the mininet ofport numbers match up to the ovs port numbers.
It also verifies that the port numbers match up to the interface numbers
+12 -9
View File
@@ -3,32 +3,35 @@
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
import sys
from mininet.node import Host
from mininet.util import ensureRoot, waitListening
from mininet.log import info, warn, output
ensureRoot()
timeout = 5
print "*** Creating nodes"
info( "*** Creating nodes\n" )
h1 = Host( 'h1' )
root = Host( 'root', inNamespace=False )
print "*** Creating links"
info( "*** Creating link\n" )
h1.linkTo( root )
print h1
info( h1 )
print "*** Configuring nodes"
info( "*** Configuring nodes\n" )
h1.setIP( '10.0.0.1', 8 )
root.setIP( '10.0.0.2', 8 )
print "*** Creating banner file"
info( "*** Creating banner file\n" )
f = open( '/tmp/%s.banner' % h1.name, 'w' )
f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) )
f.close()
print "*** Running sshd"
info( "*** Running sshd\n" )
cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name
# add arguments from the command line
if len( sys.argv ) > 1:
@@ -37,7 +40,7 @@ h1.cmd( cmd )
listening = waitListening( server=h1, port=22, timeout=timeout )
if listening:
print "*** You may now ssh into", h1.name, "at", h1.IP()
output( "*** You may now ssh into", h1.name, "at", h1.IP(), '\n' )
else:
print ( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ) )
warn( "*** Warning: after %s seconds, %s is not listening on port 22"
% ( timeout, h1.name ), '\n' )
+4 -6
View File
@@ -22,7 +22,7 @@ to temporary private directories. To do this, simply create a list of
directories to be made private. A tmpfs will then be mounted on them.
You may use both temporary and persistent directories at the same
time. In the following privateDirs string, each host will have a
time. In the following privateDirs string, each host will have a
persistent directory in the root filesystem at
"/tmp/(hostname)/var/run" mounted on "/var/run". Each host will also
have a temporary private directory mounted on "/var/log".
@@ -48,8 +48,8 @@ from functools import partial
def testHostWithPrivateDirs():
"Test bind mounts"
topo = SingleSwitchTopo( 10 )
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/var/run', '/tmp/%(name)s/var/run' ),
privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ),
( '/var/run', '/tmp/%(name)s/var/run' ),
'/var/mn' ]
host = partial( Host,
privateDirs=privateDirs )
@@ -57,7 +57,7 @@ def testHostWithPrivateDirs():
net.start()
directories = [ directory[ 0 ] if isinstance( directory, tuple )
else directory for directory in privateDirs ]
info( 'Private Directories:', directories, '\n' )
info( 'Private Directories:', directories, '\n' )
CLI( net )
net.stop()
@@ -65,5 +65,3 @@ if __name__ == '__main__':
setLogLevel( 'info' )
testHostWithPrivateDirs()
info( 'Done.\n')
+288 -136
View File
@@ -74,24 +74,68 @@ Things to do:
- hifi support (e.g. delay compensation)
"""
from mininet.node import Node, Host, OVSSwitch, Controller
from mininet.link import Link, Intf
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, makeIntfPair, errRun, retry
from mininet.util import quietRun, errRun
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from mininet.clean import addCleanupCallback
from signal import signal, SIGINT, SIG_IGN
from subprocess import Popen, PIPE, STDOUT
import os
from random import randrange
from sys import exit
import sys
import re
from itertools import groupby
from operator import attrgetter
from distutils.version import StrictVersion
def findUser():
"Try to return logged-in (usually non-root) user"
return (
# If we're running sudo
os.environ.get( 'SUDO_USER', False ) or
# Logged-in user (if we have a tty)
( quietRun( 'who am i' ).split() or [ False ] )[ 0 ] or
# Give up and return effective user
quietRun( 'whoami' ).strip() )
class ClusterCleanup( object ):
"Cleanup callback"
inited = False
serveruser = {}
@classmethod
def add( cls, server, user='' ):
"Add an entry to server: user dict"
if not cls.inited:
addCleanupCallback( cls.cleanup )
if not user:
user = findUser()
cls.serveruser[ server ] = user
@classmethod
def cleanup( cls ):
"Clean up"
info( '*** Cleaning up cluster\n' )
for server, user in cls.serveruser.iteritems():
if server == 'localhost':
# Handled by mininet.clean.cleanup()
continue
else:
cmd = [ 'su', user, '-c',
'ssh %s@%s sudo mn -c' % ( user, server ) ]
info( cmd, '\n' )
info( quietRun( cmd ) )
# BL note: so little code is required for remote nodes,
# we will probably just want to update the main Node()
# class to enable it for remote access! However, there
@@ -113,7 +157,7 @@ class RemoteMixin( object ):
'-o', 'ForwardAgent=yes', '-tt' ]
def __init__( self, name, server='localhost', user=None, serverIP=None,
controlPath=False, splitInit=False, **kwargs):
controlPath=True, splitInit=False, **kwargs):
"""Instantiate a remote node
name: name of remote node
server: remote server (optional)
@@ -123,8 +167,10 @@ class RemoteMixin( object ):
**kwargs: see Node()"""
# We connect to servers by IP address
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()
self.serverIP = ( serverIP if serverIP
else self.findServerIP( self.server ) )
self.user = user if user else findUser()
ClusterCleanup.add( server=server, user=user )
if controlPath is True:
# Set a default control path for shared SSH connections
controlPath = '/tmp/mn-%r@%h:%p'
@@ -137,28 +183,16 @@ class RemoteMixin( object ):
self.sshcmd += [ '-o', 'ControlPath=' + self.controlPath,
'-o', 'ControlMaster=auto',
'-o', 'ControlPersist=' + '1' ]
self.sshcmd = self.sshcmd + [ self.dest ]
self.sshcmd += [ self.dest ]
self.isRemote = True
else:
self.dest = None
self.sshcmd = []
self.isRemote = False
# Satisfy pylint
self.shell, self.pid = None, None
super( RemoteMixin, self ).__init__( name, **kwargs )
@staticmethod
def findUser():
"Try to return logged-in (usually non-root) user"
try:
# If we're running sudo
return os.environ[ 'SUDO_USER' ]
except:
try:
# Logged-in user (if we have a tty)
return quietRun( 'who am i' ).split()[ 0 ]
except:
# Give up and return effective user
return quietRun( 'whoami' )
# Determine IP address of local host
_ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
@@ -179,14 +213,15 @@ class RemoteMixin( object ):
def startShell( self, *args, **kwargs ):
"Start a shell process for running commands"
if self.isRemote:
kwargs.update( mnopts='-c' )
kwargs.update( mnopts='-cp' )
super( RemoteMixin, self ).startShell( *args, **kwargs )
if self.splitInit:
self.sendCmd( 'echo $$' )
else:
self.pid = int( self.cmd( 'echo $$' ) )
# Optional split initialization
self.sendCmd( 'echo $$' )
if not self.splitInit:
self.finishInit()
def finishInit( self ):
"Wait for split initialization to complete"
self.pid = int( self.waitOutput() )
def rpopen( self, *cmd, **opts ):
@@ -204,7 +239,7 @@ class RemoteMixin( object ):
args: string or list of strings
returns: stdout and stderr"""
popen = self.rpopen( *cmd, **opts )
# print 'RCMD: POPEN:', popen
# info( 'RCMD: POPEN:', popen, '\n' )
# These loops are tricky to get right.
# Once the process exits, we can read
# EOF twice if necessary.
@@ -243,7 +278,7 @@ class RemoteMixin( object ):
# Drop privileges
cmd = [ 'sudo', '-E', '-u', self.user ] + cmd
params.update( preexec_fn=self._ignoreSignal )
debug( '_popen', ' '.join(cmd), params )
debug( '_popen', cmd, '\n' )
popen = super( RemoteMixin, self )._popen( cmd, **params )
return popen
@@ -253,16 +288,9 @@ class RemoteMixin( object ):
def addIntf( self, *args, **kwargs ):
"Override: use RemoteLink.moveIntf"
return super( RemoteMixin, self).addIntf( *args,
moveIntfFn=RemoteLink.moveIntf, **kwargs )
# kwargs.update( moveIntfFn=RemoteLink.moveIntf )
return super( RemoteMixin, self).addIntf( *args, **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"
@@ -276,20 +304,50 @@ class RemoteHost( RemoteNode ):
class RemoteOVSSwitch( RemoteMixin, OVSSwitch ):
"Remote instance of Open vSwitch"
OVSVersions = {}
def __init__( self, *args, **kwargs ):
# No batch startup yet
kwargs.update( batch=True )
super( RemoteOVSSwitch, self ).__init__( *args, **kwargs )
def isOldOVS( self ):
"Is remote switch using an old OVS version?"
cls = type( self )
if self.server not in cls.OVSVersions:
# pylint: disable=not-callable
vers = self.cmd( 'ovs-vsctl --version' )
cls.OVSVersions[ self.server ] = re.findall( '\d+\.\d+', vers )[ 0 ]
# pylint: enable=not-callable
cls.OVSVersions[ self.server ] = re.findall(
r'\d+\.\d+', vers )[ 0 ]
return ( StrictVersion( cls.OVSVersions[ self.server ] ) <
StrictVersion( '1.10' ) )
StrictVersion( '1.10' ) )
@classmethod
def batchStartup( cls, switches, **_kwargs ):
"Start up switches in per-server batches"
key = attrgetter( 'server' )
for server, switchGroup in groupby( sorted( switches, key=key ), key ):
info( '(%s)' % server )
group = tuple( switchGroup )
switch = group[ 0 ]
OVSSwitch.batchStartup( group, run=switch.cmd )
return switches
@classmethod
def batchShutdown( cls, switches, **_kwargs ):
"Stop switches in per-server batches"
key = attrgetter( 'server' )
for server, switchGroup in groupby( sorted( switches, key=key ), key ):
info( '(%s)' % server )
group = tuple( switchGroup )
switch = group[ 0 ]
OVSSwitch.batchShutdown( group, run=switch.rcmd )
return switches
class RemoteLink( Link ):
"A RemoteLink is a link between nodes which may be on different servers"
def __init__( self, node1, node2, **kwargs ):
@@ -301,71 +359,73 @@ class RemoteLink( Link ):
self.tunnel = None
kwargs.setdefault( 'params1', {} )
kwargs.setdefault( 'params2', {} )
self.cmd = None # satisfy pylint
Link.__init__( self, node1, node2, **kwargs )
def stop( self ):
"Stop this link"
if self.tunnel:
self.tunnel.terminate()
self.intf1.delete()
self.intf2.delete()
else:
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None ):
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
node1, node2 = self.node1, self.node2
node1 = self.node1 if node1 is None else node1
node2 = self.node2 if node2 is None else node2
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:
# Remote link on same remote server
return makeIntfPair( intfname1, intfname2, addr1, addr2,
run=node1.rcmd )
if server1 == server2:
# Link within same server
return Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs,
runCmd=None )
# Otherwise, make a tunnel
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
addr1, addr2 )
return self.tunnel
@staticmethod
def moveIntf( intf, node, printError=True ):
def moveIntf( intf, node ):
"""Move remote interface from root ns to node
intf: string, interface
dstNode: destination Node
srcNode: source Node or None (default) for root ns
printError: if true, print error"""
srcNode: source Node or None (default) for root ns"""
intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, node.pid )
node.rcmd( cmd )
links = node.cmd( 'ip link show' )
if not ( ' %s:' % intf ) in links:
if printError:
error( '*** Error: RemoteLink.moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
result = node.rcmd( cmd )
if result:
raise Exception('error executing command %s' % cmd)
return True
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'
assert node1.server != node2.server
# 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 )
debug( '\n*** Make SSH tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
# 1. Create tap interfaces
for node in node1, node2:
# For now we are hard-wiring tap9, which we will rename
node.rcmd( 'ip link delete tap9', stderr=PIPE )
cmd = 'ip tuntap add dev tap9 mode tap user ' + node.user
node.rcmd( cmd )
links = node.rcmd( 'ip link show' )
# print 'after add, links =', links
assert 'tap9' in links
result = node.rcmd( cmd )
if result:
raise Exception( 'error creating tap9 on %s: %s' %
( node, result ) )
# 2. Create ssh tunnel between tap interfaces
# -n: close stdin
dest = '%s@%s' % ( node2.user, node2.serverIP )
@@ -378,29 +438,25 @@ class RemoteLink( Link ):
debug( 'Waiting for tunnel to come up...\n' )
ch = tunnel.stdout.read( 1 )
if ch != '@':
error( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
tunnel.terminate()
tunnel.wait()
error( ch + tunnel.stdout.read() )
error( tunnel.stderr.read() )
exit( 1 )
raise Exception( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
# 3. Move interfaces if necessary
for node in node1, node2:
if node.inNamespace:
retry( 3, .01, RemoteLink.moveIntf, 'tap9', node )
if not self.moveIntf( 'tap9', node ):
raise Exception( 'interface move failed on node %s' % node )
# 4. Rename tap interfaces to desired names
for node, intf, addr in ( ( node1, intfname1, addr1 ),
( node2, intfname2, addr2 ) ):
( node2, intfname2, addr2 ) ):
if not addr:
node.cmd( 'ip link set tap9 name', intf )
result = node.cmd( 'ip link set tap9 name', intf )
else:
node.cmd( 'ip link set tap9 name', intf, 'address', addr )
for node, intf in ( ( node1, intfname1 ), ( node2, intfname2 ) ):
assert intf in node.cmd( 'ip link show' )
result = node.cmd( 'ip link set tap9 name', intf,
'address', addr )
if result:
raise Exception( 'error renaming %s: %s' % ( intf, result ) )
return tunnel
def status( self ):
@@ -417,13 +473,98 @@ class RemoteLink( Link ):
return result
class RemoteSSHLink( RemoteLink ):
"Remote link using SSH tunnels"
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
class RemoteGRELink( RemoteLink ):
"Remote link using GRE tunnels"
GRE_KEY = 0
def __init__(self, node1, node2, **kwargs):
RemoteLink.__init__( self, node1, node2, **kwargs )
def stop( self ):
"Stop this link"
if self.tunnel:
self.intf1.delete()
self.intf2.delete()
else:
Link.stop( self )
self.tunnel = None
def makeIntfPair( self, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
(override this method [and possibly delete()]
to change link type)"""
node1 = self.node1 if node1 is None else node1
node2 = self.node2 if node2 is None else node2
server1 = getattr( node1, 'server', 'localhost' )
server2 = getattr( node2, 'server', 'localhost' )
if server1 == server2:
# Link within same server
Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
node1, node2, deleteIntfs=deleteIntfs )
# Need to reduce the MTU of all emulated hosts to 1450 for GRE
# tunneling, otherwise packets larger than 1400 bytes cannot be
# successfully transmitted through the tunnel.
node1.cmd('ip link set dev %s mtu 1450' % intfname1)
node2.cmd('ip link set dev %s mtu 1450' % intfname2)
else:
# Otherwise, make a tunnel
self.makeTunnel( node1, node2, intfname1, intfname2, addr1, addr2 )
self.tunnel = 1
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 != node2.server
if node2.server == 'localhost':
return self.makeTunnel( node2, node1, intfname2, intfname1,
addr2, addr1 )
IP1, IP2 = node1.serverIP, node2.serverIP
# GRE tunnel needs to be set up with the IP of the local interface
# that connects the remote node, NOT '127.0.0.1' of localhost
if node1.server == 'localhost':
output = quietRun('ip route get %s' % node2.serverIP)
IP1 = output.split(' src ')[1].split()[0]
debug( '\n*** Make GRE tunnel ' + node1.server + ':' + intfname1 +
' == ' + node2.server + ':' + intfname2 )
tun1 = 'local ' + IP1 + ' remote ' + IP2
tun2 = 'local ' + IP2 + ' remote ' + IP1
self.__class__.GRE_KEY += 1
for (node, intfname, addr, tun) in [(node1, intfname1, addr1, tun1),
(node2, intfname2, addr2, tun2)]:
node.rcmd('ip link delete ' + intfname)
result = node.rcmd('ip link add name ' + intfname + ' type gretap '
+ tun + ' ttl 64 key '
+ str( self.__class__.GRE_KEY) )
if result:
raise Exception('error creating gretap on %s: %s'
% (node, result))
if addr:
node.rcmd('ip link set %s address %s' % (intfname, addr))
node.rcmd('ip link set dev %s up' % intfname)
node.rcmd('ip link set dev %s mtu 1450' % intfname)
if not self.moveIntf(intfname, node):
raise Exception('interface move failed on node %s' % node)
# Some simple placement algorithms for MininetCluster
class Placer( object ):
"Node placement algorithm for MininetCluster"
def __init__( self, servers=None, nodes=None, hosts=None,
switches=None, controllers=None, links=None ):
switches=None, controllers=None, links=None ):
"""Initialize placement object
servers: list of servers
nodes: list of all nodes
@@ -442,8 +583,9 @@ class Placer( object ):
def place( self, node ):
"Return server for a given node"
assert self, node # satisfy pylint
# Default placement: run locally
return None
return 'localhost'
class RandomPlacer( Placer ):
@@ -451,6 +593,7 @@ class RandomPlacer( Placer ):
def place( self, nodename ):
"""Random placement function
nodename: node name"""
assert nodename # please pylint
# This may be slow with lots of servers
return self.servers[ randrange( 0, len( self.servers ) ) ]
@@ -459,7 +602,7 @@ class RoundRobinPlacer( Placer ):
"""Round-robin placement
Note this will usually result in cross-server links between
hosts and switches"""
def __init__( self, *args, **kwargs ):
Placer.__init__( self, *args, **kwargs )
self.next = 0
@@ -467,6 +610,7 @@ class RoundRobinPlacer( Placer ):
def place( self, nodename ):
"""Round-robin placement function
nodename: node name"""
assert nodename # please pylint
# This may be slow with lots of servers
server = self.servers[ self.next ]
self.next = ( self.next + 1 ) % len( self.servers )
@@ -485,7 +629,7 @@ class SwitchBinPlacer( Placer ):
self.sset = frozenset( self.switches )
self.cset = frozenset( self.controllers )
# Server and switch placement indices
self.placement = self.calculatePlacement()
self.placement = self.calculatePlacement()
@staticmethod
def bin( nodes, servers ):
@@ -552,7 +696,7 @@ class HostSwitchBinPlacer( Placer ):
scount = len( self.servers )
self.hbin = max( int( len( self.hosts ) / scount ), 1 )
self.sbin = max( int( len( self.switches ) / scount ), 1 )
self.cbin = max( int( len( self.controllers ) / scount ) , 1 )
self.cbin = max( int( len( self.controllers ) / scount ), 1 )
info( 'scount:', scount )
info( 'bins:', self.hbin, self.sbin, self.cbin, '\n' )
self.servdict = dict( enumerate( self.servers ) )
@@ -560,7 +704,7 @@ class HostSwitchBinPlacer( Placer ):
self.sset = frozenset( self.switches )
self.cset = frozenset( self.controllers )
self.hind, self.sind, self.cind = 0, 0, 0
def place( self, nodename ):
"""Simple placement algorithm:
place nodes into evenly sized bins"""
@@ -580,7 +724,6 @@ class HostSwitchBinPlacer( Placer ):
return server
# The MininetCluster class is not strictly necessary.
# However, it has several purposes:
# 1. To set up ssh connection sharing/multiplexing
@@ -614,7 +757,7 @@ class MininetCluster( Mininet ):
if not self.serverIP:
self.serverIP = { server: RemoteMixin.findServerIP( server )
for server in self.servers }
self.user = params.pop( 'user', RemoteMixin.findUser() )
self.user = params.pop( 'user', findUser() )
if params.pop( 'precheck' ):
self.precheck()
self.connections = {}
@@ -626,6 +769,7 @@ class MininetCluster( Mininet ):
def popen( self, cmd ):
"Popen() for server connections"
assert self # please pylint
old = signal( SIGINT, SIG_IGN )
conn = Popen( cmd, stdin=PIPE, stdout=PIPE, close_fds=True )
signal( SIGINT, old )
@@ -643,13 +787,13 @@ class MininetCluster( Mininet ):
for server in self.servers:
ip = self.serverIP[ server ]
if not server or server == 'localhost':
continue
continue
info( server, '' )
dest = '%s@%s' % ( self.user, ip )
cmd = [ 'sudo', '-E', '-u', self.user ]
cmd += self.sshcmd + [ '-n', dest, 'sudo true' ]
debug( ' '.join( cmd ), '\n' )
out, err, code = errRun( cmd )
_out, _err, code = errRun( cmd )
if code != 0:
error( '\nstartConnection: server connection check failed '
'to %s using command:\n%s\n'
@@ -657,18 +801,19 @@ class MininetCluster( Mininet ):
result |= code
if result:
error( '*** Server precheck failed.\n'
'*** Make sure that the above ssh command works correctly.\n'
'*** Make sure that the above ssh command works'
' correctly.\n'
'*** You may also need to run mn -c on all nodes, and/or\n'
'*** use sudo -E.\n' )
exit( 1 )
sys.exit( 1 )
info( '\n' )
def modifiedaddHost( self, *args, **kwargs ):
"Slightly modify addHost"
assert self # please pylint
kwargs[ 'splitInit' ] = True
return Mininet.addHost( *args, **kwargs )
def placeNodes( self ):
"""Place nodes on servers (if they don't have a server), and
start shell processes"""
@@ -684,7 +829,7 @@ class MininetCluster( Mininet ):
for node in nodes:
config = self.topo.nodeInfo( node )
# keep local server name consistent accross nodes
if 'server' in config.keys() and config[ 'server' ] == None:
if 'server' in config.keys() and config[ 'server' ] is None:
config[ 'server' ] = 'localhost'
server = config.setdefault( 'server', placer.place( node ) )
if server:
@@ -700,10 +845,13 @@ class MininetCluster( Mininet ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
# Update IP address for controller that may not be local
if ( isinstance( controller, Controller)
and controller.IP() == '127.0.0.1'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
if ( isinstance( controller, Controller )
and controller.IP() == '127.0.0.1' ):
links = controller.cmd( 'ip link show' )
eth0 = re.findall( ' (.*eth0):', links )
if not eth0:
raise Exception( 'Cannot find IP address for controller eth0' )
Intf( eth0[ 0 ], node=controller ).updateIP()
return controller
def buildFromTopo( self, *args, **kwargs ):
@@ -714,11 +862,11 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels():
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=RemoteLink )
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2', server='ubuntu2' )
net = Mininet( host=RemoteHost, link=link )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server=remote )
net.addLink( h1, h2 )
net.start()
net.pingAll()
@@ -729,30 +877,30 @@ def testNsTunnels():
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2' ):
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
"Test remote Node classes"
print '*** Remote Node Test'
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=RemoteLink )
info( '*** Remote Node Test\n' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
print "*** Creating local h1"
info( "*** Creating local h1\n" )
h1 = net.addHost( 'h1' )
print "*** Creating remote h2"
info( "*** Creating remote h2\n" )
h2 = net.addHost( 'h2', server=remote )
print "*** Creating local s1"
info( "*** Creating local s1\n" )
s1 = net.addSwitch( 's1' )
print "*** Creating remote s2"
info( "*** Creating remote s2\n" )
s2 = net.addSwitch( 's2', server=remote )
print "*** Adding links"
info( "*** Adding links\n" )
net.addLink( h1, s1 )
net.addLink( s1, s2 )
net.addLink( h2, s2 )
net.start()
print 'Mininet is running on', quietRun( 'hostname' ).strip()
info( 'Mininet is running on', quietRun( 'hostname' ).strip(), '\n' )
for node in c0, h1, h2, s1, s2:
print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip()
info( 'Node', node, 'is running on',
node.cmd( 'hostname' ).strip(), '\n' )
net.pingAll()
CLI( net )
net.stop()
@@ -790,11 +938,11 @@ def ClusterController( *args, **kwargs):
Intf( 'eth0', node=controller ).updateIP()
return controller
def testRemoteTopo():
def testRemoteTopo( link=RemoteGRELink ):
"Test remote Node classes using Mininet()/Topo() API"
topo = LinearTopo( 2 )
net = Mininet( topo=topo, host=HostPlacer, switch=SwitchPlacer,
link=RemoteLink, controller=ClusterController )
link=link, controller=ClusterController )
net.start()
net.pingAll()
net.stop()
@@ -804,18 +952,17 @@ def testRemoteTopo():
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches():
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
"Test with local hosts and remote switches"
servers = [ 'localhost', 'ubuntu2']
servers = [ 'localhost', remote]
topo = TreeTopo( depth=4, fanout=2 )
net = MininetCluster( topo=topo, servers=servers,
net = MininetCluster( topo=topo, servers=servers, link=link,
placement=RoundRobinPlacer )
net.start()
net.pingAll()
net.stop()
#
# For testing and demo purposes it would be nice to draw the
# network graph and color it based on server.
@@ -823,31 +970,36 @@ def testRemoteSwitches():
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster():
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
"Test MininetCluster()"
servers = [ 'localhost', 'ubuntu2' ]
servers = [ 'localhost', remote ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers,
net = MininetCluster( topo=topo, servers=servers, link=link,
placement=SwitchBinPlacer )
net.start()
net.pingAll()
net.stop()
def signalTest():
def signalTest( remote='ubuntu2'):
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server='ubuntu1' )
h = RemoteHost( 'h0', server=remote )
h.shell.send_signal( SIGINT )
h.shell.poll()
if h.shell.returncode is None:
print 'OK: ', h, 'has not exited'
info( 'signalTest: SUCCESS: ', h, 'has not exited after SIGINT', '\n' )
else:
print 'FAILURE:', h, 'exited with code', h.shell.returncode
info( 'signalTest: FAILURE:', h, 'exited with code',
h.shell.returncode, '\n' )
h.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
# testRemoteTopo()
# testRemoteNet()
# testMininetCluster()
# testRemoteSwitches()
signalTest()
remoteServer = 'ubuntu2'
remoteLink = RemoteSSHLink
testRemoteTopo(link=remoteLink)
testNsTunnels( remote=remoteServer, link=remoteLink )
testRemoteNet( remote=remoteServer, link=remoteLink)
testMininetCluster( remote=remoteServer, link=remoteLink)
testRemoteSwitches( remote=remoteServer, link=remoteLink)
signalTest( remote=remoteServer )
+2 -2
View File
@@ -12,11 +12,11 @@ from mininet.topo import SingleSwitchTopo
def clusterSanity():
"Sanity check for cluster mode"
topo = SingleSwitchTopo()
net = MininetCluster( topo=topo )
net = MininetCluster( topo=topo )
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
if __name__ == '__main__':
setLogLevel( 'info' )
clusterSanity()
+31 -18
View File
@@ -5,6 +5,7 @@
from mininet.cli import CLI
from mininet.log import output, error
# pylint: disable=global-statement
nx, graphviz_layout, plt = None, None, None # Will be imported on demand
@@ -15,32 +16,42 @@ class ClusterCLI( CLI ):
def colorsFor( seq ):
"Return a list of background colors for a sequence"
colors = [ 'red', 'lightgreen', 'cyan', 'yellow', 'orange',
'magenta', 'pink', 'grey', 'brown',
'white' ]
'magenta', 'pink', 'grey', 'brown',
'white' ]
slen, clen = len( seq ), len( colors )
reps = max( 1, slen / clen )
colors = colors * reps
colors = colors[ 0 : slen ]
return colors
def do_plot( self, line ):
def do_plot( self, _line ):
"Plot topology colored by node placement"
# Import networkx if needed
global nx, plt
global nx, plt, graphviz_layout
if not nx:
try:
import networkx as nx
import matplotlib.pyplot as plt
# pylint: disable=import-error
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
plt = pyplot # satisfy pylint
import pygraphviz
assert pygraphviz # silence pyflakes
except:
# Networkx moved this around
if hasattr( nx, 'graphviz_layout' ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
return
# Make a networkx Graph
g = nx.Graph()
mn = self.mn
servers, hosts, switches = mn.servers, mn.hosts, mn.switches
servers = getattr( mn, 'servers', [ 'localhost' ] )
hosts, switches = mn.hosts, mn.switches
nodes = hosts + switches
g.add_nodes_from( nodes )
links = [ ( link.intf1.node, link.intf2.node )
@@ -50,13 +61,16 @@ class ClusterCLI( CLI ):
# shapes = hlen * [ 's' ] + slen * [ 'o' ]
color = dict( zip( servers, self.colorsFor( servers ) ) )
# Plot it!
pos = nx.graphviz_layout( g )
pos = graphviz_layout( g )
opts = { 'ax': None, 'font_weight': 'bold',
'width': 2, 'edge_color': 'darkblue' }
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 )
'width': 2, 'edge_color': 'darkblue' }
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,
node_color=scolors, node_shape='o', **opts )
# Get rid of axes, add title, and show
@@ -68,7 +82,7 @@ class ClusterCLI( CLI ):
plt.title( 'Node Placement', fontweight='bold' )
plt.show()
def do_status( self, line ):
def do_status( self, _line ):
"Report on node shell status"
nodes = self.mn.hosts + self.mn.switches
for node in nodes:
@@ -82,8 +96,7 @@ class ClusterCLI( CLI ):
else:
output( 'All nodes are still running.\n' )
def do_placement( self, line ):
def do_placement( self, _line ):
"Describe node placement"
mn = self.mn
nodes = mn.hosts + mn.switches + mn.controllers
+4 -3
View File
@@ -2,7 +2,9 @@
"clusterdemo.py: demo of Mininet Cluster Edition prototype"
from mininet.examples.cluster import MininetCluster, SwitchBinPlacer
from mininet.examples.cluster import ( MininetCluster, SwitchBinPlacer,
RemoteLink )
# ^ Could also use: RemoteSSHLink, RemoteGRELink
from mininet.topolib import TreeTopo
from mininet.log import setLogLevel
from mininet.examples.clustercli import ClusterCLI as CLI
@@ -11,7 +13,7 @@ def demo():
"Simple Demo of Cluster Mode"
servers = [ 'localhost', 'ubuntu2', 'ubuntu3' ]
topo = TreeTopo( depth=3, fanout=3 )
net = MininetCluster( topo=topo, servers=servers,
net = MininetCluster( topo=topo, servers=servers, Link=RemoteLink,
placement=SwitchBinPlacer )
net.start()
CLI( net )
@@ -20,4 +22,3 @@ def demo():
if __name__ == '__main__':
setLogLevel( 'info' )
demo()
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/python
"clusterperf.py compare the maximum throughput between SSH and GRE tunnels"
from mininet.examples.cluster import RemoteSSHLink, RemoteGRELink, RemoteHost
from mininet.net import Mininet
from mininet.log import setLogLevel
def perf(Link):
"Test connectivity nand performance over Link"
net = Mininet( host=RemoteHost, link=Link )
h1 = net.addHost( 'h1')
h2 = net.addHost( 'h2', server='ubuntu2' )
net.addLink( h1, h2 )
net.start()
net.pingAll()
net.iperf()
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perf( RemoteSSHLink )
perf( RemoteGRELink )
+1 -1
View File
@@ -327,7 +327,7 @@ class ConsoleApp( Frame ):
elif units[0] == 'b':
val *= 10 ** -9
self.updates += 1
self.bw += val
self.bw += val
if self.updates >= self.hostCount:
self.graph.addBar( self.bw )
self.bw = 0
+1 -1
View File
@@ -17,7 +17,7 @@ setLogLevel( 'info' )
# Ignore the warning message that the remote isn't (yet) running
c0 = Controller( 'c0', port=6633 )
c1 = Controller( 'c1', port=6634 )
c2 = RemoteController( 'c2', ip='127.0.0.1' )
c2 = RemoteController( 'c2', ip='127.0.0.1', port=6633 )
cmap = { 's1': c0, 's2': c1, 's3': c2 }
+12 -11
View File
@@ -11,49 +11,50 @@ Note that one could also create a custom switch class and pass it into
the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.log import setLogLevel, info
def multiControllerNet():
"Create a network from semi-scratch with multiple controllers."
net = Mininet( controller=Controller, switch=OVSSwitch )
print "*** Creating (reference) controllers"
info( "*** Creating (reference) controllers\n" )
c1 = net.addController( 'c1', port=6633 )
c2 = net.addController( 'c2', port=6634 )
print "*** Creating switches"
info( "*** Creating switches\n" )
s1 = net.addSwitch( 's1' )
s2 = net.addSwitch( 's2' )
print "*** Creating hosts"
hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ]
info( "*** Creating hosts\n" )
hosts1 = [ net.addHost( 'h%d' % n ) for n in ( 3, 4 ) ]
hosts2 = [ net.addHost( 'h%d' % n ) for n in ( 5, 6 ) ]
print "*** Creating links"
info( "*** Creating links\n" )
for h in hosts1:
net.addLink( s1, h )
for h in hosts2:
net.addLink( s2, h )
net.addLink( s1, s2 )
print "*** Starting network"
info( "*** Starting network\n" )
net.build()
c1.start()
c2.start()
s1.start( [ c1 ] )
s2.start( [ c2 ] )
print "*** Testing network"
info( "*** Testing network\n" )
net.pingAll()
print "*** Running CLI"
info( "*** Running CLI\n" )
CLI( net )
print "*** Stopping network"
info( "*** Stopping network\n" )
net.stop()
if __name__ == '__main__':
+10 -3
View File
@@ -27,15 +27,22 @@ from mininet.log import setLogLevel, info
class DataController( Controller ):
"""Data Network Controller.
patched to avoid checkListening error"""
patched to avoid checkListening error and to delete intfs"""
def checkListening( self ):
"Ignore spurious error"
pass
def stop( self, *args, **kwargs ):
"Make sure intfs are deleted"
kwargs.update( deleteIntfs=True )
super( DataController, self ).stop( *args, **kwargs )
class MininetFacade( object ):
"""Mininet object facade that allows a single CLI to
talk to one or more networks"""
def __init__( self, net, *args, **kwargs ):
"""Create MininetFacade object.
net: Primary Mininet object
@@ -114,7 +121,7 @@ class ControlNetwork( Topo ):
def run():
"Create control and data networks, and invoke the CLI"
info( '* Creating Control Network\n' )
ctopo = ControlNetwork( n=4, dataController=DataController )
cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
+50 -16
View File
@@ -2,6 +2,30 @@
"""
cpu.py: test iperf bandwidth for varying cpu limits
Since we are limiting the hosts (only), we should expect the iperf
processes to be affected, as well as any system processing which is
billed to the hosts.
We reserve >50% of cycles for system processing; we assume that
this is enough for it not to affect results. Hosts are limited to
40% of total cycles, which we assume is enough to make them CPU
bound.
As CPU performance increases over time, we may have to reduce the
overall CPU allocation so that the host processing is still CPU bound.
This is perhaps an argument for specifying performance in a more
system-independent manner.
It would also be nice to have a better handle on limiting packet
processing cycles. It's not entirely clear to me how those are
billed to user or system processes if we are using OVS with a kernel
datapath. With a user datapath, they are easier to account for, but
overall performance is usually lower.
Although the iperf client uses more CPU and should be CPU bound (?),
we measure the received data at the server since the client transmit
rate includes buffering.
"""
from mininet.net import Mininet
@@ -11,7 +35,7 @@ from mininet.util import custom, waitListening
from mininet.log import setLogLevel, info
def bwtest( cpuLimits, period_us=100000, seconds=5 ):
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
"""Example/test of link and CPU bandwidth limits
cpu: cpu limit as fraction of overall CPU time"""
@@ -20,26 +44,35 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
results = {}
for sched in 'rt', 'cfs':
print '*** Testing with', sched, 'bandwidth limiting'
info( '*** Testing with', sched, 'bandwidth limiting\n' )
for cpu in cpuLimits:
# cpu is the cpu fraction for all hosts, so we divide
# it across two hosts
host = custom( CPULimitedHost, sched=sched,
period_us=period_us,
cpu=cpu )
cpu=.5*cpu )
try:
net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except
except:
info( '*** Skipping host %s\n' % sched )
info( '*** Skipping scheduler %s\n' % sched )
break
net.start()
net.pingAll()
hosts = [ net.getNodeByName( h ) for h in topo.hosts() ]
client, server = hosts[ 0 ], hosts[ -1 ]
server.cmd( 'iperf -s -p 5001 &' )
info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' %
( 100.0 * cpu ) )
# We measure at the server because it doesn't include
# the client's buffer fill rate
popen = server.popen( 'iperf -yc -s -p 5001' )
waitListening( client, server, 5001 )
result = client.cmd( 'iperf -yc -t %s -c %s' % (
seconds, server.IP() ) ).split( ',' )
# ignore empty result from waitListening/telnet
popen.stdout.readline()
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
result = popen.stdout.readline().split( ',' )
bps = float( result[ -1 ] )
server.cmdPrint( 'kill %iperf' )
popen.terminate()
net.stop()
updated = results.get( sched, [] )
updated += [ ( cpu, bps ) ]
@@ -51,22 +84,23 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ):
def dump( results ):
"Dump results"
fmt = '%s\t%s\t%s'
fmt = '%s\t%s\t%s\n'
print
print fmt % ( 'sched', 'cpu', 'client MB/s' )
print
info( '\n' )
info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) )
for sched in sorted( results.keys() ):
entries = results[ sched ]
for cpu, bps in entries:
pct = '%.2f%%' % ( cpu * 100 )
mbps = bps / 1e6
print fmt % ( sched, pct, mbps )
pct = '%d%%' % ( cpu * 100 )
mbps = '%.2e' % bps
info( fmt % ( sched, pct, mbps ) )
if __name__ == '__main__':
setLogLevel( 'info' )
limits = [ .45, .4, .3, .2, .1 ]
# These are the limits for the hosts/iperfs - the
# rest is for system processes
limits = [ .5, .4, .3, .2, .1 ]
out = bwtest( limits )
dump( out )
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/python
"""
fakecluster.py: a fake cluster for testing Mininet cluster edition!!!
We are going to self-host Mininet by creating a virtual cluster
for cluster edition.
Note: ssh is kind of a mess - you end up having to do things
like h1 sudo -E -u openflow ssh 10.2
"""
from mininet.net import Mininet
from mininet.nodelib import LinuxBridge, Server
from mininet.cli import CLI
from mininet.topo import Topo, SingleSwitchTopo
from mininet.log import setLogLevel, warn
from mininet.util import errRun, quietRun
from mininet.link import Link
from functools import partial
from sys import argv
class MininetServer( Server ):
"A server (for nested Mininet) that runs ssh and ovs"
privateDirs = [ '/var/run/sshd', '/etc/openvswitch',
'/var/run/openvswitch', '/var/log/openvswitch' ]
def __init__( self, *args, **kwargs ):
"Turn on ovs by default"
kwargs.setdefault( 'ovs', True )
super( MininetServer, self ).__init__( *args, **kwargs )
def config( self, **kwargs ):
"""Configure/start sshd and other stuff
ovs: start Open vSwitch?"""
self.ovs = kwargs.get( 'ovs' )
super( MininetServer, self ).config( **kwargs )
if self.ovs:
self.service( 'openvswitch-switch start' )
def terminate( self, *args, **kwargs ):
"Shut down services and terminate server"
if self.ovs:
self.service( 'openvswitch-switch stop' )
super( MininetServer, self ).terminate( *args, **kwargs )
class ServerLink( Link ):
def intfName( self, node, n ):
"Override to avoid destruction by cleanup!"
# This is kind of ugly... for some reason 'eth0' fails so
# we just use 'm1eth0'; however, this should nest reasonably.
return ( node.name + 'eth' + repr( n ) if isinstance( node, Server )
else node.name + '-eth' + repr( n ) )
def makeIntfPair( self, *args, **kwargs ):
"Override to use quietRun"
kwargs.update( runCmd=quietRun )
super( ServerLink, self ).makeIntfPair( *args, **kwargs )
class ClusterTopo( Topo ):
"Cluster topology: m1..mN"
def build( self, n ):
ms1 = self.addSwitch( 'ms1' )
for i in range( 1, n + 1 ):
h = self.addHost( 'm%d' % i )
self.addLink( h, ms1, cls=ServerLink )
def test( serverCount ):
"Test this setup"
setLogLevel( 'info' )
topo = ClusterTopo( serverCount )
host = partial( MininetServer, ssh=True, ovs=True)
net = Mininet( topo=topo, host=host, switch=LinuxBridge, ipBase='10.0/24' )
MininetServer.updateHostsFiles( net.hosts )
# addNAT().configDefault() also connects root namespace to Mininet
net.addNAT().configDefault()
net.start()
CLI( net )
net.stop()
if __name__ == '__main__':
n = 8 if len( argv ) != 2 else int( argv[ 1 ] )
test( n )
+5 -3
View File
@@ -5,7 +5,8 @@ This example shows how to add an interface (for example a real
hardware interface) to a network after the network is created.
"""
import re, sys
import re
import sys
from mininet.cli import CLI
from mininet.log import setLogLevel, info, error
@@ -16,10 +17,11 @@ from mininet.util import quietRun
def checkIntf( intf ):
"Make sure intf exists and is not configured."
if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True )
if not config:
error( 'Error:', intf, 'does not exist!\n' )
exit( 1 )
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
ips = re.findall( r'\d+\.\d+\.\d+\.\d+', config )
if ips:
error( 'Error:', intf, 'has an IP address,'
'and is probably in use!\n' )
+3 -3
View File
@@ -21,7 +21,7 @@ def intfOptions():
link1 = net.addLink( h1, s1, cls=TCLink )
net.addLink( h2, s1 )
net.start()
# flush out latency from reactive forwarding delay
net.pingAll()
@@ -34,12 +34,12 @@ def intfOptions():
link1.intf1.config( loss=50 )
info( '\n' )
net.iperf( ( h1, h2 ), l4Type='UDP' )
info( '\n*** Configuring one intf with delay of 15ms\n' )
link1.intf1.config( delay='15ms' )
info( '\n*** Run a ping to confirm delay\n' )
net.pingPairFull()
info( '\n*** Done testing\n' )
net.stop()
+4 -2
View File
@@ -27,9 +27,11 @@ def limit( bw=10, cpu=.1 ):
info( '*** Testing with', sched, 'bandwidth limiting\n' )
if sched == 'rt':
release = quietRun( 'uname -r' ).strip('\r\n')
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release )
output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s'
% release )
if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
info( '*** RT Scheduler is not enabled in your kernel. Skipping this test\n' )
info( '*** RT Scheduler is not enabled in your kernel. '
'Skipping this test\n' )
continue
host = custom( CPULimitedHost, sched=sched, cpu=cpu )
net = Mininet( topo=myTopo, intf=intf, host=host )
+20 -20
View File
@@ -23,10 +23,11 @@ of switches, this example demonstrates:
"""
from mininet.net import Mininet
from mininet.node import UserSwitch, OVSKernelSwitch, Controller
from mininet.topo import Topo
from mininet.log import lg
from mininet.log import lg, info
from mininet.util import irange, quietRun
from mininet.link import TCLink
from functools import partial
@@ -83,44 +84,43 @@ def linearBandwidthTest( lengths ):
assert 'reno' in output
for datapath in switches.keys():
print "*** testing", datapath, "datapath"
info( "*** testing", datapath, "datapath\n" )
Switch = switches[ datapath ]
results[ datapath ] = []
link = partial( TCLink, delay='1ms' )
link = partial( TCLink, delay='2ms', bw=10 )
net = Mininet( topo=topo, switch=Switch,
controller=Controller, waitConnected=True,
link=link )
net.start()
print "*** testing basic connectivity"
info( "*** testing basic connectivity\n" )
for n in lengths:
net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] )
print "*** testing bandwidth"
info( "*** testing bandwidth\n" )
for n in lengths:
src, dst = net.hosts[ 0 ], net.hosts[ n ]
# Try to prime the pump to reduce PACKET_INs during test
# since the reference controller is reactive
src.cmd( 'telnet', dst.IP(), '5001' )
print "testing", src.name, "<->", dst.name,
bandwidth = net.iperf( [ src, dst ], seconds=10 )
print bandwidth
info( "testing", src.name, "<->", dst.name, '\n' )
# serverbw = received; _clientbw = buffered
serverbw, _clientbw = net.iperf( [ src, dst ], seconds=10 )
info( serverbw, '\n' )
flush()
results[ datapath ] += [ ( n, bandwidth ) ]
results[ datapath ] += [ ( n, serverbw ) ]
net.stop()
for datapath in switches.keys():
print
print "*** Linear network results for", datapath, "datapath:"
print
info( "\n*** Linear network results for", datapath, "datapath:\n" )
result = results[ datapath ]
print "SwitchCount\tiperf Results"
for switchCount, bandwidth in result:
print switchCount, '\t\t',
print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client'
print
print
info( "SwitchCount\tiperf Results\n" )
for switchCount, serverbw in result:
info( switchCount, '\t\t' )
info( serverbw, '\n' )
info( '\n')
info( '\n' )
if __name__ == '__main__':
lg.setLogLevel( 'info' )
sizes = [ 1, 10, 20, 40, 60, 80, 100 ]
print "*** Running linearBandwidthTest", sizes
sizes = [ 1, 10, 20, 40, 60, 80 ]
info( "*** Running linearBandwidthTest", sizes, '\n' )
linearBandwidthTest( sizes )
+48 -24
View File
@@ -2,32 +2,39 @@
"""
linuxrouter.py: Example network with Linux IP router
This example converts a Node into a router using IP forwarding
already built into Linux.
The topology contains a router with three IP subnets:
- 192.168.1.0/24 (interface IP: 192.168.1.1)
- 172.16.0.0/12 (interface IP: 172.16.0.1)
- 10.0.0.0/8 (interface IP: 10.0.0.1)
The example topology creates a router and three IP subnets:
It also contains three hosts, one in each subnet:
- h1 (IP: 192.168.1.100)
- h2 (IP: 172.16.0.100)
- h3 (IP: 10.0.0.100)
- 192.168.1.0/24 (r0-eth1, IP: 192.168.1.1)
- 172.16.0.0/12 (r0-eth2, IP: 172.16.0.1)
- 10.0.0.0/8 (r0-eth3, IP: 10.0.0.1)
Routing entries can be added to the routing tables of the
hosts or router using the "ip route add" or "route add" command.
See the man pages for more details.
Each subnet consists of a single host connected to
a single switch:
r0-eth1 - s1-eth1 - h1-eth0 (IP: 192.168.1.100)
r0-eth2 - s2-eth1 - h2-eth0 (IP: 172.16.0.100)
r0-eth3 - s3-eth1 - h3-eth0 (IP: 10.0.0.100)
The example relies on default routing entries that are
automatically created for each router interface, as well
as 'defaultRoute' parameters for the host interfaces.
Additional routes may be added to the router or hosts by
executing 'ip route' or 'route' commands on the router or hosts.
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
class LinuxRouter( Node ):
"A Node with IP forwarding enabled."
@@ -42,23 +49,40 @@ class LinuxRouter( Node ):
class NetworkTopo( Topo ):
"A simple topology of a router with three subnets (one host in each)."
"A LinuxRouter connecting three IP subnets"
def build( self, **_opts ):
defaultIP = '192.168.1.1/24' # IP address for r0-eth1
router = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP )
s1, s2, s3 = [ self.addSwitch( s ) for s in ( 's1', 's2', 's3' ) ]
self.addLink( s1, router, intfName2='r0-eth1',
params2={ 'ip' : defaultIP } ) # for clarity
self.addLink( s2, router, intfName2='r0-eth2',
params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( s3, router, intfName2='r0-eth3',
params2={ 'ip' : '10.0.0.1/8' } )
h1 = self.addHost( 'h1', ip='192.168.1.100/24',
defaultRoute='via 192.168.1.1' )
h2 = self.addHost( 'h2', ip='172.16.0.100/12',
defaultRoute='via 172.16.0.1' )
h3 = self.addHost( 'h3', ip='10.0.0.100/8',
defaultRoute='via 10.0.0.1' )
for h, s in [ (h1, s1), (h2, s2), (h3, s3) ]:
self.addLink( h, s )
def build( self, n=2, h=1, **opts ):
router = self.addNode( 'r0', cls=LinuxRouter, ip='192.168.1.1/24' )
h1 = self.addHost( 'h1', ip='192.168.1.100/24', defaultRoute='via 192.168.1.1' )
h2 = self.addHost( 'h2', ip='172.16.0.100/12', defaultRoute='via 172.16.0.1' )
h3 = self.addHost( 'h3', ip='10.0.0.100/8', defaultRoute='via 10.0.0.1' )
self.addLink( h1, router, intfName2='r0-eth1', params2={ 'ip' : '192.168.1.1/24' } )
self.addLink( h2, router, intfName2='r0-eth2', params2={ 'ip' : '172.16.0.1/12' } )
self.addLink( h3, router, intfName2='r0-eth3', params2={ 'ip' : '10.0.0.1/8' } )
def run():
"Test linux router"
topo = NetworkTopo()
net = Mininet( topo=topo, controller=None ) # no controller needed
net = Mininet( topo=topo ) # controller is used by s1-s3
net.start()
info( '*** Routing Table on Router\n' )
print net[ 'r0' ].cmd( 'route' )
info( '*** Routing Table on Router:\n' )
info( net[ 'r0' ].cmd( 'route' ) )
CLI( net )
net.stop()
+931 -903
View File
File diff suppressed because it is too large. Load diff
+13 -11
View File
@@ -19,10 +19,11 @@ to-do:
- think about clearing last hop - why doesn't that work?
"""
from mininet.net import Mininet
from mininet.node import OVSSwitch
from mininet.topo import LinearTopo
from mininet.log import output, warn
from mininet.log import info, output, warn, setLogLevel
from random import randint
@@ -59,7 +60,7 @@ class MobilitySwitch( OVSSwitch ):
def validatePort( self, intf ):
"Validate intf's OF port number"
ofport = int( self.cmd( 'ovs-vsctl get Interface', intf,
'ofport' ) )
'ofport' ) )
if ofport != self.ports[ intf ]:
warn( 'WARNING: ofport for', intf, 'is actually', ofport,
'\n' )
@@ -105,30 +106,31 @@ def moveHost( host, oldSwitch, newSwitch, newPort=None ):
def mobilityTest():
"A simple test of mobility"
print '* Simple mobility test'
info( '* Simple mobility test\n' )
net = Mininet( topo=LinearTopo( 3 ), switch=MobilitySwitch )
print '* Starting network:'
info( '* Starting network:\n' )
net.start()
printConnections( net.switches )
print '* Testing network'
info( '* Testing network\n' )
net.pingAll()
print '* Identifying switch interface for h1'
info( '* Identifying switch interface for h1\n' )
h1, old = net.get( 'h1', 's1' )
for s in 2, 3, 1:
new = net[ 's%d' % s ]
port = randint( 10, 20 )
print '* Moving', h1, 'from', old, 'to', new, 'port', port
info( '* Moving', h1, 'from', old, 'to', new, 'port', port, '\n' )
hintf, sintf = moveHost( h1, old, new, newPort=port )
print '*', hintf, 'is now connected to', sintf
print '* Clearing out old flows'
info( '*', hintf, 'is now connected to', sintf, '\n' )
info( '* Clearing out old flows\n' )
for sw in net.switches:
sw.dpctl( 'del-flows' )
print '* New network:'
info( '* New network:\n' )
printConnections( net.switches )
print '* Testing connectivity:'
info( '* Testing connectivity:\n' )
net.pingAll()
old = new
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
mobilityTest()
+4 -3
View File
@@ -9,9 +9,9 @@ from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.net import Mininet
from mininet.topo import Topo
def runMultiLink():
"Create and run multiple link network"
topo = simpleMultiLinkTopo( n=2 )
net = Mininet( topo=topo )
net.start()
@@ -19,13 +19,14 @@ def runMultiLink():
net.stop()
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
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 )
+5 -4
View File
@@ -8,10 +8,11 @@ multiple hosts and monitor their output interactively for a period=
of time.
"""
from mininet.net import Mininet
from mininet.node import Node
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel
from mininet.log import info, setLogLevel
from select import poll, POLLIN
from time import time
@@ -34,8 +35,8 @@ def startpings( host, targetips ):
' done; '
'done &' )
print ( '*** Host %s (%s) will be pinging ips: %s' %
( host.name, host.IP(), targetips ) )
info( '*** Host %s (%s) will be pinging ips: %s\n' %
( host.name, host.IP(), targetips ) )
host.cmd( cmd )
@@ -69,7 +70,7 @@ def multiping( netsize, chunksize, seconds):
readable = poller.poll(1000)
for fd, _mask in readable:
node = Node.outToNode[ fd ]
print '%s:' % node.name, node.monitor().strip()
info( '%s:' % node.name, node.monitor().strip(), '\n' )
# Stop pings
for host in hosts:
+6 -4
View File
@@ -5,14 +5,16 @@ Simple example of sending output to multiple files and
monitoring them
"""
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.log import setLogLevel
from mininet.log import info, setLogLevel
from time import time
from select import poll, POLLIN
from subprocess import Popen, PIPE
def monitorFiles( outfiles, seconds, timeoutms ):
"Monitor set of files and return [(host, line)...]"
devnull = open( '/dev/null', 'w' )
@@ -53,7 +55,7 @@ def monitorTest( N=3, seconds=3 ):
net = Mininet( topo )
net.start()
hosts = net.hosts
print "Starting test..."
info( "Starting test...\n" )
server = hosts[ 0 ]
outfiles, errfiles = {}, {}
for h in hosts:
@@ -67,10 +69,10 @@ def monitorTest( N=3, seconds=3 ):
'>', outfiles[ h ],
'2>', errfiles[ h ],
'&' )
print "Monitoring output for", seconds, "seconds"
info( "Monitoring output for", seconds, "seconds\n" )
for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
if h:
print '%s: %s' % ( h.name, line )
info( '%s: %s\n' % ( h.name, line ) )
for h in hosts:
h.cmd('kill %ping')
net.stop()
+7 -96
View File
@@ -2,111 +2,22 @@
"""
Example to create a Mininet topology and connect it to the internet via NAT
through eth0 on the host.
Glen Gibb, February 2011
(slight modifications by BL, 5/13)
"""
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node
from mininet.log import lg, info
from mininet.topolib import TreeNet
#################################
def startNAT( root, inetIntf='eth0', subnet='10.0/8' ):
"""Start NAT/forwarding between Mininet and external network
root: node to access iptables from
inetIntf: interface for internet access
subnet: Mininet subnet (default 10.0/8)="""
# Identify the interface connecting to the mininet network
localIntf = root.defaultIntf()
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
root.cmd( 'iptables -P INPUT ACCEPT' )
root.cmd( 'iptables -P OUTPUT ACCEPT' )
root.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' )
root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' )
root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' )
root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' )
# Instruct the kernel to perform forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=1' )
def stopNAT( root ):
"""Stop NAT/forwarding between Mininet and external network"""
# Flush any currently active rules
root.cmd( 'iptables -F' )
root.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
root.cmd( 'sysctl net.ipv4.ip_forward=0' )
def fixNetworkManager( root, intf ):
"""Prevent network-manager from messing with our interface,
by specifying manual configuration in /etc/network/interfaces
root: a node in the root namespace (for running commands)
intf: interface name"""
cfile = '/etc/network/interfaces'
line = '\niface %s inet manual\n' % intf
config = open( cfile ).read()
if line not in config:
print '*** Adding', line.strip(), 'to', cfile
with open( cfile, 'a' ) as f:
f.write( line )
# Probably need to restart network-manager to be safe -
# hopefully this won't disconnect you
root.cmd( 'service network-manager restart' )
def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'):
"""Connect the network to the internet
switch: switch to connect to root namespace
rootip: address for interface in root namespace
subnet: Mininet subnet"""
switch = network.get( switch )
prefixLen = subnet.split( '/' )[ 1 ]
# Create a node in root namespace
root = Node( 'root', inNamespace=False )
# Prevent network-manager from interfering with our interface
fixNetworkManager( root, 'root-eth0' )
# Create link between root NS and switch
link = network.addLink( root, switch )
link.intf1.setIP( rootip, prefixLen )
# Start network that now includes link to root namespace
network.start()
# Start NAT and establish forwarding
startNAT( root )
# Establish routes from end hosts
for host in network.hosts:
host.cmd( 'ip route flush root 0/0' )
host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() )
host.cmd( 'route add default gw', rootip )
return root
if __name__ == '__main__':
lg.setLogLevel( 'info')
net = TreeNet( depth=1, fanout=4 )
# Configure and start NATted connectivity
rootnode = connectToInternet( net )
print "*** Hosts are running and should have internet connectivity"
print "*** Type 'exit' or control-D to shut down network"
# Add NAT connectivity
net.addNAT().configDefault()
net.start()
info( "*** Hosts are running and should have internet connectivity\n" )
info( "*** Type 'exit' or control-D to shut down network\n" )
CLI( net )
# Shut down NAT
stopNAT( rootnode )
net.stop()
+5 -6
View File
@@ -14,7 +14,7 @@ natnet.py: Example network with NATs
| |
s1 s2
| |
h1 h2
h1 h2
"""
@@ -27,7 +27,7 @@ from mininet.util import irange
class InternetTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, h=1, **opts):
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
# set up inet switch
@@ -44,15 +44,15 @@ class InternetTopo(Topo):
localSubnet = '192.168.%d.0/24' % i
natParams = { 'ip' : '%s/24' % localIP }
# add NAT to topology
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet,
inetIntf=inetIntf, localIntf=localIntf)
switch = self.addSwitch('s%d' % i)
# connect NAT to inet and local switches
self.addLink(nat, inetSwitch, intfName1=inetIntf)
self.addLink(nat, switch, intfName1=localIntf, params1=natParams)
# add host and connect to local switch
host = self.addHost('h%d' % i,
ip='192.168.%d.100/24' % i,
host = self.addHost('h%d' % i,
ip='192.168.%d.100/24' % i,
defaultRoute='via %s' % localIP)
self.addLink(host, switch)
@@ -67,4 +67,3 @@ def run():
if __name__ == '__main__':
setLogLevel('info')
run()
+21 -15
View File
@@ -1,11 +1,12 @@
#!/usr/bin/python
"""
Create a network with 5 hosts, numbered 1-4 and 9.
Create a network with 5 hosts, numbered 1-4 and 9.
Validate that the port numbers match to the interface name,
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
@@ -13,16 +14,19 @@ from mininet.log import setLogLevel, info, warn
def validatePort( switch, intf ):
"Validate intf's OF port number"
ofport = int( switch.cmd( 'ovs-vsctl get Interface', intf,
'ofport' ) )
'ofport' ) )
if ofport != switch.ports[ intf ]:
warn( 'WARNING: ofport for', intf, 'is actually', ofport, '\n' )
return 0
else:
return 1
def net():
def testPortNumbering():
"Create a network with 5 hosts."
"""Test port numbering:
Create a network with 5 hosts (using Mininet's
mid-level API) and check that implicit and
explicit port numbering works as expected."""
net = Mininet( controller=Controller )
@@ -45,30 +49,32 @@ def net():
net.addLink( h2, s1 )
net.addLink( h3, s1 )
net.addLink( h4, s1 )
net.addLink( h5, s1, port1 = 1, port2 = 9 ) # specify a different port to connect host 5 to on the switch.
# specify a different port to connect host 5 to on the switch.
net.addLink( h5, s1, port1=1, port2= 9)
info( '*** Starting network\n' )
net.start()
# print the interfaces and their port numbers
info( '\n*** printing and validating the ports running on each interface\n' )
info( '\n*** printing and validating the ports '
'running on each interface\n' )
for intfs in s1.intfList():
if not intfs.name == "lo":
info( intfs, ': ', s1.ports[intfs],
'\n' )
info ( 'Validating that', intfs, 'is actually on port', s1.ports[intfs], '... ' )
info( intfs, ': ', s1.ports[intfs],
'\n' )
info( 'Validating that', intfs,
'is actually on port', s1.ports[intfs], '... ' )
if validatePort( s1, intfs ):
info( 'Validated.\n' )
print '\n'
info( '\n' )
# test the network with pingall
net.pingAll()
print '\n'
info( '\n' )
info( '*** Stopping network' )
info( '*** Stopping network\n' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
net()
testPortNumbering()
+3 -2
View File
@@ -5,10 +5,11 @@ This example monitors a number of hosts using host.popen() and
pmonitor()
"""
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.topo import SingleSwitchTopo
from mininet.log import setLogLevel
from mininet.log import setLogLevel, info
from mininet.util import custom, pmonitor
def monitorhosts( hosts=5, sched='cfs' ):
@@ -27,7 +28,7 @@ def monitorhosts( hosts=5, sched='cfs' ):
# Monitor them and print output
for host, line in pmonitor( popens ):
if host:
print "<%s>: %s" % ( host.name, line.strip() )
info( "<%s>: %s" % ( host.name, line ) )
# Done
net.stop()
+6 -3
View File
@@ -5,6 +5,8 @@
from mininet.net import Mininet
from mininet.topo import SingleSwitchTopo
from mininet.util import pmonitor
from mininet.log import setLogLevel, info
from time import time
from signal import SIGINT
@@ -14,20 +16,21 @@ def pmonitorTest( N=3, seconds=10 ):
net = Mininet( topo )
net.start()
hosts = net.hosts
print "Starting test..."
info( "Starting test...\n" )
server = hosts[ 0 ]
popens = {}
for h in hosts:
popens[ h ] = h.popen('ping', server.IP() )
print "Monitoring output for", seconds, "seconds"
info( "Monitoring output for", seconds, "seconds\n" )
endTime = time() + seconds
for h, line in pmonitor( popens, timeoutms=500 ):
if h:
print '<%s>: %s' % ( h.name, line ),
info( '<%s>: %s' % ( h.name, line ) )
if time() >= endTime:
for p in popens.values():
p.send_signal( SIGINT )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
pmonitorTest()
+2 -1
View File
@@ -8,6 +8,7 @@ but it exposes the configuration details and allows customization.
For most tasks, the higher-level API will be preferable.
"""
from mininet.net import Mininet
from mininet.node import Node
from mininet.link import Link
@@ -40,7 +41,7 @@ def scratchNet( cname='controller', cargs='-v ptcp:' ):
switch.cmd( 'ovs-vsctl del-br dp0' )
switch.cmd( 'ovs-vsctl add-br dp0' )
for intf in switch.intfs.values():
print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf )
switch.cmd( 'ovs-vsctl add-port dp0 %s\n' % intf )
# Note: controller and switch are in root namespace, and we
# can connect via loopback interface
+1 -1
View File
@@ -52,7 +52,7 @@ def scratchNetUser( cname='controller', cargs='ptcp:' ):
info( '*** Starting controller and user datapath\n' )
controller.cmd( cname + ' ' + cargs + '&' )
switch.cmd( 'ifconfig lo 127.0.0.1' )
intfs = [ str( i ) for i in sintf1, sintf2 ]
intfs = str( sintf1 ), str( sintf2 )
switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' )
switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' )
+27 -15
View File
@@ -9,40 +9,52 @@ iperf will hang indefinitely if the TCP handshake fails
to complete.
"""
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel
from mininet.log import setLogLevel, info
class SingleSwitchTopo(Topo):
from sys import argv
# It would be nice if we didn't have to do this:
# pylint: disable=arguments-differ
class SingleSwitchTopo( Topo ):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
def build( self, n=2, lossy=True ):
switch = self.addSwitch('s1')
for h in range(n):
# Each host gets 50%/n of system CPU
host = self.addHost('h%s' % (h + 1),
cpu=.5 / n)
# 10 Mbps, 5ms delay, 10% loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
if lossy:
# 10 Mbps, 5ms delay, 10% packet loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=10, use_htb=True)
else:
# 10 Mbps, 5ms delay, no packet loss
self.addLink(host, switch,
bw=10, delay='5ms', loss=0, use_htb=True)
def perfTest():
def perfTest( lossy=True ):
"Create network and run simple performance test"
topo = SingleSwitchTopo( n=4 )
topo = SingleSwitchTopo( n=4, lossy=lossy )
net = Mininet( topo=topo,
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
host=CPULimitedHost, link=TCLink,
autoStaticArp=True )
net.start()
print "Dumping host connections"
info( "Dumping host connections\n" )
dumpNodeConnections(net.hosts)
print "Testing bandwidth between h1 and h4"
info( "Testing bandwidth between h1 and h4\n" )
h1, h4 = net.getNodeByName('h1', 'h4')
net.iperf( ( h1, h4 ), l4Type='UDP' )
net.stop()
if __name__ == '__main__':
setLogLevel('info')
perfTest()
setLogLevel( 'info' )
# Prevent test_simpleperf from failing due to packet loss
perfTest( lossy=( 'testmode' not in argv ) )
+9 -12
View File
@@ -20,12 +20,12 @@ import sys
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg
from mininet.log import lg, info
from mininet.node import Node
from mininet.topolib import TreeTopo
from mininet.link import Link
from mininet.util import waitListening
def TreeNet( depth=1, fanout=2, **kwargs ):
"Convenience function for creating tree networks."
topo = TreeTopo( depth, fanout )
@@ -39,7 +39,7 @@ def connectToRootNS( network, switch, ip, routes ):
routes: host networks to route to"""
# Create a node in root namespace and link to switch 0
root = Node( 'root', inNamespace=False )
intf = Link( root, switch ).intf1
intf = network.addLink( root, switch ).intf1
root.setIP( ip, intf=intf )
# Start network that now includes link to root namespace
network.start()
@@ -60,17 +60,14 @@ def sshd( network, cmd='/usr/sbin/sshd', opts='-D',
connectToRootNS( network, switch, ip, routes )
for host in network.hosts:
host.cmd( cmd + ' ' + opts + '&' )
print "*** Waiting for ssh daemons to start"
info( "*** Waiting for ssh daemons to start\n" )
for server in network.hosts:
waitListening( server=server, port=22, timeout=5 )
print
print "*** Hosts are running sshd at the following addresses:"
print
info( "\n*** Hosts are running sshd at the following addresses:\n" )
for host in network.hosts:
print host.name, host.IP()
print
print "*** Type 'exit' or control-D to shut down network"
info( host.name, host.IP(), '\n' )
info( "\n*** Type 'exit' or control-D to shut down network\n" )
CLI( network )
for host in network.hosts:
host.cmd( 'kill %' + cmd )
@@ -81,6 +78,6 @@ if __name__ == '__main__':
net = TreeNet( depth=1, fanout=4 )
# get sshd args from the command line or use default args
# useDNS=no -u0 to avoid reverse DNS lookup timeout
opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
argvopts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else (
'-D -o UseDNS=no -u0' )
sshd( net, opts=opts )
sshd( net, opts=argvopts )
+2 -1
View File
@@ -32,7 +32,8 @@ def runTests( testDir, verbosity=1 ):
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
# run tests
MininetTestRunner( verbosity=verbosity ).run( testSuite )
success = MininetTestRunner( verbosity=verbosity ).run( testSuite ).wasSuccessful()
sys.exit( 0 if success else 1 )
if __name__ == '__main__':
# get the directory containing example tests
+1 -1
View File
@@ -36,7 +36,7 @@ class testBareSSHD( unittest.TestCase ):
'-o StrictModes=no' )
p = pexpect.spawn( cmd )
runOpts = [ 'You may now ssh into h1 at 10.0.0.1',
'after 5 seconds, h1 is not listening on port 22',
'after 5 seconds, h1 is not listening on port 22',
pexpect.EOF, pexpect.TIMEOUT ]
while True:
index = p.expect( runOpts )
+13 -12
View File
@@ -5,13 +5,12 @@ Test for cpu.py
results format:
sched cpu client MB/s
cfs 45.00% 13254.669841
cfs 40.00% 11822.441399
cfs 30.00% 5112.963009
cfs 20.00% 3449.090009
cfs 10.00% 2271.741564
sched cpu received bits/sec
cfs 50% 8.14e+09
cfs 40% 6.48e+09
cfs 30% 4.56e+09
cfs 20% 2.84e+09
cfs 10% 1.29e+09
"""
@@ -26,21 +25,23 @@ class testCPU( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testCPU( self ):
"Verify that CPU utilization is monotonically decreasing for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.cpu' )
p = pexpect.spawn( 'python -m mininet.examples.cpu', timeout=300 )
# matches each line from results( shown above )
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)',
opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)',
pexpect.EOF ]
scheds = []
while True:
index = p.expect( opts, timeout=600 )
index = p.expect( opts )
if index == 0:
sched = p.match.group( 1 )
sched = p.match.group( 1 )
cpu = float( p.match.group( 2 ) )
bw = float( p.match.group( 3 ) )
if sched not in scheds:
scheds.append( sched )
else:
self.assertTrue( bw < previous_bw )
self.assertTrue( bw < previous_bw,
"%e should be less than %e\n" %
( bw, previous_bw ) )
previous_bw = bw
else:
break
+1 -1
View File
@@ -19,7 +19,7 @@ class testEmptyNet( unittest.TestCase ):
p.sendline( 'pingall' )
p.expect ( '(\d+)% dropped' )
percent = int( p.match.group( 1 ) ) if p.match else -1
self.assertEqual( percent, 0 )
self.assertEqual( percent, 0 )
p.expect( self.prompt )
# iperf test
p.sendline( 'iperf' )
+2 -2
View File
@@ -57,8 +57,8 @@ class testHwintf( unittest.TestCase ):
p.wait()
def tearDown( self ):
self.h3.terminate()
self.n0.terminate()
self.h3.stop( deleteIntfs=True )
self.n0.stop( deleteIntfs=True )
if __name__ == '__main__':
setLogLevel( 'warning' )
+10 -8
View File
@@ -13,27 +13,29 @@ class testIntfOptions( unittest.TestCase ):
def testIntfOptions( self ):
"verify that intf.config is correctly limiting traffic"
p = pexpect.spawn( 'python -m mininet.examples.intfoptions ' )
tolerance = .8
tolerance = .2 # plus or minus 20%
opts = [ "Results: \['([\d\.]+) .bits/sec",
"Results: \['10M', '([\d\.]+) .bits/sec",
"h(\d+)->h(\d+): (\d)/(\d), rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
"h(\d+)->h(\d+): (\d)/(\d),"
"rtt min/avg/max/mdev ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms",
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
if index == 0:
BW = 5
bw = float( p.match.group( 1 ) )
self.assertGreaterEqual( bw, float( 5 * tolerance ) )
self.assertLessEqual( bw, float( 5 + 5 * ( 1 - tolerance ) ) )
self.assertGreaterEqual( bw, BW * ( 1 - tolerance ) )
self.assertLessEqual( bw, BW * ( 1 + tolerance ) )
elif index == 1:
BW = 10
measuredBw = float( p.match.group( 1 ) )
loss = ( measuredBw / BW ) * 100
self.assertGreaterEqual( loss, 50 * tolerance )
self.assertLessEqual( loss, 50 + 50 * ( 1 - tolerance ) )
self.assertGreaterEqual( loss, 50 * ( 1 - tolerance ) )
self.assertLessEqual( loss, 50 * ( 1 + tolerance ) )
elif index == 2:
delay = float( p.match.group( 6 ) )
self.assertGreaterEqual( delay, 15 * tolerance )
self.assertLessEqual( delay, 15 + 15 * ( 1 - tolerance ) )
self.assertGreaterEqual( delay, 15 * ( 1 - tolerance ) )
self.assertLessEqual( delay, 15 * ( 1 + tolerance ) )
else:
break
+2 -2
View File
@@ -14,8 +14,8 @@ class testLimit( unittest.TestCase ):
def testLimit( self ):
"Verify that CPU limits are within a 2% tolerance of limit for each scheduler"
p = pexpect.spawn( 'python -m mininet.examples.limit' )
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
opts = [ '\*\*\* Testing network ([\d\.]+) Mbps',
'\*\*\* Results: \[([\d\., ]+)\]',
pexpect.EOF ]
count = 0
bw = 0
+4 -4
View File
@@ -15,8 +15,8 @@ class testLinearBandwidth( unittest.TestCase ):
"Verify that bandwidth is monotonically decreasing as # of hops increases"
p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' )
count = 0
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
opts = [ '\*\*\* Linear network results',
'(\d+)\s+([\d\.]+) (.bits)',
pexpect.EOF ]
while True:
index = p.expect( opts, timeout=600 )
@@ -34,8 +34,8 @@ class testLinearBandwidth( unittest.TestCase ):
bw *= 10 ** 9
# check that we have a previous result to compare to
if n != 1:
info = ( 'bw: %d bits/s across %d switches, '
'previous: %d bits/s across %d switches' %
info = ( 'bw: %.2e bits/s across %d switches, '
'previous: %.2e bits/s across %d switches' %
( bw, n, previous_bw, previous_n ) )
self.assertTrue( bw < previous_bw, info )
previous_bw, previous_n = bw, n
+2 -2
View File
@@ -22,14 +22,14 @@ class testMultiLink( unittest.TestCase ):
hostToIntfs = intfsOutput.split( '\r\n' )[ 1:3 ]
intfList = []
for hostToIntf in hostToIntfs:
intfList += [ intf for intf in
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 )
+1 -1
View File
@@ -11,7 +11,7 @@ from collections import defaultdict
class testMultiPing( unittest.TestCase ):
def testMultiPing( self ):
"""Verify that each target is pinged at least once, and
"""Verify that each target is pinged at least once, and
that pings to 'real' targets are successful and unknown targets fail"""
p = pexpect.spawn( 'python -m mininet.examples.multiping' )
opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)",
+3 -2
View File
@@ -16,7 +16,7 @@ class testMultiPoll( unittest.TestCase ):
"(h\d+): \d+ bytes from",
"Monitoring output for (\d+) seconds",
pexpect.EOF ]
pings = {}
pings, seconds = {}, -1
while True:
index = p.expect( opts )
if index == 0:
@@ -32,7 +32,8 @@ class testMultiPoll( unittest.TestCase ):
self.assertTrue( len( pings ) > 0 )
# make sure we have received at least one ping per second
for count in pings.values():
self.assertTrue( count >= seconds )
self.assertTrue( count >= seconds,
'%d pings < %d seconds' % ( count, seconds ) )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -14,7 +14,7 @@ class testNAT( unittest.TestCase ):
prompt = 'mininet>'
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
@unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ),
'Destination IP is not reachable' )
def testNAT( self ):
"Attempt to ping an IP on the Internet and verify 0% packet loss"
+3 -3
View File
@@ -15,8 +15,8 @@ class testNumberedports( unittest.TestCase ):
def testConsistency( self ):
"""verify consistency between mininet and ovs ports"""
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
'Validating that s1-eth\d is actually on port \d ... WARNING',
opts = [ 'Validating that s1-eth\d is actually on port \d ... Validated.',
'Validating that s1-eth\d is actually on port \d ... WARNING',
pexpect.EOF ]
correct_ports = True
count = 0
@@ -34,7 +34,7 @@ class testNumberedports( unittest.TestCase ):
def testNumbering( self ):
"""verify that all of the port numbers are printed correctly and consistent with their interface"""
p = pexpect.spawn( 'python -m mininet.examples.numberedports' )
opts = [ 's1-eth(\d+) : (\d+)',
opts = [ 's1-eth(\d+) : (\d+)',
pexpect.EOF ]
count_intfs = 0
while True:
+1 -1
View File
@@ -14,7 +14,7 @@ class testScratchNet( unittest.TestCase ):
def pingTest( self, name ):
"Verify that no ping packets were dropped"
p = pexpect.spawn( 'python -m %s' % name )
index = p.expect( self.opts )
index = p.expect( self.opts, timeout=120 )
self.assertEqual( index, 0 )
def testPingKernel( self ):
+5 -5
View File
@@ -16,15 +16,15 @@ class testSimplePerf( unittest.TestCase ):
@unittest.skipIf( '-quick' in sys.argv, 'long test' )
def testE2E( self ):
"Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance
BW = 10
TOLERANCE = .8
expectedBw = BW * TOLERANCE
p = pexpect.spawn( 'python -m mininet.examples.simpleperf' )
TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
# check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
measuredBw = float( p.match.group( 1 ) )
lowerBound = expectedBw * TOLERANCE
upperBound = expectedBw + expectedBw * ( 1 - TOLERANCE )
lowerBound = BW * ( 1 - TOLERANCE )
upperBound = BW + ( 1 + TOLERANCE )
self.assertGreaterEqual( measuredBw, lowerBound )
self.assertLessEqual( measuredBw, upperBound )
p.wait()
+3 -3
View File
@@ -14,19 +14,19 @@ class testSSHD( unittest.TestCase ):
def connected( self, ip ):
"Log into ssh server, check banner, then exit"
# Note: this test will fail if "Welcome" is not in the sshd banner
# Note: this test will fail if "Welcome" is not in the sshd banner
# and '#'' or '$'' are not in the prompt
p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 )
while True:
index = p.expect( self.opts )
if index == 0:
print p.match.group(0)
print( p.match.group(0) )
p.sendline( 'yes' )
elif index == 1:
return False
elif index == 2:
p.sendline( 'exit' )
p.wait()
p.wait()
return True
else:
return False
+5 -3
View File
@@ -17,13 +17,15 @@ class testTree1024( unittest.TestCase ):
"Run the example and do a simple ping test from h1 to h1024"
p = pexpect.spawn( 'python -m mininet.examples.tree1024' )
p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up
p.sendline( 'h1 ping -c 1 h1024' )
p.sendline( 'h1 ping -c 20 h1024' )
p.expect ( '(\d+)% packet loss' )
percent = int( p.match.group( 1 ) ) if p.match else -1
packetLossPercent = int( p.match.group( 1 ) ) if p.match else -1
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
self.assertEqual( percent, 0 )
# Tolerate slow startup on some systems - we should revisit this
# and determine the root cause.
self.assertLess( packetLossPercent, 60 )
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -47,4 +47,4 @@ class testVLANHost( unittest.TestCase ):
self.assertEqual( i, 0 ) # check vlan intf is present
if __name__ == '__main__':
unittest.main()
unittest.main()
+2 -2
View File
@@ -9,10 +9,10 @@ and running sysctl -p. Check util/sysctl_addon.
from mininet.cli import CLI
from mininet.log import setLogLevel
from mininet.node import OVSKernelSwitch
from mininet.node import OVSSwitch
from mininet.topolib import TreeNet
if __name__ == '__main__':
setLogLevel( 'info' )
network = TreeNet( depth=2, fanout=32, switch=OVSKernelSwitch )
network = TreeNet( depth=2, fanout=32, switch=OVSSwitch )
network.run( CLI, network )
+6 -6
View File
@@ -2,7 +2,8 @@
"Create a 64-node tree network, and test connectivity using ping."
from mininet.log import setLogLevel
from mininet.log import setLogLevel, info
from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
from mininet.topolib import TreeNet
@@ -15,17 +16,16 @@ def treePing64():
'Open vSwitch kernel': OVSKernelSwitch }
for name in switches:
print "*** Testing", name, "datapath"
info( "*** Testing", name, "datapath\n" )
switch = switches[ name ]
network = TreeNet( depth=2, fanout=8, switch=switch )
result = network.run( network.pingAll )
results[ name ] = result
print
print "*** Tree network ping results:"
info( "\n*** Tree network ping results:\n" )
for name in switches:
print "%s: %d%% packet loss" % ( name, results[ name ] )
print
info( "%s: %d%% packet loss\n" % ( name, results[ name ] ) )
info( '\n' )
if __name__ == '__main__':
setLogLevel( 'info' )
+11 -12
View File
@@ -30,17 +30,18 @@ from mininet.util import quietRun
from mininet.log import error
class VLANHost( Host ):
"Host connected to VLAN interface"
def config( self, vlan=100, **params ):
def config( self, vlan=100, **params ):
"""Configure VLANHost according to (optional) parameters:
vlan: VLAN ID for default interface"""
r = super( Host, self ).config( **params )
r = super( VLANHost, self ).config( **params )
intf = self.defaultIntf()
# remove IP from default, "physical" interface
self.cmd( 'ifconfig %s inet 0' % intf )
# create VLAN interface
# create VLAN interface
self.cmd( 'vconfig add %s %d' % ( intf, vlan ) )
# assign the host's IP to the VLAN interface
self.cmd( 'ifconfig %s.%d inet %s' % ( intf, vlan, params['ip'] ) )
@@ -69,6 +70,8 @@ def exampleAllHosts( vlan ):
CLI( net )
net.stop()
# pylint: disable=arguments-differ
class VLANStarTopo( Topo ):
"""Example topology that uses host in multiple VLANs
@@ -90,7 +93,7 @@ class VLANStarTopo( Topo ):
self.addLink( h, s1 )
def exampleCustomTags( vlan ):
def exampleCustomTags():
"""Simple example that exercises VLANStarTopo"""
net = Mininet( topo=VLANStarTopo() )
@@ -110,16 +113,12 @@ if __name__ == '__main__':
setLogLevel( 'info' )
if not quietRun( 'which vconfig' ):
error( "Cannot find command 'vconfig'\nThe packge",
error( "Cannot find command 'vconfig'\nThe package",
"'vlan' is required in Ubuntu or Debian,",
"or 'vconfig' in Fedora\n" )
exit()
try:
vlan = int( sys.argv[ 1 ] )
except Exception:
vlan = None
if vlan:
exampleAllHosts( vlan )
if len( sys.argv ) >= 2:
exampleAllHosts( vlan=int( sys.argv[ 1 ] ) )
else:
exampleCustomTags( vlan )
exampleCustomTags()
+86 -55
View File
@@ -10,7 +10,8 @@ It may also get rid of 'false positives', but hopefully
nothing irreplaceable!
"""
from subprocess import Popen, PIPE, check_output as co
from subprocess import ( Popen, PIPE, check_output as co,
CalledProcessError )
import time
from mininet.log import info
@@ -24,73 +25,103 @@ def sh( cmd ):
def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)"
sh( 'pkill -9 -f %s' % pattern )
# Make sure they are gone
while True:
try:
pids = co( 'pgrep -f %s' % pattern )
except:
pids = ''
pids = co( [ 'pgrep', '-f', pattern ] ).split( '\n' )
except CalledProcessError:
pids = []
# Don't kill init
pids = [ pid for pid in pids if pid and pid != '1' ]
if pids:
sh( 'pkill -f 9 mininet:' )
sh( "kill -9 %s" % ' '.join( pids ) )
time.sleep( .5 )
else:
break
def cleanup():
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
class Cleanup( object ):
"Wrapper for cleanup()"
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
"\n")
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs'
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
callbacks = []
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
@classmethod
def cleanup( cls):
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
"pings/noxes\n" )
zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
'lt-nox_core ovs-openflowd ovs-controller'
'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
# Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless.
# Send SIGTERM first to give processes a chance to shutdown cleanly.
sh( 'killall ' + zombies + ' 2> /dev/null' )
time.sleep( 1 )
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
# And kill off sudo mnexec
sh( 'pkill -9 -f "sudo mnexec"')
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).splitlines()
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing OVS datapaths" )
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps:
sh( "ovs-vsctl " + " -- ".join( "--if-exists del-br " + dp
for dp in dps if dp ) )
# And in case the above didn't work...
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
for dp in dps:
sh( 'ovs-vsctl del-br ' + dp )
info( "*** Removing old X11 tunnels\n" )
cleanUpScreens()
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | "
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'" ).splitlines()
for link in links:
if link:
sh( "ip link del " + link )
info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'"
).splitlines()
for dp in dps:
if dp:
sh( 'dpctl deldp ' + dp )
info( "*** Killing stale mininet node processes\n" )
killprocs( 'mininet:' )
info( "*** Removing OVS datapaths\n" )
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps:
sh( "ovs-vsctl " + " -- ".join( "--if-exists del-br " + dp
for dp in dps if dp ) )
# And in case the above didn't work...
dps = sh( "ovs-vsctl --timeout=1 list-br" ).strip().splitlines()
for dp in dps:
sh( 'ovs-vsctl del-br ' + dp )
info ( "*** Shutting down stale tunnels\n" )
killprocs( 'Tunnel=Ethernet' )
killprocs( '.ssh/mn')
sh( 'rm -f ~/.ssh/mn/*' )
info( "*** Cleanup complete.\n" )
info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | "
"egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'"
).splitlines()
# Delete blocks of links
n = 1000 # chunk size
for i in range( 0, len( links ), n ):
cmd = ';'.join( 'ip link del %s' % link
for link in links[ i : i + n ] )
sh( '( %s ) 2> /dev/null' % cmd )
if 'tap9' in sh( 'ip link show' ):
info( "*** Removing tap9 - assuming it's from cluster edition\n" )
sh( 'ip link del tap9' )
info( "*** Killing stale mininet node processes\n" )
killprocs( 'mininet:' )
info( "*** Shutting down stale tunnels\n" )
killprocs( 'Tunnel=Ethernet' )
killprocs( '.ssh/mn')
sh( 'rm -f ~/.ssh/mn/*' )
# Call any additional cleanup code if necessary
for callback in cls.callbacks:
callback()
info( "*** Cleanup complete.\n" )
@classmethod
def addCleanupCallback( cls, callback ):
"Add cleanup callback"
if callback not in cls.callbacks:
cls.callbacks.append( callback )
cleanup = Cleanup.cleanup
addCleanupCallback = Cleanup.addCleanupCallback
+61 -36
View File
@@ -37,7 +37,7 @@ import atexit
from mininet.log import info, output, error
from mininet.term import makeTerms, runX11
from mininet.util import ( quietRun, dumpNodeConnections,
dumpPorts )
dumpPorts )
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
@@ -45,6 +45,10 @@ class CLI( Cmd ):
prompt = 'mininet> '
def __init__( self, mininet, stdin=sys.stdin, script=None ):
"""Start and run interactive or batch mode CLI
mininet: Mininet network object
stdin: standard input for CLI
script: script to run in batch mode"""
self.mn = mininet
# Local variable bindings for py command
self.locals = { 'net': mininet }
@@ -56,33 +60,56 @@ class CLI( Cmd ):
Cmd.__init__( self )
info( '*** Starting CLI:\n' )
# Set up history if readline is available
try:
import readline
except ImportError:
pass
else:
history_path = os.path.expanduser('~/.mininet_history')
if os.path.isfile(history_path):
readline.read_history_file(history_path)
atexit.register(lambda: readline.write_history_file(history_path))
if self.inputFile:
self.do_source( self.inputFile )
return
self.initReadline()
self.run()
readlineInited = False
@classmethod
def initReadline( cls ):
"Set up history if readline is available"
# Only set up readline once to prevent multiplying the history file
if cls.readlineInited:
return
cls.readlineInited = True
try:
from readline import ( read_history_file, write_history_file,
set_history_length )
except ImportError:
pass
else:
history_path = os.path.expanduser( '~/.mininet_history' )
if os.path.isfile( history_path ):
read_history_file( history_path )
set_history_length( 1000 )
atexit.register( lambda: write_history_file( history_path ) )
def run( self ):
"Run our cmdloop(), catching KeyboardInterrupt"
while True:
try:
# Make sure no nodes are still waiting
for node in self.mn.values():
while node.waiting:
info( 'stopping', node, '\n' )
node.sendInt()
node.waitOutput()
if self.isatty():
quietRun( 'stty echo sane intr "^C"' )
quietRun( 'stty echo sane intr ^C' )
self.cmdloop()
break
except KeyboardInterrupt:
output( '\nInterrupt\n' )
# Output a message - unless it's also interrupted
# pylint: disable=broad-except
try:
output( '\nInterrupt\n' )
except Exception:
pass
# pylint: enable=broad-except
def emptyline( self ):
"Don't repeat last command when you hit return."
@@ -93,11 +120,6 @@ class CLI( Cmd ):
self.locals.update( self.mn )
return self.locals
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
' <node> command {args}\n'
@@ -128,7 +150,7 @@ class CLI( Cmd ):
nodes = ' '.join( sorted( self.mn ) )
output( 'available nodes are: \n%s\n' % nodes )
def do_ports( self, line ):
def do_ports( self, _line ):
"display ports and interfaces for each switch"
dumpPorts( self.mn.switches )
@@ -139,10 +161,11 @@ class CLI( Cmd ):
def do_sh( self, line ):
"""Run an external shell command
Usage: sh [cmd args]"""
assert self # satisfy pylint and allow override
call( line, shell=True )
# do_py() and do_px() need to catch any exception during eval()/exec()
# pylint: disable-msg=W0703
# pylint: disable=broad-except
def do_py( self, line ):
"""Evaluate a Python expression.
@@ -155,21 +178,21 @@ class CLI( Cmd ):
output( result + '\n' )
else:
output( repr( result ) + '\n' )
except Exception, e:
except Exception as e:
output( str( e ) + '\n' )
# We are in fact using the exec() pseudo-function
# pylint: disable-msg=W0122
# pylint: disable=exec-used
def do_px( self, line ):
"""Execute a Python statement.
Node names may be used, e.g.: px print h1.cmd('ls')"""
try:
exec( line, globals(), self.getLocals() )
except Exception, e:
except Exception as e:
output( str( e ) + '\n' )
# pylint: enable-msg=W0703,W0122
# pylint: enable=broad-except,exec-used
def do_pingall( self, line ):
"Ping between all hosts."
@@ -284,6 +307,7 @@ class CLI( Cmd ):
def do_exit( self, _line ):
"Exit"
assert self # satisfy pylint and allow override
return 'exited by user command'
def do_quit( self, line ):
@@ -346,20 +370,21 @@ class CLI( Cmd ):
elapsed = time.time() - start
self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
def do_links( self, line ):
def do_links( self, _line ):
"Report on links"
for link in self.mn.links:
print link, link.status()
output( link, link.status(), '\n' )
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' )
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 :
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 ]
@@ -369,19 +394,21 @@ class CLI( Cmd ):
elif command == 'stop':
self.mn.get( sw ).stop( deleteIntfs=False )
else:
error( 'invalid command: switch <switch name> {start, stop}\n' )
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.
Past the first CLI argument, node names are automatically replaced with
corresponding IP addrs."""
Overridden to run shell commands when a node is the first
CLI argument. Past the first CLI argument, node names are
automatically replaced with corresponding IP addrs."""
first, args, line = self.parseline( line )
if first in self.mn:
if not args:
print "*** Enter a command for node: %s <cmd>" % first
error( '*** Please enter a command for node: %s <cmd>\n'
% first )
return
node = self.mn[ first ]
rest = args.split( ' ' )
@@ -397,8 +424,6 @@ class CLI( Cmd ):
else:
error( '*** Unknown command: %s\n' % line )
# pylint: enable-msg=R0201
def waitForNode( self, node ):
"Wait for a node to finish, and print its output."
# Pollers
+150 -30
View File
@@ -26,6 +26,8 @@ Link: basic link class for creating veth pairs
from mininet.log import info, error, debug
from mininet.util import makeIntfPair, quietRun
import mininet.node
import re
class Intf( object ):
@@ -33,7 +35,7 @@ class Intf( object ):
"Basic interface object that can configure itself."
def __init__( self, name, node=None, port=None, link=None,
mac=None, srcNode=None, **params ):
mac=None, **params ):
"""name: interface name (e.g. h1-eth0)
node: owning node (where this intf most likely lives)
link: parent link if we're part of a link
@@ -43,13 +45,19 @@ class Intf( object ):
self.link = link
self.mac = mac
self.ip, self.prefixLen = None, None
# if interface is lo, we know the ip is 127.0.0.1.
# This saves an ifconfig command per node
if self.name == 'lo':
self.ip = '127.0.0.1'
self.prefixLen = 8
# Add to node (and move ourselves if necessary )
node.addIntf( self, port=port )
if node:
moveIntfFn = params.pop( 'moveIntfFn', None )
if moveIntfFn:
node.addIntf( self, port=port, moveIntfFn=moveIntfFn )
else:
node.addIntf( self, port=port )
# Save params for future reference
self.params = params
self.config( **params )
@@ -71,7 +79,8 @@ class Intf( object ):
return self.ifconfig( ipstr, 'up' )
else:
if prefixLen is None:
raise Exception( 'No prefix length set for IP address %s' % ( ipstr, ) )
raise Exception( 'No prefix length set for IP address %s'
% ( ipstr, ) )
self.ip, self.prefixLen = ipstr, prefixLen
return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
@@ -90,7 +99,8 @@ class Intf( object ):
"Return updated IP address based on ifconfig"
# use pexec instead of node.cmd so that we dont read
# backgrounded output from the cli.
ifconfig, _err, _exitCode = self.node.pexec( 'ifconfig %s' % self.name )
ifconfig, _err, _exitCode = self.node.pexec(
'ifconfig %s' % self.name )
ips = self._ipMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
return self.ip
@@ -190,13 +200,16 @@ class Intf( object ):
def delete( self ):
"Delete interface"
self.cmd( 'ip link del ' + self.name )
if self.node.inNamespace:
# Link may have been dumped into root NS
quietRun( 'ip link del ' + self.name )
# We used to do this, but it slows us down:
# if self.node.inNamespace:
# Link may have been dumped into root NS
# quietRun( 'ip link del ' + self.name )
self.node.delIntf( self )
self.link = None
def status( self ):
"Return intf status as a string"
links, err_, result_ = self.node.pexec( 'ip link show' )
links, _err, _result = self.node.pexec( 'ip link show' )
if self.name in links:
return "OK"
else:
@@ -214,15 +227,19 @@ class TCIntf( Intf ):
Allows specification of bandwidth limits (various methods)
as well as delay, loss and max queue length"""
# The parameters we use seem to work reasonably up to 1 Gb/sec
# For higher data rates, we will probably need to change them.
bwParamMax = 1000
def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False ):
"Return tc commands to set bandwidth"
cmds, parent = [], ' root '
if bw and ( bw < 0 or bw > 1000 ):
error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' )
if bw and ( bw < 0 or bw > self.bwParamMax ):
error( 'Bandwidth limit', bw, 'is outside supported range 0..%d'
% self.bwParamMax, '- ignoring\n' )
elif bw is not None:
# BL: this seems a bit brittle...
if ( speedup > 0 and
@@ -238,7 +255,7 @@ class TCIntf( Intf ):
+ 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
elif use_tbf:
if latency_ms is None:
latency_ms = 15 * 8 / bw
latency_ms = 15.0 * 8 / bw
cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
@@ -281,7 +298,7 @@ class TCIntf( Intf ):
netemargs = '%s%s%s%s' % (
'delay %s ' % delay if delay is not None else '',
'%s ' % jitter if jitter is not None else '',
'loss %d ' % loss if loss is not None else '',
'loss %.5f ' % loss if loss is not None else '',
'limit %d' % max_queue_size if max_queue_size is not None
else '' )
if netemargs:
@@ -298,16 +315,40 @@ class TCIntf( Intf ):
return self.cmd( c )
def config( self, bw=None, delay=None, jitter=None, loss=None,
disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False,
gro=False, txo=True, rxo=True,
speedup=0, use_hfsc=False, use_tbf=False,
latency_ms=None, enable_ecn=False, enable_red=False,
max_queue_size=None, **params ):
"Configure the port and set its properties."
"""Configure the port and set its properties.
bw: bandwidth in b/s (e.g. '10m')
delay: transmit delay (e.g. '1ms' )
jitter: jitter (e.g. '1ms')
loss: loss (e.g. '1%' )
gro: enable GRO (False)
txo: enable transmit checksum offload (True)
rxo: enable receive checksum offload (True)
speedup: experimental switch-side bw option
use_hfsc: use HFSC scheduling
use_tbf: use TBF scheduling
latency_ms: TBF latency parameter
enable_ecn: enable ECN (False)
enable_red: enable RED (False)
max_queue_size: queue limit parameter for netem"""
# Support old names for parameters
gro = not params.pop( 'disable_gro', not gro )
result = Intf.config( self, **params)
# Disable GRO
if disable_gro:
self.cmd( 'ethtool -K %s gro off' % self )
def on( isOn ):
"Helper method: bool -> 'on'/'off'"
return 'on' if isOn else 'off'
# Set offload parameters with ethool
self.cmd( 'ethtool -K', self,
'gro', on( gro ),
'tx', on( txo ),
'rx', on( rxo ) )
# Optimization: return if nothing else to configure
# Question: what happens if we want to reset things?
@@ -317,7 +358,7 @@ class TCIntf( Intf ):
# Clear existing configuration
tcoutput = self.tc( '%s qdisc show dev %s' )
if "priomap" not in tcoutput:
if "priomap" not in tcoutput and "noqueue" not in tcoutput:
cmds = [ '%s qdisc del dev %s root' ]
else:
cmds = []
@@ -332,15 +373,16 @@ class TCIntf( Intf ):
# Delay/jitter/loss/max_queue_size using netem
delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter,
loss=loss, max_queue_size=max_queue_size,
parent=parent )
loss=loss,
max_queue_size=max_queue_size,
parent=parent )
cmds += delaycmds
# Ugly but functional: display configuration info
stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) +
( [ '%s delay' % delay ] if delay is not None else [] ) +
( [ '%s jitter' % jitter ] if jitter is not None else [] ) +
( ['%d%% loss' % loss ] if loss is not None else [] ) +
( ['%.5f%% loss' % loss ] if loss is not None else [] ) +
( [ 'ECN' ] if enable_ecn else [ 'RED' ]
if enable_red else [] ) )
info( '(' + ' '.join( stuff ) + ') ' )
@@ -364,10 +406,11 @@ class Link( object ):
"""A basic link is just a veth pair.
Other types of links could be tunnels, link emulators, etc.."""
# pylint: disable=too-many-branches
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, addr1=None, addr2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None ):
params2=None, fast=True ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
@@ -402,7 +445,14 @@ class Link( object ):
if not intfName2:
intfName2 = self.intfName( node2, params2[ 'port' ] )
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
self.fast = fast
if fast:
params1.setdefault( 'moveIntfFn', self._ignore )
params2.setdefault( 'moveIntfFn', self._ignore )
self.makeIntfPair( intfName1, intfName2, addr1, addr2,
node1, node2, deleteIntfs=False )
else:
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
if not cls1:
cls1 = intf
@@ -416,28 +466,47 @@ class Link( object ):
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
# pylint: enable=too-many-branches
@staticmethod
def _ignore( *args, **kwargs ):
"Ignore any arguments"
pass
def intfName( self, node, n ):
"Construct a canonical interface name node-ethN for interface n."
# Leave this as an instance method for now
assert self
return node.name + '-eth' + repr( n )
@classmethod
def makeIntfPair( _cls, intfname1, intfname2, addr1=None, addr2=None ):
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
node1=None, node2=None, deleteIntfs=True,
runCmd=quietRun ):
"""Create pair of interfaces
intfname1: name of interface 1
intfname2: name of interface 2
intfname1: name for interface 1
intfname2: name for interface 2
addr1: MAC address for interface 1 (optional)
addr2: MAC address for interface 2 (optional)
node1: home node for interface 1 (optional)
node2: home node for interface 2 (optional)
(override this method [and possibly delete()]
to change link type)"""
return makeIntfPair( intfname1, intfname2, addr1, addr2 )
# Leave this as a class method for now
assert cls
return makeIntfPair( intfname1, intfname2, addr1, addr2, node1, node2,
deleteIntfs=deleteIntfs, runCmd=runCmd )
def delete( self ):
"Delete this link"
self.intf1.delete()
self.intf1 = None
self.intf2.delete()
self.intf2 = None
def stop( self ):
"Override to stop and clean up link as needed"
pass
self.delete()
def status( self ):
"Return link status as a string"
@@ -446,6 +515,42 @@ class Link( object ):
def __str__( self ):
return '%s<->%s' % ( self.intf1, self.intf2 )
class OVSIntf( Intf ):
"Patch interface on an OVSSwitch"
def ifconfig( self, *args ):
cmd = ' '.join( args )
if cmd == 'up':
# OVSIntf is always up
return
else:
raise Exception( 'OVSIntf cannot do ifconfig ' + cmd )
class OVSLink( Link ):
"""Link that makes patch links between OVSSwitches
Warning: in testing we have found that no more
than ~64 OVS patch links should be used in row."""
def __init__( self, node1, node2, **kwargs ):
"See Link.__init__() for options"
from mininet.node import OVSSwitch
self.isPatchLink = False
if ( isinstance( node1, OVSSwitch ) and
isinstance( node2, OVSSwitch ) ):
self.isPatchLink = True
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
Link.__init__( self, node1, node2, **kwargs )
def makeIntfPair( self, *args, **kwargs ):
"Usually delegated to OVSSwitch"
if self.isPatchLink:
return None, None
else:
return Link.makeIntfPair( *args, **kwargs )
class TCLink( Link ):
"Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None,
@@ -458,3 +563,18 @@ class TCLink( Link ):
addr1=addr1, addr2=addr2,
params1=params,
params2=params )
class TCULink( TCLink ):
"""TCLink with default settings optimized for UserSwitch
(txo=rxo=0/False). Unfortunately with recent Linux kernels,
enabling TX and RX checksum offload on veth pairs doesn't work
well with UserSwitch: either it gets terrible performance or
TCP packets with bad checksums are generated, forwarded, and
*dropped* due to having bad checksums! OVS and LinuxBridge seem
to cope with this somehow, but it is likely to be an issue with
many software Ethernet bridges."""
def __init__( self, *args, **kwargs ):
kwargs.update( txo=False, rxo=False )
TCLink.__init__( self, *args, **kwargs )
+9 -8
View File
@@ -124,8 +124,8 @@ class MininetLogger( Logger, object ):
self.setLevel( level )
self.handlers[ 0 ].setLevel( level )
# pylint: disable-msg=E0202
# "An attribute inherited from mininet.log hide this method"
# pylint: disable=method-hidden
# "An attribute inherited from mininet.log hide this method" (sic)
# Not sure why this is occurring - this function definitely gets called.
# See /usr/lib/python2.5/logging/__init__.py; modified from warning()
@@ -142,7 +142,7 @@ class MininetLogger( Logger, object ):
if self.isEnabledFor( OUTPUT ):
self._log( OUTPUT, msg, args, kwargs )
# pylint: enable-msg=E0202
# pylint: enable=method-hidden
lg = MininetLogger()
@@ -160,7 +160,7 @@ def makeListCompatible( fn ):
"Generated function. Closure-ish."
if len( args ) == 1:
return fn( *args )
args = ' '.join( [ str( arg ) for arg in args ] )
args = ' '.join( str( arg ) for arg in args )
return fn( args )
# Fix newfn's name and docstring
@@ -168,9 +168,10 @@ def makeListCompatible( fn ):
setattr( newfn, '__doc__', fn.__doc__ )
return newfn
info, output, warn, error, debug = (
lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
makeListCompatible( f ) for f in
lg.info, lg.output, lg.warn, lg.error, lg.debug ]
_loggers = lg.info, lg.output, lg.warn, lg.error, lg.debug
_loggers = tuple( makeListCompatible( logger )
for logger in _loggers )
lg.info, lg.output, lg.warn, lg.error, lg.debug = _loggers
info, output, warn, error, debug = _loggers
setLogLevel = lg.setLogLevel
+153 -66
View File
@@ -89,7 +89,7 @@ method may be called to shut down the network.
import os
import re
import select
import signal
from signal import SIGKILL
import random
from time import sleep
@@ -98,15 +98,17 @@ from math import ceil
from mininet.cli import CLI
from mininet.log import info, error, debug, output, warn
from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
Controller )
from mininet.nodelib import NAT
from mininet.link import Link, Intf
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening )
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.2.0b1"
VERSION = "2.3.0d1"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -142,7 +144,9 @@ class Mininet( object ):
self.intf = intf
self.ipBase = ipBase
self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
self.nextIP = 1 # start for address allocation
hostIP = ( 0xffffffff >> self.prefixLen ) & self.ipBaseNum
# Start for address allocation
self.nextIP = hostIP if hostIP > 0 else 1
self.inNamespace = inNamespace
self.xterms = xterms
self.cleanup = cleanup
@@ -169,7 +173,6 @@ class Mininet( object ):
if topo and build:
self.build()
def waitConnected( self, timeout=None, delay=.5 ):
"""wait for each switch to connect to a controller,
up to 5 seconds
@@ -225,6 +228,24 @@ class Mininet( object ):
self.nameToNode[ name ] = h
return h
def delNode( self, node, nodes=None):
"""Delete node
node: node to delete
nodes: optional list to delete from (e.g. self.hosts)"""
if nodes is None:
nodes = ( self.hosts if node in self.hosts else
( self.switches if node in self.switches else
( self.controllers if node in self.controllers else
[] ) ) )
node.stop( deleteIntfs=True )
node.terminate()
nodes.remove( node )
del self.nameToNode[ node.name ]
def delHost( self, host ):
"Delete a host"
self.delNode( host, nodes=self.hosts )
def addSwitch( self, name, cls=None, **params ):
"""Add switch.
name: name of switch to add
@@ -243,6 +264,10 @@ class Mininet( object ):
self.nameToNode[ name ] = sw
return sw
def delSwitch( self, switch ):
"Delete a switch"
self.delNode( switch, nodes=self.switches )
def addController( self, name='c0', controller=None, **params ):
"""Add controller.
controller: Controller class"""
@@ -253,25 +278,41 @@ class Mininet( object ):
if isinstance( name, Controller ):
controller_new = name
# Pylint thinks controller is a str()
# pylint: disable=E1103
# pylint: disable=maybe-no-member
name = controller_new.name
# pylint: enable=E1103
# pylint: enable=maybe-no-member
else:
controller_new = controller( name, **params )
# Add new controller to net
if controller_new: # allow controller-less setups
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
return controller_new
def addNAT( self, name='nat0', connect=True, inNamespace=False, **params ):
def delController( self, controller ):
"""Delete a controller
Warning - does not reconfigure switches, so they
may still attempt to connect to it!"""
self.delNode( controller )
def addNAT( self, name='nat0', connect=True, inNamespace=False,
**params):
"""Add a NAT to the Mininet network
name: name of NAT node
connect: switch to connect to | True (s1) | None
inNamespace: create in a network namespace
params: other NAT node params, notably:
ip: used as default gateway address"""
nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
subnet=self.ipBase, **params )
# find first switch and create link
if connect:
# connect the nat to the first switch
self.addLink( nat, self.switches[ 0 ] )
# set the default route on hosts
if not isinstance( connect, Node ):
# Use first switch if not specified
connect = self.switches[ 0 ]
# Connect the nat to the switch
self.addLink( nat, connect )
# Set the default route on hosts
natIP = nat.params[ 'ip' ].split('/')[ 0 ]
for host in self.hosts:
if host.inNamespace:
@@ -292,9 +333,13 @@ class Mininet( object ):
# Even more convenient syntax for node lookup and iteration
def __getitem__( self, key ):
"""net [ name ] operator: Return node(s) with given name(s)"""
"net[ name ] operator: Return node with given name"
return self.nameToNode[ key ]
def __delitem__( self, key ):
"del net[ name ] operator - delete node with given name"
self.delNode( self.nameToNode[ key ] )
def __iter__( self ):
"return iterator over node names"
for node in chain( self.hosts, self.switches, self.controllers ):
@@ -324,7 +369,7 @@ class Mininet( object ):
@staticmethod
def randMac():
"Return a random, non-multicast MAC address"
return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
0x020000000000 )
def addLink( self, node1, node2, port1=None, port2=None,
@@ -346,6 +391,8 @@ class Mininet( object ):
options.setdefault( 'port1', port1 )
if port2 is not None:
options.setdefault( 'port2', port2 )
if self.intf is not None:
options.setdefault( 'intf', self.intf )
# Set default MAC - this should probably be in Link
options.setdefault( 'addr1', self.randMac() )
options.setdefault( 'addr2', self.randMac() )
@@ -354,6 +401,30 @@ class Mininet( object ):
self.links.append( link )
return link
def delLink( self, link ):
"Remove a link from this network"
link.delete()
self.links.remove( link )
def linksBetween( self, node1, node2 ):
"Return Links between node1 and node2"
return [ link for link in self.links
if ( node1, node2 ) in (
( link.intf1.node, link.intf2.node ),
( link.intf2.node, link.intf1.node ) ) ]
def delLinkBetween( self, node1, node2, index=0, allLinks=False ):
"""Delete link(s) between node1 and node2
index: index of link to delete if multiple links (0)
allLinks: ignore index and delete all such links (False)
returns: deleted link(s)"""
links = self.linksBetween( node1, node2 )
if not allLinks:
links = [ links[ index ] ]
for link in links:
self.delLink( link )
return links
def configHosts( self ):
"Configure a set of hosts."
for host in self.hosts:
@@ -391,7 +462,7 @@ class Mininet( object ):
if not isinstance( classes, list ):
classes = [ classes ]
for i, cls in enumerate( classes ):
# Allow Controller objects because nobody understands currying
# Allow Controller objects because nobody understands partial()
if isinstance( cls, Controller ):
self.addController( cls )
else:
@@ -404,7 +475,12 @@ class Mininet( object ):
info( '\n*** Adding switches:\n' )
for switchName in topo.switches():
self.addSwitch( switchName, **topo.nodeInfo( switchName) )
# A bit ugly: add batch parameter if appropriate
params = topo.nodeInfo( switchName)
cls = params.get( 'cls', self.switch )
if hasattr( cls, 'batchStartup' ):
params.setdefault( 'batch', True )
self.addSwitch( switchName, **params )
info( switchName + ' ' )
info( '\n*** Adding links:\n' )
@@ -448,7 +524,7 @@ class Mininet( object ):
def stopXterms( self ):
"Kill each xterm."
for term in self.terms:
os.kill( term.pid, signal.SIGKILL )
term.send_signal( SIGKILL )
cleanUpScreens()
def staticArp( self ):
@@ -471,6 +547,13 @@ class Mininet( object ):
for switch in self.switches:
info( switch.name + ' ')
switch.start( self.controllers )
started = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches )
started.update( { s: s for s in success } )
info( '\n' )
if self.waitConn:
self.waitConnected()
@@ -485,19 +568,25 @@ class Mininet( object ):
if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms()
info( '*** Stopping %i switches\n' % len( self.switches ) )
for swclass, switches in groupby( sorted( self.switches, key=type ), type ):
if hasattr( swclass, 'batchShutdown' ):
swclass.batchShutdown( switches )
for switch in self.switches:
info( switch.name + ' ' )
switch.stop()
switch.terminate()
info( '\n' )
info( '*** Stopping %i links\n' % len( self.links ) )
for link in self.links:
info( '.' )
link.stop()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {}
for swclass, switches in groupby(
sorted( self.switches, key=type ), type ):
switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches )
stopped.update( { s: s for s in success } )
for switch in self.switches:
info( switch.name + ' ' )
if switch not in stopped:
switch.stop()
switch.terminate()
info( '\n' )
info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( host.name + ' ' )
@@ -521,13 +610,13 @@ class Mininet( object ):
if hosts is None:
hosts = self.hosts
poller = select.poll()
Node = hosts[ 0 ] # so we can call class method fdToNode
h1 = hosts[ 0 ] # so we can call class method fdToNode
for host in hosts:
poller.register( host.stdout )
while True:
ready = poller.poll( timeoutms )
for fd, event in ready:
host = Node.fdToNode( fd )
host = h1.fdToNode( fd )
if event & select.POLLIN:
line = host.readline()
if line is not None:
@@ -545,7 +634,7 @@ class Mininet( object ):
# Check for downed link
if 'connect: Network is unreachable' in pingOutput:
return 1, 0
r = r'(\d+) packets transmitted, (\d+) received'
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -574,7 +663,8 @@ class Mininet( object ):
if timeout:
opts = '-W %s' % timeout
if dest.intfs:
result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
result = node.cmd( 'ping -c1 %s %s' %
(opts, dest.IP()) )
sent, received = self._parsePing( result )
else:
sent, received = 0, 0
@@ -606,7 +696,7 @@ class Mininet( object ):
m = re.search( r, pingOutput )
if m is not None:
return errorTuple
r = r'(\d+) packets transmitted, (\d+) received'
r = r'(\d+) packets transmitted, (\d+)( packets)? received'
m = re.search( r, pingOutput )
if m is None:
error( '*** Error: could not parse ping output: %s\n' %
@@ -699,50 +789,48 @@ class Mininet( object ):
# XXX This should be cleaned up
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None,
seconds=5):
def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
seconds=5, port=5001):
"""Run iperf between two hosts.
hosts: list of hosts; if None, uses opposite hosts
hosts: list of hosts; if None, uses first and last hosts
l4Type: string, one of [ TCP, UDP ]
udpBw: bandwidth target for UDP test
format: iperf format argument if any
fmt: iperf format argument if any
seconds: iperf time to transmit
port: iperf port
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"""
if not quietRun( 'which telnet' ):
error( 'Cannot find telnet in $PATH - required for iperf test' )
return
if not hosts:
hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
else:
assert len( hosts ) == 2
hosts = hosts or [ self.hosts[ 0 ], self.hosts[ -1 ] ]
assert len( hosts ) == 2
client, server = hosts
output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
output( "%s and %s\n" % ( client.name, server.name ) )
output( '*** Iperf: testing', l4Type, 'bandwidth between',
client, 'and', server, '\n' )
server.cmd( 'killall -9 iperf' )
iperfArgs = 'iperf '
iperfArgs = 'iperf -p %d ' % port
bwArgs = ''
if l4Type == 'UDP':
iperfArgs += '-u '
bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if format:
iperfArgs += '-f %s ' %format
server.sendCmd( iperfArgs + '-s', printPid=True )
servout = ''
while server.lastPid is None:
servout += server.monitor()
if fmt:
iperfArgs += '-f %s ' % fmt
server.sendCmd( iperfArgs + '-s' )
if l4Type == 'TCP':
while 'Connected' not in client.cmd(
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
info( 'Waiting for iperf to start up...' )
sleep(.5)
if not waitListening( client, server.IP(), port ):
raise Exception( 'Could not connect to iperf on port %d'
% port )
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
server.IP() + ' ' + bwArgs )
debug( 'Client output: %s\n' % cliout )
servout = ''
# We want the last *b/sec from the iperf server output
# for TCP, there are two of them because of waitListening
count = 2 if l4Type == 'TCP' else 1
while len( re.findall( '/sec', servout ) ) < count:
servout += server.monitor( timeoutms=5000 )
server.sendInt()
servout += server.waitOutput()
debug( 'Server output: %s\n' % servout )
@@ -755,10 +843,9 @@ class Mininet( object ):
def runCpuLimitTest( self, cpu, duration=5 ):
"""run CPU limit test with 'while true' processes.
cpu: desired CPU fraction of each host
duration: test duration in seconds
duration: test duration in seconds (integer)
returns a single list of measured CPU fractions as floats.
"""
cores = int( quietRun( 'nproc' ) )
pct = cpu * 100
info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
hosts = self.hosts
@@ -776,12 +863,14 @@ class Mininet( object ):
# get the initial cpu time for each host
for host in hosts:
outputs[ host ] = []
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r' ) as f:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
host, 'r' ) as f:
time[ host ] = float( f.read() )
for _ in range( 5 ):
for _ in range( duration ):
sleep( 1 )
for host in hosts:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r' ) as f:
with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
host, 'r' ) as f:
readTime = float( f.read() )
outputs[ host ].append( ( ( readTime - time[ host ] )
/ 1000000000 ) / cores * 100 )
@@ -808,10 +897,8 @@ class Mininet( object ):
elif dst not in self.nameToNode:
error( 'dst not in network: %s\n' % dst )
else:
if isinstance( src, basestring ):
src = self.nameToNode[ src ]
if isinstance( dst, basestring ):
dst = self.nameToNode[ dst ]
src = self.nameToNode[ src ]
dst = self.nameToNode[ dst ]
connections = src.connectionsTo( dst )
if len( connections ) == 0:
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
+424 -248
View File
File diff suppressed because it is too large. Load diff
+139 -48
View File
@@ -4,11 +4,14 @@ Node Library for Mininet
This contains additional Node types which you may find to be useful.
"""
from mininet.node import Node, Switch
from mininet.node import Node, Host, Switch
from mininet.log import info, warn
from mininet.moduledeps import pathCheck
from mininet.util import quietRun
import re
from tempfile import NamedTemporaryFile
class LinuxBridge( Switch ):
"Linux Bridge (with optional spanning tree)"
@@ -32,8 +35,8 @@ class LinuxBridge( Switch ):
return 'forwarding' in self.cmd( 'brctl showstp', self )
else:
return True
def start( self, controllers ):
def start( self, _controllers ):
"Start Linux bridge"
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
@@ -46,10 +49,12 @@ class LinuxBridge( Switch ):
self.cmd( 'brctl addif', self, i )
self.cmd( 'ifconfig', self, 'up' )
def stop( self ):
"Stop Linux bridge"
def stop( self, deleteIntfs=True ):
"""Stop Linux bridge
deleteIntfs: delete interfaces? (True)"""
self.cmd( 'ifconfig', self, 'down' )
self.cmd( 'brctl delbr', self )
super( LinuxBridge, self ).stop( deleteIntfs )
def dpctl( self, *args ):
"Run brctl command"
@@ -57,47 +62,57 @@ class LinuxBridge( Switch ):
@classmethod
def setup( cls ):
"Make sure our class dependencies are available"
"Check dependencies and warn about firewalling"
pathCheck( 'brctl', moduleName='bridge-utils' )
# Disable Linux bridge firewalling so that traffic can flow!
for table in 'arp', 'ip', 'ip6':
cmd = 'sysctl net.bridge.bridge-nf-call-%stables' % table
out = quietRun( cmd ).strip()
if out.endswith( '1' ):
warn( 'Warning: Linux bridge may not work with', out, '\n' )
class NAT( Node ):
"""NAT: Provides connectivity to external network"""
"NAT: Provides connectivity to external network"
def __init__( self, name, inetIntf=None, subnet='10.0/8', localIntf=None, **params):
def __init__( self, name, subnet='10.0/8',
localIntf=None, flush=False, **params):
"""Start NAT/forwarding between Mininet and external network
subnet: Mininet subnet (default 10.0/8)
flush: flush iptables before installing NAT rules"""
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 if inetIntf else self.getGatewayIntf()
self.subnet = subnet
self.localIntf = localIntf
self.flush = flush
self.forwardState = self.cmd( 'sysctl -n net.ipv4.ip_forward' ).strip()
def config( self, **params ):
super( NAT, self).config( **params )
"""Configure the NAT and iptables"""
super( NAT, self).config( **params )
if not self.localIntf:
self.localIntf = self.defaultIntf()
self.localIntf = self.defaultIntf()
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
if self.flush:
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
self.cmd( 'iptables -P INPUT ACCEPT' )
self.cmd( 'iptables -P OUTPUT ACCEPT' )
self.cmd( 'iptables -P FORWARD DROP' )
# Flush any currently active rules
# TODO: is this safe?
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Create default entries for unmatched traffic
self.cmd( 'iptables -P INPUT ACCEPT' )
self.cmd( 'iptables -P OUTPUT ACCEPT' )
self.cmd( 'iptables -P FORWARD DROP' )
# Configure NAT
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, '-s', self.subnet, '-j MASQUERADE' )
# Install NAT rules
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',
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -A POSTROUTING',
'-s', self.subnet, "'!'", '-d', self.subnet,
'-j MASQUERADE' )
# Instruct the kernel to perform forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
@@ -116,24 +131,100 @@ class NAT( Node ):
# 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
# TODO: is this safe?
self.cmd( 'iptables -F' )
self.cmd( 'iptables -t nat -F' )
# Instruct the kernel to stop forwarding
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
"Stop NAT/forwarding between Mininet and external network"
# Remote NAT rules
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
self.cmd( 'iptables -D FORWARD',
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -D FORWARD',
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
self.cmd( 'iptables -t nat -D POSTROUTING',
'-s', self.subnet, '\'!\'', '-d', self.subnet,
'-j MASQUERADE' )
# Put the forwarding state back to what it was
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
super( NAT, self ).terminate()
class Server( Host ):
"Run sshd in a net/mnt/pid/uts namespace, with private /etc/hosts"
ns = [ 'net', 'mnt', 'pid', 'uts' ]
overlayDirs = [ '/etc', '/var/run', '/var/log' ]
privateDirs = [ '/var/run/sshd', ]
def __init__( self, *args, **kwargs ):
"""Add overlay dirs and private dirs, and change permissions
ssh: run sshd? (True)"""
kwargs.setdefault( 'inNamespace', True )
kwargs.setdefault( 'ns', self.ns )
kwargs.setdefault( 'privateDirs', self.privateDirs )
kwargs.setdefault( 'overlayDirs', self.overlayDirs )
kwargs.setdefault( 'ssh', True )
super( Server, self ).__init__( *args, **kwargs )
# Change permissions, mainly for ssh
for pdir in self.privateDirs:
self.cmd( 'chown root:root', pdir )
self.cmd( 'chmod 755', pdir )
@staticmethod
def updateHostsFiles( servers, tmpdirs=[ '/tmp', '/var/tmp' ] ):
"""Update local hosts files on a list of servers
servers: list of servers
tmpdir: tmp dir shared between mn and servers"""
# For large configurations it will be more efficient
# to use a DNS server
hosts = ( '# Mininet hosts file\n'
'127.0.0.1 localhost %s\n' +
''.join( '%s %s\n' % ( t.IP(), t )
for t in servers ) )
for s in servers:
dirs = ( getattr( s, 'overlayDirs', [] ) +
getattr( s, 'privateDirs', [] ) )
if '/etc' in dirs:
tmpdirs = [ d for d in tmpdirs if d not in dirs ]
if tmpdirs:
with NamedTemporaryFile( dir=tmpdirs[ 0 ] ) as tmpfile:
tmpfile.write( hosts % s )
tmpfile.flush()
s.cmd( 'cp', tmpfile.name, '/etc/hosts' )
else:
warn( 'not updating hosts file on %s\n' % s )
def service( self, cmd ):
"""Start or stop a service
usage: service( 'ssh stop' )"""
self.cmd( '/etc/init.d/%s' % cmd )
def motd( self ):
"Return login message as a string"
return 'Welcome to Mininet host %s at %s' % ( self, self.IP() )
def startSSH( self, motdPath='/var/run/motd.dynamic' ):
"Update motd, Clear out utmp/wtmp/btmp, and start sshd"
# Note: /var/run and /var/log must be overlays!
assert ( '/var/run' in ( self.overlayDirs + self.privateDirs ) and
'/var/log' in ( self.overlayDirs + self.privateDirs ) )
self.cmd( "echo '%s' > %s" % ( self.motd(), motdPath ) )
self.cmd( 'truncate -s0 /var/run/utmp /var/log/wtmp* /var/log/btmp*' )
# sshd.pid should really be in /var/run/sshd instead of /var/run
self.cmd( 'rm /var/run/sshd.pid' )
self.cmd( '/etc/init.d/ssh start' )
def config( self, **kwargs ):
"""Configure/start sshd and other stuff
ssh: start sshd? (True )"""
super( Server, self ).config( **kwargs )
self.ssh = kwargs.get( 'ssh' )
if self.ssh:
self.startSSH()
if 'uts' in self.ns:
self.cmd( 'hostname', self )
def terminate( self, *args, **kwargs ):
"Shut down services and terminate server"
if self.ssh:
self.service( 'ssh stop' )
super( Server, self ).terminate( *args, **kwargs )
+65 -13
View File
@@ -5,13 +5,43 @@ Utility functions to run a terminal (connected via socat(1)) on each host.
Requires socat(1) and xterm(1).
Optionally uses gnome-terminal.
"""
from os import environ
from mininet.log import error
from mininet.util import quietRun, errRun
def tunnelX11( node, display=None):
from os import environ, getpid, path, setsid
from subprocess import Popen, PIPE, STDOUT
from tempfile import NamedTemporaryFile
def getAuthX11( display ):
"Return X11 credentials for display"
host, screen = display.split( ':' )
host = host.split( '/' )[ 0 ]
hostname = quietRun( 'hostname' ).strip()
# First, try hostname:display
if host == 'localhost':
host = hostname
result = quietRun( 'xauth list %s:%s' % ( host, screen ) )
# Otherwise, try hostname/unix:display
if not result:
result = quietRun( 'xauth list %s/unix:%s' % ( host, screen ) )
items = result.strip().split()
if len( items ) != 3:
raise Exception( "getAuthX11: could not fetch credentials for " +
display )
return items
# This is tricky with uts and pid namespaces
# For uts namespaces, we create and use a private $XAUTHORITY
# and add credentials for the node's hostname.
# To enable pid namespaces to work, we proxy the X11
# socket twice using socat - first with a shared socket in /tmp, and
# second with a TCP listener in the host network namespace.
# Note that this will fail if /tmp is not shared - we should
# probably think about this some more. We could potentially
# specify a globally shared directory somehow if /tmp is
# private.
def tunnelX11( node, display=None ):
"""Create an X11 tunnel from node:6000 to the root host
display: display on root host (optional)
returns: node $DISPLAY, Popen object for tunnel"""
@@ -28,14 +58,33 @@ def tunnelX11( node, display=None):
quietRun( 'xhost +si:localuser:root' )
return display, None
else:
# Create a tunnel for the TCP connection
hostname = quietRun( 'hostname' ).strip()
port = 6000 + int( float( screen ) )
connection = r'TCP\:%s\:%s' % ( host, port )
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
if 'uts' in node.ns and ( hostname in display or
'localhost' in display ):
# Use private xauth file, and add credentials
# for this hostname
if not hasattr( node, 'xauthFile' ):
node.xauthFile = NamedTemporaryFile()
_display, proto, cookie = getAuthX11( display )
creds = '%s %s %s' % ( '%s/unix:%s' % ( node.name, screen ),
proto, cookie )
node.cmd( 'export XAUTHORITY=' + node.xauthFile.name )
node.cmd( 'xauth -f $XAUTHORITY add ' + creds )
# Create a shared unix socket in /tmp
# This can conflict if we are running nested Mininet
# in a pid namespace, and it will also fail if /tmp is not
# shared
socket = '/tmp/mininet.x11.%d' % getpid()
if not hasattr( tunnelX11, 'socket' ):
cmd = ( 'socat unix-listen:%s,fork tcp:localhost:%d' %
( socket, port ) ).split()
tunnelX11.socket = Popen( cmd )
# Create a tunnel for the TCP connection
cmd = 'socat tcp-listen:%d,fork,reuseaddr unix:%s' % ( port, socket )
return 'localhost:' + screen, node.popen( cmd )
def makeTerm( node, title='Node', term='xterm', display=None ):
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
"""Create an X11 tunnel to the node and start up a terminal.
node: Node object
title: base title
@@ -45,8 +94,8 @@ def makeTerm( node, title='Node', term='xterm', display=None ):
if not node.inNamespace:
title += ' (root)'
cmds = {
'xterm': [ 'xterm', '-title', title, '-display' ],
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
'xterm': [ 'xterm', '-title', title ],
'gterm': [ 'gnome-terminal', '--title', title ]
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
@@ -54,7 +103,10 @@ def makeTerm( node, title='Node', term='xterm', display=None ):
display, tunnel = tunnelX11( node, display )
if display is None:
return []
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
env = [ 'env', 'TERM=ansi', 'DISPLAY=%s' % display ]
if hasattr( node, 'xauthFile' ):
env += [ 'XAUTHORITY=%s' % node.xauthFile.name ]
term = node.popen( env + cmds[ term ] + [ '-e', cmd ] )
return [ tunnel, term ] if tunnel else [ term ]
def runX11( node, cmd ):
@@ -67,7 +119,7 @@ def runX11( node, cmd ):
def cleanUpScreens():
"Remove moldy socat X11 tunnels."
errRun( "pkill -9 -f mnexec.*socat" )
errRun( "pkill -9 -f socat.*mininet" )
def makeTerms( nodes, title='Node', term='xterm' ):
"""Create terminals.
+8 -6
View File
@@ -6,7 +6,7 @@ Run all mininet core tests
-quick : skip tests that take more than ~30 seconds
"""
import unittest
from unittest import defaultTestLoader, TextTestRunner
import os
import sys
from mininet.util import ensureRoot
@@ -19,13 +19,15 @@ def runTests( testDir, verbosity=1 ):
ensureRoot()
cleanup()
# discover all tests in testDir
testSuite = unittest.defaultTestLoader.discover( testDir )
testSuite = defaultTestLoader.discover( testDir )
# run tests
unittest.TextTestRunner( verbosity=verbosity ).run( testSuite )
success = ( TextTestRunner( verbosity=verbosity )
.run( testSuite ).wasSuccessful() )
sys.exit( 0 if success else 1 )
if __name__ == '__main__':
setLogLevel( 'warning' )
# get the directory containing example tests
testDir = os.path.dirname( os.path.realpath( __file__ ) )
verbosity = 2 if '-v' in sys.argv else 1
runTests( testDir, verbosity )
thisdir = os.path.dirname( os.path.realpath( __file__ ) )
vlevel = 2 if '-v' in sys.argv else 1
runTests( testDir=thisdir, verbosity=vlevel )
+26 -14
View File
@@ -4,6 +4,7 @@
Test creation and pings for topologies with link and/or CPU options."""
import unittest
import sys
from functools import partial
from mininet.net import Mininet
@@ -13,6 +14,7 @@ from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Number of hosts for each test
N = 2
@@ -38,7 +40,13 @@ class testOptionsTopoCommon( object ):
"""Verify ability to create networks with host and link options
(common code)."""
switchClass = None # overridden in subclasses
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
cleanup()
def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
"Generic topology-with-options test runner."
@@ -79,7 +87,7 @@ class testOptionsTopoCommon( object ):
upperBound, lowerBound ) )
msg += info
self.assertGreaterEqual( float( measured ),lowerBound, msg=msg )
self.assertGreaterEqual( float( measured ), lowerBound, msg=msg )
self.assertLessEqual( float( measured ), upperBound, msg=msg )
def testCPULimits( self ):
@@ -96,7 +104,8 @@ class testOptionsTopoCommon( object ):
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop()
hostUsage = '\n'.join( 'h%s: %s' %
( n + 1, results[ ( n - 1 ) * 5: ( n * 5 ) - 1 ] )
( n + 1,
results[ (n - 1) * 5 : (n * 5) - 1 ] )
for n in range( N ) )
hoptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in hopts.items() )
@@ -106,7 +115,8 @@ class testOptionsTopoCommon( object ):
'hopts = %s\n'
'host = CPULimitedHost\n'
'Switch = %s\n'
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr, self.switchClass ) )
% ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
self.switchClass ) )
for pct in results:
#divide cpu by 100 to convert from percentage to fraction
self.assertWithinTolerance( pct/100, CPU_FRACTION,
@@ -115,7 +125,8 @@ class testOptionsTopoCommon( object ):
def testLinkBandwidth( self ):
"Verify that link bandwidths are accurate within a bound."
if self.switchClass is UserSwitch:
self.skipTest ( 'UserSwitch has very poor performance, so skip for now' )
self.skipTest( 'UserSwitch has very poor performance -'
' skipping for now' )
BW = 5 # Mbps
BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first;
@@ -124,7 +135,7 @@ class testOptionsTopoCommon( object ):
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass,
waitConnected=True )
bw_strs = mn.run( mn.iperf, format='m' )
bw_strs = mn.run( mn.iperf, fmt='m' )
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting link bandwidth limited to %d Mbps per link\n'
@@ -141,7 +152,7 @@ class testOptionsTopoCommon( object ):
# As long as the kernel doesn't wait a long time before
# delivering bytes to the iperf server, its reported data rate
# should be close to the actual receive rate.
serverRate, clientRate = bw_strs
serverRate, _clientRate = bw_strs
bw = float( serverRate.split(' ')[0] )
self.assertWithinTolerance( bw, BW, BW_TOLERANCE, msg )
@@ -160,12 +171,12 @@ class testOptionsTopoCommon( object ):
mn.stop()
test_outputs = ping_delays[0]
# Ignore unused variables below
# pylint: disable-msg=W0612
# pylint: disable=W0612
node, dest, ping_outputs = test_outputs
sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
pingFailMsg = 'sent %s pings, only received %s' % ( sent, received )
self.assertEqual( sent, received, msg=pingFailMsg )
# pylint: enable-msg=W0612
# pylint: enable=W0612
loptsStr = ', '.join( '%s: %s' % ( opt, value )
for opt, value in lopts.items() )
msg = ( '\nTesting Link Delay of %s ms\n'
@@ -181,10 +192,9 @@ class testOptionsTopoCommon( object ):
for rttval in [rttmin, rttavg, rttmax]:
# Multiply delay by 4 to cover there & back on two links
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
DELAY_TOLERANCE, msg )
def testLinkLoss( self ):
"Verify that we see packet drops with a high configured loss rate."
LOSS_PERCENT = 99
@@ -212,7 +222,8 @@ class testOptionsTopoCommon( object ):
'lopts = %s\n'
'host = default\n'
'switch = %s\n'
% ( LOSS_PERCENT, dropped_total, N, loptsStr, self.switchClass ) )
% ( LOSS_PERCENT, dropped_total, N, loptsStr,
self.switchClass ) )
self.assertGreater( dropped_total, 0, msg )
@@ -245,9 +256,10 @@ class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
'Reference user switch is not installed' )
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
"Verify ability to create networks with host and link options (UserSwitch)."
"""Verify ability to create networks with host and link options
(UserSwitch)."""
longMessage = True
switchClass = UserSwitch
+13 -4
View File
@@ -4,6 +4,7 @@
Test creation and all-pairs ping for each included mininet topo type."""
import unittest
import sys
from functools import partial
from mininet.net import Mininet
@@ -12,6 +13,7 @@ from mininet.node import UserSwitch, OVSSwitch, IVSSwitch
from mininet.topo import SingleSwitchTopo, LinearTopo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
# Tell pylint not to complain about calls to other class
# pylint: disable=E1101
@@ -19,7 +21,13 @@ from mininet.util import quietRun
class testSingleSwitchCommon( object ):
"Test ping with single switch topology (common code)."
switchClass = None # overridden in subclasses
switchClass = None # overridden in subclasses
@staticmethod
def tearDown():
"Clean up if necessary"
if sys.exc_info != ( None, None, None ):
cleanup()
def testMinimal( self ):
"Ping test on minimal topology"
@@ -51,7 +59,7 @@ class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ):
switchClass = IVSSwitch
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
'Reference user switch is not installed' )
class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
"Test ping with single switch topology (Userspace switch)."
switchClass = UserSwitch
@@ -63,11 +71,12 @@ class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ):
class testLinearCommon( object ):
"Test all-pairs ping with LinearNet (common code)."
switchClass = None # overridden in subclasses
switchClass = None # overridden in subclasses
def testLinear5( self ):
"Ping test on a 5-switch topology"
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller, waitConnected=True )
mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host,
Controller, waitConnected=True )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
+53 -45
View File
@@ -4,40 +4,58 @@
Regression tests for switch dpid assignment."""
import unittest
from functools import partial
import re
import sys
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import UserSwitch, OVSSwitch, OVSLegacyKernelSwitch, IVSSwitch
from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
from mininet.topo import Topo
from mininet.log import setLogLevel
from mininet.util import quietRun
from mininet.clean import cleanup
class testSwitchDpidAssignmentCommon ( object ):
"""Verify Switch dpid assignment."""
class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
"Verify Switch dpid assignment."
switchClass = None # overridden in subclasses
switchClass = OVSSwitch # overridden in subclasses
def testDefaultDpid ( self ):
def tearDown( self ):
"Clean up if necessary"
# satisfy pylint
assert self
if sys.exc_info != ( None, None, None ):
cleanup()
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' )
switch = Mininet( Topo(),
self.switchClass,
Host, Controller ).addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid )
def dpidFrom( self, num ):
"Compute default dpid from number"
fmt = ( '%0' + str( self.switchClass.dpidLen ) + 'x' )
return fmt % num
def testActualDpidAssignment( self ):
"""Verify that Switch dpid is the actual dpid assigned if dpid is
"""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' )
dpid = self.dpidFrom( 0xABCD )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch(
's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid )
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
"""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' )
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.')
@@ -47,43 +65,33 @@ class testSwitchDpidAssignmentCommon ( object ):
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) )
switch = Mininet( Topo(), self.switchClass,
Host, Controller ).addSwitch( 's123' )
class testSwitchOVSKernel( testSwitchDpidAssignmentCommon, unittest.TestCase ):
"""Test dpid assignnment of OVS Kernel Switch."""
switchClass = OVSSwitch
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
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."""
class OVSUser( OVSSwitch):
"OVS User Switch convenience class"
def __init__( self, *args, **kwargs ):
kwargs.update( datapath='user' )
OVSSwitch.__init__( self, *args, **kwargs )
class testSwitchOVSUser( TestSwitchDpidAssignmentOVS ):
"Test dpid assignnment of OVS User Switch."
switchClass = OVSUser
@unittest.skipUnless( quietRun( 'which ivs-ctl' ),
'IVS switch is not installed' )
class testSwitchIVS( TestSwitchDpidAssignmentOVS ):
"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."""
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
'Reference user switch is not installed' )
class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
"Test dpid assignment of Userspace switch."
switchClass = UserSwitch
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
+55 -24
View File
@@ -12,13 +12,19 @@ import os
import re
from mininet.util import quietRun
from distutils.version import StrictVersion
from time import sleep
def tsharkVersion():
"Return tshark version"
versionStr = quietRun( 'tshark -v' )
versionMatch = re.findall( 'TShark \d+.\d+.\d+', versionStr )[0]
return versionMatch.split()[ 1 ]
versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
return versionMatch[ 0 ]
# pylint doesn't understand pexpect.match, unfortunately!
# pylint:disable=maybe-no-member
class testWalkthrough( unittest.TestCase ):
"Test Mininet walkthrough"
prompt = 'mininet>'
@@ -31,6 +37,8 @@ class testWalkthrough( unittest.TestCase ):
def testWireshark( self ):
"Use tshark to test the of dissector"
# Satisfy pylint
assert self
if StrictVersion( tsharkVersion() ) < StrictVersion( '1.12.0' ):
tshark = pexpect.spawn( 'tshark -i lo -R of' )
else:
@@ -40,6 +48,8 @@ class testWalkthrough( unittest.TestCase ):
mn.expect( '0% dropped' )
tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
tshark.sendintr()
mn.expect( pexpect.EOF )
tshark.expect( pexpect.EOF )
def testBasic( self ):
"Test basic CLI commands (help, nodes, net, dump)"
@@ -51,7 +61,7 @@ class testWalkthrough( unittest.TestCase ):
self.assertEqual( index, 0, 'No output for "help" command')
# nodes command
p.sendline( 'nodes' )
p.expect( '([chs]\d ?){4}' )
p.expect( r'([chs]\d ?){4}' )
nodes = p.match.group( 0 ).split()
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
p.expect( self.prompt )
@@ -67,14 +77,15 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt )
# dump command
p.sendline( 'dump' )
expected = [ '<\w+ (%s)' % n for n in nodes ]
expected = [ r'<\w+ (%s)' % n for n in nodes ]
actual = []
for _ in nodes:
index = p.expect( expected )
node = p.match.group( 1 )
actual.append( node )
p.expect( '\n' )
self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' )
self.assertEqual( actual.sort(), nodes.sort(),
'"nodes" and "dump" differ' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
@@ -83,7 +94,9 @@ class testWalkthrough( unittest.TestCase ):
"Test ifconfig and ps on h1 and s1"
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
# Third pattern is a local interface beginning with 'eth' or 'en'
interfaces = [ 'h1-eth0', 's1-eth1', r'[^-](eth|en)\w*\d', 'lo',
self.prompt ]
# h1 ifconfig
p.sendline( 'h1 ifconfig -a' )
ifcount = 0
@@ -109,7 +122,7 @@ class testWalkthrough( unittest.TestCase ):
ifcount += 1
else:
break
self.assertEqual( ifcount, 3, 'Missing interfaces on s1')
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
# h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
@@ -118,10 +131,13 @@ class testWalkthrough( unittest.TestCase ):
p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt )
s1Output = p.before
# strip command from ps output
h1Output = h1Output.split( '\n', 1 )[ 1 ]
s1Output = s1Output.split( '\n', 1 )[ 1 ]
self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs')
# strip command from ps output and compute diffs
h1Output = h1Output.split( '\n' )[ 1: ]
s1Output = s1Output.split( '\n' )[ 1: ]
diffs = set( h1Output ).difference( set( s1Output ) )
# allow up to two diffs to account for daemons, etc.
self.assertTrue( len( diffs ) <= 2,
'h1 and s1 "ps" output differ too much: %s' % diffs )
p.sendline( 'exit' )
p.wait()
@@ -143,6 +159,11 @@ class testWalkthrough( unittest.TestCase ):
p = pexpect.spawn( 'mn' )
p.expect( self.prompt )
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
# The walkthrough doesn't specify a delay here, and
# we also don't read the output (also a possible problem),
# but for now let's wait a couple of seconds to make
# it less likely to fail due to the race condition.
sleep( 2 )
p.expect( self.prompt )
p.sendline( ' h2 wget -O - h1' )
p.expect( '200 OK' )
@@ -161,7 +182,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( pexpect.EOF )
# test iperf
p = pexpect.spawn( 'mn --test iperf' )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
@@ -170,7 +191,7 @@ class testWalkthrough( unittest.TestCase ):
"Test pingall on single,3 and linear,4 topos"
# testing single,3
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
p.expect( '(\d+)/(\d+) received')
p.expect( r'(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
@@ -178,7 +199,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( pexpect.EOF )
# testing linear,4
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
p.expect( '(\d+)/(\d+) received')
p.expect( r'(\d+)/(\d+) received')
received = int( p.match.group( 1 ) )
sent = int( p.match.group( 2 ) )
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
@@ -191,14 +212,15 @@ class testWalkthrough( unittest.TestCase ):
# test bw
p.expect( self.prompt )
p.sendline( 'iperf' )
p.expect( "Results: \['([\d\.]+) Mbits/sec'," )
p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s')
self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s')
self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
p.expect( self.prompt )
# test delay
p.sendline( 'h1 ping -c 4 h2' )
p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
p.expect( r'rtt min/avg/max/mdev = '
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay > 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' )
@@ -222,10 +244,13 @@ class testWalkthrough( unittest.TestCase ):
def testCustomTopo( self ):
"Start Mininet using a custom topo, then run pingall"
# Satisfy pylint
assert self
custom = os.path.dirname( os.path.realpath( __file__ ) )
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
custom = os.path.normpath( custom )
p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom )
p = pexpect.spawn(
'mn --custom %s --topo mytopo --test pingall' % custom )
p.expect( '0% dropped' )
p.expect( pexpect.EOF )
@@ -237,13 +262,15 @@ class testWalkthrough( unittest.TestCase ):
p.sendline( 'h%d ifconfig' % i )
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
p.expect( self.prompt )
p.sendline( 'exit' )
p.expect( pexpect.EOF )
def testSwitches( self ):
"Run iperf test using user and ovsk switches"
switches = [ 'user', 'ovsk' ]
for sw in switches:
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
p.expect( "Results: \['([\d\.]+) .bits/sec'," )
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
bw = float( p.match.group( 1 ) )
self.assertTrue( bw > 0 )
p.expect( pexpect.EOF )
@@ -251,7 +278,7 @@ class testWalkthrough( unittest.TestCase ):
def testBenchmark( self ):
"Run benchmark and verify that it takes less than 2 seconds"
p = pexpect.spawn( 'mn --test none' )
p.expect( 'completed in ([\d\.]+) seconds' )
p.expect( r'completed in ([\d\.]+) seconds' )
time = float( p.match.group( 1 ) )
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
@@ -275,7 +302,7 @@ class testWalkthrough( unittest.TestCase ):
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
# verify that all hosts a reachable
p.sendline( 'pingall' )
p.expect( '(\d+)% dropped' )
p.expect( r'(\d+)% dropped' )
dropped = int( p.match.group( 1 ) )
self.assertEqual( dropped, 0, 'pingall failed')
p.expect( self.prompt )
@@ -327,11 +354,15 @@ class testWalkthrough( unittest.TestCase ):
'Github is not reachable; cannot download Pox' )
def testRemoteController( self ):
"Test Mininet using Pox controller"
# Satisfy pylint
assert self
if not os.path.exists( '/tmp/pox' ):
p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p = pexpect.spawn(
'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
p.expect( pexpect.EOF )
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net = pexpect.spawn(
'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
net.expect( '0% dropped' )
net.expect( pexpect.EOF )
pox.sendintr()
+22 -10
View File
@@ -78,7 +78,6 @@ class MultiGraph( object ):
"Return list of graph edges"
return list( self.edges_iter( data=data, keys=keys ) )
def __getitem__( self, node ):
"Return link dict for given src node"
return self.edge[ node ]
@@ -101,7 +100,7 @@ class Topo( object ):
"Data center network representation for structured multi-trees."
def __init__( self, *args, **params ):
"""Topo object.
"""Topo object.
Optional named parameters:
hinfo: default host options
sopts: default switch options
@@ -111,7 +110,8 @@ class Topo( object ):
self.hopts = params.pop( 'hopts', {} )
self.sopts = params.pop( 'sopts', {} )
self.lopts = params.pop( 'lopts', {} )
self.ports = {} # ports[src][dst][sport] is port on dst that connects to src
# ports[src][dst][sport] is port on dst that connects to src
self.ports = {}
self.build( *args, **params )
def build( self, *args, **params ):
@@ -146,7 +146,7 @@ class Topo( object ):
return result
def addLink( self, node1, node2, port1=None, port2=None,
key=None, **opts ):
key=None, **opts ):
"""node1, node2: nodes to link together
port1, port2: ports (optional)
opts: link options (optional)
@@ -187,7 +187,7 @@ class Topo( object ):
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 ):
for _src, _dst, key, info in self.g.edges_iter( data=True, keys=True ):
node1, node2 = info[ 'node1' ], info[ 'node2' ]
if withKeys:
if withInfo:
@@ -207,7 +207,7 @@ class Topo( object ):
withInfo: return link info
returns: list of ( src, dst [,key, info ] )"""
links = list( self.iterLinks( withKeys, withInfo ) )
if not sorted:
if not sort:
return links
# Ignore info when sorting
tupleSize = 3 if withKeys else 2
@@ -256,7 +256,7 @@ class Topo( object ):
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 )
@@ -287,10 +287,13 @@ class Topo( object ):
return sorted( items, key=natural )
# Our idiom defines additional parameters in build(param...)
# pylint: disable=arguments-differ
class SingleSwitchTopo( Topo ):
"Single switch connected to k hosts."
def build( self, k=2, **opts ):
def build( self, k=2, **_opts ):
"k: number of hosts"
self.k = k
switch = self.addSwitch( 's1' )
@@ -302,7 +305,8 @@ class SingleSwitchTopo( Topo ):
class SingleSwitchReversedTopo( Topo ):
"""Single switch connected to k hosts, with reversed ports.
The lowest-numbered host is connected to the highest-numbered port.
Useful to verify that Mininet properly handles custom port numberings."""
Useful to verify that Mininet properly handles custom port
numberings."""
def build( self, k=2 ):
"k: number of hosts"
@@ -314,10 +318,16 @@ class SingleSwitchReversedTopo( Topo ):
port1=0, port2=( k - h + 1 ) )
class MinimalTopo( SingleSwitchTopo ):
"Minimal topology with two hosts and one switch"
def build( self ):
return SingleSwitchTopo.build( self, k=2 )
class LinearTopo( Topo ):
"Linear topology of k switches, with n hosts per switch."
def build( self, k=2, n=1, **opts):
def build( self, k=2, n=1, **_opts):
"""k: number of switches
n: number of hosts per switch"""
self.k = k
@@ -340,3 +350,5 @@ class LinearTopo( Topo ):
if lastSwitch:
self.addLink( switch, lastSwitch )
lastSwitch = switch
# pylint: enable=arguments-differ
+49 -13
View File
@@ -3,6 +3,9 @@
from mininet.topo import Topo
from mininet.net import Mininet
# The build() method is expected to do this:
# pylint: disable=arguments-differ
class TreeTopo( Topo ):
"Topology for a tree network with a given depth and fanout."
@@ -41,11 +44,20 @@ class TorusTopo( Topo ):
with the default controller or any Ethernet bridge
without STP turned on! It can be used with STP, e.g.:
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
def build( self, x, y ):
def build( self, x, y, n=1, wrap=True ):
"""x: number of switches per row
y: number of rows
n: number of hosts per switch
wrap: torus rather than grid? (True)"""
if x < 3 or y < 3:
raise Exception( 'Please use 3x3 or greater for compatibility '
'with 2.1' )
'with 2.1' )
if n == 1:
genHostName = lambda loc, k: 'h%s' % ( loc )
else:
genHostName = lambda loc, k: 'h%sx%d' % ( loc, k )
hosts, switches, dpid = {}, {}, 0
# Create and wire interior
for i in range( 0, x ):
@@ -53,17 +65,41 @@ class TorusTopo( Topo ):
loc = '%dx%d' % ( i + 1, j + 1 )
# dpid cannot be zero for OVS
dpid = ( i + 1 ) * 256 + ( j + 1 )
switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%016x' % dpid )
host = hosts[ i, j ] = self.addHost( 'h' + loc )
self.addLink( host, switch )
switch = switches[ i, j ] = self.addSwitch(
's' + loc, dpid='%x' % dpid )
for k in range( 0, n ):
host = hosts[ i, j, k ] = self.addHost(
genHostName( loc, k + 1 ) )
self.addLink( host, switch )
# Connect switches
for i in range( 0, x ):
for j in range( 0, y ):
sw1 = switches[ i, j ]
sw2 = switches[ i, ( j + 1 ) % y ]
sw3 = switches[ ( i + 1 ) % x, j ]
self.addLink( sw1, sw2 )
self.addLink( sw1, sw3 )
for j in range( 0, y):
sw = switches[ i, j ]
right = switches[ ( i + 1 ) % x, j ]
down = switches[ i, ( j + 1 ) % y ]
if wrap or i + 1 < x:
self.addLink( sw, right )
if wrap or j + 1 < y:
self.addLink( sw, down )
class GridTopo( TorusTopo ):
"""2-D Grid topology
WARNING: this topology has LOOPS and WILL NOT WORK
with the default controller or any Ethernet bridge
without STP turned on! It can be used with STP, e.g.:
# mn --topo grid,3,3 --switch lxbr,stp=1 --test pingall"""
def build( self, x, y, n=1, wrap=False ):
"""x: number of switches per row
y: number of rows
n: number of hosts per switch
wrap: torus rather than grid (False)"""
super( GridTopo, self ).build( x, y, n, wrap )
topos = { 'tree': TreeTopo,
'torus': TorusTopo,
'grid': GridTopo }
# pylint: enable=arguments-differ
+159 -84
View File
@@ -1,5 +1,6 @@
"Utility functions for Mininet."
from mininet.log import output, info, error, warn, debug
from time import sleep
@@ -25,7 +26,7 @@ def checkRun( cmd ):
return check_call( cmd.split( ' ' ) )
# pylint doesn't understand explicit type checking
# pylint: disable-msg=E1103
# pylint: disable=maybe-no-member
def oldQuietRun( *cmd ):
"""Run a command, routing stderr to stdout, and return the output.
@@ -56,6 +57,7 @@ def oldQuietRun( *cmd ):
# This is a bit complicated, but it enables us to
# monitor command output as it is happening
# pylint: disable=too-many-branches
def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code
cmd: string or list of command and args
@@ -77,6 +79,7 @@ def errRun( *cmd, **kwargs ):
cmd = [ str( arg ) for arg in cmd ]
elif isinstance( cmd, list ) and shell:
cmd = " ".join( arg for arg in cmd )
debug( '*** errRun:', cmd, '\n' )
popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
# We use poll() because select() doesn't work with large fd numbers,
# and thus communicate() doesn't work either
@@ -91,21 +94,31 @@ def errRun( *cmd, **kwargs ):
errDone = False
while not outDone or not errDone:
readable = poller.poll()
for fd, _event in readable:
for fd, event in readable:
f = fdtofile[ fd ]
data = f.read( 1024 )
if echo:
output( data )
if f == popen.stdout:
out += data
if data == '':
if event & POLLIN:
data = f.read( 1024 )
if echo:
output( data )
if f == popen.stdout:
out += data
if data == '':
outDone = True
elif f == popen.stderr:
err += data
if data == '':
errDone = True
else: # POLLHUP or something unexpected
if f == popen.stdout:
outDone = True
elif f == popen.stderr:
err += data
if data == '':
elif f == popen.stderr:
errDone = True
poller.unregister( fd )
returncode = popen.wait()
debug( out, err, returncode )
return out, err, returncode
# pylint: enable=too-many-branches
def errFail( *cmd, **kwargs ):
"Run a command using errRun and raise exception on nonzero exit"
@@ -119,8 +132,7 @@ def quietRun( cmd, **kwargs ):
"Run a command and return merged stdout and stderr"
return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
# pylint: enable-msg=E1103
# pylint: disable-msg=E1101
# pylint: enable=maybe-no-member
def isShellBuiltin( cmd ):
"Return True if cmd is a bash builtin."
@@ -133,8 +145,6 @@ def isShellBuiltin( cmd ):
isShellBuiltin.builtIns = None
# pylint: enable-msg=E1101
# Interface management
#
# Interfaces are managed as strings which are simply the
@@ -148,27 +158,42 @@ isShellBuiltin.builtIns = None
# live in the root namespace and thus do not have to be
# explicitly moved.
def makeIntfPair( intf1, intf2, addr1=None, addr2=None, run=quietRun ):
"""Make a veth pair connecting intf1 and intf2.
intf1: string, interface
intf2: string, interface
node: node to run on or None (default)
returns: ip link add result"""
# Delete any old interfaces with the same names
run( 'ip link del ' + intf1 )
run( 'ip link del ' + intf2 )
def makeIntfPair( intf1, intf2, addr1=None, addr2=None, node1=None, node2=None,
deleteIntfs=True, runCmd=None ):
"""Make a veth pair connnecting new interfaces intf1 and intf2
intf1: name for interface 1
intf2: name for interface 2
addr1: MAC address for interface 1 (optional)
addr2: MAC address for interface 2 (optional)
node1: home node for interface 1 (optional)
node2: home node for interface 2 (optional)
deleteIntfs: delete intfs before creating them
runCmd: function to run shell commands (quietRun)
raises Exception on failure"""
if not runCmd:
runCmd = quietRun if not node1 else node1.cmd
runCmd2 = quietRun if not node2 else node2.cmd
if deleteIntfs:
# Delete any old interfaces with the same names
runCmd( 'ip link del ' + intf1 )
runCmd2( 'ip link del ' + intf2 )
# Create new pair
ns1 = 1 if not node1 else node1.pid
ns2 = 1 if not node2 else node2.pid
if addr1 is None and addr2 is None:
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
cmdOutput = runCmd( 'ip link add name %s netns %s '
'type veth peer name %s '
'netns %s' % ( intf1, ns1, intf2, ns2 ) )
else:
cmd = ( 'ip link add name ' + intf1 + ' address ' + addr1 +
' type veth peer name ' + intf2 + ' address ' + addr2 )
cmdOutput = run( cmd )
if cmdOutput == '':
return True
else:
error( "Error creating interface pair: %s " % cmdOutput )
return False
cmdOutput = runCmd( 'ip link add name %s '
'address %s netns %s '
'type veth peer name %s '
'address %s '
'netns %s' %
( intf1, addr1, ns1, intf2, addr2, ns2 ) )
if cmdOutput:
raise Exception( "Error creating interface pair (%s,%s): %s " %
( intf1, intf2, cmdOutput ) )
def retry( retries, delaySecs, fn, *args, **keywords ):
"""Try something several times before giving up.
@@ -202,8 +227,8 @@ def moveIntfNoRetry( intf, dstNode, printError=False ):
return False
return True
def moveIntf( intf, dstNode, srcNode=None, printError=True,
retries=3, delaySecs=0.001 ):
def moveIntf( intf, dstNode, printError=True,
retries=3, delaySecs=0.001 ):
"""Move interface to node, retrying on failure.
intf: string, interface
dstNode: destination Node
@@ -296,8 +321,8 @@ def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
def ipParse( ip ):
"Parse an IP address and return an unsigned int."
args = [ int( arg ) for arg in ip.split( '.' ) ]
while ( len(args) < 4 ):
args.append( 0 )
while len(args) < 4:
args.insert( len(args) - 1, 0 )
return ipNum( *args )
def netParse( ipstr ):
@@ -427,9 +452,12 @@ def fixLimits():
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
#Increase number of PTYs for nodes
sysctlTestAndSet( 'kernel.pty.max', 20000 )
except:
# pylint: disable=broad-except
except Exception:
warn( "*** Error setting resource limits. "
"Mininet's performance may be affected.\n" )
# pylint: enable=broad-except
def mountCgroups():
"Make sure cgroups file system is mounted"
@@ -481,47 +509,91 @@ def custom( cls, **params ):
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
return customized
def splitArgs( argstr ):
"""Split argument string into usable python arguments
argstr: argument string with format fn,arg2,kw1=arg3...
returns: fn, args, kwargs"""
split = argstr.split( ',' )
fn = split[ 0 ]
params = split[ 1: ]
def parseArgs( argstr ):
"""Parse argument string
returns args, kwargs"""
# One step at a time: support param=[a,b,c]
paramre = r'\[[^\]]*\]|[^,\[\]]+'
paramre = r'\w+=\[[^\]]*\]|\w+\[^,\[\]]+' + paramre
params = re.findall( paramre, argstr )
# Parse lists
for i, arg in enumerate( params ):
if arg.startswith( '[' ) and arg.endswith( ']' ):
arg = arg[ 1 : -1 ]
print "recurse on", arg
params[ i ] = parseArgs( args )
# Convert int and float args; removes the need for function
# to be flexible with input arg formats.
args = [ makeNumeric( s ) for s in params if '=' not in s ]
kwargs = {}
for s in [ p for p in params if '=' in p ]:
key, val = s.split( '=', 1 )
kwargs[ key ] = makeNumeric( val )
return fn, args, kwargs
key, arg = s.split( '=', 1 )
if arg.startswith( '[' ) and arg.endswith( ']' ):
arg = arg[ 1 : -1 ]
print 'recurse on', arg
arg = parseArgs( arg )[ 0 ]
else:
arg = makeNumeric( arg )
kwargs[ key ] = arg
return args, kwargs
def customConstructor( constructors, argStr ):
"""Return custom constructor based on argStr
The args and key/val pairs in argsStr will be automatically applied
when the generated constructor is later used.
def splitArgs( argstr ):
"""Split argument string into usable python arguments
argstr: argument string with format fn,arg2,kw1=arg3...
returns: fn, args, kwargs"""
args, kwargs = parseArgs( argstr )
return args[ 0 ], args[ 1: ], kwargs
def customClass( classes, argStr ):
"""Return customized class based on argStr
The args and key/val pairs in argStr will be automatically applied
when the generated class is later used.
"""
cname, newargs, kwargs = splitArgs( argStr )
constructor = constructors.get( cname, None )
if not constructor:
cname, args, kwargs = splitArgs( argStr )
cls = classes.get( cname, None )
if not cls:
raise Exception( "error: %s is unknown - please specify one of %s" %
( cname, constructors.keys() ) )
( cname, classes.keys() ) )
if not args and not kwargs:
return cls
def customized( name, *args, **params ):
"Customized constructor, useful for Node, Link, and other classes"
params = params.copy()
params.update( kwargs )
if not newargs:
return constructor( name, *args, **params )
if args:
warn( 'warning: %s replacing %s with %s\n' % (
constructor, args, newargs ) )
return constructor( name, *newargs, **params )
return specialClass( cls, append=args, defaults=kwargs )
def specialClass( cls, prepend=None, append=None,
defaults=None, override=None ):
"""Like functools.partial, but it returns a class
prepend: arguments to prepend to argument list
append: arguments to append to argument list
defaults: default values for keyword arguments
override: keyword arguments to override"""
if prepend is None:
prepend = []
if append is None:
append = []
if defaults is None:
defaults = {}
if override is None:
override = {}
class CustomClass( cls ):
"Customized subclass with preset args/params"
def __init__( self, *args, **params ):
newparams = defaults.copy()
newparams.update( params )
newparams.update( override )
cls.__init__( self, *( list( prepend ) + list( args ) +
list( append ) ),
**newparams )
CustomClass.__name__ = '%s%s' % ( cls.__name__, defaults )
return CustomClass
customized.__name__ = 'customConstructor(%s)' % argStr
return customized
def buildTopo( topos, topoStr ):
"""Create topology from string with format (object, arg1, arg2,...).
@@ -538,30 +610,33 @@ def ensureRoot():
Probably we should only sudo when needed as per Big Switch's patch.
"""
if os.getuid() != 0:
print "*** Mininet must run as root."
error( '*** Mininet must run as root.\n' )
exit( 1 )
return
def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
"""Wait until server is listening on port.
returns True if server is listening"""
run = ( client.cmd if client else
partial( quietRun, shell=True ) )
if not run( 'which telnet' ):
runCmd = ( client.cmd if client else
partial( quietRun, shell=True ) )
if not runCmd( 'which telnet' ):
raise Exception('Could not find telnet' )
# pylint: disable=maybe-no-member
serverIP = server if isinstance( server, basestring ) else server.IP()
cmd = ( 'sh -c "echo A | telnet -e A %s %s"' %
( serverIP, port ) )
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
time = 0
while 'Connected' not in run( cmd ):
if timeout:
print time
if time >= timeout:
error( 'could not connect to %s on port %d\n'
% ( server, port ) )
return False
output('waiting for', server,
'to listen on port', port, '\n')
result = runCmd( cmd )
while 'Connected' not in result:
if 'No route' in result:
rtable = runCmd( 'route' )
error( 'no route to %s:\n%s' % ( server, rtable ) )
return False
if timeout and time >= timeout:
error( 'could not connect to %s on port %d\n' % ( server, port ) )
return False
debug( 'waiting for', server, 'to listen on port', port, '\n' )
info( '.' )
sleep( .5 )
time += .5
result = runCmd( cmd )
return True
+198 -100
View File
@@ -5,9 +5,9 @@
*
* - closing all file descriptors except stdin/out/error
* - detaching from a controlling tty using setsid
* - running in network and mount namespaces
* - running in network and other namespaces
* - printing out the pid of a process so we can identify it later
* - attaching to a namespace and cgroup
* - attaching to namespace(s) and cgroup
* - setting RT scheduling
*
* Partially based on public domain setsid(1)
@@ -24,21 +24,29 @@
#include <sched.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#if !defined(VERSION)
#define VERSION "(devel)"
#endif
void usage(char *name)
{
printf("Execution utility for Mininet\n\n"
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Usage: %s [-cdmnPpu] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"Options:\n"
" -c: close all file descriptors except stdin/out/error\n"
" -d: detach from tty by calling setsid()\n"
" -n: run in new network and mount namespaces\n"
" -m: run in a new mount namespace\n"
" -n: run in a new network namespace\n"
" -P: run in a new pid namespace (implies -m)\n"
" -u: run in a new UTS (ipc, hostname) namespace\n"
" -p: print ^A + pid\n"
" -a pid: attach to pid's network and mount namespaces\n"
" -a pid: attach to pid's namespaces\n"
" -g group: add to cgroup\n"
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
" -v: print version\n",
@@ -46,10 +54,13 @@ void usage(char *name)
}
#if !defined(setns)
int setns(int fd, int nstype)
{
return syscall(__NR_setns, fd, nstype);
}
#endif
/* Validate alphanumeric path foo1/bar2/baz */
void validate(char *path)
@@ -63,6 +74,7 @@ void validate(char *path)
}
}
/* Add our pid to cgroup */
void cgroup(char *gname)
{
@@ -92,107 +104,193 @@ void cgroup(char *gname)
}
}
/* Attach to ns 'name' if present; return 1 if pidns */
int attachns( pid_t pid, char *name ) {
char path[PATH_MAX];
int nsid, err;
int pidns = 0;
snprintf(path, PATH_MAX, "/proc/%d/ns/%s", pid, name) ;
if ((nsid = open(path, O_RDONLY)) < 0)
return nsid;
if (strcmp(name, "pid") == 0) {
struct stat buf1, buf2;
int stat1 = stat(path, &buf1);
int stat2 = stat("/proc/self/ns/pid", &buf2);
/* Don't reattach to the same pid ns */
if (stat1 == 0 && stat2 == 0 &&
buf1.st_dev == buf2.st_dev &&
buf1.st_ino == buf2.st_ino)
return 0;
pidns = 1;
}
if ((err = setns(nsid, 0)) < 0) {
perror("setns");
return err;
}
return pidns;
}
/* Attach to pid's namespaces - returns 1 if pidns */
int attach(int pid) {
char *cwd = get_current_dir_name();
char path[PATH_MAX];
int pidns = 0;
attachns(pid, "net");
attachns(pid, "uts");
if (attachns(pid, "pid") == 1)
pidns = 1;
if (attachns(pid, "mnt") != 0) {
/* Plan B: chroot into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
}
}
/* chdir to correct working directory */
if (chdir(cwd) != 0)
perror(cwd);
return pidns;
}
int main(int argc, char *argv[])
{
int c;
int fd;
char path[PATH_MAX];
int nsid;
int pid;
char *cwd = get_current_dir_name();
static struct sched_param sp;
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
/* Argument flags */
int flags = 0;
int closefds = 0;
int attachpid = 0;
char *cgrouparg = NULL;
int detachtty = 0;
int printpid = 0;
int rtprio = 0;
int pidns = 0;
int dofork = 0;
while ((c = getopt(argc, argv, "+cdmnPpa:g:r:uvh")) != -1)
switch(c) {
case 'c':
/* close file descriptors except stdin/out/error */
for (fd = getdtablesize(); fd > 2; fd--)
close(fd);
break;
case 'd':
/* detach from tty */
if (getpgrp() == getpid()) {
switch(fork()) {
case -1:
perror("fork");
return 1;
case 0: /* child */
break;
default: /* parent */
return 0;
}
}
setsid();
break;
case 'n':
/* run in network and mount namespaces */
if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
perror("unshare");
return 1;
}
/* mount sysfs to pick up the new network namespace */
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
perror("mount");
return 1;
}
break;
case 'p':
/* print pid */
printf("\001%d\n", getpid());
fflush(stdout);
break;
case 'a':
/* Attach to pid's network namespace and mount namespace */
pid = atoi(optarg);
sprintf(path, "/proc/%d/ns/net", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0) {
perror(path);
return 1;
}
if (setns(nsid, 0) != 0) {
perror("setns");
return 1;
}
/* Plan A: call setns() to attach to mount namespace */
sprintf(path, "/proc/%d/ns/mnt", pid);
nsid = open(path, O_RDONLY);
if (nsid < 0 || setns(nsid, 0) != 0) {
/* Plan B: chroot/chdir into pid's root file system */
sprintf(path, "/proc/%d/root", pid);
if (chroot(path) < 0) {
perror(path);
return 1;
}
}
/* chdir to correct working directory */
if (chdir(cwd) != 0) {
perror(cwd);
return 1;
}
break;
case 'g':
/* Attach to cgroup */
cgroup(optarg);
break;
case 'r':
/* Set RT scheduling priority */
sp.sched_priority = atoi(optarg);
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
perror("sched_setscheduler");
return 1;
}
break;
case 'v':
printf("%s\n", VERSION);
exit(0);
case 'h':
usage(argv[0]);
exit(0);
default:
usage(argv[0]);
exit(1);
case 'c': closefds = 1; break;
case 'd': detachtty = 1; break;
case 'm': flags |= CLONE_NEWNS; break;
case 'n': flags |= CLONE_NEWNET; break;
case 'p': printpid = 1; break;
case 'P': flags |= CLONE_NEWPID | CLONE_NEWNS; break;
case 'a': attachpid = atoi(optarg);break;
case 'g': cgrouparg = optarg ; break;
case 'r': rtprio = atoi(optarg); break;
case 'u': flags |= CLONE_NEWUTS; break;
case 'v': printf("%s\n", VERSION); exit(0);
case 'h': usage(argv[0]); exit(0);
default: usage(argv[0]); exit(1);
}
if (closefds) {
/* close file descriptors except stdin/out/error */
int fd;
for (fd = getdtablesize(); fd > 2; fd--) close(fd);
}
if (attachpid)
/* Attach to existing namespace(s) */
pidns = attach(attachpid);
else {
/* Create new namespace(s) */
if (unshare(flags) == -1) {
perror("unshare");
return 1;
}
}
if (flags & CLONE_NEWPID || pidns)
/* pidns requires fork/wait; child will be pid 1 */
dofork = 1;
if (detachtty && getpgrp() == getpid())
/* Fork so that we will no longer be pgroup leader */
dofork = 1;
else
/* We don't need a new session, only a new pgroup */
detachtty = 0;
if (detachtty)
/* Create a new session - and by implication a new process group */
setsid();
else
/* Use a new process group (in the current session)
* so Mininet can use killpg without unintended effects */
setpgid(0, 0);
if (dofork) {
/* Fork and then wait if necessary */
pid_t pid = fork();
switch(pid) {
int status;
case -1:
perror("fork");
return 1;
case 0:
/* Child continues below */
break;
default:
/* We print the *child pid* in *parent's pidns* if needed */
if (printpid) {
printf("\001%d\n", pid);
fflush(stdout);
}
/* For pid namespace, we need to fork and wait for child ;-( */
if (flags & CLONE_NEWPID || pidns) {
wait(&status);
}
return 0;
}
}
if (printpid && !dofork) {
/* Print child pid if we didn't fork/aren't in a pidns */
assert(!pidns);
printf("\001%d\n", getpid());
fflush(stdout);
}
if (flags & CLONE_NEWPID) {
/* Child remounts /proc for ps */
if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
perror("mountproc");
}
}
/* Attach to cgroup if necessary */
if (cgrouparg) cgroup(cgrouparg);
if (flags & CLONE_NEWNET & CLONE_NEWNS) {
/* Mount sysfs to pick up the new network namespace */
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
perror("mount");
return 1;
}
}
if (rtprio != 0) {
/* Set RT scheduling priority */
static struct sched_param sp;
sp.sched_priority = atoi(optarg);
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
perror("sched_setscheduler");
return 1;
}
}
if (optind < argc) {
execvp(argv[optind], &argv[optind]);
+24 -24
View File
@@ -25,20 +25,20 @@ clean=false
declare -a hosts=()
user=$(whoami)
SSHDIR=/tmp/mn/ssh
USERDIR=/home/$user/.ssh
usage=$'./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
USERDIR=$HOME/.ssh
usage="./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
Authenticate yourself and other cluster nodes to each other
via ssh for mininet cluster edition. By default, we use a
temporary ssh setup. An ssh directory is mounted over
/home/user/.ssh on each machine in the cluster.
$USERDIR on each machine in the cluster.
-h: display this help
-p: create a persistent ssh setup. This will add
new ssh keys and known_hosts to each nodes
/home/user/.ssh directory
$USERDIR directory
-c: method to clean up a temporary ssh setup.
Any hosts taken as arguments will be cleaned
'
"
persistentSetup() {
echo "***creating key pair"
@@ -74,40 +74,40 @@ tempSetup() {
echo "***creating temporary ssh directory"
mkdir -p $SSHDIR
echo "***creating key pair"
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f /tmp/mn/ssh/id_rsa -N '' &> /dev/null
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f $SSHDIR/id_rsa -N '' &> /dev/null
echo "***mounting temporary ssh directory"
sudo mount --bind $SSHDIR /home/$user/.ssh
sudo mount --bind $SSHDIR $USERDIR
cp $SSHDIR/id_rsa.pub $SSHDIR/authorized_keys
for host in $hosts; do
echo "***copying public key to $host"
ssh-copy-id $user@$host &> /dev/null
echo "***mounting remote temporary ssh directory for $host"
ssh -o ForwardAgent=yes $user@$host "
mkdir -p /tmp/mn/ssh
cp /home/$user/.ssh/authorized_keys $SSHDIR/authorized_keys
sudo mount --bind $SSHDIR /home/$user/.ssh"
echo "***copying key pair to $host"
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying public key to $host"
ssh-copy-id $user@$host &> /dev/null
echo "***mounting remote temporary ssh directory for $host"
ssh -o ForwardAgent=yes $user@$host "
mkdir -p $SSHDIR
cp $USERDIR/authorized_keys $SSHDIR/authorized_keys
sudo mount --bind $SSHDIR $USERDIR"
echo "***copying key pair to $host"
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying known_hosts to $host"
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
done
for host in $hosts; do
echo "***copying known_hosts to $host"
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
done
}
cleanup() {
for host in $hosts; do
echo "***cleaning up $host"
ssh $user@$host "sudo umount /home/$user/.ssh
ssh $user@$host "sudo umount $USERDIR
sudo rm -rf $SSHDIR"
done
echo "**unmounting local directories"
sudo umount /home/$user/.ssh
sudo umount $USERDIR
echo "***removing temporary ssh directory"
sudo rm -rf $SSHDIR
echo "done!"
+3 -3
View File
@@ -30,7 +30,7 @@ def fixParam( line ):
def fixReturns( line ):
"Change returns: foo to @return foo"
return re.sub( 'returns:', r'@returns', line )
def fixLine( line ):
global comment
match = spaces.match( line )
@@ -69,7 +69,7 @@ def funTest():
).splitlines( True )
fixLines( testFun )
def fixLines( lines, fid ):
for line in lines:
os.write( fid, fixLine( line ) )
@@ -86,4 +86,4 @@ if __name__ == '__main__':
+190 -103
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Mininet install script for Ubuntu (and Debian Lenny)
# Mininet install script for Ubuntu (and Debian Wheezy+)
# Brandon Heller (brandonh@stanford.edu)
# Fail on error
@@ -13,7 +13,7 @@ set -o nounset
MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )"
# Set up build directory, which by default is the working directory
# unless the working directory is a subdirectory of mininet,
# unless the working directory is a subdirectory of mininet,
# in which case we use the directory containing mininet
BUILD_DIR="$(pwd -P)"
case $BUILD_DIR in
@@ -36,24 +36,38 @@ if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi
test -e /etc/debian_version && DIST="Debian"
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then
install='sudo apt-get -y install'
remove='sudo apt-get -y remove'
# Truly non-interactive apt-get installation
install='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q install'
remove='sudo DEBIAN_FRONTEND=noninteractive apt-get -y -q remove'
pkginst='sudo dpkg -i'
update='sudo apt-get'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install lsb-release
fi
fi
test -e /etc/fedora-release && DIST="Fedora"
if [ "$DIST" = "Fedora" ]; then
test -e /etc/redhat-release && DIST="RedHatEnterpriseServer"
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
install='sudo yum -y install'
remove='sudo yum -y erase'
pkginst='sudo rpm -ivh'
update='sudo yum'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install redhat-lsb-core
fi
fi
test -e /etc/SuSE-release && DIST="SUSE Linux"
if [ "$DIST" = "SUSE Linux" ]; then
install='sudo zypper --non-interactive install '
remove='sudo zypper --non-interactive remove '
pkginst='sudo rpm -ivh'
# Prereqs for this script
if ! which lsb_release &> /dev/null; then
$install openSUSE-release
fi
fi
if which lsb_release &> /dev/null; then
DIST=`lsb_release -is`
RELEASE=`lsb_release -rs`
@@ -66,8 +80,12 @@ echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH"
KERNEL_NAME=`uname -r`
KERNEL_HEADERS=kernel-headers-${KERNEL_NAME}
if ! echo $DIST | egrep 'Ubuntu|Debian|Fedora'; then
echo "Install.sh currently only supports Ubuntu, Debian and Fedora."
# Treat Raspbian as Debian
[ "$DIST" = 'Raspbian' ] && DIST='Debian'
DISTS='Ubuntu|Debian|Fedora|RedHatEnterpriseServer|SUSE LINUX'
if ! echo $DIST | egrep "$DISTS" >/dev/null; then
echo "Install.sh currently only supports $DISTS."
exit 1
fi
@@ -101,8 +119,11 @@ OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
function kernel {
echo "Install Mininet-compatible kernel if necessary"
sudo apt-get update
$install linux-image-$KERNEL_NAME
$update update
if ! $install linux-image-$KERNEL_NAME; then
echo "Could not install linux-image-$KERNEL_NAME"
echo "Skipping - assuming installed kernel is OK."
fi
}
function kernel_clean {
@@ -120,14 +141,18 @@ function kernel_clean {
# Install Mininet deps
function mn_deps {
echo "Installing Mininet dependencies"
if [ "$DIST" = "Fedora" ]; then
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8
ethtool help2man pyflakes pylint python-pep8 python-pexpect
elif [ "$DIST" = "SUSE LINUX" ]; then
$install gcc make socat psmisc xterm openssh iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man python-pyflakes python3-pylint python-pep8 python-pexpect
else
$install gcc make socat psmisc xterm ssh iperf iproute telnet \
$install gcc make socat psmisc xterm ssh iperf iproute2 telnet \
python-setuptools cgroup-bin ethtool help2man \
pyflakes pylint pep8
pyflakes pylint pep8 python-pexpect
fi
echo "Installing Mininet core"
@@ -153,12 +178,16 @@ function of {
echo "Installing OpenFlow reference implementation..."
cd $BUILD_DIR
$install autoconf automake libtool make gcc
if [ "$DIST" = "Fedora" ]; then
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install git pkgconfig glibc-devel
elif [ "$DIST" = "SUSE LINUX" ]; then
$install git pkgconfig glibc-devel
else
$install git-core autotools-dev pkg-config libc6-dev
fi
git clone git://openflowswitch.org/openflow.git
# was: git clone git://openflowswitch.org/openflow.git
# Use our own fork on github for now:
git clone git://github.com/mininet/openflow
cd $BUILD_DIR/openflow
# Patch controller to handle more than 16 switches
@@ -189,17 +218,24 @@ function of13 {
fi
# Install netbee
NBEESRC="nbeesrc-jan-10-2013"
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
NBEESRC="nbeesrc-feb-24-2015"
NBEEDIR="netbee"
else
NBEESRC="nbeesrc-jan-10-2013"
NBEEDIR="nbeesrc-jan-10-2013"
fi
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
wget -nc ${NBEEURL}${NBEESRC}.zip
unzip ${NBEESRC}.zip
cd ${NBEESRC}/src
cd ${NBEEDIR}/src
cmake .
make
cd $BUILD_DIR/
sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
sudo ldconfig
sudo cp -R ${NBEESRC}/include/ /usr/
sudo cp -R ${NBEEDIR}/include/ /usr/
# Resume the install:
cd $BUILD_DIR/ofsoftswitch13
@@ -214,8 +250,10 @@ function of13 {
function install_wireshark {
if ! which wireshark; then
echo "Installing Wireshark"
if [ "$DIST" = "Fedora" ]; then
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install wireshark wireshark-gnome
elif [ "$DIST" = "SUSE LINUX" ]; then
$install wireshark
else
$install wireshark tshark
fi
@@ -258,53 +296,69 @@ function ubuntuOvs {
OVS_SRC=$BUILD_DIR/openvswitch
OVS_TARBALL_LOC=http://openvswitch.org/releases
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 12.04; then
rm -rf $OVS_SRC
mkdir -p $OVS_SRC
cd $OVS_SRC
if ! echo "$DIST" | egrep "Ubuntu|Debian" > /dev/null; then
echo "OS must be Ubuntu or Debian"
$cd BUILD_DIR
return
fi
if [ "$DIST" = "Ubuntu" ] && ! version_ge $RELEASE 12.04; then
echo "Ubuntu version must be >= 12.04"
cd $BUILD_DIR
return
fi
if [ "$DIST" = "Debian" ] && ! version_ge $RELEASE 7.0; then
echo "Debian version must be >= 7.0"
cd $BUILD_DIR
return
fi
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
tar xzf openvswitch-$OVS_RELEASE.tar.gz
else
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
cd $BUILD_DIR
return
fi
rm -rf $OVS_SRC
mkdir -p $OVS_SRC
cd $OVS_SRC
# Remove any old packages
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
openvswitch-pki openvswitch-switch
# Get build deps
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
pkg-config bzip2 openssl python-all procps python-qt4 \
python-zopeinterface python-twisted-conch dkms
# Build OVS
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
cd ..
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
echo "Ignoring error installing openvswitch-controller"
fi
modinfo openvswitch
sudo ovs-vsctl show
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service openvswitch-controller stop; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/openvswitch-controller ]; then
sudo update-rc.d openvswitch-controller disable
fi
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
tar xzf openvswitch-$OVS_RELEASE.tar.gz
else
echo "Failed to install Open vSwitch. OS must be Ubuntu >= 12.04"
cd $BUILD_DIR
return
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
cd $BUILD_DIR
return
fi
# Remove any old packages
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-pki openvswitch-switch \
openvswitch-controller || true
# Get build deps
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
pkg-config bzip2 openssl python-all procps python-qt4 \
python-zopeinterface python-twisted-conch dkms dh-python dh-autoreconf \
uuid-runtime
# Build OVS
parallel=`grep processor /proc/cpuinfo | wc -l`
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
DEB_BUILD_OPTIONS='parallel=$parallel nocheck' fakeroot debian/rules binary
cd ..
for pkg in common datapath-dkms pki switch; do
pkg=openvswitch-${pkg}_$OVS_RELEASE*.deb
echo "Installing $pkg"
$pkginst $pkg
done
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb 2>/dev/null; then
echo "Ignoring error installing openvswitch-controller"
fi
/sbin/modinfo openvswitch
sudo ovs-vsctl show
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service openvswitch-controller stop 2>/dev/null; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/openvswitch-controller ]; then
sudo update-rc.d openvswitch-controller disable
fi
}
@@ -314,39 +368,47 @@ function ubuntuOvs {
function ovs {
echo "Installing Open vSwitch..."
if [ "$DIST" == "Fedora" ]; then
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install openvswitch openvswitch-controller
return
fi
# Manually installing openvswitch-datapath may be necessary
# for manually built kernel .debs using Debian's defective kernel
# packaging, which doesn't yield usable headers.
if ! dpkg --get-selections | grep openvswitch-datapath; then
# If you've already installed a datapath, assume you
# know what you're doing and don't need dkms datapath.
# Otherwise, install it.
$install openvswitch-datapath-dkms
if [ "$DIST" = "Ubuntu" ] && ! version_ge $RELEASE 14.04; then
# Older Ubuntu versions need openvswitch-datapath/-dkms
# Manually installing openvswitch-datapath may be necessary
# for manually built kernel .debs using Debian's defective kernel
# packaging, which doesn't yield usable headers.
if ! dpkg --get-selections | grep openvswitch-datapath; then
# If you've already installed a datapath, assume you
# know what you're doing and don't need dkms datapath.
# Otherwise, install it.
$install openvswitch-datapath-dkms
fi
fi
$install openvswitch-switch
OVSC=""
if $install openvswitch-controller; then
OVSC="openvswitch-controller"
else
echo "Attempting to install openvswitch-testcontroller"
if $install openvswitch-testcontroller; then
OVSC="openvswitch-testcontroller"
else
echo "Failed - skipping openvswitch-testcontroller"
fi
fi
if [ "$OVSC" ]; then
# Switch can run on its own, but
# Mininet should control the controller
# This appears to only be an issue on Ubuntu/Debian
if sudo service openvswitch-controller stop; then
if sudo service $OVSC stop; then
echo "Stopped running controller"
fi
if [ -e /etc/init.d/openvswitch-controller ]; then
sudo update-rc.d openvswitch-controller disable
fi
else
echo "Attempting to install openvswitch-testcontroller"
if ! $install openvswitch-testcontroller; then
echo "Failed - giving up"
if [ -e /etc/init.d/$OVSC ]; then
sudo update-rc.d $OVSC disable
fi
fi
}
function remove_ovs {
@@ -376,12 +438,19 @@ function ivs {
IVS_SRC=$BUILD_DIR/ivs
# Install dependencies
$install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev
$install gcc make
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install git pkgconfig libnl3-devel libcap-devel openssl-devel
else
$install git-core pkg-config libnl-3-dev libnl-route-3-dev \
libnl-genl-3-dev
fi
# Install IVS from source
cd $BUILD_DIR
git clone git://github.com/floodlight/ivs $IVS_SRC --recursive
git clone git://github.com/floodlight/ivs $IVS_SRC
cd $IVS_SRC
git submodule update --init
make
sudo make install
}
@@ -392,27 +461,20 @@ function ryu {
# install Ryu dependencies"
$install autoconf automake g++ libtool python make
if [ "$DIST" = "Ubuntu" ]; then
$install libxml2 libxslt-dev python-pip python-dev
sudo pip install gevent
elif [ "$DIST" = "Debian" ]; then
$install libxml2 libxslt-dev python-pip python-dev
sudo pip install gevent
if [ "$DIST" = "Ubuntu" -o "$DIST" = "Debian" ]; then
$install gcc python-pip python-dev libffi-dev libssl-dev \
libxml2-dev libxslt1-dev zlib1g-dev
fi
# if needed, update python-six
SIX_VER=`pip show six | grep Version | awk '{print $2}'`
if version_ge 1.7.0 $SIX_VER; then
echo "Installing python-six version 1.7.0..."
sudo pip install -I six==1.7.0
fi
# fetch RYU
cd $BUILD_DIR/
git clone git://github.com/osrg/ryu.git ryu
cd ryu
# install ryu
sudo python ./setup.py install
sudo pip install -r tools/pip-requires -r tools/optional-requires \
-r tools/test-requires
sudo python setup.py install
# Add symbolic link to /usr/bin
sudo ln -s ./bin/ryu-manager /usr/local/bin/ryu-manager
@@ -523,13 +585,17 @@ function oftest {
function cbench {
echo "Installing cbench..."
if [ "$DIST" = "Fedora" ]; then
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
elif [ "$DIST" = "SUSE LINUX" ]; then
$install net-snmp-devel libpcap-devel libconfig-devel
else
$install libsnmp-dev libpcap-dev libconfig-dev
fi
cd $BUILD_DIR/
git clone git://gitosis.stanford.edu/oflops.git
# was: git clone git://gitosis.stanford.edu/oflops.git
# Use our own fork on github for now:
git clone git://github.com/mininet/oflops
cd oflops
sh boot.sh || true # possible error in autoreconf, so run twice
sh boot.sh
@@ -554,9 +620,9 @@ function vm_other {
# BLACKLIST=/etc/modprobe.d/blacklist
#fi
#sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST"
echo "Disabling IPv6"
# Disable IPv6
if ! grep 'disable IPv6' /etc/sysctl.conf; then
if ! grep 'disable_ipv6' /etc/sysctl.conf; then
echo 'Disabling IPv6'
echo '
# Mininet: disable IPv6
@@ -564,6 +630,13 @@ net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
fi
# Since the above doesn't disable neighbor discovery, also do this:
if ! grep 'ipv6.disable' /etc/default/grub; then
sudo sed -i -e \
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 /' \
/etc/default/grub
sudo update-grub
fi
# Disabling IPv6 breaks X11 forwarding via ssh
line='AddressFamily inet'
file='/etc/ssh/sshd_config'
@@ -586,7 +659,7 @@ net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
$install ntp
# Install vconfig for VLAN example
if [ "$DIST" = "Fedora" ]; then
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install vconfig
else
$install vlan
@@ -667,6 +740,20 @@ function vm_clean {
rm -f ~/.ssh/id_rsa* ~/.ssh/known_hosts
sudo rm -f ~/.ssh/authorized_keys*
# Remove SSH keys and regenerate on boot
echo 'Removing SSH keys from /etc/ssh/'
sudo rm -f /etc/ssh/*key*
if ! grep mininet /etc/rc.local >& /dev/null; then
sudo sed -i -e "s/exit 0//" /etc/rc.local
echo '
# mininet: regenerate ssh keys if we deleted them
if ! stat -t /etc/ssh/*key* >/dev/null 2>&1; then
/usr/sbin/dpkg-reconfigure openssh-server
fi
exit 0
' | sudo tee -a /etc/rc.local > /dev/null
fi
# Remove Mininet files
#sudo rm -f /lib/modules/python2.5/site-packages/mininet*
#sudo rm -f /usr/bin/mnexec
@@ -681,7 +768,7 @@ function vm_clean {
# Note: you can shrink the .vmdk in vmware using
# vmware-vdiskmanager -k *.vmdk
echo "Zeroing out disk blocks for efficient compaction..."
time sudo dd if=/dev/zero of=/tmp/zero bs=1M
time sudo dd if=/dev/zero of=/tmp/zero bs=1M || true
sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero
}
+1 -8
View File
@@ -33,12 +33,5 @@ if [ -d "$cgroup" ]; then
cg="-g $host"
fi
# Check whether host should be running in a chroot dir
rootdir="/var/run/mn/$host/root"
if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
cmd="'cd `pwd`; exec $cmd'"
cmd="chroot $rootdir /bin/bash -c $cmd"
fi
cmd="exec sudo mnexec $cg -a $pid $cmd"
cmd="exec sudo -E mnexec $cg -a $pid $cmd"
eval $cmd
+2 -1
View File
@@ -40,6 +40,7 @@ Bob Lantz, rlantz@cs.stanford.edu
1/24/2010
"""
from __future__ import print_function
import re, sys
def fixUnderscoreTriplet( match ):
@@ -195,4 +196,4 @@ def convertFromPep8( program ):
return program
if __name__ == '__main__':
print convertFromPep8( sys.stdin.read() )
print( convertFromPep8( sys.stdin.read() ) )
+4 -4
View File
@@ -4,11 +4,11 @@ from subprocess import check_output as co
from sys import exit
# Actually run bin/mn rather than importing via python path
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True )
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
version = version.strip()
# Find all Mininet path references
lines = co( "egrep -or 'Mininet [0-9\.]+\w*' *", shell=True )
lines = co( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
error = False
@@ -16,8 +16,8 @@ for line in lines.split( '\n' ):
if line and 'Binary' not in line:
fname, fversion = line.split( ':' )
if version != fversion:
print "%s: incorrect version '%s' (should be '%s')" % (
fname, fversion, version )
print( "%s: incorrect version '%s' (should be '%s')" % (
fname, fversion, version ) )
error = True
if error:
+62 -51
View File
@@ -54,6 +54,7 @@ NoKVM = False # Don't use kvm and use emulation instead
Branch = None # Branch to update and check out before testing
Zip = False # Archive .ovf and .vmdk into a .zip file
Forward = [] # VM port forwarding options (-redir)
Chown = '' # Build directory owner
VMImageDir = os.environ[ 'HOME' ] + '/vm-images'
@@ -66,36 +67,24 @@ isoURLs = {
'precise64server':
'http://mirrors.kernel.org/ubuntu-releases/12.04/'
'ubuntu-12.04.5-server-amd64.iso',
'quantal32server':
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
'ubuntu-12.10-server-i386.iso',
'quantal64server':
'http://mirrors.kernel.org/ubuntu-releases/12.10/'
'ubuntu-12.10-server-amd64.iso',
'raring32server':
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
'ubuntu-13.04-server-i386.iso',
'raring64server':
'http://mirrors.kernel.org/ubuntu-releases/13.04/'
'ubuntu-13.04-server-amd64.iso',
'saucy32server':
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
'ubuntu-13.10-server-i386.iso',
'saucy64server':
'http://mirrors.kernel.org/ubuntu-releases/13.10/'
'ubuntu-13.10-server-amd64.iso',
'trusty32server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04-server-i386.iso',
'ubuntu-14.04.4-server-i386.iso',
'trusty64server':
'http://mirrors.kernel.org/ubuntu-releases/14.04/'
'ubuntu-14.04-server-amd64.iso',
'utopic32server':
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
'ubuntu-14.10-server-i386.iso',
'utopic64server':
'http://mirrors.kernel.org/ubuntu-releases/14.10/'
'ubuntu-14.10-server-amd64.iso',
'ubuntu-14.04.4-server-amd64.iso',
'wily32server':
'http://mirrors.kernel.org/ubuntu-releases/15.10/'
'ubuntu-15.10-server-i386.iso',
'wily64server':
'http://mirrors.kernel.org/ubuntu-releases/15.10/'
'ubuntu-15.10-server-amd64.iso',
'xenial32server':
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
'ubuntu-16.04.1-server-i386.iso',
'xenial64server':
'http://mirrors.kernel.org/ubuntu-releases/16.04/'
'ubuntu-16.04.1-server-amd64.iso',
}
@@ -129,9 +118,9 @@ def log( *args, **kwargs ):
msg = ' '.join( str( arg ) for arg in args )
output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg )
if cr:
print output
print( output )
else:
print output,
print( output, )
# Optionally mirror to LogFile
if type( LogFile ) is file:
if cr:
@@ -165,7 +154,7 @@ def depend():
run( 'sudo apt-get -qy update' )
run( 'sudo apt-get -qy install'
' kvm cloud-utils genisoimage qemu-kvm qemu-utils'
' e2fsprogs dnsmasq curl'
' e2fsprogs curl'
' python-setuptools mtools zip' )
run( 'sudo easy_install pexpect' )
@@ -219,7 +208,7 @@ def attachNBD( cow, flags='' ):
continue
srun( 'modprobe nbd max-part=64' )
srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) )
print
print()
return nbd
raise Exception( "Error: could not find unused /dev/nbdX device" )
@@ -238,7 +227,10 @@ def extractKernel( image, flavor, imageDir=VMImageDir ):
return kernel, initrd
log( '* Extracting kernel to', kernel )
nbd = attachNBD( image, flags='-r' )
print srun( 'partx ' + nbd )
try:
print( srun( 'partx ' + nbd ) )
except:
log( 'Warning - partx failed with error' )
# Assume kernel is in partition 1/boot/vmlinuz*generic for now
part = nbd + 'p1'
mnt = mkdtemp()
@@ -262,8 +254,9 @@ def findBaseImage( flavor, size='8G' ):
# Detect race condition with multiple builds
perms = stat( image )[ ST_MODE ] & 0777
if perms != 0444:
raise Exception( 'Error - %s is writable ' % image +
'; are multiple builds running?' )
raise Exception( 'Error - base image %s is writable.' % image +
' Are multiple builds running? if not,'
' remove %s and try again.' % image )
else:
# We create VMImageDir here since we are called first
run( 'mkdir -p %s' % VMImageDir )
@@ -323,7 +316,7 @@ zerombr yes
clearpart --all --initlabel
#Automatic partitioning
autopart
#System authorization infomation
#System authorization information
auth --useshadow --enablemd5
#Firewall configuration
firewall --disabled
@@ -395,6 +388,10 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
accel = 'tcg'
else:
accel = 'kvm'
try:
run( 'kvm-ok' )
except:
raise Exception( 'kvm-ok failed; try using --nokvm' )
cmd = [ 'sudo', kvm,
'-machine', 'accel=%s' % accel,
'-nographic',
@@ -435,12 +432,13 @@ def installUbuntu( iso, image, logfilename='install.log', memory=1024 ):
log( '* Ubuntu installation completed in %.2f seconds' % elapsed )
def boot( cow, kernel, initrd, logfile, memory=1024 ):
def boot( cow, kernel, initrd, logfile, memory=1024, cpuCores=1 ):
"""Boot qemu/kvm with a COW disk and local/user data store
cow: COW disk path
kernel: kernel path
logfile: log file for pexpect object
memory: memory size in MB
cpuCores: number of CPU cores to use
returns: pexpect object to qemu process"""
# pexpect might not be installed until after depend() is called
global pexpect
@@ -469,6 +467,8 @@ def boot( cow, kernel, initrd, logfile, memory=1024 ):
'-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ]
if Forward:
cmd += sum( [ [ '-redir', f ] for f in Forward ], [] )
if cpuCores > 1:
cmd += [ '-smp cores=%s' % cpuCores ]
cmd = ' '.join( cmd )
log( '* BOOTING VM FROM', cow )
log( cmd )
@@ -580,6 +580,9 @@ def useTest( vm, prompt=Prompt ):
log( '* Restoring logging to stdout' )
vm.logfile = stdout
# A convenient alias for use - 'run'; we might want to allow
# 'run' to take a parameter
runTest = useTest
def checkOutBranch( vm, branch, prompt=Prompt ):
# This is a bit subtle; it will check out an existing branch (e.g. master)
@@ -789,18 +792,20 @@ def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ):
post: command line to run in VM after tests
prompt: shell prompt (default '$ ')
memory: memory size in MB"""
global LogFile, Zip
global LogFile, Zip, Chown
start = time()
lstart = localtime()
date = strftime( '%y%m%d-%H-%M-%S', lstart)
ovfdate = strftime( '%y%m%d', lstart )
dir = 'mn-%s-%s' % ( flavor, date )
if Branch:
dir = 'mn-%s-%s-%s' % ( Branch, flavor, date )
dirname = 'mn-%s-%s-%s' % ( Branch, flavor, date )
try:
os.mkdir( dir )
os.mkdir( dir)
except:
raise Exception( "Failed to create build directory %s" % dir )
if Chown:
run( 'chown %s %s' % ( Chown, dir ) )
os.chdir( dir )
LogFile = open( 'build.log', 'w' )
log( '* Logging to', abspath( LogFile.name ) )
@@ -881,14 +886,15 @@ def getMininetVersion( vm ):
return version
def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
def bootAndRun( image, prompt=Prompt, memory=1024, cpuCores=1, outputFile=None,
runFunction=None, **runArgs ):
"""Boot and test VM
tests: list of tests to run
pre: command line to run in VM before tests
post: command line to run in VM after tests
prompt: shell prompt (default '$ ')
memory: VM memory size in MB"""
memory: VM memory size in MB
cpuCores: number of CPU cores to use"""
bootTestStart = time()
basename = path.basename( image )
image = abspath( image )
@@ -906,7 +912,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
suffix='.testlog', delete=False )
log( '* Logging VM output to', logfile.name )
vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile,
memory=memory )
memory=memory, cpuCores=cpuCores )
login( vm )
log( '* Waiting for prompt after login' )
vm.expect( prompt )
@@ -928,7 +934,7 @@ def bootAndRun( image, prompt=Prompt, memory=1024, outputFile=None,
def buildFlavorString():
"Return string listing valid build flavors"
return 'valid build flavors: ( %s )' % ' '.join( sorted( isoURLs ) )
return 'valid build flavors: %s' % ' '.join( sorted( isoURLs ) )
def testDict():
@@ -944,15 +950,16 @@ def testDict():
def testString():
"Return string listing valid tests"
return 'valid tests: ( %s )' % ' '.join( testDict().keys() )
tests = [ '%s <%s>' % ( name, func.__doc__ )
for name, func in testDict().iteritems() ]
return 'valid tests: %s' % ', '.join( tests )
def parseArgs():
"Parse command line arguments and run"
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward
global LogToConsole, NoKVM, Branch, Zip, TIMEOUT, Forward, Chown
parser = argparse.ArgumentParser( description='Mininet VM build script',
epilog=buildFlavorString() + ' ' +
testString() )
epilog='' )
parser.add_argument( '-v', '--verbose', action='store_true',
help='send VM output to console rather than log file' )
parser.add_argument( '-d', '--depend', action='store_true',
@@ -972,7 +979,7 @@ def parseArgs():
help='Boot and test an existing VM image' )
parser.add_argument( '-t', '--test', metavar='test', default=[],
action='append',
help='specify a test to run' )
help='specify a test to run; ' + testString() )
parser.add_argument( '-w', '--timeout', metavar='timeout', type=int,
default=0, help='set expect timeout' )
parser.add_argument( '-r', '--run', metavar='cmd', default='',
@@ -982,18 +989,20 @@ def parseArgs():
parser.add_argument( '-b', '--branch', metavar='branch',
help='branch to install and/or check out and test' )
parser.add_argument( 'flavor', nargs='*',
help='VM flavor(s) to build (e.g. raring32server)' )
help='VM flavor(s) to build; ' + buildFlavorString() )
parser.add_argument( '-z', '--zip', action='store_true',
help='archive .ovf and .vmdk into .zip file' )
parser.add_argument( '-o', '--out',
help='output file for test image (vmdk)' )
parser.add_argument( '-f', '--forward', default=[], action='append',
help='forward VM ports to local server, e.g. tcp:5555::22' )
parser.add_argument( '-u', '--chown', metavar='user',
help='specify an owner for build directory' )
args = parser.parse_args()
if args.depend:
depend()
if args.list:
print buildFlavorString()
print( buildFlavorString() )
if args.clean:
cleanup()
if args.verbose:
@@ -1010,10 +1019,12 @@ def parseArgs():
Forward = args.forward
if not args.test and not args.run and not args.post:
args.test = [ 'sanity', 'core' ]
if args.chown:
Chown = args.chown
for flavor in args.flavor:
if flavor not in isoURLs:
print "Unknown build flavor:", flavor
print buildFlavorString()
print( "Unknown build flavor:", flavor )
print( buildFlavorString() )
break
try:
build( flavor, tests=args.test, pre=args.run, post=args.post,
+6 -3
View File
@@ -11,7 +11,8 @@ 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 sed -i -e 's/splash//' /etc/default/grub
sudo sed -i -e 's/quiet/text/' /etc/default/grub
sudo update-grub
# Update from official archive
sudo apt-get update
@@ -34,13 +35,15 @@ git clone git://github.com/mininet/mininet
# Optionally check out branch
if [ "$1" != "" ]; then
pushd mininet
git checkout -b $1 $1
#git checkout -b $1 $1
# TODO branch will in detached HEAD state if it is not master
git checkout $1
popd
fi
# Install Mininet
time mininet/util/install.sh
# Finalize VM
time mininet/util/install.sh -tc
time mininet/util/install.sh -tcd
# Ignoring this since NOX classic is deprecated
#if ! grep NOX_CORE_DIR .bashrc; then
# echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc