Sunday, September 02, 2018

[UPDATE] net/mininet: new MASTER_SITES, remove patches, and use uploaded assets

On Thu, Aug 16, 2018 at 08:37:48PM -0700, Ayaka Koshibe wrote:
> Hi,
>
> I've been talking with the maintainer of the project and was told that I can
> use my fork of the repo as the upstream for this port. I wasn't sure about
> what to do with the version, so I'm tentatively calling it 2.2.2, since
> that is the latest release.
>
> I've also uploaded a tarball, and removed the patches that are in the tree.
>
> The manpages also broke again, as help2man was looking for a file in WRKBUILD
> - the tweak for that is also included here.
>
> Makefile/distinfo/PLIST diffs are at the top, and the rest are all deletions.
>
> OK?

ping?

> Thanks,
> Ayaka
>
>
> Index: Makefile
> ===================================================================
> RCS file: /cvs/ports/net/mininet/Makefile,v
> retrieving revision 1.9
> diff -u -p -u -r1.9 Makefile
> --- Makefile 9 May 2018 14:56:19 -0000 1.9
> +++ Makefile 16 Aug 2018 05:27:51 -0000
> @@ -2,11 +2,9 @@
>
> COMMENT = emulator for rapid prototyping of software defined networks
>
> -DISTNAME = mininet-0.0.20170813
> -REVISION = 6
> -GH_ACCOUNT = mininet
> -GH_PROJECT = mininet
> -GH_COMMIT = 87e26ef931ee6063332ceba77db472140f832d3a
> +V= 2.2.2
> +DISTNAME = mininet-$V
> +EXTRACT_SUFX= .tar.gz
>
> CATEGORIES = net
>
> @@ -19,6 +17,8 @@ PERMIT_PACKAGE_CDROM = Yes
>
> WANTLIB = c
>
> +MASTER_SITES= https://github.com/akoshibe/mininet/releases/download/v$V/
> +
> MODULES = lang/python
> MODPY_SETUPTOOLS = Yes
> BUILD_DEPENDS = devel/help2man
> @@ -28,9 +28,10 @@ RUN_DEPENDS = net/socat \
> SEPARATE_BUILD = Yes
>
> do-build:
> - ${CC} ${CFLAGS} ${WRKSRC}/mnexec.c -o ${WRKBUILD}/mnexec
> + ${CC} ${CFLAGS} ${WRKSRC}/mnexec.c ${WRKSRC}/mnexec_bsd.c \
> + -o ${WRKBUILD}/mnexec
> help2man -N -n "create a Mininet network." --no-discard-stderr \
> - "PYTHONPATH=${WRKBUILD} ${MODPY_BIN} -B ${WRKSRC}/bin/mn" \
> + "PYTHONPATH=${WRKSRC} ${MODPY_BIN} -B ${WRKSRC}/bin/mn" \
> -o ${WRKBUILD}/mn.1
> help2man -N -n "execution utility for Mininet." -h "-h" -v "-v" \
> --no-discard-stderr ${WRKBUILD}/mnexec -o ${WRKBUILD}/mnexec.1
> Index: distinfo
> ===================================================================
> RCS file: /cvs/ports/net/mininet/distinfo,v
> retrieving revision 1.1.1.1
> diff -u -p -u -r1.1.1.1 distinfo
> --- distinfo 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ distinfo 16 Aug 2018 05:27:51 -0000
> @@ -1,2 +1,2 @@
> -SHA256 (mininet-0.0.20170813-87e26ef9.tar.gz) = /YDr4x0oZz6aBDSFAT7buk7hcqwRPrCYu0eIGF21yS4=
> -SIZE (mininet-0.0.20170813-87e26ef9.tar.gz) = 226371
> +SHA256 (mininet-2.2.2.tar.gz) = wvkGrX/GjypDtAifPtTcL+ADJ6SZQgerNE69DFIVNlY=
> +SIZE (mininet-2.2.2.tar.gz) = 248145
> Index: pkg/PLIST
> ===================================================================
> RCS file: /cvs/ports/net/mininet/pkg/PLIST,v
> retrieving revision 1.5
> diff -u -p -u -r1.5 PLIST
> --- pkg/PLIST 17 Apr 2018 19:39:33 -0000 1.5
> +++ pkg/PLIST 16 Aug 2018 05:27:51 -0000
> @@ -2,12 +2,12 @@
> bin/mn
> @bin bin/mnexec
> lib/python${MODPY_VERSION}/site-packages/mininet/
> -lib/python${MODPY_VERSION}/site-packages/mininet-2.3.0d1-py${MODPY_VERSION}.egg-info/
> -lib/python${MODPY_VERSION}/site-packages/mininet-2.3.0d1-py${MODPY_VERSION}.egg-info/PKG-INFO
> -lib/python${MODPY_VERSION}/site-packages/mininet-2.3.0d1-py${MODPY_VERSION}.egg-info/SOURCES.txt
> -lib/python${MODPY_VERSION}/site-packages/mininet-2.3.0d1-py${MODPY_VERSION}.egg-info/dependency_links.txt
> -lib/python${MODPY_VERSION}/site-packages/mininet-2.3.0d1-py${MODPY_VERSION}.egg-info/requires.txt
> -lib/python${MODPY_VERSION}/site-packages/mininet-2.3.0d1-py${MODPY_VERSION}.egg-info/top_level.txt
> +lib/python${MODPY_VERSION}/site-packages/mininet-2.2.2-py${MODPY_VERSION}.egg-info/
> +lib/python${MODPY_VERSION}/site-packages/mininet-2.2.2-py${MODPY_VERSION}.egg-info/PKG-INFO
> +lib/python${MODPY_VERSION}/site-packages/mininet-2.2.2-py${MODPY_VERSION}.egg-info/SOURCES.txt
> +lib/python${MODPY_VERSION}/site-packages/mininet-2.2.2-py${MODPY_VERSION}.egg-info/dependency_links.txt
> +lib/python${MODPY_VERSION}/site-packages/mininet-2.2.2-py${MODPY_VERSION}.egg-info/requires.txt
> +lib/python${MODPY_VERSION}/site-packages/mininet-2.2.2-py${MODPY_VERSION}.egg-info/top_level.txt
> lib/python${MODPY_VERSION}/site-packages/mininet/__init__.py
> lib/python${MODPY_VERSION}/site-packages/mininet/__init__.pyc
> lib/python${MODPY_VERSION}/site-packages/mininet/baseintf.py
> @@ -42,8 +42,12 @@ lib/python${MODPY_VERSION}/site-packages
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/__init__.pyc
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/intf.py
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/intf.pyc
> +lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/mnopts.py
> +lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/mnopts.pyc
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/node.py
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/node.pyc
> +lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/nodelib.py
> +lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/nodelib.pyc
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/util.py
> lib/python${MODPY_VERSION}/site-packages/mininet/openbsd/util.pyc
> lib/python${MODPY_VERSION}/site-packages/mininet/term.py
> Index: patches/patch-README_md
> ===================================================================
> RCS file: patches/patch-README_md
> diff -N patches/patch-README_md
> --- patches/patch-README_md 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,128 +0,0 @@
> -$OpenBSD: patch-README_md,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -
> -Index: README.md
> ---- README.md.orig
> -+++ README.md
> -@@ -1,11 +1,15 @@
> - Mininet: Rapid Prototyping for Software Defined Networks
> - ========================================================
> --*The best way to emulate almost any network on your laptop!*
> -
> --Mininet 2.3.0d1
> -+Fork of Mininet 2.3.0d1
> -
> --[![Build Status][1]](https://travis-ci.org/mininet/mininet)
> -+This is a heavily refactored version of Mininet that also supports
> -+FreeBSD and OpenBSD, and attempts to make it easier to add support for
> -+non-Linux systems. As such, the native install instructions are
> -+slightly different. This is also a heavy work-in-progress so things
> -+may be broken or unsupported.
> -
> -+
> - ### What is Mininet?
> -
> - Mininet emulates a complete network of hosts, links, and switches
> -@@ -19,20 +23,7 @@ especially those using OpenFlow and SDN. OpenFlow-bas
> - controllers prototyped in Mininet can usually be transferred to
> - hardware with minimal changes for full line-rate execution.
> -
> --### How does it work?
> -
> --Mininet creates virtual networks using process-based virtualization
> --and network namespaces - features that are available in recent Linux
> --kernels. In Mininet, hosts are emulated as `bash` processes running in
> --a network namespace, so any code that would normally run on a Linux
> --server (like a web server or client program) should run just fine
> --within a Mininet "Host". The Mininet "Host" will have its own private
> --network interface and can only see its own processes. Switches in
> --Mininet are software-based switches like Open vSwitch or the OpenFlow
> --reference switch. Links are virtual ethernet pairs, which live in the
> --Linux kernel and connect our emulated switches to emulated hosts
> --(processes).
> --
> - ### Features
> -
> - Mininet includes:
> -@@ -67,32 +58,21 @@ Mininet includes:
> -
> - `mn -c`
> -
> --### New features in this release
> -+ (Note: this is a fairly blunt command that may remove non-Mininet
> -+ related things)
> -
> --This is primarily a performance improvement and bug fix release.
> -
> --- Batch startup has been implemented for Open vSwitch, improving
> -- startup performance.
> -+Note, different platform ports support varying subsets of the base
> -+(Linux) version described below. Refer to the documentation/notes for
> -+each platform for the specifics.
> -
> --- OVS patch links have been implemented via OVSLink and --link ovs
> -
> -- 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.
> --
> --- You can now easily install Mininet on a Raspberry Pi ;-)
> --
> --- Additional information for this release and previous releases
> -- may be found in the release notes on docs.mininet.org
> --
> - ### Installation
> -
> --See `INSTALL` for installation instructions and details.
> -+The installation instructions and other details are found in the
> -+`INSTALL.*` files.
> -
> -+
> - ### Documentation
> -
> - In addition to the API documentation (`make doc`), much useful
> -@@ -102,31 +82,19 @@ to the Python API, is available on the
> - There is also a wiki which you are encouraged to read and to
> - contribute to, particularly the Frequently Asked Questions (FAQ.)
> -
> --### Support
> -+Details about FreeBSD support are available on the
> -+[FreeBSD wiki Mininet page](https://wiki.freebsd.org/Mininet).
> -
> --Mininet is community-supported. We encourage you to join the
> --Mininet mailing list, `mininet-discuss` at:
> -+The details and status of OpenBSD support are found in
> -+`INSTALL.OpenBSD`.
> -
> --<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
> -
> --### Join Us
> -+### Support
> -
> --Thanks again to all of the Mininet contributors!
> -+This fork of Mininet is an experiment that isn't supported by the
> -+Mininet community; However, Mininet-related questions pertaining to
> -+the features of the original Mininet can be directed to
> -+`mininet-discuss`:
> -
> --Mininet is an open source project and is currently hosted
> --at <https://github.com/mininet>. You are encouraged to download
> --the code, examine it, modify it, and submit bug reports, bug fixes,
> --feature requests, new features and other issues and pull requests.
> --Thanks to everyone who has contributed code to the Mininet project
> --(see CONTRIBUTORS for more info!) It is because of everyone's
> --hard work that Mininet continues to grow and improve.
> -+<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
> -
> --### Enjoy Mininet
> --
> --Best wishes, and we look forward to seeing what you can do with
> --Mininet to change the networking world!
> --
> --Bob Lantz
> --Mininet Core Team
> --
> --[1]: https://travis-ci.org/mininet/mininet.svg?branch=master
> Index: patches/patch-bin_mn
> ===================================================================
> RCS file: patches/patch-bin_mn
> diff -N patches/patch-bin_mn
> --- patches/patch-bin_mn 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,67 +0,0 @@
> -$OpenBSD: patch-bin_mn,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -Choose node types that are applicable given an OS.
> -Index: bin/mn
> ---- bin/mn.orig
> -+++ bin/mn
> -@@ -26,10 +26,10 @@ from mininet.log import lg, LEVELS, info, debug, warn,
> - from mininet.net import Mininet, MininetWithControlNet, VERSION
> - from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
> - Ryu, NOX, RemoteController, findController,
> -- DefaultController, NullController,
> -+ DefaultController, NullController, Switchd,
> - UserSwitch, OVSSwitch, OVSBridge,
> -- IVSSwitch )
> --from mininet.nodelib import LinuxBridge
> -+ IVSSwitch, IfSwitch, DefaultSwitch )
> -+from mininet.nodelib import ClassicBridge
> - from mininet.link import Link, TCLink, TCULink, OVSLink
> - from mininet.topo import ( SingleSwitchTopo, LinearTopo,
> - SingleSwitchReversedTopo, MinimalTopo )
> -@@ -64,8 +64,9 @@ SWITCHES = { 'user': UserSwitch,
> - # Keep ovsk for compatibility with 2.0
> - 'ovsk': OVSSwitch,
> - 'ivs': IVSSwitch,
> -- 'lxbr': LinuxBridge,
> -- 'default': OVSSwitch }
> -+ 'ifsw': IfSwitch,
> -+ 'sysbr': ClassicBridge,
> -+ 'default': DefaultSwitch }
> -
> - HOSTDEF = 'proc'
> - HOSTS = { 'proc': Host,
> -@@ -78,6 +79,7 @@ CONTROLLERS = { 'ref': Controller,
> - 'nox': NOX,
> - 'remote': RemoteController,
> - 'ryu': Ryu,
> -+ 'swd': Switchd,
> - 'default': DefaultController, # Note: overridden below
> - 'none': NullController }
> -
> -@@ -314,8 +316,6 @@ class MininetRunner( object ):
> - def begin( self ):
> - "Create and run mininet."
> -
> -- global CLI
> --
> - opts = self.options
> -
> - if opts.cluster:
> -@@ -340,7 +340,7 @@ class MininetRunner( object ):
> - 'for default switch!\n' )
> - info( '*** Falling back to OVS Bridge\n' )
> - opts.switch = 'ovsbr'
> -- elif opts.switch not in ( 'ovsbr', 'lxbr' ):
> -+ elif opts.switch not in ( 'ovsbr', 'sysbr' ):
> - raise Exception( "Could not find a default controller "
> - "for switch %s" %
> - opts.switch )
> -@@ -387,8 +387,7 @@ class MininetRunner( object ):
> - if opts.ensure_value( 'nat', False ):
> - mn.addNAT( *opts.nat_args, **opts.nat_kwargs ).configDefault()
> -
> -- # --custom files can set CLI or change mininet.cli.CLI
> -- globals().setdefault( 'CLI', mininet.cli.CLI )
> -+ CLI = mininet.cli.CLI
> -
> - if opts.pre:
> - CLI( mn, script=opts.pre )
> Index: patches/patch-examples_linearbandwidth_py
> ===================================================================
> RCS file: patches/patch-examples_linearbandwidth_py
> diff -N patches/patch-examples_linearbandwidth_py
> --- patches/patch-examples_linearbandwidth_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,29 +0,0 @@
> -$OpenBSD: patch-examples_linearbandwidth_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -KernelSwitch is either OVS or if_switch
> -Index: examples/linearbandwidth.py
> ---- examples/linearbandwidth.py.orig
> -+++ examples/linearbandwidth.py
> -@@ -1,4 +1,4 @@
> --#!/usr/bin/python
> -+#!/usr/bin/env python
> -
> - """
> - Test bandwidth (using iperf) on linear networks of varying size,
> -@@ -25,7 +25,7 @@ of switches, this example demonstrates:
> -
> -
> - from mininet.net import Mininet
> --from mininet.node import UserSwitch, OVSKernelSwitch, Controller
> -+from mininet.node import UserSwitch, KernelSwitch, Controller
> - from mininet.topo import Topo
> - from mininet.log import lg, info
> - from mininet.util import irange, quietRun
> -@@ -71,7 +71,7 @@ def linearBandwidthTest( lengths ):
> - hostCount = switchCount + 1
> -
> - switches = { 'reference user': UserSwitch,
> -- 'Open vSwitch kernel': OVSKernelSwitch }
> -+ 'Open vSwitch kernel': KernelSwitch }
> -
> - # UserSwitch is horribly slow with recent kernels.
> - # We can reinstate it once its performance is fixed
> Index: patches/patch-examples_miniedit_py
> ===================================================================
> RCS file: patches/patch-examples_miniedit_py
> diff -N patches/patch-examples_miniedit_py
> --- patches/patch-examples_miniedit_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,53 +0,0 @@
> -$OpenBSD: patch-examples_miniedit_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -KernelSwitch - OVS or if_switch
> -Index: examples/miniedit.py
> ---- examples/miniedit.py.orig
> -+++ examples/miniedit.py
> -@@ -1,4 +1,4 @@
> --#!/usr/bin/python
> -+#!/usr/bin/env python
> -
> - """
> - MiniEdit: a simple network editor for Mininet
> -@@ -1686,12 +1686,12 @@ class MiniEdit( Frame ):
> - # debug( "Now saving under %s\n" % fileName )
> - f = open(fileName, 'wb')
> -
> -- f.write("#!/usr/bin/python\n")
> -+ f.write("#!/usr/bin/env python\n")
> - f.write("\n")
> - f.write("from mininet.net import Mininet\n")
> - f.write("from mininet.node import Controller, RemoteController, OVSController\n")
> - f.write("from mininet.node import CPULimitedHost, Host, Node\n")
> -- f.write("from mininet.node import OVSKernelSwitch, UserSwitch\n")
> -+ f.write("from mininet.node import KernelSwitch, UserSwitch\n")
> - if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
> - f.write("from mininet.node import IVSSwitch\n")
> - f.write("from mininet.cli import CLI\n")
> -@@ -1769,7 +1769,7 @@ class MiniEdit( Frame ):
> - f.write(" "+name+" = net.addHost('"+name+"', cls=Node, ip='0.0.0.0')\n")
> - f.write(" "+name+".cmd('sysctl -w net.ipv4.ip_forward=1')\n")
> - if 'LegacySwitch' in tags:
> -- f.write(" "+name+" = net.addSwitch('"+name+"', cls=OVSKernelSwitch, failMode='standalone')\n")
> -+ f.write(" "+name+" = net.addSwitch('"+name+"', cls=KernelSwitch, failMode='standalone')\n")
> - if 'Switch' in tags:
> - opts = self.switchOpts[name]
> - nodeNum = opts['nodeNum']
> -@@ -1782,7 +1782,7 @@ class MiniEdit( Frame ):
> - elif self.appPrefs['switchType'] == 'userns':
> - f.write(", cls=UserSwitch, inNamespace=True")
> - else:
> -- f.write(", cls=OVSKernelSwitch")
> -+ f.write(", cls=KernelSwitch")
> - elif opts['switchType'] == 'ivs':
> - f.write(", cls=IVSSwitch")
> - elif opts['switchType'] == 'user':
> -@@ -1790,7 +1790,7 @@ class MiniEdit( Frame ):
> - elif opts['switchType'] == 'userns':
> - f.write(", cls=UserSwitch, inNamespace=True")
> - else:
> -- f.write(", cls=OVSKernelSwitch")
> -+ f.write(", cls=KernelSwitch")
> - if 'dpctl' in opts:
> - f.write(", listenPort="+opts['dpctl'])
> - if 'dpid' in opts:
> Index: patches/patch-examples_multitest_py
> ===================================================================
> RCS file: patches/patch-examples_multitest_py
> diff -N patches/patch-examples_multitest_py
> --- patches/patch-examples_multitest_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,32 +0,0 @@
> -$OpenBSD: patch-examples_multitest_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -KernelSwitch - OVS or if_switch
> -Index: examples/multitest.py
> ---- examples/multitest.py.orig
> -+++ examples/multitest.py
> -@@ -1,4 +1,4 @@
> --#!/usr/bin/python
> -+#!/usr/bin/env python
> -
> - """
> - This example shows how to create a network and run multiple tests.
> -@@ -8,7 +8,7 @@ For a more complicated test example, see udpbwtest.py.
> - from mininet.cli import CLI
> - from mininet.log import lg, info
> - from mininet.net import Mininet
> --from mininet.node import OVSKernelSwitch
> -+from mininet.node import KernelSwitch
> - from mininet.topolib import TreeTopo
> -
> - def ifconfigTest( net ):
> -@@ -20,9 +20,9 @@ def ifconfigTest( net ):
> - if __name__ == '__main__':
> - lg.setLogLevel( 'info' )
> - info( "*** Initializing Mininet and kernel modules\n" )
> -- OVSKernelSwitch.setup()
> -+ KernelSwitch.setup()
> - info( "*** Creating network\n" )
> -- network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch )
> -+ network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=KernelSwitch )
> - info( "*** Starting network\n" )
> - network.start()
> - info( "*** Running ping test\n" )
> Index: patches/patch-examples_treeping64_py
> ===================================================================
> RCS file: patches/patch-examples_treeping64_py
> diff -N patches/patch-examples_treeping64_py
> --- patches/patch-examples_treeping64_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,27 +0,0 @@
> -$OpenBSD: patch-examples_treeping64_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -
> -Index: examples/treeping64.py
> ---- examples/treeping64.py.orig
> -+++ examples/treeping64.py
> -@@ -1,10 +1,10 @@
> --#!/usr/bin/python
> -+#!/usr/bin/env python
> -
> - "Create a 64-node tree network, and test connectivity using ping."
> -
> -
> - from mininet.log import setLogLevel, info
> --from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch
> -+from mininet.node import UserSwitch, KernelSwitch # , KernelSwitch
> - from mininet.topolib import TreeNet
> -
> - def treePing64():
> -@@ -13,7 +13,7 @@ def treePing64():
> - results = {}
> - switches = { # 'reference kernel': KernelSwitch,
> - 'reference user': UserSwitch,
> -- 'Open vSwitch kernel': OVSKernelSwitch }
> -+ 'kernel': KernelSwitch }
> -
> - for name in switches:
> - info( "*** Testing", name, "datapath\n" )
> Index: patches/patch-mininet_baseintf_py
> ===================================================================
> RCS file: patches/patch-mininet_baseintf_py
> diff -N patches/patch-mininet_baseintf_py
> --- patches/patch-mininet_baseintf_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,185 +0,0 @@
> -$OpenBSD: patch-mininet_baseintf_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -OS-agnostic parts of link.py's Intf class.
> -Index: mininet/baseintf.py
> ---- mininet/baseintf.py.orig
> -+++ mininet/baseintf.py
> -@@ -0,0 +1,179 @@
> -+"""
> -+The base network interface object that Links and Nodes understand, and other
> -+interface objects are based upon.
> -+"""
> -+
> -+from mininet.log import error
> -+import re
> -+
> -+class BaseIntf( object ):
> -+ """Basic interface object that can configure itself."""
> -+
> -+ def __init__( self, name, node, port=None, link=None,
> -+ 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
> -+ other arguments are passed to config()"""
> -+ self.node = node
> -+ self.name = name
> -+ self.link = link
> -+ self.mac = mac
> -+ self.ip, self.prefixLen = None, None
> -+
> -+ # if interface is lo/lo0, we know the ip is 127.0.0.1.
> -+ # This saves an ifconfig command per node
> -+ if self.name == 'lo' or self.name == 'lo0':
> -+ self.ip = '127.0.0.1'
> -+ self.prefixLen = 8
> -+ # Add to node (and move ourselves if necessary )
> -+ 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 )
> -+
> -+ def cmd( self, *args, **kwargs ):
> -+ "Run a command in our owning node"
> -+ return self.node.cmd( *args, **kwargs )
> -+
> -+ def ifconfig( self, *args ):
> -+ "Configure ourselves using ifconfig"
> -+ return self.cmd( 'ifconfig', self.name, *args )
> -+
> -+ def setIP( self, ipstr, prefixLen=None ):
> -+ """Set our IP address"""
> -+ # This is a sign that we should perhaps rethink our prefix
> -+ # mechanism and/or the way we specify IP addresses
> -+ if '/' in ipstr:
> -+ self.ip, self.prefixLen = ipstr.split( '/' )
> -+ return self.ifconfig( ipstr, 'up' )
> -+ else:
> -+ if prefixLen is None:
> -+ raise Exception( 'No prefix length set for IP address %s'
> -+ % ( ipstr, ) )
> -+ self.ip, self.prefixLen = ipstr, prefixLen
> -+ return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
> -+
> -+ def setMAC( self, macstr ):
> -+ """Set the MAC address for an interface.
> -+ macstr: MAC address as string"""
> -+ pass
> -+
> -+ _ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
> -+ _macMatchRegex = re.compile( r'..:..:..:..:..:..' )
> -+
> -+ def updateIP( self ):
> -+ "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 )
> -+ ips = self._ipMatchRegex.findall( ifconfig )
> -+ self.ip = ips[ 0 ] if ips else None
> -+ return self.ip
> -+
> -+ def updateMAC( self ):
> -+ "Return updated MAC address based on ifconfig"
> -+ ifconfig = self.ifconfig()
> -+ macs = self._macMatchRegex.findall( ifconfig )
> -+ self.mac = macs[ 0 ] if macs else None
> -+ return self.mac
> -+
> -+ # Instead of updating ip and mac separately,
> -+ # use one ifconfig call to do it simultaneously.
> -+ # This saves an ifconfig command, which improves performance.
> -+
> -+ def updateAddr( self ):
> -+ "Return IP address and MAC address based on ifconfig."
> -+ ifconfig = self.ifconfig()
> -+ ips = self._ipMatchRegex.findall( ifconfig )
> -+ macs = self._macMatchRegex.findall( ifconfig )
> -+ self.ip = ips[ 0 ] if ips else None
> -+ self.mac = macs[ 0 ] if macs else None
> -+ return self.ip, self.mac
> -+
> -+ def IP( self ):
> -+ "Return IP address"
> -+ return self.ip
> -+
> -+ def MAC( self ):
> -+ "Return MAC address"
> -+ return self.mac
> -+
> -+ def isUp( self, setUp=False ):
> -+ "Return whether interface is up"
> -+ if setUp:
> -+ cmdOutput = self.ifconfig( 'up' )
> -+ # no output indicates success
> -+ if cmdOutput:
> -+ error( "Error setting %s up: %s " % ( self.name, cmdOutput ) )
> -+ return False
> -+ else:
> -+ return True
> -+ else:
> -+ return "UP" in self.ifconfig()
> -+
> -+ def rename( self, newname ):
> -+ """Rename interface"""
> -+ pass
> -+
> -+ # The reason why we configure things in this way is so
> -+ # That the parameters can be listed and documented in
> -+ # the config method.
> -+ # Dealing with subclasses and superclasses is slightly
> -+ # annoying, but at least the information is there!
> -+
> -+ def setParam( self, results, method, **param ):
> -+ """Internal method: configure a *single* parameter
> -+ results: dict of results to update
> -+ method: config method name
> -+ param: arg=value (ignore if value=None)
> -+ value may also be list or dict"""
> -+ name, value = param.items()[ 0 ]
> -+ f = getattr( self, method, None )
> -+ if not f or value is None:
> -+ return
> -+ if isinstance( value, list ):
> -+ result = f( *value )
> -+ elif isinstance( value, dict ):
> -+ result = f( **value )
> -+ else:
> -+ result = f( value )
> -+ results[ name ] = result
> -+ return result
> -+
> -+ def config( self, mac=None, ip=None, ifconfig=None,
> -+ up=True, **_params ):
> -+ """Configure Node according to (optional) parameters:
> -+ mac: MAC address
> -+ ip: IP address
> -+ ifconfig: arbitrary interface configuration
> -+ Subclasses should override this method and call
> -+ the parent class's config(**params)"""
> -+ # If we were overriding this method, we would call
> -+ # the superclass config method here as follows:
> -+ # r = Parent.config( **params )
> -+ r = {}
> -+ self.setParam( r, 'setMAC', mac=mac )
> -+ self.setParam( r, 'setIP', ip=ip )
> -+ self.setParam( r, 'isUp', up=up )
> -+ self.setParam( r, 'ifconfig', ifconfig=ifconfig )
> -+ return r
> -+
> -+ def delete( self ):
> -+ "Delete interface"
> -+ pass
> -+
> -+ def status( self ):
> -+ "Return intf status as a string"
> -+ pass
> -+
> -+ def __repr__( self ):
> -+ return '<%s %s>' % ( self.__class__.__name__, self.name )
> -+
> -+ def __str__( self ):
> -+ return self.name
> Index: patches/patch-mininet_basenode_py
> ===================================================================
> RCS file: patches/patch-mininet_basenode_py
> diff -N patches/patch-mininet_basenode_py
> --- patches/patch-mininet_basenode_py 7 Dec 2017 06:33:40 -0000 1.3
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,546 +0,0 @@
> -$OpenBSD: patch-mininet_basenode_py,v 1.3 2017/12/07 06:33:40 akoshibe Exp $
> -OS-agnostic parts of node.py.
> -Turn on PID tracking by default since it's used to reliably stop
> -processes.
> -Index: mininet/basenode.py
> ---- mininet/basenode.py.orig
> -+++ mininet/basenode.py
> -@@ -0,0 +1,538 @@
> -+"""
> -+The base node object that other network nodes are based upon. A node implements
> -+the lightweight virtualization needed to implement hosts and switches.
> -+"""
> -+import os
> -+import pty
> -+import re
> -+import select
> -+from subprocess import Popen, PIPE
> -+
> -+plat = os.uname()[ 0 ]
> -+if plat == 'FreeBSD':
> -+ from mininet.freebsd.util import LO, moveIntf
> -+elif plat == 'Linux':
> -+ from mininet.linux.util import LO, moveIntf
> -+else:
> -+ from mininet.openbsd.util import LO, moveIntf
> -+
> -+from mininet.log import info, error, warn, debug
> -+from mininet.util import quietRun
> -+from mininet.moduledeps import pathCheck
> -+from mininet.link import Link
> -+from re import findall
> -+
> -+
> -+class BaseNode( object ):
> -+ """A virtual network node is simply a shell in a network namespace.
> -+ We communicate with it using pipes."""
> -+
> -+ portBase = 0 # Nodes always start with eth0/port0, even in OF 1.0
> -+
> -+ def __init__( self, name, inNamespace=True, **params ):
> -+ """name: name of node
> -+ inNamespace: in network namespace?
> -+ privateDirs: list of private directory strings or tuples
> -+ params: Node parameters (see config() for details)"""
> -+
> -+ # Make sure class actually works
> -+ self.checkSetup()
> -+
> -+ self.name = params.get( 'name', name )
> -+ self.privateDirs = params.get( 'privateDirs', [] )
> -+ self.inNamespace = params.get( 'inNamespace', inNamespace )
> -+
> -+ # Stash configuration parameters for future reference
> -+ self.params = params
> -+
> -+ self.intfs = {} # dict of port numbers to interfaces
> -+ self.ports = {} # dict of interfaces to port numbers
> -+ # replace with Port objects, eventually ?
> -+ self.nameToIntf = {} # dict of interface names to Intfs
> -+
> -+ # Make pylint happy
> -+ ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
> -+ self.lastPid, self.lastCmd, self.pollOut ) = (
> -+ None, None, None, None, None, None, None, None )
> -+ self.waiting = False
> -+ self.readbuf = ''
> -+
> -+ # Start command interpreter shell
> -+ self.startShell()
> -+ self.mountPrivateDirs()
> -+
> -+ # File descriptor to node mapping support
> -+ # Class variables and methods
> -+
> -+ inToNode = {} # mapping of input fds to nodes
> -+ outToNode = {} # mapping of output fds to nodes
> -+
> -+ @classmethod
> -+ def fdToNode( cls, fd ):
> -+ """Return node corresponding to given file descriptor.
> -+ fd: file descriptor
> -+ returns: node"""
> -+ node = cls.outToNode.get( fd )
> -+ return node or cls.inToNode.get( fd )
> -+
> -+ def getShell( self, master, slave, mnopts=None ):
> -+ # OS-specific virtualization method - overriden in system nodes
> -+ pass
> -+
> -+ def isShellBuiltin( self, cmd ):
> -+ # Shell-specific check - overridden in system nodes
> -+ pass
> -+
> -+ # Command support via shell process in namespace
> -+ def startShell( self, mnopts=None ):
> -+ "Start a shell process for running commands"
> -+ if self.shell:
> -+ error( "%s: shell is already running\n" % self.name )
> -+ return
> -+
> -+ # Spawn a shell subprocess in a pseudo-tty, to disable buffering
> -+ # in the subprocess and insulate it from signals (e.g. SIGINT)
> -+ # received by the parent
> -+ master, slave = pty.openpty()
> -+ self.shell = self.getShell( master, slave, mnopts )
> -+ self.stdin = os.fdopen( master, 'rw' )
> -+ self.stdout = self.stdin
> -+ self.pid = self.shell.pid
> -+ self.pollOut = select.poll()
> -+ self.pollOut.register( self.stdout )
> -+ # Maintain mapping between file descriptors and nodes
> -+ # This is useful for monitoring multiple nodes
> -+ # using select.poll()
> -+ self.outToNode[ self.stdout.fileno() ] = self
> -+ self.inToNode[ self.stdin.fileno() ] = self
> -+ self.execed = False
> -+ self.lastCmd = None
> -+ self.lastPid = None
> -+ self.readbuf = ''
> -+ # Wait for prompt
> -+ while True:
> -+ data = self.read( 1024 )
> -+ if data[ -1 ] == chr( 127 ):
> -+ break
> -+ self.pollOut.poll()
> -+ self.waiting = False
> -+ # +m: disable job control notification
> -+ self.cmd( 'unset HISTFILE; stty -echo; set +m' )
> -+
> -+ def mountPrivateDirs( self ):
> -+ "mount private directories - overridden"
> -+ pass
> -+
> -+ def unmountPrivateDirs( self ):
> -+ "mount private directories - overridden"
> -+ pass
> -+
> -+ def _popen( self, cmd, **params ):
> -+ """Internal method: spawn and return a process
> -+ cmd: command to run (list)
> -+ params: parameters to Popen()"""
> -+ # Leave this is as an instance method for now
> -+ assert self
> -+ return Popen( cmd, **params )
> -+
> -+ def cleanup( self ):
> -+ "Help python collect its garbage."
> -+ self.shell = None
> -+
> -+ # Subshell I/O, commands and control
> -+
> -+ def read( self, maxbytes=1024 ):
> -+ """Buffered read from node, potentially blocking.
> -+ maxbytes: maximum number of bytes to return"""
> -+ count = len( self.readbuf )
> -+ if count < maxbytes:
> -+ data = os.read( self.stdout.fileno(), maxbytes - count )
> -+ self.readbuf += data
> -+ if maxbytes >= len( self.readbuf ):
> -+ result = self.readbuf
> -+ self.readbuf = ''
> -+ else:
> -+ result = self.readbuf[ :maxbytes ]
> -+ self.readbuf = self.readbuf[ maxbytes: ]
> -+ return result
> -+
> -+ def readline( self ):
> -+ """Buffered readline from node, potentially blocking.
> -+ returns: line (minus newline) or None"""
> -+ self.readbuf += self.read( 1024 )
> -+ if '\n' not in self.readbuf:
> -+ return None
> -+ pos = self.readbuf.find( '\n' )
> -+ line = self.readbuf[ 0: pos ]
> -+ self.readbuf = self.readbuf[ pos + 1: ]
> -+ return line
> -+
> -+ # overridden in some platforms
> -+ def write( self, data ):
> -+ """Write data to node.
> -+ data: string"""
> -+ os.write( self.stdin.fileno(), data )
> -+
> -+ def terminate( self ):
> -+ "Send kill signal to Node and clean up after it."
> -+ pass
> -+
> -+ def stop( self, deleteIntfs=False ):
> -+ """Stop node.
> -+ deleteIntfs: delete interfaces? (False)"""
> -+ if deleteIntfs:
> -+ self.deleteIntfs()
> -+ self.terminate()
> -+
> -+ def waitReadable( self, timeoutms=None ):
> -+ """Wait until node's output is readable.
> -+ timeoutms: timeout in ms or None to wait indefinitely.
> -+ returns: result of poll()"""
> -+ if len( self.readbuf ) == 0:
> -+ return self.pollOut.poll( timeoutms )
> -+
> -+ def sendCmd( self, *args, **kwargs ):
> -+ """Send a command, followed by a command to echo a sentinel,
> -+ and return without waiting for the command to complete.
> -+ args: command and arguments, or string
> -+ printPid: print command's PID? (False)"""
> -+ assert self.shell and not self.waiting
> -+ printPid = kwargs.get( 'printPid', True )
> -+ # Allow sendCmd( [ list ] )
> -+ if len( args ) == 1 and isinstance( args[ 0 ], list ):
> -+ cmd = args[ 0 ]
> -+ # Allow sendCmd( cmd, arg1, arg2... )
> -+ elif len( args ) > 0:
> -+ cmd = args
> -+ # Convert to string
> -+ if not isinstance( cmd, str ):
> -+ cmd = ' '.join( [ str( c ) for c in cmd ] )
> -+ if not re.search( r'\w', cmd ):
> -+ # Replace empty commands with something harmless
> -+ cmd = 'echo -n'
> -+ self.lastCmd = cmd
> -+ # if a builtin command is backgrounded, it still yields a PID
> -+ if len( cmd ) > 0 and cmd[ -1 ] == '&':
> -+ # print ^A{pid}\n so monitor() can set lastPid
> -+ cmd += ' printf "\\001%d\\012" $! '
> -+ elif printPid and not self.isShellBuiltin( cmd ):
> -+ cmd = 'mnexec -p ' + cmd
> -+ self.write( cmd + '\n' )
> -+ self.lastPid = None
> -+ self.waiting = True
> -+
> -+ def monitor( self, timeoutms=None, findPid=True ):
> -+ """Monitor and return the output of a command.
> -+ Set self.waiting to False if command has completed.
> -+ timeoutms: timeout in ms or None to wait indefinitely
> -+ findPid: look for PID from mnexec -p"""
> -+ ready = self.waitReadable( timeoutms )
> -+ if not ready:
> -+ return ''
> -+ data = self.read( 1024 )
> -+ pidre = r'\[\d+\] \d+\r\n'
> -+ # Look for PID
> -+ marker = chr( 1 ) + r'\d+\r\n'
> -+ if findPid and chr( 1 ) in data:
> -+ # suppress the job and PID of a backgrounded command
> -+ if re.findall( pidre, data ):
> -+ data = re.sub( pidre, '', data )
> -+ # Marker can be read in chunks; continue until all of it is read
> -+ while not re.findall( marker, data ):
> -+ data += self.read( 1024 )
> -+ markers = re.findall( marker, data )
> -+ if markers:
> -+ self.lastPid = int( markers[ 0 ][ 1: ] )
> -+ data = re.sub( marker, '', data )
> -+ # Look for sentinel/EOF
> -+ if len( data ) > 0 and data[ -1 ] == chr( 127 ):
> -+ self.waiting = False
> -+ data = data[ :-1 ]
> -+ elif chr( 127 ) in data:
> -+ self.waiting = False
> -+ data = data.replace( chr( 127 ), '' )
> -+ return data
> -+
> -+ def waitOutput( self, verbose=False, findPid=True ):
> -+ """Wait for a command to complete.
> -+ Completion is signaled by a sentinel character, ASCII(127)
> -+ appearing in the output stream. Wait for the sentinel and return
> -+ the output, including trailing newline.
> -+ verbose: print output interactively"""
> -+ log = info if verbose else debug
> -+ output = ''
> -+ while self.waiting:
> -+ data = self.monitor( findPid=findPid )
> -+ output += data
> -+ log( data )
> -+ return output
> -+
> -+ def cmd( self, *args, **kwargs ):
> -+ """Send a command, wait for output, and return it.
> -+ cmd: string"""
> -+ verbose = kwargs.get( 'verbose', False )
> -+ log = info if verbose else debug
> -+ log( '*** %s : %s\n' % ( self.name, args ) )
> -+ if self.shell:
> -+ self.sendCmd( *args, **kwargs )
> -+ return self.waitOutput( verbose )
> -+ else:
> -+ warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
> -+
> -+ def cmdPrint( self, *args):
> -+ """Call cmd and printing its output
> -+ cmd: string"""
> -+ return self.cmd( *args, **{ 'verbose': True } )
> -+
> -+ def popen( self, *args, **kwargs ):
> -+ """Return a Popen() object in our namespace
> -+ args: Popen() args, single list, or string
> -+ kwargs: Popen() keyword args"""
> -+ pass
> -+
> -+ def pexec( self, *args, **kwargs ):
> -+ """Execute a command using popen
> -+ returns: out, err, exitcode"""
> -+ popen = self.popen( *args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
> -+ **kwargs )
> -+ # Warning: this can fail with large numbers of fds!
> -+ out, err = popen.communicate()
> -+ exitcode = popen.wait()
> -+ return out, err, exitcode
> -+
> -+ # Interface management, configuration, and routing
> -+
> -+ # BL notes: This might be a bit redundant or over-complicated.
> -+ # However, it does allow a bit of specialization, including
> -+ # changing the canonical interface names. It's also tricky since
> -+ # the real interfaces are created as veth pairs, so we can't
> -+ # make a single interface at a time.
> -+
> -+ def newPort( self ):
> -+ "Return the next port number to allocate."
> -+ if len( self.ports ) > 0:
> -+ return max( self.ports.values() ) + 1
> -+ return self.portBase
> -+
> -+ def addIntf( self, intf, port=None, moveIntfFn=moveIntf ):
> -+ """Add an interface.
> -+ intf: interface
> -+ port: port number (optional, typically OpenFlow port number)
> -+ moveIntfFn: function to move interface (optional)"""
> -+ if port is None:
> -+ port = self.newPort()
> -+ self.intfs[ port ] = intf
> -+ self.ports[ intf ] = port
> -+ self.nameToIntf[ intf.name ] = intf
> -+ debug( '\n' )
> -+ debug( 'added intf %s (%d) to node %s\n' % (
> -+ intf, port, self.name ) )
> -+ if self.inNamespace:
> -+ debug( 'moving', intf, 'into namespace for', self.name, '\n' )
> -+ moveIntfFn( intf.name, self )
> -+
> -+ def delIntf( self, intf ):
> -+ """Remove interface from Node's known interfaces
> -+ Note: to fully delete interface, call intf.delete() instead"""
> -+ port = self.ports.get( intf )
> -+ if port is not None:
> -+ del self.intfs[ port ]
> -+ del self.ports[ intf ]
> -+ del self.nameToIntf[ intf.name ]
> -+
> -+ def defaultIntf( self ):
> -+ "Return interface for lowest port"
> -+ ports = self.intfs.keys()
> -+ if ports:
> -+ return self.intfs[ min( ports ) ]
> -+ else:
> -+ warn( '*** defaultIntf: warning:', self.name,
> -+ 'has no interfaces\n' )
> -+
> -+ def intf( self, intf=None ):
> -+ """Return our interface object with given string name,
> -+ default intf if name is falsy (None, empty string, etc).
> -+ or the input intf arg.
> -+
> -+ Having this fcn return its arg for Intf objects makes it
> -+ easier to construct functions with flexible input args for
> -+ interfaces (those that accept both string names and Intf objects).
> -+ """
> -+ if not intf:
> -+ return self.defaultIntf()
> -+ elif isinstance( intf, basestring ):
> -+ return self.nameToIntf[ intf ]
> -+ else:
> -+ return intf
> -+
> -+ def connectionsTo( self, node):
> -+ "Return [ intf1, intf2... ] for all intfs that connect self to node."
> -+ # We could optimize this if it is important
> -+ connections = []
> -+ for intf in self.intfList():
> -+ link = intf.link
> -+ if link:
> -+ node1, node2 = link.intf1.node, link.intf2.node
> -+ if node1 == self and node2 == node:
> -+ connections += [ ( intf, link.intf2 ) ]
> -+ elif node1 == node and node2 == self:
> -+ connections += [ ( intf, link.intf1 ) ]
> -+ return connections
> -+
> -+ def deleteIntfs( self, checkName=True ):
> -+ """Delete all of our interfaces.
> -+ checkName: only delete interfaces that contain our name"""
> -+ # In theory the interfaces should go away after we shut down.
> -+ # However, this takes time, so we're better off removing them
> -+ # explicitly so that we won't get errors if we run before they
> -+ # have been removed by the kernel. Unfortunately this is very slow,
> -+ # at least with Linux kernels before 2.6.33
> -+ for intf in self.intfs.values():
> -+ # Protect against deleting hardware interfaces
> -+ if ( self.name in intf.name ) or ( not checkName ):
> -+ intf.delete()
> -+ info( '.' )
> -+
> -+ # Routing support
> -+
> -+ def setARP( self, ip, mac ):
> -+ """Add an ARP entry.
> -+ ip: IP address as string
> -+ mac: MAC address as string"""
> -+ result = self.cmd( 'arp', '-s', ip, mac )
> -+ return result
> -+
> -+ def setHostRoute( self, ip, intf ):
> -+ """Add route to host.
> -+ ip: IP address as dotted decimal
> -+ intf: string, interface name"""
> -+ pass
> -+
> -+ def setDefaultRoute( self, intf=None ):
> -+ """Set the default route to go through intf.
> -+ intf: Intf or {dev <intfname> via <gw-ip> ...}"""
> -+ pass
> -+
> -+ # Convenience and configuration methods
> -+
> -+ def setMAC( self, mac, intf=None ):
> -+ """Set the MAC address for an interface.
> -+ intf: intf or intf name
> -+ mac: MAC address as string"""
> -+ return self.intf( intf ).setMAC( mac )
> -+
> -+ def setIP( self, ip, prefixLen=8, intf=None, **kwargs ):
> -+ """Set the IP address for an interface.
> -+ intf: intf or intf name
> -+ ip: IP address as a string
> -+ prefixLen: prefix length, e.g. 8 for /8 or 16M addrs
> -+ kwargs: any additional arguments for intf.setIP"""
> -+ return self.intf( intf ).setIP( ip, prefixLen, **kwargs )
> -+
> -+ def IP( self, intf=None ):
> -+ "Return IP address of a node or specific interface."
> -+ return self.intf( intf ).IP()
> -+
> -+ def MAC( self, intf=None ):
> -+ "Return MAC address of a node or specific interface."
> -+ return self.intf( intf ).MAC()
> -+
> -+ def intfIsUp( self, intf=None ):
> -+ "Check if an interface is up."
> -+ return self.intf( intf ).isUp()
> -+
> -+ # The reason why we configure things in this way is so
> -+ # That the parameters can be listed and documented in
> -+ # the config method.
> -+ # Dealing with subclasses and superclasses is slightly
> -+ # annoying, but at least the information is there!
> -+
> -+ def setParam( self, results, method, **param ):
> -+ """Internal method: configure a *single* parameter
> -+ results: dict of results to update
> -+ method: config method name
> -+ param: arg=value (ignore if value=None)
> -+ value may also be list or dict"""
> -+ name, value = param.items()[ 0 ]
> -+ if value is None:
> -+ return
> -+ f = getattr( self, method, None )
> -+ if not f:
> -+ return
> -+ if isinstance( value, list ):
> -+ result = f( *value )
> -+ elif isinstance( value, dict ):
> -+ result = f( **value )
> -+ else:
> -+ result = f( value )
> -+ results[ name ] = result
> -+ return result
> -+
> -+ def config( self, mac=None, ip=None,
> -+ defaultRoute=None, lo='up', **_params ):
> -+ """Configure Node according to (optional) parameters:
> -+ mac: MAC address for default interface
> -+ ip: IP address for default interface
> -+ ifconfig: arbitrary interface configuration
> -+ Subclasses should override this method and call
> -+ the parent class's config(**params)"""
> -+ # If we were overriding this method, we would call
> -+ # the superclass config method here as follows:
> -+ # r = Parent.config( **_params )
> -+ r = {}
> -+ self.setParam( r, 'setMAC', mac=mac )
> -+ self.setParam( r, 'setIP', ip=ip )
> -+ self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute )
> -+ # This should be examined
> -+ self.cmd( 'ifconfig %s %s' % ( LO, lo ) )
> -+ return r
> -+
> -+ def configDefault( self, **moreParams ):
> -+ "Configure with default parameters"
> -+ self.params.update( moreParams )
> -+ self.config( **self.params )
> -+
> -+ # This is here for backward compatibility
> -+ def linkTo( self, node, link=Link ):
> -+ """(Deprecated) Link to another node
> -+ replace with Link( node1, node2)"""
> -+ return link( self, node )
> -+
> -+ # Other methods
> -+
> -+ def intfList( self ):
> -+ "List of our interfaces sorted by port number"
> -+ return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
> -+
> -+ def intfNames( self ):
> -+ "The names of our interfaces sorted by port number"
> -+ return [ str( i ) for i in self.intfList() ]
> -+
> -+ def __repr__( self ):
> -+ "More informative string representation"
> -+ intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
> -+ for i in self.intfList() ] ) )
> -+ return '<%s %s: %s pid=%s> ' % (
> -+ self.__class__.__name__, self.name, intfs, self.pid )
> -+
> -+ def __str__( self ):
> -+ "Abbreviated string representation"
> -+ return self.name
> -+
> -+ # Automatic class setup support
> -+
> -+ isSetup = False
> -+
> -+ @classmethod
> -+ def checkSetup( cls ):
> -+ "Make sure our class and superclasses are set up"
> -+ while cls and not getattr( cls, 'isSetup', True ):
> -+ cls.setup()
> -+ cls.isSetup = True
> -+ # Make pylint happy
> -+ cls = getattr( type( cls ), '__base__', None )
> -+
> -+ @classmethod
> -+ def setup( cls ):
> -+ "Make sure our class dependencies are available"
> -+ pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
> Index: patches/patch-mininet_clean_py
> ===================================================================
> RCS file: patches/patch-mininet_clean_py
> diff -N patches/patch-mininet_clean_py
> --- patches/patch-mininet_clean_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,189 +0,0 @@
> -$OpenBSD: patch-mininet_clean_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -
> -Index: mininet/clean.py
> ---- mininet/clean.py.orig
> -+++ mininet/clean.py
> -@@ -9,7 +9,7 @@ code), this script may be used to get rid of unwanted
> - It may also get rid of 'false positives', but hopefully
> - nothing irreplaceable!
> - """
> --
> -+from os import uname
> - from subprocess import ( Popen, PIPE, check_output as co,
> - CalledProcessError )
> - import time
> -@@ -17,27 +17,114 @@ import time
> - from mininet.log import info
> - from mininet.term import cleanUpScreens
> -
> --
> - def sh( cmd ):
> - "Print a command and send it to the shell"
> - info( cmd + '\n' )
> - return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
> -
> --def killprocs( pattern ):
> -+def _coPids( pattern ):
> -+ try:
> -+ return co( [ 'pgrep', '-f', pattern ] )
> -+ except CalledProcessError:
> -+ return ''
> -+
> -+def _popenPids( pattern ):
> -+ try:
> -+ p1 = Popen( [ 'ps' ], stdout=PIPE )
> -+ pids = co( [ 'awk', '/%s/{print $1}' % pattern ], stdin=p1.stdout )
> -+ p1.stdout.close()
> -+ return pids
> -+ except CalledProcessError:
> -+ return ''
> -+
> -+def killprocs( pidsFunc, 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', pattern ] )
> -- except CalledProcessError:
> -- pids = ''
> -+ pids = pidsFunc( pattern )
> - if pids:
> - sh( 'pkill -9 -f %s' % pattern )
> - time.sleep( .5 )
> - else:
> - break
> -
> -+def killnodes( pidsFunc, pattern='[m]ininet' ):
> -+ "kill processes representing nodes"
> -+ killprocs( pidsFunc, pattern )
> -+ nodes = sh( 'jls name' ).split('\n')
> -+ for node in nodes:
> -+ if 'mininet:' in node:
> -+ sh( 'jail -r %s 2>/dev/null' % node )
> -+
> -+def _iplinkClean( listCmd=None ):
> -+ """ link cleanup using 'ip link' """
> -+ 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' )
> -+
> -+def _ifClean( listCmd ):
> -+ links = sh( listCmd ).splitlines()
> -+ # Delete blocks of links
> -+ n = 1000 # chunk size
> -+ for i in range( 0, len( links ), n ):
> -+ cmd = ';'.join( 'ifconfig %s destroy' % link
> -+ for link in links[ i : i + n ] )
> -+ sh( '( %s ) 2> /dev/null' % cmd )
> -+
> -+def _ifcfgClean( listCmd ):
> -+ """ link cleanup with 'ifconfig'"""
> -+ _ifClean( listCmd )
> -+ if 'tap9' in sh( 'ifconfig' ):
> -+ info( "*** Removing tap9 - assuming it's from cluster edition\n" )
> -+ sh( 'ifconfig tap9 destroy' )
> -+
> -+def _ifcfgCleanLo( listCmd ):
> -+ """ link cleanup with 'ifconfig' that assumes formattable listCmd
> -+ that takes an interface unit name (see args for OpenBSD)"""
> -+ _ifClean( listCmd % 'switch' )
> -+ _ifcfgClean( listCmd % 'pair' )
> -+
> -+ los = sh( listCmd % 'lo' ).splitlines()
> -+ n = 256 # chunk size - can only have 256 max, per rdomain(4)
> -+ for i in range( 1, len( los ), n ):
> -+ cmd = ';'.join( 'ifconfig %s rdomain 0 destroy' % lo
> -+ for lo in los[ i : i + n ] )
> -+ sh( '( %s ) 2> /dev/null' % cmd )
> -+
> -+
> -+platform = uname()[ 0 ]
> -+if platform == 'FreeBSD':
> -+ cleanLinks = _ifcfgClean
> -+ args = "ifconfig -l | egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'"
> -+ pidsFunc = _popenPids
> -+ cleanNodes = killnodes
> -+ zkill_cmd = ( 'killall -9 ping mnexec ryu-manager' )
> -+elif platform == 'Linux':
> -+ cleanLinks, args = _iplinkClean, None
> -+ pidsFunc = _coPids
> -+ cleanNodes = killprocs
> -+ zkill_cmd = ( 'killall -9 controller ofprotocol ofdatapath ping nox_core'
> -+ 'lt-nox_core ovs-openflowd ovs-controller'
> -+ 'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
> -+else: # OpenBSD
> -+ cleanLinks = _ifcfgCleanLo
> -+ args = "ifconfig %s | sed -n 's|\(^[a-z]\{1,\}[0-9]\{1,\}\):.*|\\1| p'"
> -+ pidsFunc = _coPids
> -+ cleanNodes = killprocs
> -+ zkill_cmd = ( 'pkill -9 ping mnexec switchd' )
> -+
> -+
> - class Cleanup( object ):
> - "Wrapper for cleanup()"
> -
> -@@ -50,19 +137,16 @@ class Cleanup( object ):
> -
> - info( "*** Removing excess controllers/ofprotocols/ofdatapaths/"
> - "pings/noxes\n" )
> -- zombies = ( 'controller ofprotocol ofdatapath ping nox_core'
> -- 'lt-nox_core ovs-openflowd ovs-controller'
> -- 'ovs-testcontroller udpbwtest mnexec ivs ryu-manager' )
> - # 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' )
> -+ sh( zkill_cmd + ' 2> /dev/null' )
> - time.sleep( 1 )
> -- sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
> -+ sh( zkill_cmd + ' 2> /dev/null' )
> -
> - # And kill off sudo mnexec
> -- sh( 'pkill -9 -f "sudo mnexec"')
> -+ sh( 'pkill -9 -f "mnexec"')
> -
> - info( "*** Removing junk from /tmp\n" )
> - sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
> -@@ -88,26 +172,14 @@ class Cleanup( object ):
> - sh( 'ovs-vsctl del-br ' + dp )
> -
> - 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 )
> -+ cleanLinks( args )
> -
> -- 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:' )
> -+ cleanNodes( pidsFunc, '[m]ininet:' )
> -
> - info( "*** Shutting down stale tunnels\n" )
> -- killprocs( 'Tunnel=Ethernet' )
> -- killprocs( '.ssh/mn')
> -+ killprocs( pidsFunc, '[T]unnel=Ethernet' )
> -+ killprocs( pidsFunc, '.ssh\/mn' )
> - sh( 'rm -f ~/.ssh/mn/*' )
> -
> - # Call any additional cleanup code if necessary
> Index: patches/patch-mininet_cli_py
> ===================================================================
> RCS file: patches/patch-mininet_cli_py
> diff -N patches/patch-mininet_cli_py
> --- patches/patch-mininet_cli_py 10 Apr 2018 07:55:10 -0000 1.2
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,31 +0,0 @@
> -$OpenBSD: patch-mininet_cli_py,v 1.2 2018/04/10 07:55:10 akoshibe Exp $
> -split() automatically splits on whitespace.
> -Index: mininet/cli.py
> ---- mininet/cli.py.orig
> -+++ mininet/cli.py
> -@@ -356,12 +356,11 @@ class CLI( Cmd ):
> - """Run dpctl (or ovs-ofctl) command on all switches.
> - Usage: dpctl command [arg1] [arg2] ..."""
> - args = line.split()
> -- if len(args) < 1:
> -- error( 'usage: dpctl command [arg1] [arg2] ...\n' )
> -- return
> - for sw in self.mn.switches:
> -- output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
> -- output( sw.dpctl( *args ) )
> -+ res = sw.dpctl( *args )
> -+ if res:
> -+ output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
> -+ output( res )
> -
> - def do_time( self, line ):
> - "Measure time taken for any command in Mininet."
> -@@ -411,7 +410,7 @@ class CLI( Cmd ):
> - % first )
> - return
> - node = self.mn[ first ]
> -- rest = args.split( ' ' )
> -+ rest = args.split()
> - # Substitute IP addresses for node names in command
> - # If updateIP() returns None, then use node name
> - rest = [ self.mn[ arg ].defaultIntf().updateIP() or arg
> Index: patches/patch-mininet_link_py
> ===================================================================
> RCS file: patches/patch-mininet_link_py
> diff -N patches/patch-mininet_link_py
> --- patches/patch-mininet_link_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,257 +0,0 @@
> -$OpenBSD: patch-mininet_link_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -virtual ethernet and related things (Intf) moved to OS-specific dirs.
> -Index: mininet/link.py
> ---- mininet/link.py.orig
> -+++ mininet/link.py
> -@@ -23,203 +23,21 @@ TCIntf: interface with bandwidth limiting and delay vi
> -
> - Link: basic link class for creating veth pairs
> - """
> -+from os import uname
> -
> -+plat = uname()[ 0 ]
> -+if plat == 'FreeBSD':
> -+ from mininet.freebsd.intf import Intf
> -+ from mininet.freebsd.util import makeIntfPair
> -+elif plat == 'Linux':
> -+ from mininet.linux.intf import Intf
> -+ from mininet.linux.util import makeIntfPair
> -+else:
> -+ from mininet.openbsd.intf import Intf
> -+ from mininet.openbsd.util import makeIntfPair
> -+
> - from mininet.log import info, error, debug
> --from mininet.util import makeIntfPair
> --import re
> -
> --class Intf( object ):
> --
> -- "Basic interface object that can configure itself."
> --
> -- def __init__( self, name, node=None, port=None, link=None,
> -- 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
> -- other arguments are passed to config()"""
> -- self.node = node
> -- self.name = name
> -- 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 )
> -- 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 )
> --
> -- def cmd( self, *args, **kwargs ):
> -- "Run a command in our owning node"
> -- return self.node.cmd( *args, **kwargs )
> --
> -- def ifconfig( self, *args ):
> -- "Configure ourselves using ifconfig"
> -- return self.cmd( 'ifconfig', self.name, *args )
> --
> -- def setIP( self, ipstr, prefixLen=None ):
> -- """Set our IP address"""
> -- # This is a sign that we should perhaps rethink our prefix
> -- # mechanism and/or the way we specify IP addresses
> -- if '/' in ipstr:
> -- self.ip, self.prefixLen = ipstr.split( '/' )
> -- return self.ifconfig( ipstr, 'up' )
> -- else:
> -- if prefixLen is None:
> -- raise Exception( 'No prefix length set for IP address %s'
> -- % ( ipstr, ) )
> -- self.ip, self.prefixLen = ipstr, prefixLen
> -- return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) )
> --
> -- def setMAC( self, macstr ):
> -- """Set the MAC address for an interface.
> -- macstr: MAC address as string"""
> -- self.mac = macstr
> -- return ( self.ifconfig( 'down' ) +
> -- self.ifconfig( 'hw', 'ether', macstr ) +
> -- self.ifconfig( 'up' ) )
> --
> -- _ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
> -- _macMatchRegex = re.compile( r'..:..:..:..:..:..' )
> --
> -- def updateIP( self ):
> -- "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 )
> -- ips = self._ipMatchRegex.findall( ifconfig )
> -- self.ip = ips[ 0 ] if ips else None
> -- return self.ip
> --
> -- def updateMAC( self ):
> -- "Return updated MAC address based on ifconfig"
> -- ifconfig = self.ifconfig()
> -- macs = self._macMatchRegex.findall( ifconfig )
> -- self.mac = macs[ 0 ] if macs else None
> -- return self.mac
> --
> -- # Instead of updating ip and mac separately,
> -- # use one ifconfig call to do it simultaneously.
> -- # This saves an ifconfig command, which improves performance.
> --
> -- def updateAddr( self ):
> -- "Return IP address and MAC address based on ifconfig."
> -- ifconfig = self.ifconfig()
> -- ips = self._ipMatchRegex.findall( ifconfig )
> -- macs = self._macMatchRegex.findall( ifconfig )
> -- self.ip = ips[ 0 ] if ips else None
> -- self.mac = macs[ 0 ] if macs else None
> -- return self.ip, self.mac
> --
> -- def IP( self ):
> -- "Return IP address"
> -- return self.ip
> --
> -- def MAC( self ):
> -- "Return MAC address"
> -- return self.mac
> --
> -- def isUp( self, setUp=False ):
> -- "Return whether interface is up"
> -- if setUp:
> -- cmdOutput = self.ifconfig( 'up' )
> -- # no output indicates success
> -- if cmdOutput:
> -- error( "Error setting %s up: %s " % ( self.name, cmdOutput ) )
> -- return False
> -- else:
> -- return True
> -- else:
> -- return "UP" in self.ifconfig()
> --
> -- def rename( self, newname ):
> -- "Rename interface"
> -- self.ifconfig( 'down' )
> -- result = self.cmd( 'ip link set', self.name, 'name', newname )
> -- self.name = newname
> -- self.ifconfig( 'up' )
> -- return result
> --
> -- # The reason why we configure things in this way is so
> -- # That the parameters can be listed and documented in
> -- # the config method.
> -- # Dealing with subclasses and superclasses is slightly
> -- # annoying, but at least the information is there!
> --
> -- def setParam( self, results, method, **param ):
> -- """Internal method: configure a *single* parameter
> -- results: dict of results to update
> -- method: config method name
> -- param: arg=value (ignore if value=None)
> -- value may also be list or dict"""
> -- name, value = param.items()[ 0 ]
> -- f = getattr( self, method, None )
> -- if not f or value is None:
> -- return
> -- if isinstance( value, list ):
> -- result = f( *value )
> -- elif isinstance( value, dict ):
> -- result = f( **value )
> -- else:
> -- result = f( value )
> -- results[ name ] = result
> -- return result
> --
> -- def config( self, mac=None, ip=None, ifconfig=None,
> -- up=True, **_params ):
> -- """Configure Node according to (optional) parameters:
> -- mac: MAC address
> -- ip: IP address
> -- ifconfig: arbitrary interface configuration
> -- Subclasses should override this method and call
> -- the parent class's config(**params)"""
> -- # If we were overriding this method, we would call
> -- # the superclass config method here as follows:
> -- # r = Parent.config( **params )
> -- r = {}
> -- self.setParam( r, 'setMAC', mac=mac )
> -- self.setParam( r, 'setIP', ip=ip )
> -- self.setParam( r, 'isUp', up=up )
> -- self.setParam( r, 'ifconfig', ifconfig=ifconfig )
> -- return r
> --
> -- def delete( self ):
> -- "Delete interface"
> -- self.cmd( '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' )
> -- if self.name in links:
> -- return "OK"
> -- else:
> -- return "MISSING"
> --
> -- def __repr__( self ):
> -- return '<%s %s>' % ( self.__class__.__name__, self.name )
> --
> -- def __str__( self ):
> -- return self.name
> --
> --
> - class TCIntf( Intf ):
> - """Interface customized by tc (traffic control) utility
> - Allows specification of bandwidth limits (various methods)
> -@@ -253,7 +71,7 @@ class TCIntf( Intf ):
> - + 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ]
> - elif use_tbf:
> - if latency_ms is None:
> -- latency_ms = 15.0 * 8 / bw
> -+ latency_ms = 15 * 8 / bw
> - cmds += [ '%s qdisc add dev %s root handle 5: tbf ' +
> - 'rate %fMbit burst 15000 latency %fms' %
> - ( bw, latency_ms ) ]
> -@@ -401,7 +219,7 @@ class TCIntf( Intf ):
> -
> - class Link( object ):
> -
> -- """A basic link is just a veth pair.
> -+ """A basic link is just a virtual ethernet pair.
> - Other types of links could be tunnels, link emulators, etc.."""
> -
> - # pylint: disable=too-many-branches
> -@@ -447,10 +265,14 @@ class Link( object ):
> - if fast:
> - params1.setdefault( 'moveIntfFn', self._ignore )
> - params2.setdefault( 'moveIntfFn', self._ignore )
> -- self.makeIntfPair( intfName1, intfName2, addr1, addr2,
> -- node1, node2, deleteIntfs=False )
> -+ p1, p2 = self.makeIntfPair( intfName1, intfName2, addr1,
> -+ addr2, node1, node2,
> -+ deleteIntfs=False )
> - else:
> -- self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
> -+ p1, p2 = self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
> -+ # Original names of interfaces, if any, and new name are different
> -+ # This is useful in cases where interface renaming isn't supported.
> -+ params1[ 'orgName' ], params2[ 'orgName' ] = p1, p2
> -
> - if not cls1:
> - cls1 = intf
> Index: patches/patch-mininet_moduledeps_py
> ===================================================================
> RCS file: patches/patch-mininet_moduledeps_py
> diff -N patches/patch-mininet_moduledeps_py
> --- patches/patch-mininet_moduledeps_py 2 Sep 2017 23:10:52 -0000 1.2
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,27 +0,0 @@
> -$OpenBSD: patch-mininet_moduledeps_py,v 1.2 2017/09/02 23:10:52 akoshibe Exp $
> -Pull out mod* commands to mininet/linux, map kld* to mod* to avoid
> -more changes
> -Index: mininet/moduledeps.py
> ---- mininet/moduledeps.py.orig
> -+++ mininet/moduledeps.py
> -@@ -4,19 +4,7 @@ from mininet.util import quietRun
> - from mininet.log import info, error, debug
> - from os import environ
> -
> --def lsmod():
> -- "Return output of lsmod."
> -- return quietRun( 'lsmod' )
> --
> --def rmmod( mod ):
> -- """Return output of lsmod.
> -- mod: module string"""
> -- return quietRun( [ 'rmmod', mod ] )
> --
> --def modprobe( mod ):
> -- """Return output of modprobe
> -- mod: module string"""
> -- return quietRun( [ 'modprobe', mod ] )
> -+from mininet.openbsd.util import ( lsmod, rmmod, modprobe )
> -
> - OF_KMOD = 'ofdatapath'
> - OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+
> Index: patches/patch-mininet_net_py
> ===================================================================
> RCS file: patches/patch-mininet_net_py
> diff -N patches/patch-mininet_net_py
> --- patches/patch-mininet_net_py 10 Apr 2018 07:55:10 -0000 1.3
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,138 +0,0 @@
> -$OpenBSD: patch-mininet_net_py,v 1.3 2018/04/10 07:55:10 akoshibe Exp $
> -
> -Index: mininet/net.py
> ---- mininet/net.py.orig
> -+++ mininet/net.py
> -@@ -96,15 +96,29 @@ from time import sleep
> - from itertools import chain, groupby
> - from math import ceil
> -
> -+plat = os.uname()[ 0 ]
> -+if plat == 'FreeBSD':
> -+ from mininet.freebsd.node import Node
> -+ from mininet.freebsd.intf import Intf
> -+ from mininet.freebsd.util import fixLimits, numCores
> -+elif plat == 'Linux':
> -+ from mininet.linux.node import Node
> -+ from mininet.linux.intf import Intf
> -+ from mininet.linux.util import fixLimits, numCores
> -+else:
> -+ from mininet.openbsd.node import Node
> -+ from mininet.openbsd.intf import Intf
> -+ from mininet.openbsd.util import fixLimits, numCores, waitListening
> -+
> - from mininet.cli import CLI
> - from mininet.log import info, error, debug, output, warn
> --from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
> -- Controller )
> -+from mininet.node import ( Host, KernelSwitch, DefaultController,
> -+ Controller, RctlHost )
> - from mininet.nodelib import NAT
> --from mininet.link import Link, Intf
> --from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
> -- macColonHex, ipStr, ipParse, netParse, ipAdd,
> -- waitListening )
> -+from mininet.link import Link
> -+from mininet.util import ( quietRun, ensureRoot,
> -+ macColonHex, ipStr, ipParse, netParse, ipAdd
> -+ )
> - from mininet.term import cleanUpScreens, makeTerms
> -
> - # Mininet version: should be consistent with README and LICENSE
> -@@ -113,7 +127,7 @@ VERSION = "2.3.0d1"
> - class Mininet( object ):
> - "Network emulation with hosts spawned in network namespaces."
> -
> -- def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
> -+ def __init__( self, topo=None, switch=KernelSwitch, host=Host,
> - controller=DefaultController, link=Link, intf=Intf,
> - build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
> - inNamespace=False,
> -@@ -807,7 +821,7 @@ class Mininet( object ):
> - client, server = hosts
> - output( '*** Iperf: testing', l4Type, 'bandwidth between',
> - client, 'and', server, '\n' )
> -- server.cmd( 'killall -9 iperf' )
> -+ server.cmd( 'pkill -9 iperf' )
> - iperfArgs = 'iperf -p %d ' % port
> - bwArgs = ''
> - if l4Type == 'UDP':
> -@@ -817,9 +831,9 @@ class Mininet( object ):
> - raise Exception( 'Unexpected l4 type: %s' % l4Type )
> - if fmt:
> - iperfArgs += '-f %s ' % fmt
> -- server.sendCmd( iperfArgs + '-s' )
> -+ server.cmd( iperfArgs + '-s &' )
> - if l4Type == 'TCP':
> -- if not waitListening( client, server.IP(), port ):
> -+ if not waitListening( client, server, port ):
> - raise Exception( 'Could not connect to iperf on port %d'
> - % port )
> - cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
> -@@ -827,8 +841,8 @@ class Mininet( object ):
> - 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
> -+ # for TCP, there are two fo them because of waitListening
> -+ count = 1
> - while len( re.findall( '/sec', servout ) ) < count:
> - servout += server.monitor( timeoutms=5000 )
> - server.sendInt()
> -@@ -849,7 +863,7 @@ class Mininet( object ):
> - pct = cpu * 100
> - info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
> - hosts = self.hosts
> -- cores = int( quietRun( 'nproc' ) )
> -+ cores = numCores()
> - # number of processes to run a while loop on per host
> - num_procs = int( ceil( cores * cpu ) )
> - pids = {}
> -@@ -863,17 +877,26 @@ 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:
> -- time[ host ] = float( f.read() )
> -+ if isinstance( host, RctlHost ):
> -+ time[ host ] = host.getCPUTime( pids[ host ] )
> -+ else:
> -+ with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
> -+ host, 'r' ) as f:
> -+ time[ host ] = float( f.read() )
> - for _ in range( duration ):
> - sleep( 1 )
> - for host in hosts:
> -- 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 )
> -+ if isinstance( host, RctlHost ):
> -+ readTime = host.getCPUTime( pids[ host ] )
> -+ # is procstat output per-core?
> -+ outputs[ host ].append( ( readTime - time[ host ] )
> -+ / cores * 100 )
> -+ else:
> -+ 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 )
> - time[ host ] = readTime
> - for h, pids in pids.items():
> - for pid in pids:
> -@@ -893,9 +916,9 @@ class Mininet( object ):
> - dst: node name
> - status: string {up, down}"""
> - if src not in self.nameToNode:
> -- error( 'src not in network: %s\n' % src )
> -+ error( 'src node not in network: %s\n' % src )
> - elif dst not in self.nameToNode:
> -- error( 'dst not in network: %s\n' % dst )
> -+ error( 'dst node not in network: %s\n' % dst )
> - else:
> - src = self.nameToNode[ src ]
> - dst = self.nameToNode[ dst ]
> Index: patches/patch-mininet_node_py
> ===================================================================
> RCS file: patches/patch-mininet_node_py
> diff -N patches/patch-mininet_node_py
> --- patches/patch-mininet_node_py 10 Apr 2018 07:55:10 -0000 1.5
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,992 +0,0 @@
> -$OpenBSD: patch-mininet_node_py,v 1.5 2018/04/10 07:55:10 akoshibe Exp $
> -Original Node functions are now in BaseNode, subclassed by OS-
> -specific Node objects.
> -Index: mininet/node.py
> ---- mininet/node.py.orig
> -+++ mininet/node.py
> -@@ -53,602 +53,44 @@ Future enhancements:
> - """
> -
> - import os
> --import pty
> - import re
> --import signal
> --import select
> --from subprocess import Popen, PIPE
> -+from subprocess import Popen
> - from time import sleep
> -
> -+plat = os.uname()[ 0 ]
> -+if plat == 'FreeBSD':
> -+ from mininet.freebsd.node import Node
> -+ from mininet.freebsd.intf import Intf
> -+ from mininet.freebsd.util import ( LO, DP_MODE, numCores, moveIntf )
> -+ OVS_RCSTR = ( 'service ovsdb-server (one)start\n'
> -+ 'service ovs-vswitchd (one)start\n' )
> -+elif plat == 'Linux':
> -+ from mininet.linux.node import Node
> -+ from mininet.linux.intf import Intf
> -+ from mininet.linux.util import ( LO, DP_MODE, numCores, moveIntf,
> -+ mountCgroups )
> -+ OVS_RCSTR = 'service openvswitch-switch start\n'
> -+else:
> -+ from mininet.openbsd.node import Node
> -+ from mininet.openbsd.intf import Intf
> -+ from mininet.openbsd.util import ( LO, DP_MODE, numCores, moveIntf )
> -+
> -+
> - from mininet.log import info, error, warn, debug
> --from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
> -- numCores, retry, mountCgroups )
> -+from mininet.util import ( quietRun, errRun, errFail, retry )
> - from mininet.moduledeps import moduleDeps, pathCheck, TUN
> --from mininet.link import Link, Intf, TCIntf, OVSIntf
> -+from mininet.link import Link, TCIntf, OVSIntf
> - from re import findall
> - from distutils.version import StrictVersion
> -
> --class Node( object ):
> -- """A virtual network node is simply a shell in a network namespace.
> -- We communicate with it using pipes."""
> -
> -- portBase = 0 # Nodes always start with eth0/port0, even in OF 1.0
> --
> -- def __init__( self, name, inNamespace=True, **params ):
> -- """name: name of node
> -- inNamespace: in network namespace?
> -- privateDirs: list of private directory strings or tuples
> -- params: Node parameters (see config() for details)"""
> --
> -- # Make sure class actually works
> -- self.checkSetup()
> --
> -- self.name = params.get( 'name', name )
> -- self.privateDirs = params.get( 'privateDirs', [] )
> -- self.inNamespace = params.get( 'inNamespace', inNamespace )
> --
> -- # Stash configuration parameters for future reference
> -- self.params = params
> --
> -- self.intfs = {} # dict of port numbers to interfaces
> -- self.ports = {} # dict of interfaces to port numbers
> -- # replace with Port objects, eventually ?
> -- self.nameToIntf = {} # dict of interface names to Intfs
> --
> -- # Make pylint happy
> -- ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
> -- self.lastPid, self.lastCmd, self.pollOut ) = (
> -- None, None, None, None, None, None, None, None )
> -- self.waiting = False
> -- self.readbuf = ''
> --
> -- # Start command interpreter shell
> -- self.startShell()
> -- self.mountPrivateDirs()
> --
> -- # File descriptor to node mapping support
> -- # Class variables and methods
> --
> -- inToNode = {} # mapping of input fds to nodes
> -- outToNode = {} # mapping of output fds to nodes
> --
> -- @classmethod
> -- def fdToNode( cls, fd ):
> -- """Return node corresponding to given file descriptor.
> -- fd: file descriptor
> -- returns: node"""
> -- node = cls.outToNode.get( fd )
> -- return node or cls.inToNode.get( fd )
> --
> -- # Command support via shell process in namespace
> -- def startShell( self, mnopts=None ):
> -- "Start a shell process for running commands"
> -- if self.shell:
> -- error( "%s: shell is already running\n" % self.name )
> -- return
> -- # mnexec: (c)lose descriptors, (d)etach from tty,
> -- # (p)rint pid, and run in (n)amespace
> -- opts = '-cd' if mnopts is None else mnopts
> -- if self.inNamespace:
> -- opts += 'n'
> -- # bash -i: force interactive
> -- # -s: pass $* to shell, and make process easy to find in ps
> -- # prompt is set to sentinel chr( 127 )
> -- cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
> -- 'bash', '--norc', '-is', 'mininet:' + self.name ]
> -- # Spawn a shell subprocess in a pseudo-tty, to disable buffering
> -- # in the subprocess and insulate it from signals (e.g. SIGINT)
> -- # received by the parent
> -- master, slave = pty.openpty()
> -- self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
> -- close_fds=False )
> -- self.stdin = os.fdopen( master, 'rw' )
> -- self.stdout = self.stdin
> -- self.pid = self.shell.pid
> -- self.pollOut = select.poll()
> -- self.pollOut.register( self.stdout )
> -- # Maintain mapping between file descriptors and nodes
> -- # This is useful for monitoring multiple nodes
> -- # using select.poll()
> -- self.outToNode[ self.stdout.fileno() ] = self
> -- self.inToNode[ self.stdin.fileno() ] = self
> -- self.execed = False
> -- self.lastCmd = None
> -- self.lastPid = None
> -- self.readbuf = ''
> -- # Wait for prompt
> -- while True:
> -- data = self.read( 1024 )
> -- if data[ -1 ] == chr( 127 ):
> -- break
> -- self.pollOut.poll()
> -- self.waiting = False
> -- # +m: disable job control notification
> -- self.cmd( 'unset HISTFILE; stty -echo; set +m' )
> --
> -- def mountPrivateDirs( self ):
> -- "mount private directories"
> -- # Avoid expanding a string into a list of chars
> -- assert not isinstance( self.privateDirs, basestring )
> -- for directory in self.privateDirs:
> -- if isinstance( directory, tuple ):
> -- # mount given private directory
> -- privateDir = directory[ 1 ] % self.__dict__
> -- mountPoint = directory[ 0 ]
> -- self.cmd( 'mkdir -p %s' % privateDir )
> -- self.cmd( 'mkdir -p %s' % mountPoint )
> -- self.cmd( 'mount --bind %s %s' %
> -- ( privateDir, mountPoint ) )
> -- else:
> -- # mount temporary filesystem on directory
> -- self.cmd( 'mkdir -p %s' % directory )
> -- self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
> --
> -- def unmountPrivateDirs( self ):
> -- "mount private directories"
> -- for directory in self.privateDirs:
> -- if isinstance( directory, tuple ):
> -- self.cmd( 'umount ', directory[ 0 ] )
> -- else:
> -- self.cmd( 'umount ', directory )
> --
> -- def _popen( self, cmd, **params ):
> -- """Internal method: spawn and return a process
> -- cmd: command to run (list)
> -- params: parameters to Popen()"""
> -- # Leave this is as an instance method for now
> -- assert self
> -- return Popen( cmd, **params )
> --
> -- def cleanup( self ):
> -- "Help python collect its garbage."
> -- # We used to do this, but it slows us down:
> -- # Intfs may end up in root NS
> -- # for intfName in self.intfNames():
> -- # if self.name in intfName:
> -- # quietRun( 'ip link del ' + intfName )
> -- self.shell = None
> --
> -- # Subshell I/O, commands and control
> --
> -- def read( self, maxbytes=1024 ):
> -- """Buffered read from node, potentially blocking.
> -- maxbytes: maximum number of bytes to return"""
> -- count = len( self.readbuf )
> -- if count < maxbytes:
> -- data = os.read( self.stdout.fileno(), maxbytes - count )
> -- self.readbuf += data
> -- if maxbytes >= len( self.readbuf ):
> -- result = self.readbuf
> -- self.readbuf = ''
> -- else:
> -- result = self.readbuf[ :maxbytes ]
> -- self.readbuf = self.readbuf[ maxbytes: ]
> -- return result
> --
> -- def readline( self ):
> -- """Buffered readline from node, potentially blocking.
> -- returns: line (minus newline) or None"""
> -- self.readbuf += self.read( 1024 )
> -- if '\n' not in self.readbuf:
> -- return None
> -- pos = self.readbuf.find( '\n' )
> -- line = self.readbuf[ 0: pos ]
> -- self.readbuf = self.readbuf[ pos + 1: ]
> -- return line
> --
> -- def write( self, data ):
> -- """Write data to node.
> -- data: string"""
> -- os.write( self.stdin.fileno(), data )
> --
> -- def terminate( self ):
> -- "Send kill signal to Node and clean up after it."
> -- self.unmountPrivateDirs()
> -- if self.shell:
> -- if self.shell.poll() is None:
> -- os.killpg( self.shell.pid, signal.SIGHUP )
> -- self.cleanup()
> --
> -- def stop( self, deleteIntfs=False ):
> -- """Stop node.
> -- deleteIntfs: delete interfaces? (False)"""
> -- if deleteIntfs:
> -- self.deleteIntfs()
> -- self.terminate()
> --
> -- def waitReadable( self, timeoutms=None ):
> -- """Wait until node's output is readable.
> -- timeoutms: timeout in ms or None to wait indefinitely.
> -- returns: result of poll()"""
> -- if len( self.readbuf ) == 0:
> -- return self.pollOut.poll( timeoutms )
> --
> -- def sendCmd( self, *args, **kwargs ):
> -- """Send a command, followed by a command to echo a sentinel,
> -- and return without waiting for the command to complete.
> -- args: command and arguments, or string
> -- printPid: print command's PID? (False)"""
> -- assert self.shell and not self.waiting
> -- printPid = kwargs.get( 'printPid', False )
> -- # Allow sendCmd( [ list ] )
> -- if len( args ) == 1 and isinstance( args[ 0 ], list ):
> -- cmd = args[ 0 ]
> -- # Allow sendCmd( cmd, arg1, arg2... )
> -- elif len( args ) > 0:
> -- cmd = args
> -- # Convert to string
> -- if not isinstance( cmd, str ):
> -- cmd = ' '.join( [ str( c ) for c in cmd ] )
> -- if not re.search( r'\w', cmd ):
> -- # Replace empty commands with something harmless
> -- cmd = 'echo -n'
> -- self.lastCmd = cmd
> -- # if a builtin command is backgrounded, it still yields a PID
> -- if len( cmd ) > 0 and cmd[ -1 ] == '&':
> -- # print ^A{pid}\n so monitor() can set lastPid
> -- cmd += ' printf "\\001%d\\012" $! '
> -- elif printPid and not isShellBuiltin( cmd ):
> -- cmd = 'mnexec -p ' + cmd
> -- self.write( cmd + '\n' )
> -- self.lastPid = None
> -- self.waiting = True
> --
> -- def sendInt( self, intr=chr( 3 ) ):
> -- "Interrupt running command."
> -- debug( 'sendInt: writing chr(%d)\n' % ord( intr ) )
> -- self.write( intr )
> --
> -- def monitor( self, timeoutms=None, findPid=True ):
> -- """Monitor and return the output of a command.
> -- Set self.waiting to False if command has completed.
> -- timeoutms: timeout in ms or None to wait indefinitely
> -- findPid: look for PID from mnexec -p"""
> -- ready = self.waitReadable( timeoutms )
> -- if not ready:
> -- return ''
> -- data = self.read( 1024 )
> -- pidre = r'\[\d+\] \d+\r\n'
> -- # Look for PID
> -- marker = chr( 1 ) + r'\d+\r\n'
> -- if findPid and chr( 1 ) in data:
> -- # suppress the job and PID of a backgrounded command
> -- if re.findall( pidre, data ):
> -- data = re.sub( pidre, '', data )
> -- # Marker can be read in chunks; continue until all of it is read
> -- while not re.findall( marker, data ):
> -- data += self.read( 1024 )
> -- markers = re.findall( marker, data )
> -- if markers:
> -- self.lastPid = int( markers[ 0 ][ 1: ] )
> -- data = re.sub( marker, '', data )
> -- # Look for sentinel/EOF
> -- if len( data ) > 0 and data[ -1 ] == chr( 127 ):
> -- self.waiting = False
> -- data = data[ :-1 ]
> -- elif chr( 127 ) in data:
> -- self.waiting = False
> -- data = data.replace( chr( 127 ), '' )
> -- return data
> --
> -- def waitOutput( self, verbose=False, findPid=True ):
> -- """Wait for a command to complete.
> -- Completion is signaled by a sentinel character, ASCII(127)
> -- appearing in the output stream. Wait for the sentinel and return
> -- the output, including trailing newline.
> -- verbose: print output interactively"""
> -- log = info if verbose else debug
> -- output = ''
> -- while self.waiting:
> -- data = self.monitor( findPid=findPid )
> -- output += data
> -- log( data )
> -- return output
> --
> -- def cmd( self, *args, **kwargs ):
> -- """Send a command, wait for output, and return it.
> -- cmd: string"""
> -- verbose = kwargs.get( 'verbose', False )
> -- log = info if verbose else debug
> -- log( '*** %s : %s\n' % ( self.name, args ) )
> -- if self.shell:
> -- self.sendCmd( *args, **kwargs )
> -- return self.waitOutput( verbose )
> -- else:
> -- warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
> --
> -- def cmdPrint( self, *args):
> -- """Call cmd and printing its output
> -- cmd: string"""
> -- return self.cmd( *args, **{ 'verbose': True } )
> --
> -- def popen( self, *args, **kwargs ):
> -- """Return a Popen() object in our namespace
> -- args: Popen() args, single list, or string
> -- kwargs: Popen() keyword args"""
> -- defaults = { 'stdout': PIPE, 'stderr': PIPE,
> -- 'mncmd':
> -- [ 'mnexec', '-da', str( self.pid ) ] }
> -- defaults.update( kwargs )
> -- if len( args ) == 1:
> -- if isinstance( args[ 0 ], list ):
> -- # popen([cmd, arg1, arg2...])
> -- cmd = args[ 0 ]
> -- elif isinstance( args[ 0 ], basestring ):
> -- # popen("cmd arg1 arg2...")
> -- cmd = args[ 0 ].split()
> -- else:
> -- raise Exception( 'popen() requires a string or list' )
> -- elif len( args ) > 0:
> -- # popen( cmd, arg1, arg2... )
> -- cmd = list( args )
> -- # Attach to our namespace using mnexec -a
> -- cmd = defaults.pop( 'mncmd' ) + cmd
> -- # Shell requires a string, not a list!
> -- if defaults.get( 'shell', False ):
> -- cmd = ' '.join( cmd )
> -- popen = self._popen( cmd, **defaults )
> -- return popen
> --
> -- def pexec( self, *args, **kwargs ):
> -- """Execute a command using popen
> -- returns: out, err, exitcode"""
> -- popen = self.popen( *args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
> -- **kwargs )
> -- # Warning: this can fail with large numbers of fds!
> -- out, err = popen.communicate()
> -- exitcode = popen.wait()
> -- return out, err, exitcode
> --
> -- # Interface management, configuration, and routing
> --
> -- # BL notes: This might be a bit redundant or over-complicated.
> -- # However, it does allow a bit of specialization, including
> -- # changing the canonical interface names. It's also tricky since
> -- # the real interfaces are created as veth pairs, so we can't
> -- # make a single interface at a time.
> --
> -- def newPort( self ):
> -- "Return the next port number to allocate."
> -- if len( self.ports ) > 0:
> -- return max( self.ports.values() ) + 1
> -- return self.portBase
> --
> -- def addIntf( self, intf, port=None, moveIntfFn=moveIntf ):
> -- """Add an interface.
> -- intf: interface
> -- port: port number (optional, typically OpenFlow port number)
> -- moveIntfFn: function to move interface (optional)"""
> -- if port is None:
> -- port = self.newPort()
> -- self.intfs[ port ] = intf
> -- self.ports[ intf ] = port
> -- self.nameToIntf[ intf.name ] = intf
> -- debug( '\n' )
> -- debug( 'added intf %s (%d) to node %s\n' % (
> -- intf, port, self.name ) )
> -- if self.inNamespace:
> -- debug( 'moving', intf, 'into namespace for', self.name, '\n' )
> -- moveIntfFn( intf.name, self )
> --
> -- def delIntf( self, intf ):
> -- """Remove interface from Node's known interfaces
> -- Note: to fully delete interface, call intf.delete() instead"""
> -- port = self.ports.get( intf )
> -- if port is not None:
> -- del self.intfs[ port ]
> -- del self.ports[ intf ]
> -- del self.nameToIntf[ intf.name ]
> --
> -- def defaultIntf( self ):
> -- "Return interface for lowest port"
> -- ports = self.intfs.keys()
> -- if ports:
> -- return self.intfs[ min( ports ) ]
> -- else:
> -- warn( '*** defaultIntf: warning:', self.name,
> -- 'has no interfaces\n' )
> --
> -- def intf( self, intf=None ):
> -- """Return our interface object with given string name,
> -- default intf if name is falsy (None, empty string, etc).
> -- or the input intf arg.
> --
> -- Having this fcn return its arg for Intf objects makes it
> -- easier to construct functions with flexible input args for
> -- interfaces (those that accept both string names and Intf objects).
> -- """
> -- if not intf:
> -- return self.defaultIntf()
> -- elif isinstance( intf, basestring):
> -- return self.nameToIntf[ intf ]
> -- else:
> -- return intf
> --
> -- def connectionsTo( self, node):
> -- "Return [ intf1, intf2... ] for all intfs that connect self to node."
> -- # We could optimize this if it is important
> -- connections = []
> -- for intf in self.intfList():
> -- link = intf.link
> -- if link:
> -- node1, node2 = link.intf1.node, link.intf2.node
> -- if node1 == self and node2 == node:
> -- connections += [ ( intf, link.intf2 ) ]
> -- elif node1 == node and node2 == self:
> -- connections += [ ( intf, link.intf1 ) ]
> -- return connections
> --
> -- def deleteIntfs( self, checkName=True ):
> -- """Delete all of our interfaces.
> -- checkName: only delete interfaces that contain our name"""
> -- # In theory the interfaces should go away after we shut down.
> -- # However, this takes time, so we're better off removing them
> -- # explicitly so that we won't get errors if we run before they
> -- # have been removed by the kernel. Unfortunately this is very slow,
> -- # at least with Linux kernels before 2.6.33
> -- for intf in self.intfs.values():
> -- # Protect against deleting hardware interfaces
> -- if ( self.name in intf.name ) or ( not checkName ):
> -- intf.delete()
> -- info( '.' )
> --
> -- # Routing support
> --
> -- def setARP( self, ip, mac ):
> -- """Add an ARP entry.
> -- ip: IP address as string
> -- mac: MAC address as string"""
> -- result = self.cmd( 'arp', '-s', ip, mac )
> -- return result
> --
> -- def setHostRoute( self, ip, intf ):
> -- """Add route to host.
> -- ip: IP address as dotted decimal
> -- intf: string, interface name"""
> -- return self.cmd( 'route add -host', ip, 'dev', intf )
> --
> -- def setDefaultRoute( self, intf=None ):
> -- """Set the default route to go through intf.
> -- intf: Intf or {dev <intfname> via <gw-ip> ...}"""
> -- # Note setParam won't call us if intf is none
> -- if isinstance( intf, basestring ) and ' ' in intf:
> -- params = intf
> -- else:
> -- params = 'dev %s' % intf
> -- # Do this in one line in case we're messing with the root namespace
> -- self.cmd( 'ip route del default; ip route add default', params )
> --
> -- # Convenience and configuration methods
> --
> -- def setMAC( self, mac, intf=None ):
> -- """Set the MAC address for an interface.
> -- intf: intf or intf name
> -- mac: MAC address as string"""
> -- return self.intf( intf ).setMAC( mac )
> --
> -- def setIP( self, ip, prefixLen=8, intf=None, **kwargs ):
> -- """Set the IP address for an interface.
> -- intf: intf or intf name
> -- ip: IP address as a string
> -- prefixLen: prefix length, e.g. 8 for /8 or 16M addrs
> -- kwargs: any additional arguments for intf.setIP"""
> -- return self.intf( intf ).setIP( ip, prefixLen, **kwargs )
> --
> -- def IP( self, intf=None ):
> -- "Return IP address of a node or specific interface."
> -- return self.intf( intf ).IP()
> --
> -- def MAC( self, intf=None ):
> -- "Return MAC address of a node or specific interface."
> -- return self.intf( intf ).MAC()
> --
> -- def intfIsUp( self, intf=None ):
> -- "Check if an interface is up."
> -- return self.intf( intf ).isUp()
> --
> -- # The reason why we configure things in this way is so
> -- # That the parameters can be listed and documented in
> -- # the config method.
> -- # Dealing with subclasses and superclasses is slightly
> -- # annoying, but at least the information is there!
> --
> -- def setParam( self, results, method, **param ):
> -- """Internal method: configure a *single* parameter
> -- results: dict of results to update
> -- method: config method name
> -- param: arg=value (ignore if value=None)
> -- value may also be list or dict"""
> -- name, value = param.items()[ 0 ]
> -- if value is None:
> -- return
> -- f = getattr( self, method, None )
> -- if not f:
> -- return
> -- if isinstance( value, list ):
> -- result = f( *value )
> -- elif isinstance( value, dict ):
> -- result = f( **value )
> -- else:
> -- result = f( value )
> -- results[ name ] = result
> -- return result
> --
> -- def config( self, mac=None, ip=None,
> -- defaultRoute=None, lo='up', **_params ):
> -- """Configure Node according to (optional) parameters:
> -- mac: MAC address for default interface
> -- ip: IP address for default interface
> -- ifconfig: arbitrary interface configuration
> -- Subclasses should override this method and call
> -- the parent class's config(**params)"""
> -- # If we were overriding this method, we would call
> -- # the superclass config method here as follows:
> -- # r = Parent.config( **_params )
> -- r = {}
> -- self.setParam( r, 'setMAC', mac=mac )
> -- self.setParam( r, 'setIP', ip=ip )
> -- self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute )
> -- # This should be examined
> -- self.cmd( 'ifconfig lo ' + lo )
> -- return r
> --
> -- def configDefault( self, **moreParams ):
> -- "Configure with default parameters"
> -- self.params.update( moreParams )
> -- self.config( **self.params )
> --
> -- # This is here for backward compatibility
> -- def linkTo( self, node, link=Link ):
> -- """(Deprecated) Link to another node
> -- replace with Link( node1, node2)"""
> -- return link( self, node )
> --
> -- # Other methods
> --
> -- def intfList( self ):
> -- "List of our interfaces sorted by port number"
> -- return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
> --
> -- def intfNames( self ):
> -- "The names of our interfaces sorted by port number"
> -- return [ str( i ) for i in self.intfList() ]
> --
> -- def __repr__( self ):
> -- "More informative string representation"
> -- intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
> -- for i in self.intfList() ] ) )
> -- return '<%s %s: %s pid=%s> ' % (
> -- self.__class__.__name__, self.name, intfs, self.pid )
> --
> -- def __str__( self ):
> -- "Abbreviated string representation"
> -- return self.name
> --
> -- # Automatic class setup support
> --
> -- isSetup = False
> --
> -- @classmethod
> -- def checkSetup( cls ):
> -- "Make sure our class and superclasses are set up"
> -- while cls and not getattr( cls, 'isSetup', True ):
> -- cls.setup()
> -- cls.isSetup = True
> -- # Make pylint happy
> -- cls = getattr( type( cls ), '__base__', None )
> --
> -- @classmethod
> -- def setup( cls ):
> -- "Make sure our class dependencies are available"
> -- pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
> --
> - class Host( Node ):
> - "A host is simply a Node"
> - pass
> -
> --class CPULimitedHost( Host ):
> -
> -+class CgroupHost( Host ):
> -+
> - "CPU limited host"
> -
> - def __init__( self, name, sched='cfs', **kwargs ):
> -@@ -702,8 +144,8 @@ class CPULimitedHost( Host ):
> - args: Popen() args, single list, or string
> - kwargs: Popen() keyword args"""
> - # Tell mnexec to execute command in our cgroup
> -- mncmd = kwargs.pop( 'mncmd', [ 'mnexec', '-g', self.name,
> -- '-da', str( self.pid ) ] )
> -+ mncmd = [ 'mnexec', '-g', self.name,
> -+ '-da', str( self.pid ) ]
> - # if our cgroup is not given any cpu time,
> - # we cannot assign the RR Scheduler.
> - if self.sched == 'rt':
> -@@ -739,7 +181,7 @@ class CPULimitedHost( Host ):
> - quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
> - result = quietRun( 'chrt -p %s' % self.pid )
> - firstline = result.split( '\n' )[ 0 ]
> -- lastword = firstline.split( ' ' )[ -1 ]
> -+ lastword = firstline.split()[ -1 ]
> - if lastword != 'SCHED_RR':
> - error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
> - return lastword
> -@@ -837,6 +279,105 @@ class CPULimitedHost( Host ):
> - cls.inited = True
> -
> -
> -+class RctlHost( Host ):
> -+ """
> -+ A CPU-limited host that uses a combination of `rctl(8)` and `cpuset(1)`
> -+ is used to constrain the host resources. Here, a host is considered to be
> -+ the jail.
> -+ """
> -+
> -+ def __init__( self, name, **kwargs ):
> -+ Host.__init__( self, name, **kwargs )
> -+ self.period_us = kwargs.get( 'period_us', 100000 )
> -+ self.pcpu = -1
> -+
> -+ def setCPUFrac( self, cpu, sched=None, numc=None ):
> -+ """Set overall CPU fraction for this host
> -+ cpu: CPU bandwidth limit (nonzero float)
> -+ sched: Scheduler (ignored but exists for compatibility)
> -+ numc: Number of cores"""
> -+ if cpu == -1:
> -+ return
> -+ if cpu < 0:
> -+ error( '*** error: fraction must be a positive value' )
> -+ return
> -+ self.pcpu = cpu
> -+ cct = numCores() if numc is None else numc
> -+ cmd = 'rctl -a jail:%s:pcpu:deny=%d' % ( self.jid, ( cpu * 100 * cct ) )
> -+ quietRun( cmd )
> -+
> -+ def setCPUs( self, cores, mems=0 ):
> -+ """Specify cores that host will run on."""
> -+ # do we want to scale back/up pcpu?
> -+ # extract valid cores to a list: mask: 0, 1 -> [0,1]
> -+ avail = quietRun( 'cpuset -g' ).split()
> -+ if avail[2] == "mask:":
> -+ valid = map( ( lambda x : int( x.split( ',' )[0] ) ), avail[ 3: ] )
> -+
> -+ if isinstance( cores, list ):
> -+ for c in cores:
> -+ if c not in valid:
> -+ error( '*** error: cannot assign target to core %d' % c )
> -+ return
> -+ args = ','.join( [ str( c ) for c in cores ] )
> -+ cct = len( cores )
> -+ else:
> -+ if cores not in valid:
> -+ error( '*** error: cannot assign target to core %d' % c )
> -+ return
> -+ else:
> -+ args = str( cores )
> -+ cct = 1
> -+
> -+ cmd = 'cpuset -l %s -j %s' % ( args, self.jid )
> -+ quietRun( cmd )
> -+
> -+ #update the resourcelimit to scale
> -+ self.setCPUFrac( self.pcpu, numc=cct )
> -+
> -+ def rulesDel( self ):
> -+ """Remove `rctl` rules associated with this host"""
> -+ _out, _err, exitcode = errRun( 'rctl -r jail:%s' % self.jid )
> -+ return exitcode
> -+
> -+ def cleanup( self ):
> -+ "Clean up Node, then clean up our resource allocation rules"
> -+ super( ResourceLimitedHost, self ).cleanup()
> -+ # no need/means to remove cpuset rules - they die with host
> -+ retry( retries=3, delaySecs=.1, fn=self.rulesDel )
> -+
> -+ def config( self, cpu=-1, cores=None, **params ):
> -+ """cpu: desired overall system CPU fraction
> -+ cores: (real) core(s) this host can run on
> -+ params: parameters for Node.config()"""
> -+ r = Node.config( self, **params )
> -+ # Was considering cpu={'cpu': cpu , 'sched': sched}, but
> -+ # that seems redundant
> -+ self.setParam( r, 'setCPUFrac', cpu=cpu )
> -+ self.setParam( r, 'setCPUs', cores=cores )
> -+ return r
> -+
> -+ def getCPUTime( self, pid ):
> -+ """Get CPU time of a process identified by pid. We do this via
> -+ procstat(1). It is janky, but 10.x procstat doesn't do libxo
> -+ output."""
> -+ res = quietRun( 'procstat -rh %s' % pid ).split('\n')
> -+ c = 0;
> -+ time = 0.0
> -+ for line in res:
> -+ if 'time' in line:
> -+ # the microsecond portion of user/kernel time
> -+ time += float(line.split(':')[-1])
> -+ c+=1
> -+ # got the two lines we need.
> -+ if c == 2:
> -+ break
> -+ return time
> -+
> -+
> -+CPULimitedHost = RctlHost if os.uname()[ 0 ] == 'FreeBSD' else CgroupHost
> -+
> -+
> - # Some important things to note:
> - #
> - # The "IP" address which setIP() assigns to the switch is not
> -@@ -873,7 +414,7 @@ class Switch( Node ):
> - self.opts = opts
> - self.listenPort = listenPort
> - if not self.inNamespace:
> -- self.controlIntf = Intf( 'lo', self, port=0 )
> -+ self.controlIntf = Intf( LO, self, port=0 )
> -
> - def defaultDpid( self, dpid=None ):
> - "Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
> -@@ -1033,7 +574,7 @@ class UserSwitch( Switch ):
> - class OVSSwitch( Switch ):
> - "Open vSwitch switch. Depends on ovs-vsctl."
> -
> -- def __init__( self, name, failMode='secure', datapath='kernel',
> -+ def __init__( self, name, failMode='secure', datapath=DP_MODE,
> - inband=False, protocols=None,
> - reconnectms=1000, stp=False, batch=False, **params ):
> - """name: name for switch
> -@@ -1072,8 +613,8 @@ class OVSSwitch( Switch ):
> - 'Make sure that Open vSwitch is installed, '
> - 'that ovsdb-server is running, and that\n'
> - '"ovs-vsctl show" works correctly.\n'
> -- 'You may wish to try '
> -- '"service openvswitch-switch start".\n' )
> -+ 'You may wish to try the following:\n\n'
> -+ + OVS_RCSTR + '\n' )
> - exit( 1 )
> - version = quietRun( 'ovs-vsctl --version' )
> - cls.OVSVersion = findall( r'\d+\.\d+', version )[ 0 ]
> -@@ -1153,7 +694,7 @@ class OVSSwitch( Switch ):
> - opts = ( ' other_config:datapath-id=%s' % self.dpid +
> - ' fail_mode=%s' % self.failMode )
> - if not self.inband:
> -- opts += ' other-config:disable-in-band=true'
> -+ opts += ' other_config:disable-in-band=true'
> - if self.datapath == 'user':
> - opts += ' datapath_type=netdev'
> - if self.protocols and not self.isOldOVS():
> -@@ -1239,7 +780,7 @@ class OVSSwitch( Switch ):
> - deleteIntfs: delete interfaces? (True)"""
> - self.cmd( 'ovs-vsctl del-br', self )
> - if self.datapath == 'user':
> -- self.cmd( 'ip link del', self )
> -+ self.cmd( deleteCmd( self ) )
> - super( OVSSwitch, self ).stop( deleteIntfs )
> -
> - @classmethod
> -@@ -1259,9 +800,84 @@ class OVSSwitch( Switch ):
> - return switches
> -
> -
> --OVSKernelSwitch = OVSSwitch
> -+class IfSwitch( Switch ):
> -+ """
> -+ OpenBSD switch(4) device switch. Supported on OpenBSD 6.1+.
> -+ TODOs : addlocal, dpid, maxflow, maxgroups, portno
> -+ """
> -
> -+ unitNo = 0 # number following device name, e.g. 0 in bridge0
> -+ local = None # local switchd instance for remote connection
> -
> -+ def __init__( self, name, **kwargs ):
> -+ self.bname = 'switch%d' % IfSwitch.unitNo
> -+ self.cdev = '/dev/' + self.bname # character device name
> -+ self.newcdev = False # created a new device?
> -+ IfSwitch.unitNo += 1
> -+ Switch.__init__( self, name, **kwargs )
> -+
> -+ def connected( self ):
> -+ "Are we forwarding yet?"
> -+ return self.bname in self.cmd( 'ifconfig switch' )
> -+
> -+ def start( self, controllers ):
> -+ "Start bridge. Retain the bridge's name to save on ifconfig calls"
> -+ rdarg = 'rdomain %d' % self.rdid if self.inNamespace else ''
> -+ dparg = 'datapath 0x%s' % self.dpid
> -+ quietRun( 'ifconfig %s create %s %s description "%s" up' %
> -+ ( self.bname, dparg, rdarg, self.name ) )
> -+ addcmd, stpcmd = '', ''
> -+ for i in self.intfList():
> -+ if i.realname and 'pair' in i.realname:
> -+ name = i.realname
> -+ addcmd += ' add ' + name
> -+ quietRun( 'ifconfig %s %s up' % ( name, rdarg ) )
> -+ quietRun( 'ifconfig ' + self.bname + addcmd )
> -+
> -+ # Connect to controller using switchctl(8) using /dev/switch*
> -+ if not os.path.exists( self.cdev ):
> -+ # try to make character device, check and try to connect again
> -+ self.cmd( 'cd /dev/ && ./MAKEDEV ' + self.bname )
> -+ self.newcdev = True
> -+
> -+ if os.path.exists( self.cdev ):
> -+ args = 'switchctl connect ' + self.cdev
> -+ ctl = controllers[ 0 ] if controllers else None
> -+ if ctl and isinstance( ctl, RemoteController ):
> -+ args += ' forward-to %s:%d' % ( ctl.IP(), ctl.port )
> -+ # start local Switchd instance and have it forward
> -+ if not IfSwitch.local:
> -+ IfSwitch.local = Switchd( 'lc0', port=6633 )
> -+ IfSwitch.local.start()
> -+ quietRun( args )
> -+ else:
> -+ error( "Can't connect to controller: %s doesn't exist" %
> -+ self.cdev )
> -+ exit( 1 )
> -+
> -+ def stop( self, deleteIntfs=True ):
> -+ """Stop bridge
> -+ deleteIntfs: delete interfaces? (True)"""
> -+ quietRun( 'ifconfig %s destroy' % self.bname )
> -+ # hack: the last switch destroys the local controller
> -+ if IfSwitch.local:
> -+ IfSwitch.local.stop()
> -+ IfSwitch.local = None
> -+ if self.newcdev:
> -+ quietRun( 'rm ' + self.cdev )
> -+ super( IfSwitch, self ).stop( deleteIntfs )
> -+
> -+ def dpctl( self, *args ):
> -+ "Run brctl command"
> -+ # actually switchctl
> -+ # choose the first switch to run switchctl(8) from
> -+ if self.bname == 'switch0':
> -+ return self.cmd( 'switchctl', *args )
> -+
> -+
> -+KernelSwitch = IfSwitch # OpenBSD
> -+
> -+
> - class OVSBridge( OVSSwitch ):
> - "OVSBridge is an OVSSwitch in standalone/bridge mode"
> -
> -@@ -1386,8 +1002,8 @@ class Controller( Node ):
> - listening = self.cmd( "echo A | telnet -e A %s %d" %
> - ( self.ip, self.port ) )
> - if 'Connected' in listening:
> -- servers = self.cmd( 'netstat -natp' ).split( '\n' )
> -- pstr = ':%d ' % self.port
> -+ servers = self.cmd( 'netstat -nlp tcp' ).split( '\n' )
> -+ pstr = '.%d ' % self.port
> - clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
> - raise Exception( "Please shut down the controller which is"
> - " running on port %d:\n" % self.port +
> -@@ -1405,7 +1021,10 @@ class Controller( Node ):
> - self.execed = False
> -
> - def stop( self, *args, **kwargs ):
> -- "Stop controller."
> -+ """
> -+ Stop controller. Find processes associated with the command, and kill
> -+ them.
> -+ """
> - self.cmd( 'kill %' + self.command )
> - self.cmd( 'wait %' + self.command )
> - super( Controller, self ).stop( *args, **kwargs )
> -@@ -1470,12 +1089,19 @@ class NOX( Controller ):
> -
> - class Ryu( Controller ):
> - "Controller to run Ryu application"
> -+
> - def __init__( self, name, *ryuArgs, **kwargs ):
> - """Init.
> - name: name to give controller.
> - ryuArgs: arguments and modules to pass to Ryu"""
> -- homeDir = quietRun( 'printenv HOME' ).strip( '\r\n' )
> -- ryuCoreDir = '%s/ryu/ryu/app/' % homeDir
> -+
> -+ if os.uname()[ 0 ] == 'FreeBSD':
> -+ import site
> -+ homeDir = site.getsitepackages()[ 0 ]
> -+ else:
> -+ homeDir = quietRun( 'printenv HOME' ).strip( '\r\n' ) + '/ryu'
> -+
> -+ ryuCoreDir = '%s/ryu/app/' % homeDir
> - if not ryuArgs:
> - warn( 'warning: no Ryu modules specified; '
> - 'running simple_switch only\n' )
> -@@ -1538,7 +1164,53 @@ class RemoteController( Controller ):
> - else:
> - return True
> -
> --DefaultControllers = ( Controller, OVSController )
> -+
> -+class Switchd( Controller ):
> -+ """
> -+ switchd(4): OpenBSD SDN sflow controller.
> -+ """
> -+ def __init__( self, name, ip='127.0.0.1', port=6653,
> -+ conf='/etc/switchd.mininet.conf', **kwargs):
> -+ cmd = '-f ' + conf
> -+ cmd += ' -D ctl_ip=%s -D port=%s' % ( ip, port )
> -+
> -+ # optional parameters (defaults)
> -+ tout = kwargs.get('timeout') # MAC address timeout, seconds (240)
> -+ vflgs = kwargs.get('vflags') # verbosity, e.g. '-vv'
> -+ csize = kwargs.get('cache') # MAC address cache size (4096)
> -+
> -+ cmd += ' -t %s' % tout if tout else ''
> -+ cmd += ' ' + vflgs if vflgs else ''
> -+ cmd += ' -t %s' % tout if tout else ''
> -+
> -+ Controller.__init__( self, name, ip=ip, port=port, command='switchd',
> -+ cargs=cmd, **kwargs )
> -+
> -+ def start( self ):
> -+ """Start <controller> <args> on controller."""
> -+ pathCheck( self.command )
> -+ if self.cdir is not None:
> -+ self.cmd( 'cd ' + self.cdir )
> -+ self.cmd( self.command + ' ' + self.cargs )
> -+ self.ctlpid = quietRun( 'pgrep -n switchd' )
> -+ self.execed = False
> -+
> -+ def stop( self, *args, **kwargs ):
> -+ self.cmd( 'kill ' + self.ctlpid )
> -+ super( Node, self ).stop()
> -+
> -+
> -+# TODO: push these uname-ey things elsewhere
> -+if plat == 'Linux':
> -+ DefaultControllers = ( Controller, OVSController )
> -+ DefaultSwitch = OVSSwitch
> -+elif plat == 'FreeBSD':
> -+ DefaultControllers = ( Ryu, )
> -+ DefaultSwitch = OVSSwitch
> -+else: # OpenBSD
> -+ DefaultControllers = ( Switchd, )
> -+ DefaultSwitch = IfSwitch
> -+
> -
> - def findController( controllers=DefaultControllers ):
> - "Return first available controller from list, if any"
> Index: patches/patch-mininet_nodelib_py
> ===================================================================
> RCS file: patches/patch-mininet_nodelib_py
> diff -N patches/patch-mininet_nodelib_py
> --- patches/patch-mininet_nodelib_py 10 Apr 2018 07:55:10 -0000 1.2
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,243 +0,0 @@
> -$OpenBSD: patch-mininet_nodelib_py,v 1.2 2018/04/10 07:55:10 akoshibe Exp $
> -
> -Index: mininet/nodelib.py
> ---- mininet/nodelib.py.orig
> -+++ mininet/nodelib.py
> -@@ -3,10 +3,11 @@ Node Library for Mininet
> -
> - This contains additional Node types which you may find to be useful.
> - """
> -+from os import uname
> -
> - from mininet.node import Node, Switch
> - from mininet.log import info, warn
> --from mininet.moduledeps import pathCheck
> -+from mininet.moduledeps import lsmod, rmmod, modprobe, pathCheck
> - from mininet.util import quietRun
> -
> -
> -@@ -69,7 +70,119 @@ class LinuxBridge( Switch ):
> - warn( 'Warning: Linux bridge may not work with', out, '\n' )
> -
> -
> --class NAT( Node ):
> -+class IfBridge( Switch ):
> -+ "FreeBSD if_bridge(4) Node (with optional spanning tree)."
> -+
> -+ nextPrio = 100 # next bridge priority for spanning tree
> -+
> -+ def __init__( self, name, stp=False, prio=None, **kwargs ):
> -+ """stp: use spanning tree protocol? (default False)
> -+ prio: optional explicit bridge priority for STP"""
> -+ self.stp = stp
> -+ if prio:
> -+ self.prio = prio
> -+ else:
> -+ self.prio = IfBridge.nextPrio
> -+ IfBridge.nextPrio += 1
> -+ Switch.__init__( self, name, **kwargs )
> -+
> -+ def connected( self ):
> -+ "Are we forwarding yet?"
> -+ if self.stp:
> -+ return 'UP' in self.cmd( 'ifconfig', self.bname )
> -+ else:
> -+ return True
> -+
> -+ def start( self, _controllers ):
> -+ "Start bridge. Retain the bridge's name to save on ifconfig calls"
> -+ res = quietRun( 'ifconfig bridge create' )[:-1]
> -+ self.bname = res
> -+ quietRun( 'ifconfig %s vnet %s' % ( res, self ) )
> -+ addcmd, stpcmd = '', ''
> -+ for i in self.intfList():
> -+ if self.name in i.name or 'epair' in i.name:
> -+ addcmd += ' addm ' + i.name
> -+ if self.stp:
> -+ # STP settings are per-port. perhaps enable that as an option.
> -+ stpcmd += ' stp ' + i.name
> -+ self.cmd( 'ifconfig', i.name, 'up' )
> -+ self.cmd( 'ifconfig', res, addcmd )
> -+ if self.stp:
> -+ # ifconfig 'stp' and 'priority' latter default 32768
> -+ self.cmd( 'ifconfig', res, 'priority', self.prio )
> -+ self.cmd( 'ifconfig', res, stpcmd )
> -+ self.cmd( 'ifconfig', res, 'up' )
> -+
> -+ def stop( self, deleteIntfs=True ):
> -+ """Stop bridge
> -+ deleteIntfs: delete interfaces? (True)"""
> -+ self.cmd( 'ifconfig %s destroy' % self.bname )
> -+ super( IfBridge, self ).stop( deleteIntfs )
> -+
> -+ def dpctl( self, *args ):
> -+ "Run brctl command"
> -+ # actually ifconfig
> -+ return self.cmd( 'ifconfig', self.bname, *args )
> -+
> -+ @classmethod
> -+ def setup( cls ):
> -+ "Check dependencies"
> -+ if 'if_bridge' not in lsmod():
> -+ modprobe( 'if_bridge' )
> -+
> -+
> -+class Bridge4( Switch ):
> -+ "OpenBSD bridge(4) Node (with optional spanning tree)."
> -+
> -+ unitNo = 0 # number following device name, e.g. 0 in bridge0
> -+
> -+ def __init__( self, name, stp=False, prio=None, **kwargs ):
> -+ """stp: use spanning tree protocol? (default False)
> -+ prio: optional explicit bridge priority for STP"""
> -+ self.stp = stp
> -+ if prio:
> -+ self.prio = prio # else automatically calculate, '-ifcost iface'
> -+ Switch.__init__( self, name, **kwargs )
> -+
> -+ def connected( self ):
> -+ "Are we forwarding yet?"
> -+ if self.stp:
> -+ return 'UP' in self.cmd( 'ifconfig', self.bname )
> -+ else:
> -+ return True
> -+
> -+ def start( self, _controllers ):
> -+ "Start bridge. Retain the bridge's name to save on ifconfig calls"
> -+ self.bname = 'bridge%d' % Bridge4.unitNo
> -+ Bridge4.unitNo += 1
> -+
> -+ rdarg = 'rdomain %d' % self.rdid if self.inNamespace else ''
> -+ quietRun( 'ifconfig %s create %s up' % ( self.bname, rdarg ) )
> -+ addcmd, stpcmd = '', ''
> -+ for i in self.intfList():
> -+ if i.realname and 'pair' in i.realname:
> -+ name = i.realname
> -+ addcmd += ' add ' + name
> -+ if self.stp:
> -+ stpcmd += ' stp ' + name
> -+ if self.prio:
> -+ stpcmd += ' ifpriority %s %d ' % ( name, self.prio )
> -+ quietRun( 'ifconfig %s %s up' % ( name, rdarg ) )
> -+ quietRun( 'ifconfig ' + self.bname + addcmd )
> -+
> -+ def stop( self, deleteIntfs=True ):
> -+ """Stop bridge
> -+ deleteIntfs: delete interfaces? (True)"""
> -+ quietRun( 'ifconfig %s destroy' % self.bname )
> -+ super( Bridge4, self ).stop( deleteIntfs )
> -+
> -+ def dpctl( self, *args ):
> -+ "Run brctl command"
> -+ # actually ifconfig
> -+ return self.cmd( 'ifconfig', self.bname, *args )
> -+
> -+
> -+class IptablesNAT( Node ):
> - "NAT: Provides connectivity to external network"
> -
> - def __init__( self, name, subnet='10.0/8',
> -@@ -143,3 +256,103 @@ class NAT( Node ):
> - # Put the forwarding state back to what it was
> - self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
> - super( NAT, self ).terminate()
> -+
> -+
> -+class IpfwNAT( Node ):
> -+ """NAT: Provides connectivity to external network using ipfw. NOTE: This
> -+ *will* mangle IPFW rules that are already present on the host!"""
> -+
> -+ nextId = 100 # unique NAT instance number
> -+ ruleBase = 10 # rule number to start from
> -+
> -+ def __init__( self, name, subnet='10.0/8',
> -+ localIntf=None, flush=False,
> -+ natid=None, rulenr=None, **params):
> -+ """Start NAT/forwarding between Mininet and external network
> -+ subnet: Mininet subnet (default 10.0/8)
> -+ flush: flush existing rules before installing NAT rules"""
> -+ super( NAT, self ).__init__( name, **params )
> -+
> -+ self.subnet = subnet
> -+ self.localIntf = localIntf
> -+ self.flush = flush
> -+ if natid:
> -+ self.natId = str( natid )
> -+ else:
> -+ self.natId = str( NAT.nextId )
> -+ NAT.nextId += 1
> -+ if rulenr:
> -+ self.ruleNr = str( rulenr )
> -+ else:
> -+ self.ruleNr = str( NAT.ruleBase )
> -+ NAT.ruleBase += 10 # rule numbers 10 apart for each for readability
> -+ self.forwardState = self.cmd( 'sysctl -n net.inet.ip.forwarding' ).strip()
> -+
> -+ def ipfw( self, *args ):
> -+ """ invoke ipfw, -q for quiet """
> -+ return self.cmd( 'ipfw -q', *args )
> -+
> -+ def config( self, **params ):
> -+ """Configure the NAT and iptables"""
> -+ super( NAT, self).config( **params )
> -+
> -+ if not self.localIntf:
> -+ self.localIntf = self.defaultIntf()
> -+
> -+ if self.flush:
> -+ self.cmd( 'sysctl net.inet.ip.forwarding=0' )
> -+ self.ipfw( 'flush' )
> -+
> -+ # Install NAT rules
> -+ self.ipfw( 'nat', self.natId, 'config if', self.localIntf, 'reset' )
> -+ self.ipfw( 'add', self.ruleNr, 'nat', self.natId,
> -+ 'all from', self.subnet, 'to any out' )
> -+ self.ipfw( 'add', self.ruleNr, 'nat', self.natId,
> -+ 'all from any to any in' )
> -+
> -+ # Instruct the kernel to perform forwarding
> -+ self.cmd( 'sysctl net.inet.ip.forwarding=1' )
> -+
> -+ def terminate( self ):
> -+ "Stop NAT/forwarding between Mininet and external network"
> -+ # Remove NAT rules
> -+ self.ipfw( 'delete', self.ruleNr )
> -+ self.ipfw( 'delete 32000' )
> -+
> -+ # Put the forwarding state back to what it was
> -+ self.cmd( 'sysctl net.inet.ip.forwarding=%s' % self.forwardState )
> -+ super( NAT, self ).terminate()
> -+
> -+ @classmethod
> -+ def setup( cls ):
> -+ """ check for dependencies, then configure and load them """
> -+ klds = lsmod()
> -+ kenv = quietRun( 'kenv net.inet.ip.fw.default_to_accept' ).strip()
> -+ deny = True if kenv == '0' else False
> -+ # if the default rule is deny all, change to allow all so hosts can
> -+ # still pass traffic. Also reload ipfw so that it is reconfigured
> -+ if deny:
> -+ quietRun( 'kenv net.inet.ip.fw.default_to_accept=1' )
> -+ if deny and 'ipfw.ko' in klds:
> -+ rmmod( 'ipfw' )
> -+ modprobe( 'ipfw' )
> -+ if 'ipfw_nat.ko' not in klds:
> -+ modprobe( 'ipfw_nat' )
> -+
> -+
> -+class PfNAT( Node ):
> -+ """
> -+ A pf-based NAT node. (to-do)
> -+ """
> -+ pass
> -+
> -+plat = uname()[ 0 ]
> -+if plat == 'FreeBSD':
> -+ NAT = IpfwNAT
> -+ ClassicBridge = IfBridge
> -+elif plat == 'OpenBSD':
> -+ NAT = PfNAT #todo
> -+ ClassicBridge = Bridge4
> -+else:
> -+ NAT = IptablesNAT
> -+ ClassicBridge = LinuxBridge
> Index: patches/patch-mininet_openbsd___init___py
> ===================================================================
> RCS file: patches/patch-mininet_openbsd___init___py
> diff -N patches/patch-mininet_openbsd___init___py
> --- patches/patch-mininet_openbsd___init___py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,7 +0,0 @@
> -$OpenBSD: patch-mininet_openbsd___init___py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -
> -Index: mininet/openbsd/__init__.py
> ---- mininet/openbsd/__init__.py.orig
> -+++ mininet/openbsd/__init__.py
> -@@ -0,0 +1 @@
> -+"Docstring to silence pylint; ignores --ignore option for __init__.py"
> Index: patches/patch-mininet_openbsd_intf_py
> ===================================================================
> RCS file: patches/patch-mininet_openbsd_intf_py
> diff -N patches/patch-mininet_openbsd_intf_py
> --- patches/patch-mininet_openbsd_intf_py 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,91 +0,0 @@
> -$OpenBSD: patch-mininet_openbsd_intf_py,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -
> -Index: mininet/openbsd/intf.py
> ---- mininet/openbsd/intf.py.orig
> -+++ mininet/openbsd/intf.py
> -@@ -0,0 +1,85 @@
> -+"""
> -+A interface object that relies on ifconfig(8) to manipulate network
> -+interfaces and devices.
> -+"""
> -+from mininet.baseintf import BaseIntf
> -+
> -+class Intf( BaseIntf ):
> -+ """Interface objects that use 'ifconfig' to configure the underlying
> -+ interface that it represents"""
> -+
> -+ index=0 # pair(4) index
> -+
> -+ def __init__( self, name, node=None, port=None, link=None,
> -+ mac=None, **params ):
> -+ self.realname = params.get( 'orgName' )
> -+ BaseIntf.__init__( self, name, node=node, port=port, link=link,
> -+ mac=mac, **params )
> -+ self.ifconfig( 'description', '"%s"' % name )
> -+
> -+ def ifconfig( self, *args ):
> -+ "Configure ourselves using ifconfig"
> -+ name = self.realname if self.realname else self.name
> -+ o, err, ext = self.node.pexec( 'ifconfig', name, *args )
> -+ if not err:
> -+ return o
> -+ return err
> -+
> -+
> -+ def setMAC( self, macstr ):
> -+ self.mac = macstr
> -+ return self.ifconfig( 'lladdr', macstr )
> -+
> -+ def rename( self, newname ):
> -+ """
> -+ Rename interface. We retain the real name of the interface as
> -+ self.realname since interfaces can't be renamed. Instead we
> -+ add a description to the device.
> -+ """
> -+ if self.name in self.node.portNames:
> -+ del self.node.portNames[ self.name ]
> -+ self.node.portNames[ newname ] = self.realname
> -+ self.name = newname
> -+ self.ifconfig( 'description', '"%s"' % newname )
> -+ return newname
> -+
> -+ def delete( self ):
> -+ "Delete interface"
> -+ self.ifconfig( 'destroy' )
> -+ if self.name in self.node.portNames:
> -+ del self.node.portNames[ self.name ]
> -+ self.node.delIntf( self )
> -+ self.link = None
> -+
> -+ def status( self ):
> -+ "Return intf status as a string"
> -+ links, _err, _result = self.node.pexec( 'ifconfig pair' )
> -+ if self.realname + ':' in links:
> -+ return "OK"
> -+ else:
> -+ return "MISSING"
> -+
> -+ def realName( self ):
> -+ """
> -+ Pretend that the interface name has changed, but retain
> -+ the real name so we can actually configure the interface
> -+ """
> -+ return self.realname
> -+
> -+ def updateIP( self ):
> -+ """
> -+ 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.realname )
> -+ ips = self._ipMatchRegex.findall( ifconfig )
> -+ self.ip = ips[ 0 ] if ips else None
> -+ return self.ip
> -+
> -+ @classmethod
> -+ def next( cls ):
> -+ idx = Intf.index
> -+ Intf.index += 1
> -+ return idx
> Index: patches/patch-mininet_openbsd_node_py
> ===================================================================
> RCS file: patches/patch-mininet_openbsd_node_py
> diff -N patches/patch-mininet_openbsd_node_py
> --- patches/patch-mininet_openbsd_node_py 7 Dec 2017 06:33:40 -0000 1.3
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,167 +0,0 @@
> -$OpenBSD: patch-mininet_openbsd_node_py,v 1.3 2017/12/07 06:33:40 akoshibe Exp $
> -
> -Index: mininet/openbsd/node.py
> ---- mininet/openbsd/node.py.orig
> -+++ mininet/openbsd/node.py
> -@@ -0,0 +1,161 @@
> -+"""
> -+Node: rdomain(4) based node. This is somewhat more similar to Linux's network
> -+namespace moreso than a jail since it creates a separate network address space
> -+only.
> -+
> -+Mininet 'hosts' are created by running shells within rdomains. Links are made of
> -+pair(4)s patched together.
> -+
> -+This is a collection of helpers that call the right commands to manipulate these
> -+components.
> -+"""
> -+import signal
> -+import re
> -+from os import killpg
> -+
> -+from subprocess import PIPE, Popen
> -+from mininet.basenode import BaseNode
> -+from mininet.log import debug, info, warn
> -+from mininet.util import quietRun
> -+
> -+from mininet.openbsd.util import moveIntf
> -+from mininet.openbsd.intf import Intf
> -+
> -+class Node( BaseNode ):
> -+ """A virtual network node that manipulates and tracks rdomains. Because of
> -+ the property of rdomains, an OpenBSD node will always come with at least
> -+ one pair interface if inNamespace=True."""
> -+
> -+ index=1 # rdomain ID, can only go to 255
> -+ builtins='. : [ alias bg break builtin cd command continue echo eval exec' \
> -+ ' exit export false fc fg getopts jobs kill let print pwd read' \
> -+ ' readonly return set shift suspend test times trap true typeset' \
> -+ ' ulimit umask unalias unset wait whence'
> -+
> -+ def __init__( self, name, inNamespace=True, **params ):
> -+ BaseNode.__init__( self, name, inNamespace, **params )
> -+ # No renaming, supply map of assigned interface names to real names
> -+ self.portNames = {}
> -+
> -+ def getShell( self, master, slave, mnopts=None ):
> -+ """
> -+ Starts a shell used by the node to run commands. If inNamespace=True,
> -+ a pair interface is created, assigned to an rdomain, and a shell is
> -+ exec'd in the rdomain.
> -+ """
> -+ execcmd = [ 'mnexec' ]
> -+ opts = '-cd' if mnopts is None else mnopts
> -+
> -+ if self.inNamespace:
> -+ # create the pair tied to an rdomain
> -+ self.pair, self.rdid = 'pair%d' % Intf.next(), Node.index
> -+ Node.index += 1
> -+ if Node.index == 256:
> -+ error( 'Exceeded supported number of hosts (255)' )
> -+ exit( 1 )
> -+ rcmd = [ 'ifconfig', self.pair, 'create', 'description',
> -+ '"%s"' % self.name, 'rdomain', '%d' % self.rdid ]
> -+ Popen( rcmd, stdout=PIPE )
> -+ execcmd = [ 'route', '-T%d' % self.rdid, 'exec' ] + execcmd
> -+ else:
> -+ self.pair = None
> -+ self.rdid = None
> -+
> -+ # -s: pass $* to shell, and make process easy to find in ps. The prompt
> -+ # is set to sentinel chr( 127 ).
> -+ cmd = execcmd + [ opts, 'env', 'PS1=' + chr( 127 ), '/bin/ksh',
> -+ '-is', 'mininet:' + self.name ]
> -+ return Popen( cmd, stdin=slave, stdout=slave, stderr=slave,
> -+ close_fds=False )
> -+
> -+ def isShellBuiltin( self, cmd ):
> -+ "Return True if cmd is a builtin."
> -+ space = cmd.find( ' ' )
> -+ if space > 0:
> -+ cmd = cmd[ :space]
> -+ return cmd in Node.builtins
> -+
> -+ def mountPrivateDirs( self ):
> -+ "mount private directories"
> -+ # **Not applicable until further notice**
> -+ pass
> -+
> -+ def unmountPrivateDirs( self ):
> -+ "mount private directories - overridden"
> -+ # **Not applicable until further notice**
> -+ pass
> -+
> -+ def terminate( self ):
> -+ """ Cleanup when node is killed. """
> -+ #self.unmountPrivateDirs()
> -+ if self.rdid:
> -+ lo = 'lo%s' % self.rdid
> -+ Popen( [ 'ifconfig', self.pair, 'destroy' ] )
> -+ # cleanup loopbacks that are left
> -+ Popen( [ 'ifconfig', lo, 'rdomain', '0', 'destroy' ] )
> -+ if self.shell:
> -+ if self.shell.poll() is None:
> -+ killpg( self.shell.pid, signal.SIGHUP )
> -+ self.cleanup()
> -+
> -+ def popen( self, *args, **kwargs ):
> -+ """Return a Popen() object in our namespace
> -+ args: Popen() args, single list, or string
> -+ kwargs: Popen() keyword args"""
> -+ defaults = { 'stdout': PIPE, 'stderr': PIPE,
> -+ 'mncmd':
> -+ [ 'route', '-T%d' % self.rdid, 'exec' ] if self.rdid else
> -+ [ 'mnexec', '-d' ] }
> -+ defaults.update( kwargs )
> -+ if len( args ) == 1:
> -+ if isinstance( args[ 0 ], list ):
> -+ # popen([cmd, arg1, arg2...])
> -+ cmd = args[ 0 ]
> -+ elif isinstance( args[ 0 ], basestring ):
> -+ # popen("cmd arg1 arg2...")
> -+ cmd = args[ 0 ].split()
> -+ else:
> -+ raise Exception( 'popen() requires a string or list' )
> -+ elif len( args ) > 0:
> -+ # popen( cmd, arg1, arg2... )
> -+ cmd = list( args )
> -+ # Attach to our namespace using mnexec -a
> -+ cmd = defaults.pop( 'mncmd' ) + cmd
> -+ # Shell requires a string, not a list!
> -+ if defaults.get( 'shell', False ):
> -+ cmd = ' '.join( cmd )
> -+ popen = self._popen( cmd, **defaults )
> -+ return popen
> -+
> -+ def sendInt( self, intr=chr( 3 ) ):
> -+ "Interrupt running command."
> -+ quietRun( 'kill -2 %s' % self.lastPid )
> -+
> -+ def setHostRoute( self, ip, intf ):
> -+ """Add route to host.
> -+ ip: IP address as dotted decimal
> -+ intf: string, interface name
> -+ intfs: interface map of names to Intf"""
> -+ # add stronger checks for interface lookup
> -+ cmd = 'route -T%s add -host %s %s'
> -+ quietRun( cmd % ( self.rdid, ip, self.intfs( intf ).IP() ) )
> -+
> -+ def setDefaultRoute( self, intf=None ):
> -+ """Set the default route to go through intf.
> -+ intf: Intf or {dev <intfname> via <gw-ip> ...}"""
> -+ # Note setParam won't call us if intf is none
> -+ if isinstance( intf, basestring ):
> -+ argv = intf.split(' ')
> -+ if 'via' not in argv[ 0 ]:
> -+ warn( '%s: setDefaultRoute takes a port name but we got: %s\n' %
> -+ ( self.name, intf ) )
> -+ return
> -+ params = argv[ -1 ]
> -+ else:
> -+ params = intf.IP()
> -+ quietRun( 'route -T%d change default %s' % ( self.rdid, params ) )
> -+
> -+
> -+ def addIntf( self, intf, port=None, moveIntfFn=moveIntf ):
> -+ self.portNames[ intf.name ] = intf.realname
> -+ super( Node, self ).addIntf( intf, port, moveIntfFn )
> Index: patches/patch-mininet_openbsd_util_py
> ===================================================================
> RCS file: patches/patch-mininet_openbsd_util_py
> diff -N patches/patch-mininet_openbsd_util_py
> --- patches/patch-mininet_openbsd_util_py 10 Apr 2018 07:55:10 -0000 1.2
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,207 +0,0 @@
> -$OpenBSD: patch-mininet_openbsd_util_py,v 1.2 2018/04/10 07:55:10 akoshibe Exp $
> -
> -Index: mininet/openbsd/util.py
> ---- mininet/openbsd/util.py.orig
> -+++ mininet/openbsd/util.py
> -@@ -0,0 +1,201 @@
> -+"""
> -+OS-specific utility functions for OpenBSD, counterpart to util.py.
> -+"""
> -+
> -+from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
> -+
> -+from mininet.log import output, error, warn, debug
> -+from mininet.util import ( quietRun, retry )
> -+
> -+from mininet.openbsd.intf import Intf
> -+
> -+LO='lo0' # loopback name.
> -+DP_MODE=None # no OVS (unless manually built?)
> -+
> -+# Interface management
> -+#
> -+# Interfaces are managed as strings which are simply the
> -+# interface names, of the form 'nodeN-ethM'.
> -+#
> -+# To connect nodes, we create virtual ethernet pairs (epairs), and then place
> -+# them in the pair of nodes that we want to communicate. We then update the
> -+# node's list of interfaces and connectivity map.
> -+#
> -+
> -+def makeIntfPair( intf1, intf2, addr1=None, addr2=None, node1=None, node2=None,
> -+ deleteIntfs=True, runCmd=None ):
> -+ """Make two patched pair(4)s 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) - ignored.
> -+ raises Exception on failure"""
> -+ if deleteIntfs:
> -+ # Delete any old interfaces with the same names - want intf-node map?
> -+ quietRun( deleteCmd( intf1, node1 ) )
> -+ quietRun( deleteCmd( intf2, node2 ) )
> -+
> -+ # If there are nodes, and they are not 'in namespaces', create pairs for
> -+ # them and patch it to a node's.
> -+ pair1 = 'pair%d' % Intf.next()
> -+ pair2 = 'pair%d' % Intf.next()
> -+ cmd1 = 'ifconfig ' + pair1 + ' create'
> -+ cmd2 = 'ifconfig ' + pair2 + ' create'
> -+ if node1 and node1.rdid:
> -+ cmd1 += ' rdomain %d' % node1.rdid
> -+ if node2 and node2.rdid:
> -+ cmd2 += ' rdomain %d' % node2.rdid
> -+ if addr1:
> -+ cmd1 += ' lladdr ' + addr1
> -+ if addr2:
> -+ cmd2 += ' lladdr ' + addr2
> -+
> -+ # make one end, then patch the other onto it to form a link
> -+ quietRun( cmd1 + ' up' )
> -+ quietRun( cmd2 + ' patch ' + pair1 + ' up' )
> -+
> -+ return pair1, pair2
> -+
> -+
> -+def deleteCmd( intf, node=None ):
> -+ """Command to destroy an interface. If only intf is specified, assume that
> -+ it's in the host and is the true name of the intf."""
> -+ if 'pair' not in intf and node:
> -+ ifobj = node.nameToIntf.get( intf )
> -+ intf = ifobj.realName() if ifobj else intf
> -+ return 'ifconfig ' + intf + ' destroy'
> -+
> -+def moveIntfNoRetry( intf, dstNode, printError=False ):
> -+ """Move interface to node from host/root space, without retrying.
> -+ intf: string, interface
> -+ dstNode: destination Node
> -+ printError: if true, print error"""
> -+ intf = str( intf )
> -+ # get the real name of this intf
> -+ if 'pair' not in intf:
> -+ intf = dstNode.portNames[ intf ]
> -+ cmd = 'ifconfig %s rdomain %s up' % ( intf, dstNode.rdid )
> -+ cmdOutput = quietRun( cmd )
> -+ # If command does not produce any output, then we can assume
> -+ # that the interface has been moved successfully.
> -+ if cmdOutput:
> -+ if printError:
> -+ error( '*** Error: moveIntf: ' + intf +
> -+ ' not successfully moved to ' + dstNode.name + ':\n',
> -+ cmdOutput )
> -+ return False
> -+ return True
> -+
> -+# duplicated in other platforms
> -+def moveIntf( intf, dstNode, printError=True,
> -+ retries=3, delaySecs=0.001 ):
> -+ """Move interface to node, retrying on failure.
> -+ intf: string, interface
> -+ dstNode: destination Node
> -+ printError: if true, print error"""
> -+ retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
> -+ printError=printError )
> -+
> -+# Other stuff we use
> -+def sysctlTestAndSet( name, limit ):
> -+ "Helper function to set sysctl limits"
> -+ oldLimit = quietRun( 'sysctl -n ' + name )
> -+ if 'sysctl' in oldLimit:
> -+ error( 'Could not set value: %s' % out )
> -+ return
> -+ if isinstance( limit, int ):
> -+ #compare integer limits before overriding
> -+ if int( oldLimit ) < limit:
> -+ quietRun( 'sysctl %s=%s' % name, limit )
> -+ else:
> -+ #overwrite non-integer limits
> -+ quietRun( 'sysctl %s=%s' % name, limit )
> -+
> -+def rlimitTestAndSet( name, limit ):
> -+ "Helper function to set rlimits"
> -+ soft, hard = getrlimit( name )
> -+ if soft < limit:
> -+ hardLimit = hard if limit < hard else limit
> -+ setrlimit( name, ( limit, hardLimit ) )
> -+
> -+def fixLimits():
> -+ "Fix ridiculously small resource limits."
> -+ # see what needs to be/should be tuned here
> -+ pass
> -+ #debug( "*** Setting resource limits\n" )
> -+ #try:
> -+ #rlimitTestAndSet( RLIMIT_NPROC, 8192 )
> -+ #rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
> -+ #Increase open file limit
> -+ #sysctlTestAndSet( 'kern.maxfiles', 10000 )
> -+ #Increase network buffer space
> -+ #sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
> -+ #sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
> -+ #sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
> -+ #sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
> -+ #sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
> -+ #Increase arp cache size
> -+ #sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
> -+ #sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
> -+ #sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
> -+ #Increase routing table size
> -+ #sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
> -+ #Increase number of PTYs for nodes
> -+ #sysctlTestAndSet( 'kernel.pty.max', 20000 )
> -+ # pylint: disable=broad-except
> -+ #except Exception:
> -+ #warn( "*** Error setting resource limits. "
> -+ # "Mininet's performance may be affected.\n" )
> -+ # pylint: enable=broad-except
> -+
> -+def numCores():
> -+ "Returns number of CPU cores based on /proc/cpuinfo"
> -+ if hasattr( numCores, 'ncores' ):
> -+ return numCores.ncores
> -+ try:
> -+ numCores.ncores = int( quietRun('sysctl -n hw.ncpu') )
> -+ except ValueError:
> -+ return 0
> -+ return numCores.ncores
> -+
> -+# Kernel module manipulation
> -+# we don't have any.
> -+
> -+def lsmod():
> -+ """Return list of currently loaded kernel modules."""
> -+ pass
> -+
> -+def rmmod( mod ):
> -+ """Attempt to unload a specified module.
> -+ mod: module string"""
> -+ pass
> -+
> -+def modprobe( mod ):
> -+ """Attempt to load a specified module.
> -+ mod: module string"""
> -+ pass
> -+
> -+def waitListening( client, server, port=80, timeout=None ):
> -+ """Wait until server is listening on port.
> -+ returns True if server is listening"""
> -+ # pylint: disable=maybe-no-member
> -+ serverIP = server.IP().replace('.', '\.')
> -+ rdid = 0 if not server.rdid else server.rdid
> -+ cmd = ( 'netstat -nlp tcp -T %d | grep -e %s\.%s -e \*\.%s' \
> -+ % ( server.rdid, serverIP, port, port ) )
> -+ time = 0
> -+ result = server.cmd( cmd )
> -+ while 'LISTEN' not in result:
> -+ 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 = server.cmd( cmd )
> -+ return True
> Index: patches/patch-mininet_util_py
> ===================================================================
> RCS file: patches/patch-mininet_util_py
> diff -N patches/patch-mininet_util_py
> --- patches/patch-mininet_util_py 7 Dec 2017 06:33:40 -0000 1.2
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,242 +0,0 @@
> -$OpenBSD: patch-mininet_util_py,v 1.2 2017/12/07 06:33:40 akoshibe Exp $
> -Remove methods that do OS-specific things to their own platform dirs.
> -Index: mininet/util.py
> ---- mininet/util.py.orig
> -+++ mininet/util.py
> -@@ -1,10 +1,9 @@
> - "Utility functions for Mininet."
> -
> -
> --from mininet.log import output, info, error, warn, debug
> -+from mininet.log import output, info, error, debug
> -
> - from time import sleep
> --from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
> - from select import poll, POLLIN, POLLHUP
> - from subprocess import call, check_call, Popen, PIPE, STDOUT
> - import re
> -@@ -18,12 +17,12 @@ from functools import partial
> - def run( cmd ):
> - """Simple interface to subprocess.call()
> - cmd: list of command params"""
> -- return call( cmd.split( ' ' ) )
> -+ return call( cmd.split() )
> -
> - def checkRun( cmd ):
> - """Simple interface to subprocess.check_call()
> - cmd: list of command params"""
> -- return check_call( cmd.split( ' ' ) )
> -+ return check_call( cmd.split() )
> -
> - # pylint doesn't understand explicit type checking
> - # pylint: disable=maybe-no-member
> -@@ -34,7 +33,7 @@ def oldQuietRun( *cmd ):
> - if len( cmd ) == 1:
> - cmd = cmd[ 0 ]
> - if isinstance( cmd, str ):
> -- cmd = cmd.split( ' ' )
> -+ cmd = cmd.split()
> - popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
> - # We can't use Popen.communicate() because it uses
> - # select(), which can't handle
> -@@ -75,7 +74,7 @@ def errRun( *cmd, **kwargs ):
> - cmd = cmd[ 0 ]
> - # Allow passing in a list or a string
> - if isinstance( cmd, str ) and not shell:
> -- cmd = cmd.split( ' ' )
> -+ cmd = cmd.split()
> - cmd = [ str( arg ) for arg in cmd ]
> - elif isinstance( cmd, list ) and shell:
> - cmd = " ".join( arg for arg in cmd )
> -@@ -134,66 +133,6 @@ def quietRun( cmd, **kwargs ):
> -
> - # pylint: enable=maybe-no-member
> -
> --def isShellBuiltin( cmd ):
> -- "Return True if cmd is a bash builtin."
> -- if isShellBuiltin.builtIns is None:
> -- isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
> -- space = cmd.find( ' ' )
> -- if space > 0:
> -- cmd = cmd[ :space]
> -- return cmd in isShellBuiltin.builtIns
> --
> --isShellBuiltin.builtIns = None
> --
> --# Interface management
> --#
> --# Interfaces are managed as strings which are simply the
> --# interface names, of the form 'nodeN-ethM'.
> --#
> --# To connect nodes, we create a pair of veth interfaces, and then place them
> --# in the pair of nodes that we want to communicate. We then update the node's
> --# list of interfaces and connectivity map.
> --#
> --# For the kernel datapath, switch interfaces
> --# live in the root namespace and thus do not have to be
> --# explicitly moved.
> --
> --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
> -- netns = 1 if not node2 else node2.pid
> -- if addr1 is None and addr2 is None:
> -- cmdOutput = runCmd( 'ip link add name %s '
> -- 'type veth peer name %s '
> -- 'netns %s' % ( intf1, intf2, netns ) )
> -- else:
> -- cmdOutput = runCmd( 'ip link add name %s '
> -- 'address %s '
> -- 'type veth peer name %s '
> -- 'address %s '
> -- 'netns %s' %
> -- ( intf1, addr1, intf2, addr2, netns ) )
> -- 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.
> - n: number of times to retry
> -@@ -208,33 +147,6 @@ def retry( retries, delaySecs, fn, *args, **keywords )
> - error( "*** gave up after %i retries\n" % tries )
> - exit( 1 )
> -
> --def moveIntfNoRetry( intf, dstNode, printError=False ):
> -- """Move interface to node, without retrying.
> -- intf: string, interface
> -- dstNode: destination Node
> -- printError: if true, print error"""
> -- intf = str( intf )
> -- cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
> -- cmdOutput = quietRun( cmd )
> -- # If ip link set does not produce any output, then we can assume
> -- # that the link has been moved successfully.
> -- if cmdOutput:
> -- if printError:
> -- error( '*** Error: moveIntf: ' + intf +
> -- ' not successfully moved to ' + dstNode.name + ':\n',
> -- cmdOutput )
> -- return False
> -- return True
> --
> --def moveIntf( intf, dstNode, printError=True,
> -- retries=3, delaySecs=0.001 ):
> -- """Move interface to node, retrying on failure.
> -- intf: string, interface
> -- dstNode: destination Node
> -- printError: if true, print error"""
> -- retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
> -- printError=printError )
> --
> - # Support for dumping network
> -
> - def dumpNodeConnections( nodes ):
> -@@ -403,73 +315,6 @@ def pmonitor(popens, timeoutms=500, readline=True,
> - else:
> - yield None, ''
> -
> --# Other stuff we use
> --def sysctlTestAndSet( name, limit ):
> -- "Helper function to set sysctl limits"
> -- #convert non-directory names into directory names
> -- if '/' not in name:
> -- name = '/proc/sys/' + name.replace( '.', '/' )
> -- #read limit
> -- with open( name, 'r' ) as readFile:
> -- oldLimit = readFile.readline()
> -- if isinstance( limit, int ):
> -- #compare integer limits before overriding
> -- if int( oldLimit ) < limit:
> -- with open( name, 'w' ) as writeFile:
> -- writeFile.write( "%d" % limit )
> -- else:
> -- #overwrite non-integer limits
> -- with open( name, 'w' ) as writeFile:
> -- writeFile.write( limit )
> --
> --def rlimitTestAndSet( name, limit ):
> -- "Helper function to set rlimits"
> -- soft, hard = getrlimit( name )
> -- if soft < limit:
> -- hardLimit = hard if limit < hard else limit
> -- setrlimit( name, ( limit, hardLimit ) )
> --
> --def fixLimits():
> -- "Fix ridiculously small resource limits."
> -- debug( "*** Setting resource limits\n" )
> -- try:
> -- rlimitTestAndSet( RLIMIT_NPROC, 8192 )
> -- rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
> -- #Increase open file limit
> -- sysctlTestAndSet( 'fs.file-max', 10000 )
> -- #Increase network buffer space
> -- sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
> -- sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
> -- sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
> -- sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
> -- sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
> -- #Increase arp cache size
> -- sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
> -- sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
> -- sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
> -- #Increase routing table size
> -- sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
> -- #Increase number of PTYs for nodes
> -- sysctlTestAndSet( 'kernel.pty.max', 20000 )
> -- # 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"
> -- mounts = quietRun( 'cat /proc/mounts' )
> -- cgdir = '/sys/fs/cgroup'
> -- csdir = cgdir + '/cpuset'
> -- if ('cgroup %s' % cgdir not in mounts and
> -- 'cgroups %s' % cgdir not in mounts):
> -- raise Exception( "cgroups not mounted on " + cgdir )
> -- if 'cpuset %s' % csdir not in mounts:
> -- errRun( 'mkdir -p ' + csdir )
> -- errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
> --
> - def natural( text ):
> - "To sort sanely/alphabetically: sorted( l, key=natural )"
> - def num( s ):
> -@@ -480,16 +325,6 @@ def natural( text ):
> - def naturalSeq( t ):
> - "Natural sort key function for sequences"
> - return [ natural( x ) for x in t ]
> --
> --def numCores():
> -- "Returns number of CPU cores based on /proc/cpuinfo"
> -- if hasattr( numCores, 'ncores' ):
> -- return numCores.ncores
> -- try:
> -- numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
> -- except ValueError:
> -- return 0
> -- return numCores.ncores
> -
> - def irange(start, end):
> - """Inclusive range from start to end (vs. Python insanity.)
> Index: patches/patch-mnexec_c
> ===================================================================
> RCS file: patches/patch-mnexec_c
> diff -N patches/patch-mnexec_c
> --- patches/patch-mnexec_c 21 Aug 2017 18:47:12 -0000 1.1.1.1
> +++ /dev/null 1 Jan 1970 00:00:00 -0000
> @@ -1,126 +0,0 @@
> -$OpenBSD: patch-mnexec_c,v 1.1.1.1 2017/08/21 18:47:12 akoshibe Exp $
> -
> -Index: mnexec.c
> ---- mnexec.c.orig
> -+++ mnexec.c
> -@@ -13,18 +13,27 @@
> - * Partially based on public domain setsid(1)
> - */
> -
> -+#ifdef __linux__
> - #define _GNU_SOURCE
> --#include <stdio.h>
> -+
> - #include <linux/sched.h>
> --#include <unistd.h>
> - #include <limits.h>
> - #include <syscall.h>
> - #include <fcntl.h>
> --#include <stdlib.h>
> - #include <sched.h>
> - #include <ctype.h>
> - #include <sys/mount.h>
> -
> -+#else /* the BSDs */
> -+
> -+#include <sys/types.h>
> -+
> -+

No comments:

Post a Comment