pax_global_header00006660000000000000000000000064122165560040014513gustar00rootroot0000000000000052 comment=433d83221ae72052b4c1ddf74d2c9d378dc16618 mininet-2.1.0/000077500000000000000000000000001221655600400131565ustar00rootroot00000000000000mininet-2.1.0/.gitattributes000066400000000000000000000000211221655600400160420ustar00rootroot00000000000000*.py diff=python mininet-2.1.0/.gitignore000066400000000000000000000001521221655600400151440ustar00rootroot00000000000000mnexec *.pyc *~ *.1 *.xcodeproj *.xcworkspace \#*\# mininet.egg-info build dist doc/html doc/latex trunk mininet-2.1.0/.pylint000066400000000000000000000213611221655600400145010ustar00rootroot00000000000000# lint Python modules using external checkers. # # This is the main checker controlling the other ones and the reports # generation. It is itself both a raw checker and an astng checker in order # to: # * handle message activation / deactivation at the module level # * handle some basic but necessary stats'data (number of classes, methods...) # [MASTER] # Specify a configuration file. #rcfile= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Profiled execution. profile=no # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore=CVS # Pickle collected data for later comparisons. persistent=yes # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= [MESSAGES CONTROL] # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time. #enable= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). disable=W0704,C0103,W0231,E1102,W0511,W0142,R0902,R0903,R0904,R0913,R0914,R0801,I0011 [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html output-format=colorized # Include message's id in outpu include-ids=yes # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". files-output=no # Tells wether to display a full report or only the messages reports=no # Python expression which should return a note less than 10 (10 is the highes # note). You have access to the variables errors warning, statement which # respectivly contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation repor # (R0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Add a comment according to your evaluation note. This is used by the global # evaluation report (R0004). comment=no # Enable the report(s) with the given id(s). #enable-report= # Disable the report(s) with the given id(s). #disable-report= # checks for : # * doc strings # * modules / classes / functions / methods / arguments / variables name # * number of arguments, local variables, branchs, returns and statements in # functions, methods # * required module attributes # * dangerous default values as arguments # * redefinition of function / method / class # * uses of the global statemen # [BASIC] # Required attributes for module, separated by a comma required-attributes= # Regular expression which should only match functions or classes name which do # not require a docstring no-docstring-rgx=__.*__ # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression which should only match correct module level names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Regular expression which should only match correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ # Regular expression which should only match correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct method names method-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct instance attribute names attr-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct argument names argument-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct variable names variable-rgx=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match correct list comprehension / # generator expression variable names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # List of builtins function names that should not be used, separated by a comma bad-functions=map,filter,apply,inpu # try to find bugs in the code using type inference # [TYPECHECK] # Tells wether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # List of classes names for which member attributes should not be checked # (useful for classes with attributes dynamicaly set). ignored-classes=SQLObjec # When zope mode is activated, add a predefined set of Zope acquired attributes # to generated-members. zope=no # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E0201 when accessed. generated-members=REQUEST,acl_users,aq_paren # checks for # * unused variables / imports # * undefined variables # * redefinition of variable from builtins or from an outer scope # * use of variable before assigmen # [VARIABLES] # Tells wether we should check for unused import in __init__ files. init-import=no # A regular expression matching names used for dummy variables (i.e. not used). dummy-variables-rgx=_|dummy # List of additional names supposed to be defined in builtins. Remember tha # you should avoid to define new builtins when possible. additional-builtins= # checks for : # * methods without self as first argumen # * overridden methods signature # * access only to existant members via self # * attributes not defined in the __init__ method # * supported interfaces implementation # * unreachable code # [CLASSES] # List of interface methods to ignore, separated by a comma. This is used for # instance to not check methods defines in Zope's Interface base class. ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,__new__,setUp # checks for sign of poor/misdesign: # * number of methods, attributes, local variables... # * size, complexity of functions, methods # [DESIGN] # Maximum number of arguments for function / method max-args=5 # Maximum number of locals for function / method body max-locals=15 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branchs=12 # Maximum number of statements in function / method body max-statements=50 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=2 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # checks for # * external modules dependencies # * relative / wildcard imports # * cyclic imports # * uses of deprecated modules # [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,string,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the # given file (report R0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report R0402 mus # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report R0402 mus # not be disabled) int-import-graph= # checks for : # * unauthorized constructions # * strict indentation # * line length # * use of <> instead of != # [FORMAT] # Maximum number of characters on a single line. max-line-length=80 # Maximum number of lines in a module max-module-lines=1500 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # checks for: # * warning notes in the code like FIXME, XXX # * PEP 263: source code with non ascii character but no encoding declaration # [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME,XXX,TODO # checks for similarities and duplicated code. This computation may be # memory / CPU intensive, so you should disable it if you experiments some # problems. # [SIMILARITIES] # Minimum lines number of a similarity. min-similarity-lines=4 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes mininet-2.1.0/CONTRIBUTORS000066400000000000000000000013671221655600400150450ustar00rootroot00000000000000Mininet Contributors Mininet is an open source project and we gratefully acknowledge the many contributions to the project! If you have contributed code into the project and are not on this list, please let us know or send a pull request. Contributors include: Mininet Core Team Bob Lantz Brandon Heller Nikhil Handigol Vimal Jeyakumar Brian O'Connor Additional Mininet Contributors Gustavo Pantuza Coelho Pinto Ryan Cox Shaun Crampton David Erickson Glen Gibb Andrew Ferguson Eder Leao Fernandes Vitaly Ivanov Rich Lane Murphy McCauley José Pedro Oliveira James Page Angad Singh Piyush Srivastava Ed Swierk Isaku Yamahata Thanks also to everyone who has submitted issues and pull requests on github, and to our friendly mininet-discuss mailing list! mininet-2.1.0/INSTALL000066400000000000000000000120101221655600400142010ustar00rootroot00000000000000 Mininet Installation/Configuration Notes ---------------------------------------- Mininet 2.1.0 --- The supported installation methods for Mininet are 1) using a pre-built VM image, and 2) native installation on Ubuntu. You can also easily create your own Mininet VM image (4). (Other distributions may be supported in the future - if you would like to contribute an installation script, we would welcome it!) 1. Easiest "installation" - use our pre-built VM image! The easiest way to get Mininet running is to start with one of our pre-built virtual machine images from Boot up the VM image, log in, and follow the instructions on the Mininet web site. One advantage of using the VM image is that it doesn't mess with your native OS installation or damage it in any way. Although a single Mininet instance can simulate multiple networks with multiple controllers, only one Mininet instance may currently be run at a time, and Mininet requires root access in the machine it's running on. Therefore, if you have a multiuser system, you may wish to consider running Mininet in a VM. 2. Next-easiest option: use our Ubuntu package! To install Mininet itself (i.e. `mn` and the Python API) on Ubuntu 12.10+: sudo apt-get install mininet Note: if you are upgrading from an older version of Mininet, make sure you remove the old OVS from `/usr/local`: sudo rm /usr/local/bin/ovs* sudo rm /usr/local/sbin/ovs* 3. Native installation from source 3.1. Native installation from source on Ubuntu 12.04+ If you're reading this, you've probably already done so, but the command to download the Mininet source code is: git clone git://github.com/mininet/mininet.git If you are running Ubuntu, you may be able to use our handy `install.sh` script, which is in `mininet/util`. *WARNING: USE AT YOUR OWN RISK!* `install.sh` is a bit intrusive and may possibly damage your OS and/or home directory, by creating/modifying several directories such as `mininet`, `openflow`, `oftest`, `pox`, etc.. Although we hope it won't do anything completely terrible, you may want to look at the script before you run it, and you should make sure your system and home directory are backed up just in case! To install Mininet itself, the OpenFlow reference implementation, and Open vSwitch, you may use: mininet/util/install.sh -fnv This should be reasonably quick, and the following command should work after the installation: sudo mn --test pingall To install ALL of the software which we use for OpenFlow tutorials, including POX, the OpenFlow WireShark dissector, the `oftest` framework, and other potentially useful software, you may use: mininet/util/install.sh -a This takes about 4 minutes on our test system. You can change the directory where the dependencies are installed using the -s flag. mininet/util/install.sh -s -a 3.2. Native installation from source on Fedora 18+. As root execute the following operations: * install git yum install git * create an user account (e.g. mininet) and add it to the wheel group useradd [...] mininet usermod -a -G wheel mininet * change the SElinux setting to permissive. It can be done temporarily with: setenforce 0 then login with the new account (e.g. mininet) and do the following: * clone the Mininet repository git clone git://github.com/mininet/mininet.git * install Mininet, the OpenFlow reference implementation, and Open vSwitch mininet/util/install.sh -fnv * enable and start openvswitch sudo systemctl enable openvswitch sudo systemctl start openvswitch * test the mininet installation sudo mn --test pingall 4. Creating your own Mininet/OpenFlow tutorial VM Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial is easy! First, create a new Ubuntu VM. Next, run two commands in it: wget https://raw.github.com/mininet/mininet/master/util/vm/install-mininet-vm.sh time install-mininet-vm.sh Finally, verify that Mininet is installed and working in the VM: sudo mn --test pingall 5. Installation on other Linux distributions Although we don't support other Linux distributions directly, it should be possible to install and run Mininet with some degree of manual effort. In general, you must have: * A Linux kernel compiled with network namespace support enabled * An OpenFlow implementation (either the reference user or kernel space implementations, or Open vSwitch.) Appropriate kernel modules (e.g. tun and ofdatapath for the reference kernel implementation) must be loaded. * Python, `bash`, `ping`, `iperf`, etc. * Root privileges (required for network device access) We encourage contribution of patches to the `install.sh` script to support other Linux distributions. Good luck! Mininet Team --- mininet-2.1.0/LICENSE000066400000000000000000000032421221655600400141640ustar00rootroot00000000000000Mininet 2.1.0 License Copyright (c) 2013 Open Networking Laboratory Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of The Leland Stanford Junior University Original authors: Bob Lantz and Brandon Heller We are making Mininet available for public use and benefit with the expectation that others will use, modify and enhance the Software and contribute those enhancements back to the community. However, since we would like to make the Software available for broadest use, with as few restrictions as possible permission is hereby granted, free of charge, to any person obtaining a copy of this Software to deal in the Software under the copyrights without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The name and trademarks of copyright holder(s) may NOT be used in advertising or publicity pertaining to the Software or any derivatives without specific, written prior permission. mininet-2.1.0/Makefile000066400000000000000000000027361221655600400146260ustar00rootroot00000000000000MININET = mininet/*.py TEST = mininet/test/*.py EXAMPLES = mininet/examples/*.py MN = bin/mn BIN = $(MN) PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN) MNEXEC = mnexec MANPAGES = mn.1 mnexec.1 P8IGN = E251,E201,E302,E202 BINDIR = /usr/bin MANDIR = /usr/share/man/man1 DOCDIRS = doc/html doc/latex PDF = doc/latex/refman.pdf all: codecheck test clean: rm -rf build dist *.egg-info *.pyc $(MNEXEC) $(MANPAGES) $(DOCDIRS) codecheck: $(PYSRC) -echo "Running code check" util/versioncheck.py pyflakes $(PYSRC) pylint --rcfile=.pylint $(PYSRC) pep8 --repeat --ignore=$(P8IGN) $(PYSRC) errcheck: $(PYSRC) -echo "Running check for errors only" pyflakes $(PYSRC) pylint -E --rcfile=.pylint $(PYSRC) test: $(MININET) $(TEST) -echo "Running tests" mininet/test/test_nets.py mininet/test/test_hifi.py mnexec: mnexec.c $(MN) mininet/net.py cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(MN) --version`\" $< -o $@ install: $(MNEXEC) $(MANPAGES) install $(MNEXEC) $(BINDIR) install $(MANPAGES) $(MANDIR) python setup.py install develop: $(MNEXEC) $(MANPAGES) # Perhaps we should link these as well install $(MNEXEC) $(BINDIR) install $(MANPAGES) $(MANDIR) python setup.py develop man: $(MANPAGES) mn.1: $(MN) PYTHONPATH=. help2man -N -n "create a Mininet network." \ --no-discard-stderr $< -o $@ mnexec.1: mnexec help2man -N -n "execution utility for Mininet." \ -h "-h" -v "-v" --no-discard-stderr ./$< -o $@ .PHONY: doc doc: man doxygen doc/doxygen.cfg make -C doc/latex mininet-2.1.0/README.md000066400000000000000000000111471221655600400144410ustar00rootroot00000000000000Mininet: Rapid Prototyping for Software Defined Networks ======================================================== *The best way to emulate almost any network on your laptop!* Version 2.1.0 ### What is Mininet? Mininet emulates a complete network of hosts, links, and switches on a single machine. To create a sample two-host, one-switch network, just run: `sudo mn` Mininet is useful for interactive development, testing, and demos, especially those using OpenFlow and SDN. OpenFlow-based network 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: * A command-line launcher (`mn`) to instantiate networks. * A handy Python API for creating networks of varying sizes and topologies. * Examples (in the `examples/` directory) to help you get started. * Full API documentation via Python `help()` docstrings, as well as the ability to generate PDF/HTML documentation with `make doc`. * Parametrized topologies (`Topo` subclasses) using the Mininet object. For example, a tree network may be created with the command: `mn --topo tree,depth=2,fanout=3` * A command-line interface (`CLI` class) which provides useful diagnostic commands (like `iperf` and `ping`), as well as the ability to run a command to a node. For example, `mininet> h11 ifconfig -a` tells host h11 to run the command `ifconfig -a` * A "cleanup" command to get rid of junk (interfaces, processes, files in /tmp, etc.) which might be left around by Mininet or Linux. Try this if things stop working! `mn -c` ### New features in 2.1.0 Mininet 2.1.0 provides a number of bug fixes as well as several new features, including: * Convenient access to `Mininet()` as a dict of nodes * X11 tunneling (wireshark in Mininet hosts, finally!) * Accurate reflection of the `Mininet()` object in the CLI * Automatically detecting and adjusting resource limits * Automatic cleanup on failure of the `mn` command * Preliminary support for running OVS in user space mode * Preliminary support (`IVSSwitch()`) for the Indigo Virtual Switch * support for installing the OpenFlow 1.3 versions of the reference user switch and NOX from CPqD * The ability to import modules from `mininet.examples` We have provided several new examples (which can easily be imported to provide useful functionality) including: * Modeling separate control and data networks: `mininet.examples.controlnet` * Connecting Mininet hosts the internet (or a LAN) using NAT: `mininet.examples.nat` * Creating per-host custom directories using bind mounts: `mininet.examples.bind` Note that examples contain experimental features which might "graduate" into mainline Mininet in the future, but they should not be considered a stable part of the Mininet API! ### Installation See `INSTALL` for installation instructions and details. ### Documentation In addition to the API documentation (`make doc`), much useful information, including a Mininet walkthrough and an introduction to the Python API, is available on the [Mininet Web Site](http://mininet.org). There is also a wiki which you are encouraged to read and to contribute to, particularly the Frequently Asked Questions (FAQ.) ### Support Mininet is community-supported. We encourage you to join the Mininet mailing list, `mininet-discuss` at: ### Contributing Mininet is an open source project and is currently hosted at . You are encouraged to download the code, examine it, modify it, and submit bug reports, bug fixes, feature requests, new features and other issues and pull requests. Thanks to everyone who has contributed to the project (see CONTRIBUTORS for more info!) Best wishes, and we look forward to seeing what you can do with Mininet to change the networking world! ### Credits The Mininet 2.1.0 Team: * Bob Lantz * Brian O'Connor mininet-2.1.0/bin/000077500000000000000000000000001221655600400137265ustar00rootroot00000000000000mininet-2.1.0/bin/mn000077500000000000000000000251111221655600400142660ustar00rootroot00000000000000#!/usr/bin/env python """ Mininet runner author: Brandon Heller (brandonh@stanford.edu) To see options: sudo mn -h Example to pull custom params (topo, switch, etc.) from a file: sudo mn --custom ~/mininet/custom/custom_example.py """ from optparse import OptionParser import os import sys import time # Fix setuptools' evil madness, and open up (more?) security holes if 'PYTHONPATH' in os.environ: sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path from mininet.clean import cleanup from mininet.cli import CLI from mininet.log import lg, LEVELS, info, debug, error from mininet.net import Mininet, MininetWithControlNet, VERSION from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, NOX, RemoteController, UserSwitch, OVSKernelSwitch, OVSLegacyKernelSwitch, IVSSwitch ) from mininet.link import Link, TCLink from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo from mininet.topolib import TreeTopo from mininet.util import custom, customConstructor from mininet.util import buildTopo # built in topologies, created only when run TOPODEF = 'minimal' TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ), 'linear': LinearTopo, 'reversed': SingleSwitchReversedTopo, 'single': SingleSwitchTopo, 'tree': TreeTopo } SWITCHDEF = 'ovsk' SWITCHES = { 'user': UserSwitch, 'ovsk': OVSKernelSwitch, 'ovsl': OVSLegacyKernelSwitch, 'ivs': IVSSwitch } HOSTDEF = 'proc' HOSTS = { 'proc': Host, 'rt': custom( CPULimitedHost, sched='rt' ), 'cfs': custom( CPULimitedHost, sched='cfs' ) } CONTROLLERDEF = 'ovsc' CONTROLLERS = { 'ref': Controller, 'ovsc': OVSController, 'nox': NOX, 'remote': RemoteController, 'none': lambda name: None } LINKDEF = 'default' LINKS = { 'default': Link, 'tc': TCLink } # optional tests to run TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp', 'none' ] ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair', 'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp' } def addDictOption( opts, choicesDict, default, name, helpStr=None ): """Convenience function to add choices dicts to OptionParser. opts: OptionParser instance choicesDict: dictionary of valid choices, must include default default: default choice key name: long option name help: string""" if default not in choicesDict: raise Exception( 'Invalid default %s for choices dict: %s' % ( default, name ) ) if not helpStr: helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) + '[,param=value...]' ) opts.add_option( '--' + name, type='string', default = default, help = helpStr ) def version( *_args ): "Print Mininet version and exit" print "%s" % VERSION exit() class MininetRunner( object ): "Build, setup, and run Mininet." def __init__( self ): "Init." self.options = None self.args = None # May be used someday for more CLI scripts self.validate = None self.parseArgs() self.setup() self.begin() def setCustom( self, name, value ): "Set custom parameters for MininetRunner." if name in ( 'topos', 'switches', 'hosts', 'controllers' ): # Update dictionaries param = name.upper() globals()[ param ].update( value ) elif name == 'validate': # Add custom validate function self.validate = value else: # Add or modify global variable or class globals()[ name ] = value def parseCustomFile( self, fileName ): "Parse custom file and add params before parsing cmd-line options." customs = {} if os.path.isfile( fileName ): execfile( fileName, customs, customs ) for name, val in customs.iteritems(): self.setCustom( name, val ) else: raise Exception( 'could not find custom file: %s' % fileName ) def parseArgs( self ): """Parse command-line args and return options object. returns: opts parse options dict""" if '--custom' in sys.argv: index = sys.argv.index( '--custom' ) if len( sys.argv ) > index + 1: filename = sys.argv[ index + 1 ] self.parseCustomFile( filename ) else: raise Exception( 'Custom file name not found' ) desc = ( "The %prog utility creates Mininet network from the\n" "command line. It can create parametrized topologies,\n" "invoke the Mininet CLI, and run tests." ) usage = ( '%prog [options]\n' '(type %prog -h for details)' ) opts = OptionParser( description=desc, usage=usage ) addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' ) addDictOption( opts, HOSTS, HOSTDEF, 'host' ) addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' ) addDictOption( opts, LINKS, LINKDEF, 'link' ) addDictOption( opts, TOPOS, TOPODEF, 'topo' ) opts.add_option( '--clean', '-c', action='store_true', default=False, help='clean and exit' ) opts.add_option( '--custom', type='string', default=None, help='read custom topo and node params from .py' + 'file' ) opts.add_option( '--test', type='choice', choices=TESTS, default=TESTS[ 0 ], help='|'.join( TESTS ) ) opts.add_option( '--xterms', '-x', action='store_true', default=False, help='spawn xterms for each node' ) opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8', help='base IP address for hosts' ) opts.add_option( '--mac', action='store_true', default=False, help='automatically set host MACs' ) opts.add_option( '--arp', action='store_true', default=False, help='set all-pairs ARP entries' ) opts.add_option( '--verbosity', '-v', type='choice', choices=LEVELS.keys(), default = 'info', help = '|'.join( LEVELS.keys() ) ) opts.add_option( '--innamespace', action='store_true', default=False, help='sw and ctrl in namespace?' ) opts.add_option( '--listenport', type='int', default=6634, help='base port for passive switch listening' ) opts.add_option( '--nolistenport', action='store_true', default=False, help="don't use passive listening " + "port") opts.add_option( '--pre', type='string', default=None, help='CLI script to run before tests' ) opts.add_option( '--post', type='string', default=None, help='CLI script to run after tests' ) opts.add_option( '--pin', action='store_true', default=False, help="pin hosts to CPU cores " "(requires --host cfs or --host rt)" ) opts.add_option( '--version', action='callback', callback=version ) self.options, self.args = opts.parse_args() # We don't accept extra arguments after the options if self.args: opts.print_help() exit() def setup( self ): "Setup and validate environment." # set logging verbosity if LEVELS[self.options.verbosity] > LEVELS['output']: print ( '*** WARNING: selected verbosity level (%s) will hide CLI ' 'output!\n' 'Please restart Mininet with -v [debug, info, output].' % self.options.verbosity ) lg.setLogLevel( self.options.verbosity ) def begin( self ): "Create and run mininet." if self.options.clean: cleanup() exit() start = time.time() topo = buildTopo( TOPOS, self.options.topo ) switch = customConstructor( SWITCHES, self.options.switch ) host = customConstructor( HOSTS, self.options.host ) controller = customConstructor( CONTROLLERS, self.options.controller ) link = customConstructor( LINKS, self.options.link ) if self.validate: self.validate( self.options ) inNamespace = self.options.innamespace Net = MininetWithControlNet if inNamespace else Mininet ipBase = self.options.ipbase xterms = self.options.xterms mac = self.options.mac arp = self.options.arp pin = self.options.pin listenPort = None if not self.options.nolistenport: listenPort = self.options.listenport mn = Net( topo=topo, switch=switch, host=host, controller=controller, link=link, ipBase=ipBase, inNamespace=inNamespace, xterms=xterms, autoSetMacs=mac, autoStaticArp=arp, autoPinCpus=pin, listenPort=listenPort ) if self.options.pre: CLI( mn, script=self.options.pre ) test = self.options.test test = ALTSPELLING.get( test, test ) mn.start() if test == 'none': pass elif test == 'all': mn.start() mn.ping() mn.iperf() elif test == 'cli': CLI( mn ) elif test != 'build': getattr( mn, test )() if self.options.post: CLI( mn, script=self.options.post ) mn.stop() elapsed = float( time.time() - start ) info( 'completed in %0.3f seconds\n' % elapsed ) if __name__ == "__main__": try: MininetRunner() except KeyboardInterrupt: info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n") cleanup() except Exception: # Print exception type_, val_, trace_ = sys.exc_info() errorMsg = ( "-"*80 + "\n" + "Caught exception. Cleaning up...\n\n" + "%s: %s\n" % ( type_.__name__, val_ ) + "-"*80 + "\n" ) error( errorMsg ) # Print stack trace to debug log import traceback stackTrace = traceback.format_exc() debug( stackTrace + "\n" ) cleanup() mininet-2.1.0/custom/000077500000000000000000000000001221655600400144705ustar00rootroot00000000000000mininet-2.1.0/custom/README000066400000000000000000000006101221655600400153450ustar00rootroot00000000000000This directory should hold configuration files for custom mininets. See custom_example.py, which loads the default minimal topology. The advantage of defining a mininet in a separate file is that you then use the --custom option in mn to run the CLI or specific tests with it. To start up a mininet with the provided custom topology, do: sudo mn --custom custom_example.py --topo mytopo mininet-2.1.0/custom/topo-2sw-2host.py000066400000000000000000000015761221655600400176020ustar00rootroot00000000000000"""Custom topology example Two directly connected switches plus a host for each switch: host --- switch --- switch --- host Adding the 'topos' dict with a key/value pair to generate our newly defined topology enables one to pass in '--topo=mytopo' from the command line. """ from mininet.topo import Topo class MyTopo( Topo ): "Simple topology example." def __init__( self ): "Create custom topo." # Initialize topology Topo.__init__( self ) # Add hosts and switches leftHost = self.addHost( 'h1' ) rightHost = self.addHost( 'h2' ) leftSwitch = self.addSwitch( 's3' ) rightSwitch = self.addSwitch( 's4' ) # Add links self.addLink( leftHost, leftSwitch ) self.addLink( leftSwitch, rightSwitch ) self.addLink( rightSwitch, rightHost ) topos = { 'mytopo': ( lambda: MyTopo() ) } mininet-2.1.0/debian/000077500000000000000000000000001221655600400144005ustar00rootroot00000000000000mininet-2.1.0/debian/changelog000066400000000000000000000016301221655600400162520ustar00rootroot00000000000000mininet (2.1.0-0ubuntu1) saucy; urgency=low * Add 2.1.0 final packaging -- Bob Lantz Wed, 18 Sep 2013 22:43:47 -0700 mininet (2.1.0~rc1-0ubuntu1) saucy; urgency=low * New upstream release candidate: - d/control: Drop dependency on python-networkx, add iperf, socat and cgroup-bin to Depends. -- James Page Wed, 28 Aug 2013 10:10:20 +0100 mininet (2.0.0-0ubuntu1) raring; urgency=low * New upstream release. -- James Page Wed, 19 Dec 2012 15:48:01 +0000 mininet (2.0.0~rc1-0ubuntu1) quantal; urgency=low * New upstream release. * Update copyright to match upstream release * Fix this message -- Bob Lantz Sun, 18 Nov 2012 00:15:09 -0800 mininet (2.0.0~d4-0ubuntu1) quantal; urgency=low * Initial release. -- Bob Lantz Tue, 07 Aug 2012 14:11:27 -0700 mininet-2.1.0/debian/compat000066400000000000000000000000021221655600400155760ustar00rootroot000000000000009 mininet-2.1.0/debian/control000066400000000000000000000013671221655600400160120ustar00rootroot00000000000000Source: mininet Section: net Priority: extra Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Bob Lantz Standards-Version: 3.9.3 Build-Depends: debhelper (>= 9~), help2man, python-dev, python-pkg-resources, python-setuptools Homepage: http://openflow.org/mininet Package: mininet Architecture: any Depends: openvswitch-switch, telnet, socat, iperf, cgroup-bin, ${misc:Depends}, ${python:Depends}, ${shlibs:Depends} Recommends: openvswitch-controller Description: Process-based network emulator Mininet is a network emulator which uses lightweight virtualization to create virtual networks for rapid prototyping of Software-Defined Network (SDN) designs using OpenFlow. mininet-2.1.0/debian/copyright000066400000000000000000000035771221655600400163470ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0 Upstream-Name: mininet Source: https://github.com/mininet/mininet Files: * Copyright: 2012-2013 Open Networking Laboratory, 2009-2012 Bob Lantz, 2009-2012 The Board of Trustees of the Leland Stanford Junior University License: Original authors: Bob Lantz and Brandon Heller . We are making Mininet available for public use and benefit with the expectation that others will use, modify and enhance the Software and contribute those enhancements back to the community. However, since we would like to make the Software available for broadest use, with as few restrictions as possible permission is hereby granted, free of charge, to any person obtaining a copy of this Software to deal in the Software under the copyrights without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. . The name and trademarks of copyright holder(s) may NOT be used in advertising or publicity pertaining to the Software or any derivatives without specific, written prior permission. mininet-2.1.0/debian/docs000066400000000000000000000000121221655600400152440ustar00rootroot00000000000000README.md mininet-2.1.0/debian/examples000066400000000000000000000000131221655600400161330ustar00rootroot00000000000000examples/* mininet-2.1.0/debian/install000066400000000000000000000000201221655600400157610ustar00rootroot00000000000000mnexec /usr/bin mininet-2.1.0/debian/manpages000066400000000000000000000000041221655600400161100ustar00rootroot00000000000000*.1 mininet-2.1.0/debian/rules000077500000000000000000000002771221655600400154660ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --buildsystem=python_distutils --with=python2 override_dh_auto_build: make man make mnexec dh_auto_build get-orig-source: uscan --force-download --rename mininet-2.1.0/debian/source/000077500000000000000000000000001221655600400157005ustar00rootroot00000000000000mininet-2.1.0/debian/source/format000066400000000000000000000000141221655600400171060ustar00rootroot000000000000003.0 (quilt) mininet-2.1.0/debian/watch000066400000000000000000000002421221655600400154270ustar00rootroot00000000000000version=3 opts=filenamemangle=s/(.*)\/archive/$1/,uversionmangle=s/([abdr].*)\.tar\.gz/~$1/ \ https://github.com/mininet/mininet/tags .*/archive\/(\d.*\.tar\.gz) mininet-2.1.0/doc/000077500000000000000000000000001221655600400137235ustar00rootroot00000000000000mininet-2.1.0/doc/doxygen.cfg000066400000000000000000001640431221655600400160710ustar00rootroot00000000000000# Doxyfile 1.5.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Mininet Python API Reference Manual" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, # and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = mininet # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.py # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = util/doxify.py # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = YES #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hiererachy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NONE # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is enabled by default, which results in a transparent # background. Warning: Depending on the platform used, enabling this option # may lead to badly anti-aliased labels on the edges of a graph (i.e. they # become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO mininet-2.1.0/examples/000077500000000000000000000000001221655600400147745ustar00rootroot00000000000000mininet-2.1.0/examples/README.md000066400000000000000000000064621221655600400162630ustar00rootroot00000000000000Mininet Examples ======================================================== These examples are intended to help you get started using Mininet's Python API. ======================================================== #### baresshd.py: This example uses Mininet's medium-level API to create an sshd process running in a namespace. Doesn't use OpenFlow. #### consoles.py: This example creates a grid of console windows, one for each node, and allows interaction with and monitoring of each console, including graphical monitoring. #### controllers.py: This example creates a network with multiple controllers, by using a custom `Switch()` subclass. #### controllers2.py: This example creates a network with multiple controllers by creating an empty network, adding nodes to it, and manually starting the switches. #### controlnet.py: This examples shows how you can model the control network as well as the data network, by actually creating two Mininet objects. #### cpu.py: This example tests iperf bandwidth for varying CPU limits. #### emptynet.py: This example demonstrates creating an empty network (i.e. with no topology object) and adding nodes to it. #### hwintf.py: This example shows how to add an interface (for example a real hardware interface) to a network after the network is created. #### limit.py: This example shows how to use link and CPU limits. #### linearbandwidth.py: This example shows how to create a custom topology programatically by subclassing Topo, and how to run a series of tests on it. #### miniedit.py: This example demonstrates creating a network via a graphical editor. #### multiping.py: This example demonstrates one method for monitoring output from multiple hosts, using `node.monitor()`. #### multipoll.py: This example demonstrates monitoring output files from multiple hosts. #### multitest.py: This example creates a network and runs multiple tests on it. #### nat.py: This example shows how to connect a Mininet network to the Internet using NAT. It also answers the eternal question "why can't I ping `google.com`?" #### popen.py: This example monitors a number of hosts using `host.popen()` and `pmonitor()`. #### popenpoll.py: This example demonstrates monitoring output from multiple hosts using the `node.popen()` interface (which returns `Popen` objects) and `pmonitor()`. #### scratchnet.py, scratchnetuser.py: These two examples demonstrate how to create a network by using the lowest- level Mininet functions. Generally the higher-level API is easier to use, but scratchnet shows what is going on behind the scenes. #### simpleperf.py: A simple example of configuring network and CPU bandwidth limits. #### sshd.py: This example shows how to run an `sshd` process in each host, allowing you to log in via `ssh`. This requires connecting the Mininet data network to an interface in the root namespace (generaly the control network already lives in the root namespace, so it does not need to be explicitly connected.) #### tree1024.py: This example attempts to create a 1024-host network, and then runs the CLI on it. It may run into scalability limits, depending on available memory and `sysctl` configuration (see `INSTALL`.) #### treeping64.py: This example creates a 64-host tree network, and attempts to check full connectivity using `ping`, for different switch/datapath types. mininet-2.1.0/examples/__init__.py000066400000000000000000000000601221655600400171010ustar00rootroot00000000000000""" Mininet Examples See README for details """ mininet-2.1.0/examples/baresshd.py000077500000000000000000000015151221655600400171460ustar00rootroot00000000000000#!/usr/bin/python "This example doesn't use OpenFlow, but attempts to run sshd in a namespace." import sys from mininet.node import Host from mininet.util import ensureRoot ensureRoot() print "*** Creating nodes" h1 = Host( 'h1' ) root = Host( 'root', inNamespace=False ) print "*** Creating links" h1.linkTo( root ) print h1 print "*** Configuring nodes" h1.setIP( '10.0.0.1', 8 ) root.setIP( '10.0.0.2', 8 ) print "*** Creating banner file" f = open( '/tmp/%s.banner' % h1.name, 'w' ) f.write( 'Welcome to %s at %s\n' % ( h1.name, h1.IP() ) ) f.close() print "*** Running sshd" cmd = '/usr/sbin/sshd -o UseDNS=no -u0 -o "Banner /tmp/%s.banner"' % h1.name # add arguments from the command line if len( sys.argv ) > 1: cmd += ' ' + ' '.join( sys.argv[ 1: ] ) h1.cmd( cmd ) print "*** You may now ssh into", h1.name, "at", h1.IP() mininet-2.1.0/examples/bind.py000077500000000000000000000144321221655600400162710ustar00rootroot00000000000000#!/usr/bin/python """ bind.py: Bind mount prototype This creates hosts with private directories as desired. """ from mininet.net import Mininet from mininet.node import Host from mininet.cli import CLI from mininet.util import errFail, quietRun, errRun from mininet.topo import SingleSwitchTopo from mininet.log import setLogLevel, info, debug from os.path import realpath from functools import partial # Utility functions for unmounting a tree MNRUNDIR = realpath( '/var/run/mn' ) def mountPoints(): "Return list of mounted file systems" mtab, _err, _ret = errFail( 'cat /proc/mounts' ) lines = mtab.split( '\n' ) mounts = [] for line in lines: if not line: continue fields = line.split( ' ') mount = fields[ 1 ] mounts.append( mount ) return mounts def unmountAll( rootdir=MNRUNDIR ): "Unmount all mounts under a directory tree" rootdir = realpath( rootdir ) # Find all mounts below rootdir # This is subtle because /foo is not # a parent of /foot dirslash = rootdir + '/' mounts = [ m for m in mountPoints() if m == dir or m.find( dirslash ) == 0 ] # Unmount them from bottom to top mounts.sort( reverse=True ) for mount in mounts: debug( 'Unmounting', mount, '\n' ) _out, err, code = errRun( 'umount', mount ) if code != 0: info( '*** Warning: failed to umount', mount, '\n' ) info( err ) class HostWithPrivateDirs( Host ): "Host with private directories" mnRunDir = MNRUNDIR def __init__(self, name, *args, **kwargs ): """privateDirs: list of private directories remounts: dirs to remount unmount: unmount dirs in cleanup? (True) Note: if unmount is False, you must call unmountAll() manually.""" self.privateDirs = kwargs.pop( 'privateDirs', [] ) self.remounts = kwargs.pop( 'remounts', [] ) self.unmount = kwargs.pop( 'unmount', True ) Host.__init__( self, name, *args, **kwargs ) self.rundir = '%s/%s' % ( self.mnRunDir, name ) self.root, self.private = None, None # set in createBindMounts if self.privateDirs: self.privateDirs = [ realpath( d ) for d in self.privateDirs ] self.createBindMounts() # These should run in the namespace before we chroot, # in order to put the right entries in /etc/mtab # Eventually this will allow a local pid space # Now we chroot and cd to wherever we were before. pwd = self.cmd( 'pwd' ).strip() self.sendCmd( 'exec chroot', self.root, 'bash -ms mininet:' + self.name ) self.waiting = False self.cmd( 'cd', pwd ) # In order for many utilities to work, # we need to remount /proc and /sys self.cmd( 'mount /proc' ) self.cmd( 'mount /sys' ) def mountPrivateDirs( self ): "Create and bind mount private dirs" for dir_ in self.privateDirs: privateDir = self.private + dir_ errFail( 'mkdir -p ' + privateDir ) mountPoint = self.root + dir_ errFail( 'mount -B %s %s' % ( privateDir, mountPoint) ) def mountDirs( self, dirs ): "Mount a list of directories" for dir_ in dirs: mountpoint = self.root + dir_ errFail( 'mount -B %s %s' % ( dir_, mountpoint ) ) @classmethod def findRemounts( cls, fstypes=None ): """Identify mount points in /proc/mounts to remount fstypes: file system types to match""" if fstypes is None: fstypes = [ 'nfs' ] dirs = quietRun( 'cat /proc/mounts' ).strip().split( '\n' ) remounts = [] for dir_ in dirs: line = dir_.split() mountpoint, fstype = line[ 1 ], line[ 2 ] # Don't re-remount directories!!! if mountpoint.find( cls.mnRunDir ) == 0: continue if fstype in fstypes: remounts.append( mountpoint ) return remounts def createBindMounts( self ): """Create a chroot directory structure, with self.privateDirs as private dirs""" errFail( 'mkdir -p '+ self.rundir ) unmountAll( self.rundir ) # Create /root and /private directories self.root = self.rundir + '/root' self.private = self.rundir + '/private' errFail( 'mkdir -p ' + self.root ) errFail( 'mkdir -p ' + self.private ) # Recursively mount / in private doort # note we'll remount /sys and /proc later errFail( 'mount -B / ' + self.root ) self.mountDirs( self.remounts ) self.mountPrivateDirs() def unmountBindMounts( self ): "Unmount all of our bind mounts" unmountAll( self.rundir ) def popen( self, *args, **kwargs ): "Popen with chroot support" chroot = kwargs.pop( 'chroot', True ) mncmd = kwargs.get( 'mncmd', [ 'mnexec', '-a', str( self.pid ) ] ) if chroot: mncmd = [ 'chroot', self.root ] + mncmd kwargs[ 'mncmd' ] = mncmd return Host.popen( self, *args, **kwargs ) def cleanup( self ): """Clean up, then unmount bind mounts unmount: actually unmount bind mounts?""" # Wait for process to actually terminate self.shell.wait() Host.cleanup( self ) if self.unmount: self.unmountBindMounts() errFail( 'rmdir ' + self.root ) # Convenience aliases findRemounts = HostWithPrivateDirs.findRemounts # Sample usage def testHostWithPrivateDirs(): "Test bind mounts" topo = SingleSwitchTopo( 10 ) remounts = findRemounts( fstypes=[ 'nfs' ] ) privateDirs = [ '/var/log', '/var/run' ] host = partial( HostWithPrivateDirs, remounts=remounts, privateDirs=privateDirs, unmount=False ) net = Mininet( topo=topo, host=host ) net.start() info( 'Private Directories:', privateDirs, '\n' ) CLI( net ) net.stop() # We do this all at once to save a bit of time info( 'Unmounting host bind mounts...\n' ) unmountAll() if __name__ == '__main__': unmountAll() setLogLevel( 'info' ) testHostWithPrivateDirs() info( 'Done.\n') mininet-2.1.0/examples/consoles.py000077500000000000000000000363751221655600400172140ustar00rootroot00000000000000#!/usr/bin/python """ consoles.py: bring up a bunch of miniature consoles on a virtual network This demo shows how to monitor a set of nodes by using Node's monitor() and Tkinter's createfilehandler(). We monitor nodes in a couple of ways: - First, each individual node is monitored, and its output is added to its console window - Second, each time a console window gets iperf output, it is parsed and accumulated. Once we have output for all consoles, a bar is added to the bandwidth graph. The consoles also support limited interaction: - Pressing "return" in a console will send a command to it - Pressing the console's title button will open up an xterm Bob Lantz, April 2010 """ import re from Tkinter import Frame, Button, Label, Text, Scrollbar, Canvas, Wm, READABLE from mininet.log import setLogLevel from mininet.topolib import TreeNet from mininet.term import makeTerms, cleanUpScreens from mininet.util import quietRun class Console( Frame ): "A simple console on a host." def __init__( self, parent, net, node, height=10, width=32, title='Node' ): Frame.__init__( self, parent ) self.net = net self.node = node self.prompt = node.name + '# ' self.height, self.width, self.title = height, width, title # Initialize widget styles self.buttonStyle = { 'font': 'Monaco 7' } self.textStyle = { 'font': 'Monaco 7', 'bg': 'black', 'fg': 'green', 'width': self.width, 'height': self.height, 'relief': 'sunken', 'insertbackground': 'green', 'highlightcolor': 'green', 'selectforeground': 'black', 'selectbackground': 'green' } # Set up widgets self.text = self.makeWidgets( ) self.bindEvents() self.sendCmd( 'export TERM=dumb' ) self.outputHook = None def makeWidgets( self ): "Make a label, a text area, and a scroll bar." def newTerm( net=self.net, node=self.node, title=self.title ): "Pop up a new terminal window for a node." net.terms += makeTerms( [ node ], title ) label = Button( self, text=self.node.name, command=newTerm, **self.buttonStyle ) label.pack( side='top', fill='x' ) text = Text( self, wrap='word', **self.textStyle ) ybar = Scrollbar( self, orient='vertical', width=7, command=text.yview ) text.configure( yscrollcommand=ybar.set ) text.pack( side='left', expand=True, fill='both' ) ybar.pack( side='right', fill='y' ) return text def bindEvents( self ): "Bind keyboard and file events." # The text widget handles regular key presses, but we # use special handlers for the following: self.text.bind( '', self.handleReturn ) self.text.bind( '', self.handleInt ) self.text.bind( '', self.handleKey ) # This is not well-documented, but it is the correct # way to trigger a file event handler from Tk's # event loop! self.tk.createfilehandler( self.node.stdout, READABLE, self.handleReadable ) # We're not a terminal (yet?), so we ignore the following # control characters other than [\b\n\r] ignoreChars = re.compile( r'[\x00-\x07\x09\x0b\x0c\x0e-\x1f]+' ) def append( self, text ): "Append something to our text frame." text = self.ignoreChars.sub( '', text ) self.text.insert( 'end', text ) self.text.mark_set( 'insert', 'end' ) self.text.see( 'insert' ) outputHook = lambda x, y: True # make pylint happier if self.outputHook: outputHook = self.outputHook outputHook( self, text ) def handleKey( self, event ): "If it's an interactive command, send it to the node." char = event.char if self.node.waiting: self.node.write( char ) def handleReturn( self, event ): "Handle a carriage return." cmd = self.text.get( 'insert linestart', 'insert lineend' ) # Send it immediately, if "interactive" command if self.node.waiting: self.node.write( event.char ) return # Otherwise send the whole line to the shell pos = cmd.find( self.prompt ) if pos >= 0: cmd = cmd[ pos + len( self.prompt ): ] self.sendCmd( cmd ) # Callback ignores event def handleInt( self, _event=None ): "Handle control-c." self.node.sendInt() def sendCmd( self, cmd ): "Send a command to our node." if not self.node.waiting: self.node.sendCmd( cmd ) def handleReadable( self, _fds, timeoutms=None ): "Handle file readable event." data = self.node.monitor( timeoutms ) self.append( data ) if not self.node.waiting: # Print prompt self.append( self.prompt ) def waiting( self ): "Are we waiting for output?" return self.node.waiting def waitOutput( self ): "Wait for any remaining output." while self.node.waiting: # A bit of a trade-off here... self.handleReadable( self, timeoutms=1000) self.update() def clear( self ): "Clear all of our text." self.text.delete( '1.0', 'end' ) class Graph( Frame ): "Graph that we can add bars to over time." def __init__( self, parent=None, bg = 'white', gheight=200, gwidth=500, barwidth=10, ymax=3.5,): Frame.__init__( self, parent ) self.bg = bg self.gheight = gheight self.gwidth = gwidth self.barwidth = barwidth self.ymax = float( ymax ) self.xpos = 0 # Create everything self.title, self.scale, self.graph = self.createWidgets() self.updateScrollRegions() self.yview( 'moveto', '1.0' ) def createScale( self ): "Create a and return a new canvas with scale markers." height = float( self.gheight ) width = 25 ymax = self.ymax scale = Canvas( self, width=width, height=height, background=self.bg ) opts = { 'fill': 'red' } # Draw scale line scale.create_line( width - 1, height, width - 1, 0, **opts ) # Draw ticks and numbers for y in range( 0, int( ymax + 1 ) ): ypos = height * (1 - float( y ) / ymax ) scale.create_line( width, ypos, width - 10, ypos, **opts ) scale.create_text( 10, ypos, text=str( y ), **opts ) return scale def updateScrollRegions( self ): "Update graph and scale scroll regions." ofs = 20 height = self.gheight + ofs self.graph.configure( scrollregion=( 0, -ofs, self.xpos * self.barwidth, height ) ) self.scale.configure( scrollregion=( 0, -ofs, 0, height ) ) def yview( self, *args ): "Scroll both scale and graph." self.graph.yview( *args ) self.scale.yview( *args ) def createWidgets( self ): "Create initial widget set." # Objects title = Label( self, text='Bandwidth (Gb/s)', bg=self.bg ) width = self.gwidth height = self.gheight scale = self.createScale() graph = Canvas( self, width=width, height=height, background=self.bg) xbar = Scrollbar( self, orient='horizontal', command=graph.xview ) ybar = Scrollbar( self, orient='vertical', command=self.yview ) graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set, scrollregion=(0, 0, width, height ) ) scale.configure( yscrollcommand=ybar.set ) # Layout title.grid( row=0, columnspan=3, sticky='new') scale.grid( row=1, column=0, sticky='nsew' ) graph.grid( row=1, column=1, sticky='nsew' ) ybar.grid( row=1, column=2, sticky='ns' ) xbar.grid( row=2, column=0, columnspan=2, sticky='ew' ) self.rowconfigure( 1, weight=1 ) self.columnconfigure( 1, weight=1 ) return title, scale, graph def addBar( self, yval ): "Add a new bar to our graph." percent = yval / self.ymax c = self.graph x0 = self.xpos * self.barwidth x1 = x0 + self.barwidth y0 = self.gheight y1 = ( 1 - percent ) * self.gheight c.create_rectangle( x0, y0, x1, y1, fill='green' ) self.xpos += 1 self.updateScrollRegions() self.graph.xview( 'moveto', '1.0' ) def clear( self ): "Clear graph contents." self.graph.delete( 'all' ) self.xpos = 0 def test( self ): "Add a bar for testing purposes." ms = 1000 if self.xpos < 10: self.addBar( self.xpos / 10 * self.ymax ) self.after( ms, self.test ) def setTitle( self, text ): "Set graph title" self.title.configure( text=text, font='Helvetica 9 bold' ) class ConsoleApp( Frame ): "Simple Tk consoles for Mininet." menuStyle = { 'font': 'Geneva 7 bold' } def __init__( self, net, parent=None, width=4 ): Frame.__init__( self, parent ) self.top = self.winfo_toplevel() self.top.title( 'Mininet' ) self.net = net self.menubar = self.createMenuBar() cframe = self.cframe = Frame( self ) self.consoles = {} # consoles themselves titles = { 'hosts': 'Host', 'switches': 'Switch', 'controllers': 'Controller' } for name in titles: nodes = getattr( net, name ) frame, consoles = self.createConsoles( cframe, nodes, width, titles[ name ] ) self.consoles[ name ] = Object( frame=frame, consoles=consoles ) self.selected = None self.select( 'hosts' ) self.cframe.pack( expand=True, fill='both' ) cleanUpScreens() # Close window gracefully Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit ) # Initialize graph graph = Graph( cframe ) self.consoles[ 'graph' ] = Object( frame=graph, consoles=[ graph ] ) self.graph = graph self.graphVisible = False self.updates = 0 self.hostCount = len( self.consoles[ 'hosts' ].consoles ) self.bw = 0 self.pack( expand=True, fill='both' ) def updateGraph( self, _console, output ): "Update our graph." m = re.search( r'(\d+.?\d*) ([KMG]?bits)/sec', output ) if not m: return val, units = float( m.group( 1 ) ), m.group( 2 ) #convert to Gbps if units[0] == 'M': val *= 10 ** -3 elif units[0] == 'K': val *= 10 ** -6 elif units[0] == 'b': val *= 10 ** -9 self.updates += 1 self.bw += val if self.updates >= self.hostCount: self.graph.addBar( self.bw ) self.bw = 0 self.updates = 0 def setOutputHook( self, fn=None, consoles=None ): "Register fn as output hook [on specific consoles.]" if consoles is None: consoles = self.consoles[ 'hosts' ].consoles for console in consoles: console.outputHook = fn def createConsoles( self, parent, nodes, width, title ): "Create a grid of consoles in a frame." f = Frame( parent ) # Create consoles consoles = [] index = 0 for node in nodes: console = Console( f, self.net, node, title=title ) consoles.append( console ) row = index / width column = index % width console.grid( row=row, column=column, sticky='nsew' ) index += 1 f.rowconfigure( row, weight=1 ) f.columnconfigure( column, weight=1 ) return f, consoles def select( self, groupName ): "Select a group of consoles to display." if self.selected is not None: self.selected.frame.pack_forget() self.selected = self.consoles[ groupName ] self.selected.frame.pack( expand=True, fill='both' ) def createMenuBar( self ): "Create and return a menu (really button) bar." f = Frame( self ) buttons = [ ( 'Hosts', lambda: self.select( 'hosts' ) ), ( 'Switches', lambda: self.select( 'switches' ) ), ( 'Controllers', lambda: self.select( 'controllers' ) ), ( 'Graph', lambda: self.select( 'graph' ) ), ( 'Ping', self.ping ), ( 'Iperf', self.iperf ), ( 'Interrupt', self.stop ), ( 'Clear', self.clear ), ( 'Quit', self.quit ) ] for name, cmd in buttons: b = Button( f, text=name, command=cmd, **self.menuStyle ) b.pack( side='left' ) f.pack( padx=4, pady=4, fill='x' ) return f def clear( self ): "Clear selection." for console in self.selected.consoles: console.clear() def waiting( self, consoles=None ): "Are any of our hosts waiting for output?" if consoles is None: consoles = self.consoles[ 'hosts' ].consoles for console in consoles: if console.waiting(): return True return False def ping( self ): "Tell each host to ping the next one." consoles = self.consoles[ 'hosts' ].consoles if self.waiting( consoles ): return count = len( consoles ) i = 0 for console in consoles: i = ( i + 1 ) % count ip = consoles[ i ].node.IP() console.sendCmd( 'ping ' + ip ) def iperf( self ): "Tell each host to iperf to the next one." consoles = self.consoles[ 'hosts' ].consoles if self.waiting( consoles ): return count = len( consoles ) self.setOutputHook( self.updateGraph ) for console in consoles: # Sometimes iperf -sD doesn't return, # so we run it in the background instead console.node.cmd( 'iperf -s &' ) i = 0 for console in consoles: i = ( i + 1 ) % count ip = consoles[ i ].node.IP() console.sendCmd( 'iperf -t 99999 -i 1 -c ' + ip ) def stop( self, wait=True ): "Interrupt all hosts." consoles = self.consoles[ 'hosts' ].consoles for console in consoles: console.handleInt() if wait: for console in consoles: console.waitOutput() self.setOutputHook( None ) # Shut down any iperfs that might still be running quietRun( 'killall -9 iperf' ) def quit( self ): "Stop everything and quit." self.stop( wait=False) Frame.quit( self ) # Make it easier to construct and assign objects def assign( obj, **kwargs ): "Set a bunch of fields in an object." obj.__dict__.update( kwargs ) class Object( object ): "Generic object you can stuff junk into." def __init__( self, **kwargs ): assign( self, **kwargs ) if __name__ == '__main__': setLogLevel( 'info' ) network = TreeNet( depth=2, fanout=4 ) network.start() app = ConsoleApp( network, width=4 ) app.mainloop() network.stop() mininet-2.1.0/examples/controllers.py000077500000000000000000000020321221655600400177140ustar00rootroot00000000000000#!/usr/bin/python """ Create a network where different switches are connected to different controllers, by creating a custom Switch() subclass. """ from mininet.net import Mininet from mininet.node import OVSSwitch, Controller, RemoteController from mininet.topolib import TreeTopo from mininet.log import setLogLevel from mininet.cli import CLI setLogLevel( 'info' ) # Two local and one "external" controller (which is actually c0) # Ignore the warning message that the remote isn't (yet) running c0 = Controller( 'c0', port=6633 ) c1 = Controller( 'c1', port=6634 ) c2 = RemoteController( 'c2', ip='127.0.0.1' ) cmap = { 's1': c0, 's2': c1, 's3': c2 } class MultiSwitch( OVSSwitch ): "Custom Switch() subclass that connects to different controllers" def start( self, controllers ): return OVSSwitch.start( self, [ cmap[ self.name ] ] ) topo = TreeTopo( depth=2, fanout=2 ) net = Mininet( topo=topo, switch=MultiSwitch, build=False ) for c in [ c0, c1 ]: net.addController(c) net.build() net.start() CLI( net ) net.stop() mininet-2.1.0/examples/controllers2.py000077500000000000000000000031311221655600400177770ustar00rootroot00000000000000#!/usr/bin/python """ This example creates a multi-controller network from semi-scratch by using the net.add*() API and manually starting the switches and controllers. This is the "mid-level" API, which is an alternative to the "high-level" Topo() API which supports parametrized topology classes. Note that one could also create a custom switch class and pass it into the Mininet() constructor. """ from mininet.net import Mininet from mininet.node import Controller, OVSSwitch from mininet.cli import CLI from mininet.log import setLogLevel def multiControllerNet(): "Create a network from semi-scratch with multiple controllers." net = Mininet( controller=Controller, switch=OVSSwitch, build=False ) print "*** Creating (reference) controllers" c1 = net.addController( 'c1', port=6633 ) c2 = net.addController( 'c2', port=6634 ) print "*** Creating switches" s1 = net.addSwitch( 's1' ) s2 = net.addSwitch( 's2' ) print "*** Creating hosts" hosts1 = [ net.addHost( 'h%d' % n ) for n in 3, 4 ] hosts2 = [ net.addHost( 'h%d' % n ) for n in 5, 6 ] print "*** Creating links" for h in hosts1: net.addLink( s1, h ) for h in hosts2: net.addLink( s2, h ) net.addLink( s1, s2 ) print "*** Starting network" net.build() c1.start() c2.start() s1.start( [ c1 ] ) s2.start( [ c2 ] ) print "*** Testing network" net.pingAll() print "*** Running CLI" CLI( net ) print "*** Stopping network" net.stop() if __name__ == '__main__': setLogLevel( 'info' ) # for CLI output multiControllerNet() mininet-2.1.0/examples/controlnet.py000077500000000000000000000112431221655600400175410ustar00rootroot00000000000000#!/usr/bin/python """ controlnet.py: Mininet with a custom control network We create two Mininet() networks, a control network and a data network, running four DataControllers on the control network to control the data network. Since we're using UserSwitch on the data network, it should correctly fail over to a backup controller. We also use a Mininet Facade to talk to both the control and data networks from a single CLI. """ from functools import partial from mininet.net import Mininet from mininet.node import Controller, UserSwitch from mininet.cli import CLI from mininet.topo import Topo from mininet.topolib import TreeTopo from mininet.log import setLogLevel, info # Some minor hacks class DataController( Controller ): """Data Network Controller. patched to avoid checkListening error""" def checkListening( self ): "Ignore spurious error" pass class MininetFacade( object ): """Mininet object facade that allows a single CLI to talk to one or more networks""" def __init__( self, net, *args, **kwargs ): """Create MininetFacade object. net: Primary Mininet object args: unnamed networks passed as arguments kwargs: named networks passed as arguments""" self.net = net self.nets = [ net ] + list( args ) + kwargs.values() self.nameToNet = kwargs self.nameToNet['net'] = net def __getattr__( self, name ): "returns attribute from Primary Mininet object" return getattr( self.net, name ) def __getitem__( self, key ): "returns primary/named networks or node from any net" #search kwargs for net named key if key in self.nameToNet: return self.nameToNet[ key ] #search each net for node named key for net in self.nets: if key in net: return net[ key ] def __iter__( self ): "Iterate through all nodes in all Mininet objects" for net in self.nets: for node in net: yield node def __len__( self ): "returns aggregate number of nodes in all nets" count = 0 for net in self.nets: count += len(net) return count def __contains__( self, key ): "returns True if node is a member of any net" return key in self.keys() def keys( self ): "returns a list of all node names in all networks" return list( self ) def values( self ): "returns a list of all nodes in all networks" return [ self[ key ] for key in self ] def items( self ): "returns (key,value) tuple list for every node in all networks" return zip( self.keys(), self.values() ) # A real control network! class ControlNetwork( Topo ): "Control Network Topology" def __init__( self, n, dataController=DataController, **kwargs ): """n: number of data network controller nodes dataController: class for data network controllers""" Topo.__init__( self, **kwargs ) # Connect everything to a single switch cs0 = self.addSwitch( 'cs0' ) # Add hosts which will serve as data network controllers for i in range( 0, n ): c = self.addHost( 'c%s' % i, cls=dataController, inNamespace=True ) self.addLink( c, cs0 ) # Connect switch to root namespace so that data network # switches will be able to talk to us root = self.addHost( 'root', inNamespace=False ) self.addLink( root, cs0 ) # Make it Happen!! def run(): "Create control and data networks, and invoke the CLI" info( '* Creating Control Network\n' ) ctopo = ControlNetwork( n=4, dataController=DataController ) cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None ) info( '* Adding Control Network Controller\n') cnet.addController( 'cc0', controller=Controller ) info( '* Starting Control Network\n') cnet.start() info( '* Creating Data Network\n' ) topo = TreeTopo( depth=2, fanout=2 ) # UserSwitch so we can easily test failover sw = partial( UserSwitch, opts='--inactivity-probe=1 --max-backoff=1' ) net = Mininet( topo=topo, switch=sw, controller=None ) info( '* Adding Controllers to Data Network\n' ) for host in cnet.hosts: if isinstance(host, Controller): net.addController( host ) info( '* Starting Data Network\n') net.start() mn = MininetFacade( net, cnet=cnet ) CLI( mn ) info( '* Stopping Data Network\n' ) net.stop() info( '* Stopping Control Network\n' ) cnet.stop() if __name__ == '__main__': setLogLevel( 'info' ) run() mininet-2.1.0/examples/cpu.py000077500000000000000000000045071221655600400161460ustar00rootroot00000000000000#!/usr/bin/python """ cpu.py: test iperf bandwidth for varying cpu limits """ from mininet.net import Mininet from mininet.node import CPULimitedHost from mininet.topolib import TreeTopo from mininet.util import custom from mininet.log import setLogLevel, output from time import sleep def waitListening(client, server, port): "Wait until server is listening on port" if not client.cmd('which telnet'): raise Exception('Could not find telnet') cmd = ('sh -c "echo A | telnet -e A %s %s"' % (server.IP(), port)) while 'Connected' not in client.cmd(cmd): output('waiting for', server, 'to listen on port', port, '\n') sleep(.5) def bwtest( cpuLimits, period_us=100000, seconds=5 ): """Example/test of link and CPU bandwidth limits cpu: cpu limit as fraction of overall CPU time""" topo = TreeTopo( depth=1, fanout=2 ) results = {} for sched in 'rt', 'cfs': print '*** Testing with', sched, 'bandwidth limiting' for cpu in cpuLimits: host = custom( CPULimitedHost, sched=sched, period_us=period_us, cpu=cpu ) net = Mininet( topo=topo, host=host ) net.start() net.pingAll() hosts = [ net.getNodeByName( h ) for h in topo.hosts() ] client, server = hosts[ 0 ], hosts[ -1 ] server.cmd( 'iperf -s -p 5001 &' ) waitListening( client, server, 5001 ) result = client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) ).split( ',' ) bps = float( result[ -1 ] ) server.cmdPrint( 'kill %iperf' ) net.stop() updated = results.get( sched, [] ) updated += [ ( cpu, bps ) ] results[ sched ] = updated return results def dump( results ): "Dump results" fmt = '%s\t%s\t%s' print print fmt % ( 'sched', 'cpu', 'client MB/s' ) print for sched in sorted( results.keys() ): entries = results[ sched ] for cpu, bps in entries: pct = '%.2f%%' % ( cpu * 100 ) mbps = bps / 1e6 print fmt % ( sched, pct, mbps ) if __name__ == '__main__': setLogLevel( 'info' ) limits = [ .45, .4, .3, .2, .1 ] out = bwtest( limits ) dump( out ) mininet-2.1.0/examples/emptynet.py000077500000000000000000000017001221655600400172140ustar00rootroot00000000000000#!/usr/bin/python """ This example shows how to create an empty Mininet object (without a topology object) and add nodes to it manually. """ from mininet.net import Mininet from mininet.node import Controller from mininet.cli import CLI from mininet.log import setLogLevel, info def emptyNet(): "Create an empty network and add nodes to it." net = Mininet( controller=Controller ) info( '*** Adding controller\n' ) net.addController( 'c0' ) info( '*** Adding hosts\n' ) h1 = net.addHost( 'h1', ip='10.0.0.1' ) h2 = net.addHost( 'h2', ip='10.0.0.2' ) info( '*** Adding switch\n' ) s3 = net.addSwitch( 's3' ) info( '*** Creating links\n' ) net.addLink( h1, s3 ) net.addLink( h2, s3 ) info( '*** Starting network\n') net.start() info( '*** Running CLI\n' ) CLI( net ) info( '*** Stopping network' ) net.stop() if __name__ == '__main__': setLogLevel( 'info' ) emptyNet() mininet-2.1.0/examples/hwintf.py000077500000000000000000000030021221655600400166430ustar00rootroot00000000000000#!/usr/bin/python """ This example shows how to add an interface (for example a real hardware interface) to a network after the network is created. """ import re, sys from mininet.cli import CLI from mininet.log import setLogLevel, info, error from mininet.net import Mininet from mininet.link import Intf from mininet.topolib import TreeTopo from mininet.util import quietRun def checkIntf( intf ): "Make sure intf exists and is not configured." if ( ' %s:' % intf ) not in quietRun( 'ip link show' ): error( 'Error:', intf, 'does not exist!\n' ) exit( 1 ) ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) ) if ips: error( 'Error:', intf, 'has an IP address,' 'and is probably in use!\n' ) exit( 1 ) if __name__ == '__main__': setLogLevel( 'info' ) # try to get hw intf from the command line; by default, use eth1 intfName = sys.argv[ 1 ] if len( sys.argv ) > 1 else 'eth1' info( '*** Connecting to hw intf: %s' % intfName ) info( '*** Checking', intfName, '\n' ) checkIntf( intfName ) info( '*** Creating network\n' ) net = Mininet( topo=TreeTopo( depth=1, fanout=2 ) ) switch = net.switches[ 0 ] info( '*** Adding hardware interface', intfName, 'to switch', switch.name, '\n' ) _intf = Intf( intfName, node=switch ) info( '*** Note: you may need to reconfigure the interfaces for ' 'the Mininet hosts:\n', net.hosts, '\n' ) net.start() CLI( net ) net.stop() mininet-2.1.0/examples/limit.py000077500000000000000000000030761221655600400164750ustar00rootroot00000000000000#!/usr/bin/python """ limit.py: example of using link and CPU limits """ from mininet.net import Mininet from mininet.link import TCIntf from mininet.node import CPULimitedHost from mininet.topolib import TreeTopo from mininet.util import custom from mininet.log import setLogLevel def testLinkLimit( net, bw ): "Run bandwidth limit test" print '*** Testing network %.2f Mbps bandwidth limit' % bw net.iperf( ) def limit( bw=10, cpu=.1 ): """Example/test of link and CPU bandwidth limits bw: interface bandwidth limit in Mbps cpu: cpu limit as fraction of overall CPU time""" intf = custom( TCIntf, bw=bw ) myTopo = TreeTopo( depth=1, fanout=2 ) for sched in 'rt', 'cfs': print '*** Testing with', sched, 'bandwidth limiting' host = custom( CPULimitedHost, sched=sched, cpu=cpu ) net = Mininet( topo=myTopo, intf=intf, host=host ) net.start() testLinkLimit( net, bw=bw ) net.runCpuLimitTest( cpu=cpu ) net.stop() def verySimpleLimit( bw=150 ): "Absurdly simple limiting test" intf = custom( TCIntf, bw=bw ) net = Mininet( intf=intf ) h1, h2 = net.addHost( 'h1' ), net.addHost( 'h2' ) net.addLink( h1, h2 ) net.start() net.pingAll() net.iperf() h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() ) h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() ) h1.cmdPrint( 'tc -s qdisc ls dev', h1.defaultIntf() ) h2.cmdPrint( 'tc -d class show dev', h2.defaultIntf() ) net.stop() if __name__ == '__main__': setLogLevel( 'info' ) limit() mininet-2.1.0/examples/linearbandwidth.py000077500000000000000000000063451221655600400205200ustar00rootroot00000000000000#!/usr/bin/python """ Test bandwidth (using iperf) on linear networks of varying size, using both kernel and user datapaths. We construct a network of N hosts and N-1 switches, connected as follows: h1 <-> s1 <-> s2 .. sN-1 | | | h2 h3 hN WARNING: by default, the reference controller only supports 16 switches, so this test WILL NOT WORK unless you have recompiled your controller to support 100 switches (or more.) In addition to testing the bandwidth across varying numbers of switches, this example demonstrates: - creating a custom topology, LinearTestTopo - using the ping() and iperf() tests from Mininet() - testing both the kernel and user switches """ from mininet.net import Mininet from mininet.node import UserSwitch, OVSKernelSwitch from mininet.topo import Topo from mininet.log import lg from mininet.util import irange import sys flush = sys.stdout.flush class LinearTestTopo( Topo ): "Topology for a string of N hosts and N-1 switches." def __init__( self, N, **params ): # Initialize topology Topo.__init__( self, **params ) # Create switches and hosts hosts = [ self.addHost( 'h%s' % h ) for h in irange( 1, N ) ] switches = [ self.addSwitch( 's%s' % s ) for s in irange( 1, N - 1 ) ] # Wire up switches last = None for switch in switches: if last: self.addLink( last, switch ) last = switch # Wire up hosts self.addLink( hosts[ 0 ], switches[ 0 ] ) for host, switch in zip( hosts[ 1: ], switches ): self.addLink( host, switch ) def linearBandwidthTest( lengths ): "Check bandwidth at various lengths along a switch chain." results = {} switchCount = max( lengths ) hostCount = switchCount + 1 switches = { 'reference user': UserSwitch, 'Open vSwitch kernel': OVSKernelSwitch } topo = LinearTestTopo( hostCount ) for datapath in switches.keys(): print "*** testing", datapath, "datapath" Switch = switches[ datapath ] results[ datapath ] = [] net = Mininet( topo=topo, switch=Switch ) net.start() print "*** testing basic connectivity" for n in lengths: net.ping( [ net.hosts[ 0 ], net.hosts[ n ] ] ) print "*** testing bandwidth" for n in lengths: src, dst = net.hosts[ 0 ], net.hosts[ n ] print "testing", src.name, "<->", dst.name, bandwidth = net.iperf( [ src, dst ] ) print bandwidth flush() results[ datapath ] += [ ( n, bandwidth ) ] net.stop() for datapath in switches.keys(): print print "*** Linear network results for", datapath, "datapath:" print result = results[ datapath ] print "SwitchCount\tiperf Results" for switchCount, bandwidth in result: print switchCount, '\t\t', print bandwidth[ 0 ], 'server, ', bandwidth[ 1 ], 'client' print print if __name__ == '__main__': lg.setLogLevel( 'info' ) sizes = [ 1, 10, 20, 40, 60, 80, 100 ] print "*** Running linearBandwidthTest", sizes linearBandwidthTest( sizes ) mininet-2.1.0/examples/miniedit.py000077500000000000000000000615651221655600400171700ustar00rootroot00000000000000#!/usr/bin/python """ MiniEdit: a simple network editor for Mininet This is a simple demonstration of how one might build a GUI application using Mininet as the network model. Development version - not entirely functional! Bob Lantz, April 2010 """ from Tkinter import Frame, Button, Label, Scrollbar, Canvas from Tkinter import Menu, BitmapImage, PhotoImage, Wm, Toplevel # someday: from ttk import * from mininet.log import setLogLevel from mininet.net import Mininet from mininet.util import ipStr from mininet.term import makeTerm, cleanUpScreens class MiniEdit( Frame ): "A simple network editor for Mininet." def __init__( self, parent=None, cheight=200, cwidth=500 ): Frame.__init__( self, parent ) self.action = None self.appName = 'MiniEdit' # Style self.font = ( 'Geneva', 9 ) self.smallFont = ( 'Geneva', 7 ) self.bg = 'white' # Title self.top = self.winfo_toplevel() self.top.title( self.appName ) # Menu bar self.createMenubar() # Editing canvas self.cheight, self.cwidth = cheight, cwidth self.cframe, self.canvas = self.createCanvas() # Toolbar self.images = miniEditImages() self.buttons = {} self.active = None self.tools = ( 'Select', 'Host', 'Switch', 'Link' ) self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' } self.toolbar = self.createToolbar() # Layout self.toolbar.grid( column=0, row=0, sticky='nsew') self.cframe.grid( column=1, row=0 ) self.columnconfigure( 1, weight=1 ) self.rowconfigure( 0, weight=1 ) self.pack( expand=True, fill='both' ) # About box self.aboutBox = None # Initialize node data self.nodeBindings = self.createNodeBindings() self.nodePrefixes = { 'Switch': 's', 'Host': 'h' } self.widgetToItem = {} self.itemToWidget = {} # Initialize link tool self.link = self.linkWidget = None # Selection support self.selection = None # Keyboard bindings self.bind( '', lambda event: self.quit() ) self.bind( '', self.deleteSelection ) self.bind( '', self.deleteSelection ) self.focus() # Event handling initalization self.linkx = self.linky = self.linkItem = None self.lastSelection = None # Model initialization self.links = {} self.nodeCount = 0 self.net = None # Close window gracefully Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit ) def quit( self ): "Stop our network, if any, then quit." self.stop() Frame.quit( self ) def createMenubar( self ): "Create our menu bar." font = self.font mbar = Menu( self.top, font=font ) self.top.configure( menu=mbar ) # Application menu appMenu = Menu( mbar, tearoff=False ) mbar.add_cascade( label=self.appName, font=font, menu=appMenu ) appMenu.add_command( label='About MiniEdit', command=self.about, font=font) appMenu.add_separator() appMenu.add_command( label='Quit', command=self.quit, font=font ) #fileMenu = Menu( mbar, tearoff=False ) #mbar.add_cascade( label="File", font=font, menu=fileMenu ) #fileMenu.add_command( label="Load...", font=font ) #fileMenu.add_separator() #fileMenu.add_command( label="Save", font=font ) #fileMenu.add_separator() #fileMenu.add_command( label="Print", font=font ) editMenu = Menu( mbar, tearoff=False ) mbar.add_cascade( label="Edit", font=font, menu=editMenu ) editMenu.add_command( label="Cut", font=font, command=lambda: self.deleteSelection( None ) ) runMenu = Menu( mbar, tearoff=False ) mbar.add_cascade( label="Run", font=font, menu=runMenu ) runMenu.add_command( label="Run", font=font, command=self.doRun ) runMenu.add_command( label="Stop", font=font, command=self.doStop ) runMenu.add_separator() runMenu.add_command( label='Xterm', font=font, command=self.xterm ) # Canvas def createCanvas( self ): "Create and return our scrolling canvas frame." f = Frame( self ) canvas = Canvas( f, width=self.cwidth, height=self.cheight, bg=self.bg ) # Scroll bars xbar = Scrollbar( f, orient='horizontal', command=canvas.xview ) ybar = Scrollbar( f, orient='vertical', command=canvas.yview ) canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set ) # Resize box resize = Label( f, bg='white' ) # Layout canvas.grid( row=0, column=1, sticky='nsew') ybar.grid( row=0, column=2, sticky='ns') xbar.grid( row=1, column=1, sticky='ew' ) resize.grid( row=1, column=2, sticky='nsew' ) # Resize behavior f.rowconfigure( 0, weight=1 ) f.columnconfigure( 1, weight=1 ) f.grid( row=0, column=0, sticky='nsew' ) f.bind( '', lambda event: self.updateScrollRegion() ) # Mouse bindings canvas.bind( '', self.clickCanvas ) canvas.bind( '', self.dragCanvas ) canvas.bind( '', self.releaseCanvas ) return f, canvas def updateScrollRegion( self ): "Update canvas scroll region to hold everything." bbox = self.canvas.bbox( 'all' ) if bbox is not None: self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ], bbox[ 3 ] ) ) def canvasx( self, x_root ): "Convert root x coordinate to canvas coordinate." c = self.canvas return c.canvasx( x_root ) - c.winfo_rootx() def canvasy( self, y_root ): "Convert root y coordinate to canvas coordinate." c = self.canvas return c.canvasy( y_root ) - c.winfo_rooty() # Toolbar def activate( self, toolName ): "Activate a tool and press its button." # Adjust button appearance if self.active: self.buttons[ self.active ].configure( relief='raised' ) self.buttons[ toolName ].configure( relief='sunken' ) # Activate dynamic bindings self.active = toolName def createToolbar( self ): "Create and return our toolbar frame." toolbar = Frame( self ) # Tools for tool in self.tools: cmd = ( lambda t=tool: self.activate( t ) ) b = Button( toolbar, text=tool, font=self.smallFont, command=cmd) if tool in self.images: b.config( height=35, image=self.images[ tool ] ) # b.config( compound='top' ) b.pack( fill='x' ) self.buttons[ tool ] = b self.activate( self.tools[ 0 ] ) # Spacer Label( toolbar, text='' ).pack() # Commands for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]: doCmd = getattr( self, 'do' + cmd ) b = Button( toolbar, text=cmd, font=self.smallFont, fg=color, command=doCmd ) b.pack( fill='x', side='bottom' ) return toolbar def doRun( self ): "Run command." self.activate( 'Select' ) for tool in self.tools: self.buttons[ tool ].config( state='disabled' ) self.start() def doStop( self ): "Stop command." self.stop() for tool in self.tools: self.buttons[ tool ].config( state='normal' ) # Generic canvas handler # # We could have used bindtags, as in nodeIcon, but # the dynamic approach used here # may actually require less code. In any case, it's an # interesting introspection-based alternative to bindtags. def canvasHandle( self, eventName, event ): "Generic canvas event handler" if self.active is None: return toolName = self.active handler = getattr( self, eventName + toolName, None ) if handler is not None: handler( event ) def clickCanvas( self, event ): "Canvas click handler." self.canvasHandle( 'click', event ) def dragCanvas( self, event ): "Canvas drag handler." self.canvasHandle( 'drag', event ) def releaseCanvas( self, event ): "Canvas mouse up handler." self.canvasHandle( 'release', event ) # Currently the only items we can select directly are # links. Nodes are handled by bindings in the node icon. def findItem( self, x, y ): "Find items at a location in our canvas." items = self.canvas.find_overlapping( x, y, x, y ) if len( items ) == 0: return None else: return items[ 0 ] # Canvas bindings for Select, Host, Switch and Link tools def clickSelect( self, event ): "Select an item." self.selectItem( self.findItem( event.x, event.y ) ) def deleteItem( self, item ): "Delete an item." # Don't delete while network is running if self.buttons[ 'Select' ][ 'state' ] == 'disabled': return # Delete from model if item in self.links: self.deleteLink( item ) if item in self.itemToWidget: self.deleteNode( item ) # Delete from view self.canvas.delete( item ) def deleteSelection( self, _event ): "Delete the selected item." if self.selection is not None: self.deleteItem( self.selection ) self.selectItem( None ) def nodeIcon( self, node, name ): "Create a new node icon." icon = Button( self.canvas, image=self.images[ node ], text=name, compound='top' ) # Unfortunately bindtags wants a tuple bindtags = [ str( self.nodeBindings ) ] bindtags += list( icon.bindtags() ) icon.bindtags( tuple( bindtags ) ) return icon def newNode( self, node, event ): "Add a new node to our canvas." c = self.canvas x, y = c.canvasx( event.x ), c.canvasy( event.y ) self.nodeCount += 1 name = self.nodePrefixes[ node ] + str( self.nodeCount ) icon = self.nodeIcon( node, name ) item = self.canvas.create_window( x, y, anchor='c', window=icon, tags=node ) self.widgetToItem[ icon ] = item self.itemToWidget[ item ] = icon self.selectItem( item ) icon.links = {} def clickHost( self, event ): "Add a new host to our canvas." self.newNode( 'Host', event ) def clickSwitch( self, event ): "Add a new switch to our canvas." self.newNode( 'Switch', event ) def dragLink( self, event ): "Drag a link's endpoint to another node." if self.link is None: return # Since drag starts in widget, we use root coords x = self.canvasx( event.x_root ) y = self.canvasy( event.y_root ) c = self.canvas c.coords( self.link, self.linkx, self.linky, x, y ) def releaseLink( self, _event ): "Give up on the current link." if self.link is not None: self.canvas.delete( self.link ) self.linkWidget = self.linkItem = self.link = None # Generic node handlers def createNodeBindings( self ): "Create a set of bindings for nodes." bindings = { '': self.clickNode, '': self.dragNode, '': self.releaseNode, '': self.enterNode, '': self.leaveNode, '': self.xterm } l = Label() # lightweight-ish owner for bindings for event, binding in bindings.items(): l.bind( event, binding ) return l def selectItem( self, item ): "Select an item and remember old selection." self.lastSelection = self.selection self.selection = item def enterNode( self, event ): "Select node on entry." self.selectNode( event ) def leaveNode( self, _event ): "Restore old selection on exit." self.selectItem( self.lastSelection ) def clickNode( self, event ): "Node click handler." if self.active is 'Link': self.startLink( event ) else: self.selectNode( event ) return 'break' def dragNode( self, event ): "Node drag handler." if self.active is 'Link': self.dragLink( event ) else: self.dragNodeAround( event ) def releaseNode( self, event ): "Node release handler." if self.active is 'Link': self.finishLink( event ) # Specific node handlers def selectNode( self, event ): "Select the node that was clicked on." item = self.widgetToItem.get( event.widget, None ) self.selectItem( item ) def dragNodeAround( self, event ): "Drag a node around on the canvas." c = self.canvas # Convert global to local coordinates; # Necessary since x, y are widget-relative x = self.canvasx( event.x_root ) y = self.canvasy( event.y_root ) w = event.widget # Adjust node position item = self.widgetToItem[ w ] c.coords( item, x, y ) # Adjust link positions for dest in w.links: link = w.links[ dest ] item = self.widgetToItem[ dest ] x1, y1 = c.coords( item ) c.coords( link, x, y, x1, y1 ) def startLink( self, event ): "Start a new link." if event.widget not in self.widgetToItem: # Didn't click on a node return w = event.widget item = self.widgetToItem[ w ] x, y = self.canvas.coords( item ) self.link = self.canvas.create_line( x, y, x, y, width=4, fill='blue', tag='link' ) self.linkx, self.linky = x, y self.linkWidget = w self.linkItem = item # Link bindings # Selection still needs a bit of work overall # Callbacks ignore event def select( _event, link=self.link ): "Select item on mouse entry." self.selectItem( link ) def highlight( _event, link=self.link ): "Highlight item on mouse entry." # self.selectItem( link ) self.canvas.itemconfig( link, fill='green' ) def unhighlight( _event, link=self.link ): "Unhighlight item on mouse exit." self.canvas.itemconfig( link, fill='blue' ) # self.selectItem( None ) self.canvas.tag_bind( self.link, '', highlight ) self.canvas.tag_bind( self.link, '', unhighlight ) self.canvas.tag_bind( self.link, '', select ) def finishLink( self, event ): "Finish creating a link" if self.link is None: return source = self.linkWidget c = self.canvas # Since we dragged from the widget, use root coords x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root ) target = self.findItem( x, y ) dest = self.itemToWidget.get( target, None ) if ( source is None or dest is None or source == dest or dest in source.links or source in dest.links ): self.releaseLink( event ) return # For now, don't allow hosts to be directly linked stags = self.canvas.gettags( self.widgetToItem[ source ] ) dtags = self.canvas.gettags( target ) if 'Host' in stags and 'Host' in dtags: self.releaseLink( event ) return x, y = c.coords( target ) c.coords( self.link, self.linkx, self.linky, x, y ) self.addLink( source, dest ) # We're done self.link = self.linkWidget = None # Menu handlers def about( self ): "Display about box." about = self.aboutBox if about is None: bg = 'white' about = Toplevel( bg='white' ) about.title( 'About' ) info = self.appName + ': a simple network editor for MiniNet' warning = 'Development version - not entirely functional!' author = 'Bob Lantz , April 2010' line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg ) line2 = Label( about, text=warning, font='Helvetica 9', bg=bg ) line3 = Label( about, text=author, font='Helvetica 9', bg=bg ) line1.pack( padx=20, pady=10 ) line2.pack(pady=10 ) line3.pack(pady=10 ) hide = ( lambda about=about: about.withdraw() ) self.aboutBox = about # Hide on close rather than destroying window Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide ) # Show (existing) window about.deiconify() def createToolImages( self ): "Create toolbar (and icon) images." # Model interface # # Ultimately we will either want to use a topo or # mininet object here, probably. def addLink( self, source, dest ): "Add link to model." source.links[ dest ] = self.link dest.links[ source ] = self.link self.links[ self.link ] = ( source, dest ) def deleteLink( self, link ): "Delete link from model." pair = self.links.get( link, None ) if pair is not None: source, dest = pair del source.links[ dest ] del dest.links[ source ] if link is not None: del self.links[ link ] def deleteNode( self, item ): "Delete node (and its links) from model." widget = self.itemToWidget[ item ] for link in widget.links.values(): # Delete from view and model self.deleteItem( link ) del self.itemToWidget[ item ] del self.widgetToItem[ widget ] def build( self ): "Build network based on our topology." net = Mininet( topo=None ) # Make controller net.addController( 'c0' ) # Make nodes for widget in self.widgetToItem: name = widget[ 'text' ] tags = self.canvas.gettags( self.widgetToItem[ widget ] ) nodeNum = int( name[ 1: ] ) if 'Switch' in tags: net.addSwitch( name ) elif 'Host' in tags: #Generate IP adddress in the 10.0/8 block ipAddr = ( 10 << 24 ) + nodeNum net.addHost( name, ip=ipStr( ipAddr ) ) else: raise Exception( "Cannot create mystery node: " + name ) # Make links for link in self.links.values(): ( src, dst ) = link srcName, dstName = src[ 'text' ], dst[ 'text' ] src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ] src.linkTo( dst ) # Build network (we have to do this separately at the moment ) net.build() return net def start( self ): "Start network." if self.net is None: self.net = self.build() self.net.start() def stop( self ): "Stop network." if self.net is not None: self.net.stop() cleanUpScreens() self.net = None def xterm( self, _ignore=None ): "Make an xterm when a button is pressed." if ( self.selection is None or self.net is None or self.selection not in self.itemToWidget ): return name = self.itemToWidget[ self.selection ][ 'text' ] if name not in self.net.nameToNode: return term = makeTerm( self.net.nameToNode[ name ], 'Host' ) self.net.terms += term def miniEditImages(): "Create and return images for MiniEdit." # Image data. Git will be unhappy. However, the alternative # is to keep track of separate binary files, which is also # unappealing. return { 'Select': BitmapImage( file='/usr/include/X11/bitmaps/left_ptr' ), 'Host': PhotoImage( data=r""" R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/ zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/ M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2 HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/ C8cSBBAQADs= """ ), 'Switch': PhotoImage( data=r""" R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/ zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/ M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA ACH5BAEAAAAALAAAAAAgABgAAAhwAAEIHEiwoMGDCBMqXMiwocOH ECNKnEixosWB3zJq3Mixo0eNAL7xG0mypMmTKPl9Cznyn8uWL/m5 /AeTpsyYI1eKlBnO5r+eLYHy9Ck0J8ubPmPOrMmUpM6UUKMa/Ui1 6saLWLNq3cq1q9evYB0GBAA7 """ ), 'Link': PhotoImage( data=r""" R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/ zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/ M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy lBmxI8mSNknm1Dnx5sCAADs= """ ) } if __name__ == '__main__': setLogLevel( 'info' ) app = MiniEdit() app.mainloop() mininet-2.1.0/examples/multiping.py000077500000000000000000000042731221655600400173670ustar00rootroot00000000000000#!/usr/bin/python """ multiping.py: monitor multiple sets of hosts using ping This demonstrates how one may send a simple shell script to multiple hosts and monitor their output interactively for a period= of time. """ from mininet.net import Mininet from mininet.node import Node from mininet.topo import SingleSwitchTopo from mininet.log import setLogLevel from select import poll, POLLIN from time import time def chunks( l, n ): "Divide list l into chunks of size n - thanks Stackoverflow" return [ l[ i: i + n ] for i in range( 0, len( l ), n ) ] def startpings( host, targetips ): "Tell host to repeatedly ping targets" targetips = ' '.join( targetips ) # Simple ping loop cmd = ( 'while true; do ' ' for ip in %s; do ' % targetips + ' echo -n %s "->" $ip ' % host.IP() + ' `ping -c1 -w 1 $ip | grep packets` ;' ' sleep 1;' ' done; ' 'done &' ) print ( '*** Host %s (%s) will be pinging ips: %s' % ( host.name, host.IP(), targetips ) ) host.cmd( cmd ) def multiping( netsize, chunksize, seconds): "Ping subsets of size chunksize in net of size netsize" # Create network and identify subnets topo = SingleSwitchTopo( netsize ) net = Mininet( topo=topo ) net.start() hosts = net.hosts subnets = chunks( hosts, chunksize ) # Create polling object fds = [ host.stdout.fileno() for host in hosts ] poller = poll() for fd in fds: poller.register( fd, POLLIN ) # Start pings for subnet in subnets: ips = [ host.IP() for host in subnet ] #adding bogus to generate packet loss ips.append( '10.0.0.200' ) for host in subnet: startpings( host, ips ) # Monitor output endTime = time() + seconds while time() < endTime: readable = poller.poll(1000) for fd, _mask in readable: node = Node.outToNode[ fd ] print '%s:' % node.name, node.monitor().strip() # Stop pings for host in hosts: host.cmd( 'kill %while' ) net.stop() if __name__ == '__main__': setLogLevel( 'info' ) multiping( netsize=20, chunksize=4, seconds=10 ) mininet-2.1.0/examples/multipoll.py000077500000000000000000000046451221655600400174030ustar00rootroot00000000000000#!/usr/bin/python """ Simple example of sending output to multiple files and monitoring them """ from mininet.topo import SingleSwitchTopo from mininet.net import Mininet from mininet.log import setLogLevel from time import time from select import poll, POLLIN from subprocess import Popen, PIPE def monitorFiles( outfiles, seconds, timeoutms ): "Monitor set of files and return [(host, line)...]" devnull = open( '/dev/null', 'w' ) tails, fdToFile, fdToHost = {}, {}, {} for h, outfile in outfiles.iteritems(): tail = Popen( [ 'tail', '-f', outfile ], stdout=PIPE, stderr=devnull ) fd = tail.stdout.fileno() tails[ h ] = tail fdToFile[ fd ] = tail.stdout fdToHost[ fd ] = h # Prepare to poll output files readable = poll() for t in tails.values(): readable.register( t.stdout.fileno(), POLLIN ) # Run until a set number of seconds have elapsed endTime = time() + seconds while time() < endTime: fdlist = readable.poll(timeoutms) if fdlist: for fd, _flags in fdlist: f = fdToFile[ fd ] host = fdToHost[ fd ] # Wait for a line of output line = f.readline().strip() yield host, line else: # If we timed out, return nothing yield None, '' for t in tails.values(): t.terminate() devnull.close() # Not really necessary def monitorTest( N=3, seconds=3 ): "Run pings and monitor multiple hosts" topo = SingleSwitchTopo( N ) net = Mininet( topo ) net.start() hosts = net.hosts print "Starting test..." server = hosts[ 0 ] outfiles, errfiles = {}, {} for h in hosts: # Create and/or erase output files outfiles[ h ] = '/tmp/%s.out' % h.name errfiles[ h ] = '/tmp/%s.err' % h.name h.cmd( 'echo >', outfiles[ h ] ) h.cmd( 'echo >', errfiles[ h ] ) # Start pings h.cmdPrint('ping', server.IP(), '>', outfiles[ h ], '2>', errfiles[ h ], '&' ) print "Monitoring output for", seconds, "seconds" for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ): if h: print '%s: %s' % ( h.name, line ) for h in hosts: h.cmd('kill %ping') net.stop() if __name__ == '__main__': setLogLevel('info') monitorTest() mininet-2.1.0/examples/multitest.py000077500000000000000000000020311221655600400173770ustar00rootroot00000000000000#!/usr/bin/python """ This example shows how to create a network and run multiple tests. 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.topolib import TreeTopo def ifconfigTest( net ): "Run ifconfig on all hosts in net." hosts = net.hosts for host in hosts: info( host.cmd( 'ifconfig' ) ) if __name__ == '__main__': lg.setLogLevel( 'info' ) info( "*** Initializing Mininet and kernel modules\n" ) OVSKernelSwitch.setup() info( "*** Creating network\n" ) network = Mininet( TreeTopo( depth=2, fanout=2 ), switch=OVSKernelSwitch ) info( "*** Starting network\n" ) network.start() info( "*** Running ping test\n" ) network.pingAll() info( "*** Running ifconfig test\n" ) ifconfigTest( network ) info( "*** Starting CLI (type 'exit' to exit)\n" ) CLI( network ) info( "*** Stopping network\n" ) network.stop() mininet-2.1.0/examples/nat.py000077500000000000000000000074111221655600400161360ustar00rootroot00000000000000#!/usr/bin/python """ Example to create a Mininet topology and connect it to the internet via NAT through eth0 on the host. Glen Gibb, February 2011 (slight modifications by BL, 5/13) """ from mininet.cli import CLI from mininet.log import lg from mininet.node import Node from mininet.topolib import TreeNet ################################# def startNAT( root, inetIntf='eth0', subnet='10.0/8' ): """Start NAT/forwarding between Mininet and external network root: node to access iptables from inetIntf: interface for internet access subnet: Mininet subnet (default 10.0/8)=""" # Identify the interface connecting to the mininet network localIntf = root.defaultIntf() # Flush any currently active rules root.cmd( 'iptables -F' ) root.cmd( 'iptables -t nat -F' ) # Create default entries for unmatched traffic root.cmd( 'iptables -P INPUT ACCEPT' ) root.cmd( 'iptables -P OUTPUT ACCEPT' ) root.cmd( 'iptables -P FORWARD DROP' ) # Configure NAT root.cmd( 'iptables -I FORWARD -i', localIntf, '-d', subnet, '-j DROP' ) root.cmd( 'iptables -A FORWARD -i', localIntf, '-s', subnet, '-j ACCEPT' ) root.cmd( 'iptables -A FORWARD -i', inetIntf, '-d', subnet, '-j ACCEPT' ) root.cmd( 'iptables -t nat -A POSTROUTING -o ', inetIntf, '-j MASQUERADE' ) # Instruct the kernel to perform forwarding root.cmd( 'sysctl net.ipv4.ip_forward=1' ) def stopNAT( root ): """Stop NAT/forwarding between Mininet and external network""" # Flush any currently active rules root.cmd( 'iptables -F' ) root.cmd( 'iptables -t nat -F' ) # Instruct the kernel to stop forwarding root.cmd( 'sysctl net.ipv4.ip_forward=0' ) def fixNetworkManager( root, intf ): """Prevent network-manager from messing with our interface, by specifying manual configuration in /etc/network/interfaces root: a node in the root namespace (for running commands) intf: interface name""" cfile = '/etc/network/interfaces' line = '\niface %s inet manual\n' % intf config = open( cfile ).read() if ( line ) not in config: print '*** Adding', line.strip(), 'to', cfile with open( cfile, 'a' ) as f: f.write( line ) # Probably need to restart network-manager to be safe - # hopefully this won't disconnect you root.cmd( 'service network-manager restart' ) def connectToInternet( network, switch='s1', rootip='10.254', subnet='10.0/8'): """Connect the network to the internet switch: switch to connect to root namespace rootip: address for interface in root namespace subnet: Mininet subnet""" switch = network.get( switch ) prefixLen = subnet.split( '/' )[ 1 ] # Create a node in root namespace root = Node( 'root', inNamespace=False ) # Prevent network-manager from interfering with our interface fixNetworkManager( root, 'root-eth0' ) # Create link between root NS and switch link = network.addLink( root, switch ) link.intf1.setIP( rootip, prefixLen ) # Start network that now includes link to root namespace network.start() # Start NAT and establish forwarding startNAT( root ) # Establish routes from end hosts for host in network.hosts: host.cmd( 'ip route flush root 0/0' ) host.cmd( 'route add -net', subnet, 'dev', host.defaultIntf() ) host.cmd( 'route add default gw', rootip ) return root if __name__ == '__main__': lg.setLogLevel( 'info') net = TreeNet( depth=1, fanout=4 ) # Configure and start NATted connectivity rootnode = connectToInternet( net ) print "*** Hosts are running and should have internet connectivity" print "*** Type 'exit' or control-D to shut down network" CLI( net ) # Shut down NAT stopNAT( rootnode ) net.stop() mininet-2.1.0/examples/popen.py000077500000000000000000000017771221655600400165060ustar00rootroot00000000000000#!/usr/bin/python """ This example monitors a number of hosts using host.popen() and pmonitor() """ from mininet.net import Mininet from mininet.node import CPULimitedHost from mininet.topo import SingleSwitchTopo from mininet.log import setLogLevel from mininet.util import custom, pmonitor def monitorhosts( hosts=5, sched='cfs' ): "Start a bunch of pings and monitor them using popen" mytopo = SingleSwitchTopo( hosts ) cpu = .5 / hosts myhost = custom( CPULimitedHost, cpu=cpu, sched=sched ) net = Mininet( topo=mytopo, host=myhost ) net.start() # Start a bunch of pings popens = {} last = net.hosts[ -1 ] for host in net.hosts: popens[ host ] = host.popen( "ping -c5 %s" % last.IP() ) last = host # Monitor them and print output for host, line in pmonitor( popens ): if host: print "<%s>: %s" % ( host.name, line.strip() ) # Done net.stop() if __name__ == '__main__': setLogLevel( 'info' ) monitorhosts( hosts=5 ) mininet-2.1.0/examples/popenpoll.py000077500000000000000000000016441221655600400173660ustar00rootroot00000000000000#!/usr/bin/python "Monitor multiple hosts using popen()/pmonitor()" from mininet.net import Mininet from mininet.topo import SingleSwitchTopo from mininet.util import pmonitor from time import time from signal import SIGINT def pmonitorTest( N=3, seconds=10 ): "Run pings and monitor multiple hosts using pmonitor" topo = SingleSwitchTopo( N ) net = Mininet( topo ) net.start() hosts = net.hosts print "Starting test..." server = hosts[ 0 ] popens = {} for h in hosts: popens[ h ] = h.popen('ping', server.IP() ) print "Monitoring output for", seconds, "seconds" endTime = time() + seconds for h, line in pmonitor( popens, timeoutms=500 ): if h: print '<%s>: %s' % ( h.name, line ), if time() >= endTime: for p in popens.values(): p.send_signal( SIGINT ) net.stop() if __name__ == '__main__': pmonitorTest() mininet-2.1.0/examples/scratchnet.py000077500000000000000000000037601221655600400175150ustar00rootroot00000000000000#!/usr/bin/python """ Build a simple network from scratch, using mininet primitives. This is more complicated than using the higher-level classes, but it exposes the configuration details and allows customization. For most tasks, the higher-level API will be preferable. """ from mininet.net import Mininet from mininet.node import Node from mininet.link import Link from mininet.log import setLogLevel, info from mininet.util import quietRun from time import sleep def scratchNet( cname='controller', cargs='-v ptcp:' ): "Create network from scratch using Open vSwitch." info( "*** Creating nodes\n" ) controller = Node( 'c0', inNamespace=False ) switch = Node( 's0', inNamespace=False ) h0 = Node( 'h0' ) h1 = Node( 'h1' ) info( "*** Creating links\n" ) Link( h0, switch ) Link( h1, switch ) info( "*** Configuring hosts\n" ) h0.setIP( '192.168.123.1/24' ) h1.setIP( '192.168.123.2/24' ) info( str( h0 ) + '\n' ) info( str( h1 ) + '\n' ) info( "*** Starting network using Open vSwitch\n" ) controller.cmd( cname + ' ' + cargs + '&' ) switch.cmd( 'ovs-vsctl del-br dp0' ) switch.cmd( 'ovs-vsctl add-br dp0' ) for intf in switch.intfs.values(): print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf ) # Note: controller and switch are in root namespace, and we # can connect via loopback interface switch.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:6633' ) info( '*** Waiting for switch to connect to controller' ) while 'is_connected' not in quietRun( 'ovs-vsctl show' ): sleep( 1 ) info( '.' ) info( '\n' ) info( "*** Running test\n" ) h0.cmdPrint( 'ping -c1 ' + h1.IP() ) info( "*** Stopping network\n" ) controller.cmd( 'kill %' + cname ) switch.cmd( 'ovs-vsctl del-br dp0' ) switch.deleteIntfs() info( '\n' ) if __name__ == '__main__': setLogLevel( 'info' ) info( '*** Scratch network demo (kernel datapath)\n' ) Mininet.init() scratchNet() mininet-2.1.0/examples/scratchnetuser.py000077500000000000000000000046271221655600400204170ustar00rootroot00000000000000#!/usr/bin/python """ Build a simple network from scratch, using mininet primitives. This is more complicated than using the higher-level classes, but it exposes the configuration details and allows customization. For most tasks, the higher-level API will be preferable. This version uses the user datapath and an explicit control network. """ from mininet.net import Mininet from mininet.node import Node from mininet.link import Link from mininet.log import setLogLevel, info def linkIntfs( node1, node2 ): "Create link from node1 to node2 and return intfs" link = Link( node1, node2 ) return link.intf1, link.intf2 def scratchNetUser( cname='controller', cargs='ptcp:' ): "Create network from scratch using user switch." # It's not strictly necessary for the controller and switches # to be in separate namespaces. For performance, they probably # should be in the root namespace. However, it's interesting to # see how they could work even if they are in separate namespaces. info( '*** Creating Network\n' ) controller = Node( 'c0' ) switch = Node( 's0') h0 = Node( 'h0' ) h1 = Node( 'h1' ) cintf, sintf = linkIntfs( controller, switch ) h0intf, sintf1 = linkIntfs( h0, switch ) h1intf, sintf2 = linkIntfs( h1, switch ) info( '*** Configuring control network\n' ) controller.setIP( '10.0.123.1/24', intf=cintf ) switch.setIP( '10.0.123.2/24', intf=sintf) info( '*** Configuring hosts\n' ) h0.setIP( '192.168.123.1/24', intf=h0intf ) h1.setIP( '192.168.123.2/24', intf=h1intf ) info( '*** Network state:\n' ) for node in controller, switch, h0, h1: info( str( node ) + '\n' ) info( '*** Starting controller and user datapath\n' ) controller.cmd( cname + ' ' + cargs + '&' ) switch.cmd( 'ifconfig lo 127.0.0.1' ) intfs = [ str( i ) for i in sintf1, sintf2 ] switch.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' ptcp: &' ) switch.cmd( 'ofprotocol tcp:' + controller.IP() + ' tcp:localhost &' ) info( '*** Running test\n' ) h0.cmdPrint( 'ping -c1 ' + h1.IP() ) info( '*** Stopping network\n' ) controller.cmd( 'kill %' + cname ) switch.cmd( 'kill %ofdatapath' ) switch.cmd( 'kill %ofprotocol' ) switch.deleteIntfs() info( '\n' ) if __name__ == '__main__': setLogLevel( 'info' ) info( '*** Scratch network demo (user datapath)\n' ) Mininet.init() scratchNetUser() mininet-2.1.0/examples/simpleperf.py000077500000000000000000000027351221655600400175260ustar00rootroot00000000000000#!/usr/bin/python """ Simple example of setting network and CPU parameters NOTE: link params limit BW, add latency, and loss. There is a high chance that pings WILL fail and that iperf will hang indefinitely if the TCP handshake fails to complete. """ from mininet.topo import Topo from mininet.net import Mininet from mininet.node import CPULimitedHost from mininet.link import TCLink from mininet.util import dumpNodeConnections from mininet.log import setLogLevel class SingleSwitchTopo(Topo): "Single switch connected to n hosts." def __init__(self, n=2, **opts): Topo.__init__(self, **opts) switch = self.addSwitch('s1') for h in range(n): # Each host gets 50%/n of system CPU host = self.addHost('h%s' % (h + 1), cpu=.5 / n) # 10 Mbps, 5ms delay, 10% loss self.addLink(host, switch, bw=10, delay='5ms', loss=10, use_htb=True) def perfTest(): "Create network and run simple performance test" topo = SingleSwitchTopo(n=4) net = Mininet(topo=topo, host=CPULimitedHost, link=TCLink) net.start() print "Dumping host connections" dumpNodeConnections(net.hosts) print "Testing network connectivity" net.pingAll() print "Testing bandwidth between h1 and h4" h1, h4 = net.getNodeByName('h1', 'h4') net.iperf((h1, h4)) net.stop() if __name__ == '__main__': setLogLevel('info') perfTest() mininet-2.1.0/examples/sshd.py000077500000000000000000000054671221655600400163260ustar00rootroot00000000000000#!/usr/bin/python """ Create a network and start sshd(8) on each host. While something like rshd(8) would be lighter and faster, (and perfectly adequate on an in-machine network) the advantage of running sshd is that scripts can work unchanged on mininet and hardware. In addition to providing ssh access to hosts, this example demonstrates: - creating a convenience function to construct networks - connecting the host network to the root namespace - running server processes (sshd in this case) on hosts """ import sys from mininet.net import Mininet from mininet.cli import CLI from mininet.log import lg from mininet.node import Node from mininet.topolib import TreeTopo from mininet.link import Link def TreeNet( depth=1, fanout=2, **kwargs ): "Convenience function for creating tree networks." topo = TreeTopo( depth, fanout ) return Mininet( topo, **kwargs ) def connectToRootNS( network, switch, ip, routes ): """Connect hosts to root namespace via switch. Starts network. network: Mininet() network object switch: switch to connect to root namespace ip: IP address for root namespace node routes: host networks to route to""" # Create a node in root namespace and link to switch 0 root = Node( 'root', inNamespace=False ) intf = Link( root, switch ).intf1 root.setIP( ip, intf=intf ) # Start network that now includes link to root namespace network.start() # Add routes from root ns to hosts for route in routes: root.cmd( 'route add -net ' + route + ' dev ' + str( intf ) ) def sshd( network, cmd='/usr/sbin/sshd', opts='-D', ip='10.123.123.1/32', routes=None, switch=None ): """Start a network, connect it to root ns, and run sshd on all hosts. ip: root-eth0 IP address in root namespace (10.123.123.1/32) routes: Mininet host networks to route to (10.0/24) switch: Mininet switch to connect to root namespace (s1)""" if not switch: switch = network[ 's1' ] # switch to use if not routes: routes = [ '10.0.0.0/24' ] connectToRootNS( network, switch, ip, routes ) for host in network.hosts: host.cmd( cmd + ' ' + opts + '&' ) print print "*** Hosts are running sshd at the following addresses:" print for host in network.hosts: print host.name, host.IP() print print "*** Type 'exit' or control-D to shut down network" CLI( network ) for host in network.hosts: host.cmd( 'kill %' + cmd ) network.stop() if __name__ == '__main__': lg.setLogLevel( 'info') net = TreeNet( depth=1, fanout=4 ) # get sshd args from the command line or use default args # useDNS=no -u0 to avoid reverse DNS lookup timeout opts = ' '.join( sys.argv[ 1: ] ) if len( sys.argv ) > 1 else ( '-D -o UseDNS=no -u0' ) sshd( net, opts=opts ) mininet-2.1.0/examples/test/000077500000000000000000000000001221655600400157535ustar00rootroot00000000000000mininet-2.1.0/examples/test/runner.py000077500000000000000000000014541221655600400176450ustar00rootroot00000000000000#!/usr/bin/env python """ Run all mininet.examples tests -v : verbose output -quick : skip tests that take more than ~30 seconds """ import unittest import os import sys from mininet.util import ensureRoot from mininet.clean import cleanup def runTests( testDir, verbosity=1 ): "discover and run all tests in testDir" # ensure root and cleanup before starting tests ensureRoot() cleanup() # discover all tests in testDir testSuite = unittest.defaultTestLoader.discover( testDir ) # run tests unittest.TextTestRunner( verbosity=verbosity ).run( testSuite ) if __name__ == '__main__': # get the directory containing example tests testDir = os.path.dirname( os.path.realpath( __file__ ) ) verbosity = 2 if '-v' in sys.argv else 1 runTests( testDir, verbosity ) mininet-2.1.0/examples/test/test_baresshd.py000077500000000000000000000035101221655600400211610ustar00rootroot00000000000000#!/usr/bin/env python """ Tests for baresshd.py """ import unittest import pexpect from time import sleep from mininet.clean import cleanup, sh class testBareSSHD( unittest.TestCase ): opts = [ '\(yes/no\)\?', 'Welcome to h1', 'refused', pexpect.EOF, pexpect.TIMEOUT ] def connected( self ): "Log into ssh server, check banner, then exit" p = pexpect.spawn( 'ssh 10.0.0.1 -i /tmp/ssh/test_rsa exit' ) while True: index = p.expect( self.opts ) if index == 0: p.sendline( 'yes' ) elif index == 1: return True else: return False def setUp( self ): # verify that sshd is not running self.assertFalse( self.connected() ) # create public key pair for testing sh( 'rm -rf /tmp/ssh' ) sh( 'mkdir /tmp/ssh' ) sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" ) sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' ) # run example with custom sshd args cmd = ( 'python -m mininet.examples.baresshd ' '-o AuthorizedKeysFile=/tmp/ssh/authorized_keys ' '-o StrictModes=no' ) sh( cmd ) def testSSH( self ): "Simple test to verify that we can ssh into h1" result = False # try to connect up to 3 times; sshd can take a while to start for _ in range( 3 ): result = self.connected() if result: break else: sleep( 1 ) self.assertTrue( result ) def tearDown( self ): # kill the ssh process sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" ) cleanup() # remove public key pair sh( 'rm -rf /tmp/ssh' ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_bind.py000077500000000000000000000043361221655600400203110ustar00rootroot00000000000000#!/usr/bin/env python """ Tests for bind.py """ import unittest import pexpect class testBind( unittest.TestCase ): prompt = 'mininet>' def setUp( self ): self.net = pexpect.spawn( 'python -m mininet.examples.bind' ) self.net.expect( "Private Directories: \[([\w\s,'/]+)\]" ) self.directories = [] # parse directories from mn output for d in self.net.match.group(1).split(', '): self.directories.append( d.strip("'") ) self.net.expect( self.prompt ) self.assertTrue( len( self.directories ) > 0 ) def testCreateFile( self ): "Create a file, a.txt, in the first private directory and verify" fileName = 'a.txt' directory = self.directories[ 0 ] path = directory + '/' + fileName self.net.sendline( 'h1 touch %s; ls %s' % ( path, directory ) ) index = self.net.expect( [ fileName, self.prompt ] ) self.assertTrue( index == 0 ) self.net.expect( self.prompt ) self.net.sendline( 'h1 rm %s' % path ) self.net.expect( self.prompt ) def testIsolation( self ): "Create a file in two hosts and verify that contents are different" fileName = 'b.txt' directory = self.directories[ 0 ] path = directory + '/' + fileName contents = { 'h1' : '1', 'h2' : '2' } # Verify file doesn't exist, then write private copy of file for host in contents: value = contents[ host ] self.net.sendline( '%s cat %s' % ( host, path ) ) self.net.expect( 'No such file' ) self.net.expect( self.prompt ) self.net.sendline( '%s echo %s > %s' % ( host, value, path ) ) self.net.expect( self.prompt ) # Verify file contents for host in contents: value = contents[ host ] self.net.sendline( '%s cat %s' % ( host, path ) ) self.net.expect( value ) self.net.expect( self.prompt ) self.net.sendline( '%s rm %s' % ( host, path ) ) self.net.expect( self.prompt ) # TODO: need more tests def tearDown( self ): self.net.sendline( 'exit' ) self.net.wait() if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_controllers.py000077500000000000000000000026671221655600400217500ustar00rootroot00000000000000#!/usr/bin/env python """ Tests for controllers.py and controllers2.py """ import unittest import pexpect class testControllers( unittest.TestCase ): prompt = 'mininet>' def connectedTest( self, name, cmap ): "Verify that switches are connected to the controller specified by cmap" p = pexpect.spawn( 'python -m %s' % name ) p.expect( self.prompt ) # but first a simple ping test p.sendline( 'pingall' ) p.expect ( '(\d+)% dropped' ) percent = int( p.match.group( 1 ) ) if p.match else -1 self.assertEqual( percent, 0 ) p.expect( self.prompt ) # verify connected controller for switch in cmap: p.sendline( 'sh ovs-vsctl get-controller %s' % switch ) p.expect( 'tcp:([\d.:]+)') actual = p.match.group(1) expected = cmap[ switch ] self.assertEqual( actual, expected ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testControllers( self ): c0 = '127.0.0.1:6633' c1 = '127.0.0.1:6634' cmap = { 's1': c0, 's2': c1, 's3': c0 } self.connectedTest( 'mininet.examples.controllers', cmap ) def testControllers2( self ): c0 = '127.0.0.1:6633' c1 = '127.0.0.1:6634' cmap = { 's1': c0, 's2': c1 } self.connectedTest( 'mininet.examples.controllers2', cmap ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_controlnet.py000077500000000000000000000026561221655600400215670ustar00rootroot00000000000000#!/usr/bin/env python """ Test for controlnet.py """ import unittest import pexpect class testControlNet( unittest.TestCase ): prompt = 'mininet>' def testPingall( self ): "Simple pingall test that verifies 0% packet drop in data network" p = pexpect.spawn( 'python -m mininet.examples.controlnet' ) p.expect( self.prompt ) p.sendline( 'pingall' ) p.expect ( '(\d+)% dropped' ) percent = int( p.match.group( 1 ) ) if p.match else -1 self.assertEqual( percent, 0 ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testFailover( self ): "Kill controllers and verify that switch, s1, fails over properly" count = 1 p = pexpect.spawn( 'python -m mininet.examples.controlnet' ) p.expect( self.prompt ) lp = pexpect.spawn( 'tail -f /tmp/s1-ofp.log' ) lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' ) ip = int( lp.match.group( 1 ) ) self.assertEqual( count, ip ) count += 1 for c in [ 'c0', 'c1' ]: p.sendline( '%s ifconfig %s-eth0 down' % ( c, c) ) p.expect( self.prompt ) lp.expect( 'tcp:\d+\.\d+\.\d+\.(\d+):\d+: connected' ) ip = int( lp.match.group( 1 ) ) self.assertEqual( count, ip ) count += 1 p.sendline( 'exit' ) p.wait() if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_cpu.py000077500000000000000000000020471221655600400201610ustar00rootroot00000000000000#!/usr/bin/env python """ Test for cpu.py """ import unittest import pexpect import sys class testCPU( unittest.TestCase ): prompt = 'mininet>' @unittest.skipIf( '-quick' in sys.argv, 'long test' ) def testCPU( self ): "Verify that CPU utilization is monotonically decreasing for each scheduler" p = pexpect.spawn( 'python -m mininet.examples.cpu' ) opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)', pexpect.EOF ] scheds = [] while True: index = p.expect( opts, timeout=600 ) if index == 0: sched = p.match.group( 1 ) cpu = float( p.match.group( 2 ) ) bw = float( p.match.group( 3 ) ) if sched not in scheds: scheds.append( sched ) previous_bw = 10 ** 4 # 10 GB/s self.assertTrue( bw < previous_bw ) previous_bw = bw else: break self.assertTrue( len( scheds ) > 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_emptynet.py000077500000000000000000000015041221655600400212340ustar00rootroot00000000000000#!/usr/bin/env python """ Test for emptynet.py """ import unittest import pexpect class testEmptyNet( unittest.TestCase ): prompt = 'mininet>' def testEmptyNet( self ): "Run simple CLI tests: pingall (verify 0% drop) and iperf (sanity)" p = pexpect.spawn( 'python -m mininet.examples.emptynet' ) p.expect( self.prompt ) # pingall test p.sendline( 'pingall' ) p.expect ( '(\d+)% dropped' ) percent = int( p.match.group( 1 ) ) if p.match else -1 self.assertEqual( percent, 0 ) p.expect( self.prompt ) # iperf test p.sendline( 'iperf' ) p.expect( "Results: \['[\d.]+ .bits/sec', '[\d.]+ .bits/sec'\]" ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_hwintf.py000077500000000000000000000035611221655600400206730ustar00rootroot00000000000000#!/usr/bin/env python """ Test for hwintf.py """ import unittest import pexpect import re from mininet.log import setLogLevel from mininet.net import Mininet from mininet.node import Node from mininet.link import Link, Intf class testHwintf( unittest.TestCase ): prompt = 'mininet>' def setUp( self ): self.h3 = Node( 't0', ip='10.0.0.3/8' ) self.n0 = Node( 't1', inNamespace=False ) Link( self.h3, self.n0 ) self.h3.configDefault() def testLocalPing( self ): "Verify connectivity between virtual hosts using pingall" p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() ) p.expect( self.prompt ) p.sendline( 'pingall' ) p.expect ( '(\d+)% dropped' ) percent = int( p.match.group( 1 ) ) if p.match else -1 self.assertEqual( percent, 0 ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testExternalPing( self ): "Verify connnectivity between virtual host and virtual-physical 'external' host " p = pexpect.spawn( 'python -m mininet.examples.hwintf %s' % self.n0.intf() ) p.expect( self.prompt ) # test ping external to internal expectStr = '(\d+) packets transmitted, (\d+) received' m = re.search( expectStr, self.h3.cmd( 'ping -v -c 1 10.0.0.1' ) ) tx = m.group( 1 ) rx = m.group( 2 ) self.assertEqual( tx, rx ) # test ping internal to external p.sendline( 'h1 ping -c 1 10.0.0.3') p.expect( expectStr ) tx = p.match.group( 1 ) rx = p.match.group( 2 ) self.assertEqual( tx, rx ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def tearDown( self ): self.h3.terminate() self.n0.terminate() if __name__ == '__main__': setLogLevel( 'warning' ) unittest.main() mininet-2.1.0/examples/test/test_limit.py000077500000000000000000000021631221655600400205070ustar00rootroot00000000000000#!/usr/bin/env python """ Test for limit.py """ import unittest import pexpect import sys class testLimit( unittest.TestCase ): @unittest.skipIf( '-quick' in sys.argv, 'long test' ) def testLimit( self ): "Verify that CPU limits are within a 2% tolerance of limit for each scheduler" p = pexpect.spawn( 'python -m mininet.examples.limit' ) opts = [ '\*\*\* Testing network ([\d\.]+) Mbps', '\*\*\* Results: \[([\d\., ]+)\]', pexpect.EOF ] count = 0 bw = 0 tolerance = 2 while True: index = p.expect( opts ) if index == 0: bw = float( p.match.group( 1 ) ) count += 1 elif index == 1: results = p.match.group( 1 ) for x in results.split( ',' ): result = float( x ) self.assertTrue( result < bw + tolerance ) self.assertTrue( result > bw - tolerance ) else: break self.assertTrue( count > 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_linearbandwidth.py000077500000000000000000000024471221655600400225350ustar00rootroot00000000000000#!/usr/bin/env python """ Test for linearbandwidth.py """ import unittest import pexpect import sys class testLinearBandwidth( unittest.TestCase ): @unittest.skipIf( '-quick' in sys.argv, 'long test' ) def testLinearBandwidth( self ): "Verify that bandwidth is monotonically decreasing as # of hops increases" p = pexpect.spawn( 'python -m mininet.examples.linearbandwidth' ) count = 0 opts = [ '\*\*\* Linear network results', '(\d+)\s+([\d\.]+) (.bits)', pexpect.EOF ] while True: index = p.expect( opts, timeout=600 ) if index == 0: previous_bw = 10 ** 10 # 10 Gbits count += 1 elif index == 1: n = int( p.match.group( 1 ) ) bw = float( p.match.group( 2 ) ) unit = p.match.group( 3 ) if unit[ 0 ] == 'K': bw *= 10 ** 3 elif unit[ 0 ] == 'M': bw *= 10 ** 6 elif unit[ 0 ] == 'G': bw *= 10 ** 9 self.assertTrue( bw < previous_bw ) previous_bw = bw else: break self.assertTrue( count > 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_multiping.py000077500000000000000000000030741221655600400214030ustar00rootroot00000000000000#!/usr/bin/env python """ Test for multiping.py """ import unittest import pexpect from collections import defaultdict class testMultiPing( unittest.TestCase ): def testMultiPing( self ): """Verify that each target is pinged at least once, and that pings to 'real' targets are successful and unknown targets fail""" p = pexpect.spawn( 'python -m mininet.examples.multiping' ) opts = [ "Host (h\d+) \(([\d.]+)\) will be pinging ips: ([\d\. ]+)", "(h\d+): ([\d.]+) -> ([\d.]+) \d packets transmitted, (\d) received", pexpect.EOF ] pings = defaultdict( list ) while True: index = p.expect( opts ) if index == 0: name = p.match.group(1) ip = p.match.group(2) targets = p.match.group(3).split() pings[ name ] += targets elif index == 1: name = p.match.group(1) ip = p.match.group(2) target = p.match.group(3) received = int( p.match.group(4) ) if target == '10.0.0.200': self.assertEqual( received, 0 ) else: self.assertEqual( received, 1 ) try: pings[ name ].remove( target ) except: pass else: break self.assertTrue( len( pings ) > 0 ) for t in pings.values(): self.assertEqual( len( t ), 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_multipoll.py000077500000000000000000000021211221655600400214040ustar00rootroot00000000000000#!/usr/bin/env python """ Test for multipoll.py """ import unittest import pexpect class testMultiPoll( unittest.TestCase ): def testMultiPoll( self ): "Verify that we receive one ping per second per host" p = pexpect.spawn( 'python -m mininet.examples.multipoll' ) opts = [ "\*\*\* (h\d) :" , "(h\d+): \d+ bytes from", "Monitoring output for (\d+) seconds", pexpect.EOF ] pings = {} while True: index = p.expect( opts ) if index == 0: name = p.match.group( 1 ) pings[ name ] = 0 elif index == 1: name = p.match.group( 1 ) pings[ name ] += 1 elif index == 2: seconds = int( p.match.group( 1 ) ) else: break self.assertTrue( len( pings ) > 0 ) # make sure we have received at least one ping per second for count in pings.values(): self.assertTrue( count >= seconds ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_multitest.py000077500000000000000000000014461221655600400214260ustar00rootroot00000000000000#!/usr/bin/env python """ Test for multitest.py """ import unittest import pexpect class testMultiTest( unittest.TestCase ): prompt = 'mininet>' def testMultiTest( self ): "Verify pingall (0% dropped) and hX-eth0 interface for each host (ifconfig)" p = pexpect.spawn( 'python -m mininet.examples.multitest' ) p.expect( '(\d+)% dropped' ) dropped = int( p.match.group( 1 ) ) self.assertEqual( dropped, 0 ) ifCount = 0 while True: index = p.expect( [ 'h\d-eth0', self.prompt ] ) if index == 0: ifCount += 1 elif index == 1: p.sendline( 'exit' ) break p.wait() self.assertEqual( ifCount, 4 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_nat.py000077500000000000000000000015271221655600400201560ustar00rootroot00000000000000#!/usr/bin/env python """ Test for nat.py """ import unittest import pexpect from mininet.util import quietRun destIP = '8.8.8.8' # Google DNS class testNAT( unittest.TestCase ): prompt = 'mininet>' @unittest.skipIf( '0 received' in quietRun( 'ping -c 1 %s' % destIP ), 'Destination IP is not reachable' ) def testNAT( self ): "Attempt to ping an IP on the Internet and verify 0% packet loss" p = pexpect.spawn( 'python -m mininet.examples.nat' ) p.expect( self.prompt ) p.sendline( 'h1 ping -c 1 %s' % destIP ) p.expect ( '(\d+)% packet loss' ) percent = int( p.match.group( 1 ) ) if p.match else -1 p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() self.assertEqual( percent, 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_popen.py000077500000000000000000000024521221655600400205130ustar00rootroot00000000000000#!/usr/bin/env python """ Test for popen.py and popenpoll.py """ import unittest import pexpect class testPopen( unittest.TestCase ): def pingTest( self, name ): "Verify that there are no dropped packets for each host" p = pexpect.spawn( 'python -m %s' % name ) opts = [ "<(h\d+)>: PING ", "<(h\d+)>: (\d+) packets transmitted, (\d+) received", pexpect.EOF ] pings = {} while True: index = p.expect( opts ) if index == 0: name = p.match.group(1) pings[ name ] = 0 elif index == 1: name = p.match.group(1) transmitted = p.match.group(2) received = p.match.group(3) # verify no dropped packets self.assertEqual( received, transmitted ) pings[ name ] += 1 else: break self.assertTrue( len(pings) > 0 ) # verify that each host has gotten results for count in pings.values(): self.assertEqual( count, 1 ) def testPopen( self ): self.pingTest( 'mininet.examples.popen' ) def testPopenPoll( self ): self.pingTest( 'mininet.examples.popenpoll' ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_scratchnet.py000077500000000000000000000012071221655600400215250ustar00rootroot00000000000000#!/usr/bin/env python """ Test for scratchnet.py """ import unittest import pexpect class testScratchNet( unittest.TestCase ): opts = [ "1 packets transmitted, 1 received, 0% packet loss", pexpect.EOF ] def pingTest( self, name ): "Verify that no ping packets were dropped" p = pexpect.spawn( 'python -m %s' % name ) index = p.expect( self.opts ) self.assertEqual( index, 0 ) def testPingKernel( self ): self.pingTest( 'mininet.examples.scratchnet' ) def testPingUser( self ): self.pingTest( 'mininet.examples.scratchnetuser' ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_simpleperf.py000077500000000000000000000034721221655600400215430ustar00rootroot00000000000000#!/usr/bin/env python """ Test for simpleperf.py """ import unittest import pexpect import re import sys from mininet.log import setLogLevel from mininet.net import Mininet from mininet.node import CPULimitedHost from mininet.link import TCLink from mininet.examples.simpleperf import SingleSwitchTopo class testSimplePerf( unittest.TestCase ): @unittest.skipIf( '-quick' in sys.argv, 'long test' ) def testE2E( self ): "Run the example and verify ping and iperf results" p = pexpect.spawn( 'python -m mininet.examples.simpleperf' ) # check ping results p.expect( "Results: (\d+)% dropped", timeout=120 ) loss = int( p.match.group( 1 ) ) self.assertTrue( loss > 0 and loss < 100 ) # check iperf results p.expect( "Results: \['([\d\.]+) .bits/sec", timeout=480 ) bw = float( p.match.group( 1 ) ) self.assertTrue( bw > 0 ) p.wait() def testTopo( self ): """Import SingleSwitchTopo from example and test connectivity between two hosts Note: this test may fail very rarely because it is non-deterministic i.e. links are configured with 10% packet loss, but if we get unlucky and none or all of the packets are dropped, the test will fail""" topo = SingleSwitchTopo( n=4 ) net = Mininet( topo=topo, host=CPULimitedHost, link=TCLink ) net.start() h1, h4 = net.get( 'h1', 'h4' ) # have h1 ping h4 ten times expectStr = '(\d+) packets transmitted, (\d+) received, (\d+)% packet loss' output = h1.cmd( 'ping -c 10 %s' % h4.IP() ) m = re.search( expectStr, output ) loss = int( m.group( 3 ) ) net.stop() self.assertTrue( loss > 0 and loss < 100 ) if __name__ == '__main__': setLogLevel( 'warning' ) unittest.main() mininet-2.1.0/examples/test/test_sshd.py000077500000000000000000000035311221655600400203320ustar00rootroot00000000000000#!/usr/bin/env python """ Test for sshd.py """ import unittest import pexpect from time import sleep from mininet.clean import sh class testSSHD( unittest.TestCase ): opts = [ '\(yes/no\)\?', 'refused', 'Welcome|\$|#', pexpect.EOF, pexpect.TIMEOUT ] def connected( self, ip ): "Log into ssh server, check banner, then exit" # Note: this test will fail if "Welcome" is not in the sshd banner # and '#'' or '$'' are not in the prompt p = pexpect.spawn( 'ssh -i /tmp/ssh/test_rsa %s' % ip, timeout=10 ) while True: index = p.expect( self.opts ) if index == 0: print p.match.group(0) p.sendline( 'yes' ) elif index == 1: return False elif index == 2: p.sendline( 'exit' ) p.wait() return True else: return False def setUp( self ): # create public key pair for testing sh( 'rm -rf /tmp/ssh' ) sh( 'mkdir /tmp/ssh' ) sh( "ssh-keygen -t rsa -P '' -f /tmp/ssh/test_rsa" ) sh( 'cat /tmp/ssh/test_rsa.pub >> /tmp/ssh/authorized_keys' ) cmd = ( 'python -m mininet.examples.sshd -D ' '-o AuthorizedKeysFile=/tmp/ssh/authorized_keys ' '-o StrictModes=no -o UseDNS=no -u0' ) # run example with custom sshd args self.net = pexpect.spawn( cmd ) self.net.expect( 'mininet>' ) def testSSH( self ): "Verify that we can ssh into all hosts (h1 to h4)" for h in range( 1, 5 ): self.assertTrue( self.connected( '10.0.0.%d' % h ) ) def tearDown( self ): self.net.sendline( 'exit' ) self.net.wait() # remove public key pair sh( 'rm -rf /tmp/ssh' ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_tree1024.py000077500000000000000000000014061221655600400206360ustar00rootroot00000000000000#!/usr/bin/env python """ Test for tree1024.py """ import unittest import pexpect import sys class testTree1024( unittest.TestCase ): prompt = 'mininet>' @unittest.skipIf( '-quick' in sys.argv, 'long test' ) def testTree1024( self ): "Run the example and do a simple ping test from h1 to h1024" p = pexpect.spawn( 'python -m mininet.examples.tree1024' ) p.expect( self.prompt, timeout=6000 ) # it takes awhile to set up p.sendline( 'h1 ping -c 1 h1024' ) p.expect ( '(\d+)% packet loss' ) percent = int( p.match.group( 1 ) ) if p.match else -1 p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() self.assertEqual( percent, 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/test/test_treeping64.py000077500000000000000000000015141221655600400213570ustar00rootroot00000000000000#!/usr/bin/env python """ Test for treeping64.py """ import unittest import pexpect import sys class testTreePing64( unittest.TestCase ): prompt = 'mininet>' @unittest.skipIf( '-quick' in sys.argv, 'long test' ) def testTreePing64( self ): "Run the example and verify ping results" p = pexpect.spawn( 'python -m mininet.examples.treeping64' ) p.expect( 'Tree network ping results:', timeout=6000 ) count = 0 while True: index = p.expect( [ '(\d+)% packet loss', pexpect.EOF ] ) if index == 0: percent = int( p.match.group( 1 ) ) if p.match else -1 self.assertEqual( percent, 0 ) count += 1 else: break self.assertTrue( count > 0 ) if __name__ == '__main__': unittest.main() mininet-2.1.0/examples/tree1024.py000077500000000000000000000010261221655600400166160ustar00rootroot00000000000000#!/usr/bin/python """ Create a 1024-host network, and run the CLI on it. If this fails because of kernel limits, you may have to adjust them, e.g. by adding entries to /etc/sysctl.conf and running sysctl -p. Check util/sysctl_addon. """ from mininet.cli import CLI from mininet.log import setLogLevel from mininet.node import OVSKernelSwitch from mininet.topolib import TreeNet if __name__ == '__main__': setLogLevel( 'info' ) network = TreeNet( depth=2, fanout=32, switch=OVSKernelSwitch ) network.run( CLI, network ) mininet-2.1.0/examples/treeping64.py000077500000000000000000000016661221655600400173510ustar00rootroot00000000000000#!/usr/bin/python "Create a 64-node tree network, and test connectivity using ping." from mininet.log import setLogLevel from mininet.node import UserSwitch, OVSKernelSwitch # , KernelSwitch from mininet.topolib import TreeNet def treePing64(): "Run ping test on 64-node tree networks." results = {} switches = { # 'reference kernel': KernelSwitch, 'reference user': UserSwitch, 'Open vSwitch kernel': OVSKernelSwitch } for name in switches: print "*** Testing", name, "datapath" switch = switches[ name ] network = TreeNet( depth=2, fanout=8, switch=switch ) result = network.run( network.pingAll ) results[ name ] = result print print "*** Tree network ping results:" for name in switches: print "%s: %d%% packet loss" % ( name, results[ name ] ) print if __name__ == '__main__': setLogLevel( 'info' ) treePing64() mininet-2.1.0/mininet/000077500000000000000000000000001221655600400146215ustar00rootroot00000000000000mininet-2.1.0/mininet/__init__.py000066400000000000000000000001071221655600400167300ustar00rootroot00000000000000"Docstring to silence pylint; ignores --ignore option for __init__.py" mininet-2.1.0/mininet/clean.py000077500000000000000000000044141221655600400162630ustar00rootroot00000000000000""" Mininet Cleanup author: Bob Lantz (rlantz@cs.stanford.edu) Unfortunately, Mininet and OpenFlow (and the Linux kernel) don't always clean up properly after themselves. Until they do (or until cleanup functionality is integrated into the Python code), this script may be used to get rid of unwanted garbage. It may also get rid of 'false positives', but hopefully nothing irreplaceable! """ from subprocess import Popen, PIPE 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 cleanup(): """Clean up junk which might be left over from old runs; do fast stuff before slow dp and link removal!""" info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes" "\n") zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core ' zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec ivs' # Note: real zombie processes can't actually be killed, since they # are already (un)dead. Then again, # you can't connect to them either, so they're mostly harmless. # Send SIGTERM first to give processes a chance to shutdown cleanly. sh( 'killall ' + zombies + ' 2> /dev/null' ) time.sleep(1) sh( 'killall -9 ' + zombies + ' 2> /dev/null' ) # And kill off sudo mnexec sh( 'pkill -9 -f "sudo mnexec"') info( "*** Removing junk from /tmp\n" ) sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' ) info( "*** Removing old X11 tunnels\n" ) cleanUpScreens() info( "*** Removing excess kernel datapaths\n" ) dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' ) for dp in dps: if dp != '': sh( 'dpctl deldp ' + dp ) info( "*** Removing OVS datapaths" ) dps = sh("ovs-vsctl --timeout=1 list-br").split( '\n' ) for dp in dps: if dp: sh( 'ovs-vsctl del-br ' + dp ) info( "*** Removing all links of the pattern foo-ethX\n" ) links = sh( r"ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' ) for link in links: if link != '': sh( "ip link del " + link ) info( "*** Cleanup complete.\n" ) mininet-2.1.0/mininet/cli.py000066400000000000000000000306011221655600400157420ustar00rootroot00000000000000""" A simple command-line interface for Mininet. The Mininet CLI provides a simple control console which makes it easy to talk to nodes. For example, the command mininet> h27 ifconfig runs 'ifconfig' on host h27. Having a single console rather than, for example, an xterm for each node is particularly convenient for networks of any reasonable size. The CLI automatically substitutes IP addresses for node names, so commands like mininet> h2 ping h3 should work correctly and allow host h2 to ping host h3 Several useful commands are provided, including the ability to list all nodes ('nodes'), to print out the network topology ('net') and to check connectivity ('pingall', 'pingpair') and bandwidth ('iperf'.) """ from subprocess import call from cmd import Cmd from os import isatty from select import poll, POLLIN import sys import time from mininet.log import info, output, error from mininet.term import makeTerms, runX11 from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections class CLI( Cmd ): "Simple command-line interface to talk to nodes." prompt = 'mininet> ' def __init__( self, mininet, stdin=sys.stdin, script=None ): self.mn = mininet # Local variable bindings for py command self.locals = { 'net': mininet } # Attempt to handle input self.stdin = stdin self.inPoller = poll() self.inPoller.register( stdin ) self.inputFile = script Cmd.__init__( self ) info( '*** Starting CLI:\n' ) if self.inputFile: self.do_source( self.inputFile ) return while True: try: # Make sure no nodes are still waiting for node in self.mn.values(): while node.waiting: node.sendInt() node.monitor() if self.isatty(): quietRun( 'stty sane' ) self.cmdloop() break except KeyboardInterrupt: output( '\nInterrupt\n' ) def emptyline( self ): "Don't repeat last command when you hit return." pass def getLocals( self ): "Local variable bindings for py command" self.locals.update( self.mn ) return self.locals # Disable pylint "Unused argument: 'arg's'" messages, as well as # "method could be a function" warning, since each CLI function # must have the same interface # pylint: disable-msg=R0201 helpStr = ( 'You may also send a command to a node using:\n' ' command {args}\n' 'For example:\n' ' mininet> h1 ifconfig\n' '\n' 'The interpreter automatically substitutes IP addresses\n' 'for node names when a node is the first arg, so commands\n' 'like\n' ' mininet> h2 ping h3\n' 'should work.\n' '\n' 'Some character-oriented interactive commands require\n' 'noecho:\n' ' mininet> noecho h2 vi foo.py\n' 'However, starting up an xterm/gterm is generally better:\n' ' mininet> xterm h2\n\n' ) def do_help( self, line ): "Describe available CLI commands." Cmd.do_help( self, line ) if line is '': output( self.helpStr ) def do_nodes( self, _line ): "List all nodes." nodes = ' '.join( sorted( self.mn ) ) output( 'available nodes are: \n%s\n' % nodes ) def do_net( self, _line ): "List network connections." dumpNodeConnections( self.mn.values() ) def do_sh( self, line ): "Run an external shell command" call( line, shell=True ) # do_py() and do_px() need to catch any exception during eval()/exec() # pylint: disable-msg=W0703 def do_py( self, line ): """Evaluate a Python expression. Node names may be used, e.g.: py h1.cmd('ls')""" try: result = eval( line, globals(), self.getLocals() ) if not result: return elif isinstance( result, str ): output( result + '\n' ) else: output( repr( result ) + '\n' ) except Exception, e: output( str( e ) + '\n' ) # We are in fact using the exec() pseudo-function # pylint: disable-msg=W0122 def do_px( self, line ): """Execute a Python statement. Node names may be used, e.g.: px print h1.cmd('ls')""" try: exec( line, globals(), self.getLocals() ) except Exception, e: output( str( e ) + '\n' ) # pylint: enable-msg=W0703,W0122 def do_pingall( self, _line ): "Ping between all hosts." self.mn.pingAll() def do_pingpair( self, _line ): "Ping between first two hosts, useful for testing." self.mn.pingPair() def do_pingallfull( self, _line ): "Ping between first two hosts, returns all ping results." self.mn.pingAllFull() def do_pingpairfull( self, _line ): "Ping between first two hosts, returns all ping results." self.mn.pingPairFull() def do_iperf( self, line ): "Simple iperf TCP test between two (optionally specified) hosts." args = line.split() if not args: self.mn.iperf() elif len(args) == 2: hosts = [] err = False for arg in args: if arg not in self.mn: err = True error( "node '%s' not in network\n" % arg ) else: hosts.append( self.mn[ arg ] ) if not err: self.mn.iperf( hosts ) else: error( 'invalid number of args: iperf src dst\n' ) def do_iperfudp( self, line ): "Simple iperf TCP test between two (optionally specified) hosts." args = line.split() if not args: self.mn.iperf( l4Type='UDP' ) elif len(args) == 3: udpBw = args[ 0 ] hosts = [] err = False for arg in args[ 1:3 ]: if arg not in self.mn: err = True error( "node '%s' not in network\n" % arg ) else: hosts.append( self.mn[ arg ] ) if not err: self.mn.iperf( hosts, l4Type='UDP', udpBw=udpBw ) else: error( 'invalid number of args: iperfudp bw src dst\n' + 'bw examples: 10M\n' ) def do_intfs( self, _line ): "List interfaces." for node in self.mn.values(): output( '%s: %s\n' % ( node.name, ','.join( node.intfNames() ) ) ) def do_dump( self, _line ): "Dump node info." for node in self.mn.values(): output( '%s\n' % repr( node ) ) def do_link( self, line ): "Bring link(s) between two nodes up or down." args = line.split() if len(args) != 3: error( 'invalid number of args: link end1 end2 [up down]\n' ) elif args[ 2 ] not in [ 'up', 'down' ]: error( 'invalid type: link end1 end2 [up down]\n' ) else: self.mn.configLinkStatus( *args ) def do_xterm( self, line, term='xterm' ): "Spawn xterm(s) for the given node(s)." args = line.split() if not args: error( 'usage: %s node1 node2 ...\n' % term ) else: for arg in args: if arg not in self.mn: error( "node '%s' not in network\n" % arg ) else: node = self.mn[ arg ] self.mn.terms += makeTerms( [ node ], term = term ) def do_x( self, line ): """Create an X11 tunnel to the given node, optionally starting a client.""" args = line.split() if not args: error( 'usage: x node [cmd args]...\n' ) else: node = self.mn[ args[ 0 ] ] cmd = args[ 1: ] self.mn.terms += runX11( node, cmd ) def do_gterm( self, line ): "Spawn gnome-terminal(s) for the given node(s)." self.do_xterm( line, term='gterm' ) def do_exit( self, _line ): "Exit" return 'exited by user command' def do_quit( self, line ): "Exit" return self.do_exit( line ) def do_EOF( self, line ): "Exit" output( '\n' ) return self.do_exit( line ) def isatty( self ): "Is our standard input a tty?" return isatty( self.stdin.fileno() ) def do_noecho( self, line ): "Run an interactive command with echoing turned off." if self.isatty(): quietRun( 'stty -echo' ) self.default( line ) if self.isatty(): quietRun( 'stty echo' ) def do_source( self, line ): "Read commands from an input file." args = line.split() if len(args) != 1: error( 'usage: source \n' ) return try: self.inputFile = open( args[ 0 ] ) while True: line = self.inputFile.readline() if len( line ) > 0: self.onecmd( line ) else: break except IOError: error( 'error reading file %s\n' % args[ 0 ] ) self.inputFile = None def do_dpctl( self, line ): "Run dpctl (or ovs-ofctl) command on all switches." 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 ) ) def do_time( self, line ): "Measure time taken for any command in Mininet." start = time.time() self.onecmd(line) elapsed = time.time() - start self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed) def default( self, line ): """Called on an input line when the command prefix is not recognized. Overridden to run shell commands when a node is the first CLI argument. Past the first CLI argument, node names are automatically replaced with corresponding IP addrs.""" first, args, line = self.parseline( line ) if first in self.mn: if not args: print "*** Enter a command for node: %s " % first return node = self.mn[ first ] rest = args.split( ' ' ) # Substitute IP addresses for node names in command rest = [ self.mn[ arg ].defaultIntf().updateIP() if arg in self.mn else arg for arg in rest ] rest = ' '.join( rest ) # Run cmd on node: builtin = isShellBuiltin( first ) node.sendCmd( rest, printPid=( not builtin ) ) self.waitForNode( node ) else: error( '*** Unknown command: %s\n' % line ) # pylint: enable-msg=R0201 def waitForNode( self, node ): "Wait for a node to finish, and print its output." # Pollers nodePoller = poll() nodePoller.register( node.stdout ) bothPoller = poll() bothPoller.register( self.stdin, POLLIN ) bothPoller.register( node.stdout, POLLIN ) if self.isatty(): # Buffer by character, so that interactive # commands sort of work quietRun( 'stty -icanon min 1' ) while True: try: bothPoller.poll() # XXX BL: this doesn't quite do what we want. if False and self.inputFile: key = self.inputFile.read( 1 ) if key is not '': node.write(key) else: self.inputFile = None if isReadable( self.inPoller ): key = self.stdin.read( 1 ) node.write( key ) if isReadable( nodePoller ): data = node.monitor() output( data ) if not node.waiting: break except KeyboardInterrupt: node.sendInt() # Helper functions def isReadable( poller ): "Check whether a Poll object has a readable fd." for fdmask in poller.poll( 0 ): mask = fdmask[ 1 ] if mask & POLLIN: return True mininet-2.1.0/mininet/examples000077700000000000000000000000001221655600400203312../examplesustar00rootroot00000000000000mininet-2.1.0/mininet/link.py000066400000000000000000000354031221655600400161350ustar00rootroot00000000000000""" link.py: interface and link abstractions for mininet It seems useful to bundle functionality for interfaces into a single class. Also it seems useful to enable the possibility of multiple flavors of links, including: - simple veth pairs - tunneled links - patchable links (which can be disconnected and reconnected via a patchbay) - link simulators (e.g. wireless) Basic division of labor: Nodes: know how to execute commands Intfs: know how to configure themselves Links: know how to connect nodes together Intf: basic interface object that can configure itself TCIntf: interface with bandwidth limiting and delay via tc Link: basic link class for creating veth pairs """ from mininet.log import info, error, debug from mininet.util import makeIntfPair, quietRun import re class Intf( object ): "Basic interface object that can configure itself." def __init__( self, name, node=None, port=None, link=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, self.ip, self.prefixLen = None, None, None # Add to node (and move ourselves if necessary ) 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: 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" ifconfig = self.ifconfig() 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 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: self.ifconfig( 'up' ) 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 type( value ) is list: result = f( *value ) elif type( value ) is 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 ) self.updateIP() self.updateMAC() return r def delete( self ): "Delete interface" self.cmd( 'ip link del ' + self.name ) if self.node.inNamespace: # Link may have been dumped into root NS quietRun( 'ip link del ' + self.name ) 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) as well as delay, loss and max queue length""" def bwCmds( self, bw=None, speedup=0, use_hfsc=False, use_tbf=False, latency_ms=None, enable_ecn=False, enable_red=False ): "Return tc commands to set bandwidth" cmds, parent = [], ' root ' if bw and ( bw < 0 or bw > 1000 ): error( 'Bandwidth', bw, 'is outside range 0..1000 Mbps\n' ) elif bw is not None: # BL: this seems a bit brittle... if ( speedup > 0 and self.node.name[0:1] == 's' ): bw = speedup # This may not be correct - we should look more closely # at the semantics of burst (and cburst) to make sure we # are specifying the correct sizes. For now I have used # the same settings we had in the mininet-hifi code. if use_hfsc: cmds += [ '%s qdisc add dev %s root handle 5:0 hfsc default 1', '%s class add dev %s parent 5:0 classid 5:1 hfsc sc ' + 'rate %fMbit ul rate %fMbit' % ( bw, bw ) ] elif use_tbf: if latency_ms is None: latency_ms = 15 * 8 / bw cmds += [ '%s qdisc add dev %s root handle 5: tbf ' + 'rate %fMbit burst 15000 latency %fms' % ( bw, latency_ms ) ] else: cmds += [ '%s qdisc add dev %s root handle 5:0 htb default 1', '%s class add dev %s parent 5:0 classid 5:1 htb ' + 'rate %fMbit burst 15k' % bw ] parent = ' parent 5:1 ' # ECN or RED if enable_ecn: cmds += [ '%s qdisc add dev %s' + parent + 'handle 6: red limit 1000000 ' + 'min 30000 max 35000 avpkt 1500 ' + 'burst 20 ' + 'bandwidth %fmbit probability 1 ecn' % bw ] parent = ' parent 6: ' elif enable_red: cmds += [ '%s qdisc add dev %s' + parent + 'handle 6: red limit 1000000 ' + 'min 30000 max 35000 avpkt 1500 ' + 'burst 20 ' + 'bandwidth %fmbit probability 1' % bw ] parent = ' parent 6: ' return cmds, parent @staticmethod def delayCmds( parent, delay=None, jitter=None, loss=None, max_queue_size=None ): "Internal method: return tc commands for delay and loss" cmds = [] if delay and delay < 0: error( 'Negative delay', delay, '\n' ) elif jitter and jitter < 0: error( 'Negative jitter', jitter, '\n' ) elif loss and ( loss < 0 or loss > 100 ): error( 'Bad loss percentage', loss, '%%\n' ) else: # Delay/jitter/loss/max queue size netemargs = '%s%s%s%s' % ( 'delay %s ' % delay if delay is not None else '', '%s ' % jitter if jitter is not None else '', 'loss %d ' % loss if loss is not None else '', 'limit %d' % max_queue_size if max_queue_size is not None else '' ) if netemargs: cmds = [ '%s qdisc add dev %s ' + parent + ' handle 10: netem ' + netemargs ] parent = ' parent 10:1 ' return cmds, parent def tc( self, cmd, tc='tc' ): "Execute tc command for our interface" c = cmd % (tc, self) # Add in tc command and our name debug(" *** executing command: %s\n" % c) return self.cmd( c ) def config( self, bw=None, delay=None, jitter=None, loss=None, disable_gro=True, speedup=0, use_hfsc=False, use_tbf=False, latency_ms=None, enable_ecn=False, enable_red=False, max_queue_size=None, **params ): "Configure the port and set its properties." result = Intf.config( self, **params) # Disable GRO if disable_gro: self.cmd( 'ethtool -K %s gro off' % self ) # Optimization: return if nothing else to configure # Question: what happens if we want to reset things? if ( bw is None and not delay and not loss and max_queue_size is None ): return # Clear existing configuration cmds = [ '%s qdisc del dev %s root' ] # Bandwidth limits via various methods bwcmds, parent = self.bwCmds( bw=bw, speedup=speedup, use_hfsc=use_hfsc, use_tbf=use_tbf, latency_ms=latency_ms, enable_ecn=enable_ecn, enable_red=enable_red ) cmds += bwcmds # Delay/jitter/loss/max_queue_size using netem delaycmds, parent = self.delayCmds( delay=delay, jitter=jitter, loss=loss, max_queue_size=max_queue_size, parent=parent ) cmds += delaycmds # Ugly but functional: display configuration info stuff = ( ( [ '%.2fMbit' % bw ] if bw is not None else [] ) + ( [ '%s delay' % delay ] if delay is not None else [] ) + ( [ '%s jitter' % jitter ] if jitter is not None else [] ) + ( ['%d%% loss' % loss ] if loss is not None else [] ) + ( [ 'ECN' ] if enable_ecn else [ 'RED' ] if enable_red else [] ) ) info( '(' + ' '.join( stuff ) + ') ' ) # Execute all the commands in our node debug("at map stage w/cmds: %s\n" % cmds) tcoutputs = [ self.tc(cmd) for cmd in cmds ] debug( "cmds:", cmds, '\n' ) debug( "outputs:", tcoutputs, '\n' ) result[ 'tcoutputs'] = tcoutputs result[ 'parent' ] = parent return result class Link( object ): """A basic link is just a veth pair. Other types of links could be tunnels, link emulators, etc..""" def __init__( self, node1, node2, port1=None, port2=None, intfName1=None, intfName2=None, intf=Intf, cls1=None, cls2=None, params1=None, params2=None ): """Create veth link to another node, making two new interfaces. node1: first node node2: second node port1: node1 port number (optional) port2: node2 port number (optional) intf: default interface class/constructor cls1, cls2: optional interface-specific constructors intfName1: node1 interface name (optional) intfName2: node2 interface name (optional) params1: parameters for interface 1 params2: parameters for interface 2""" # This is a bit awkward; it seems that having everything in # params would be more orthogonal, but being able to specify # in-line arguments is more convenient! if port1 is None: port1 = node1.newPort() if port2 is None: port2 = node2.newPort() if not intfName1: intfName1 = self.intfName( node1, port1 ) if not intfName2: intfName2 = self.intfName( node2, port2 ) self.makeIntfPair( intfName1, intfName2 ) if not cls1: cls1 = intf if not cls2: cls2 = intf if not params1: params1 = {} if not params2: params2 = {} intf1 = cls1( name=intfName1, node=node1, port=port1, link=self, **params1 ) intf2 = cls2( name=intfName2, node=node2, port=port2, link=self, **params2 ) # All we are is dust in the wind, and our two interfaces self.intf1, self.intf2 = intf1, intf2 @classmethod def intfName( cls, node, n ): "Construct a canonical interface name node-ethN for interface n." return node.name + '-eth' + repr( n ) @classmethod def makeIntfPair( cls, intf1, intf2 ): """Create pair of interfaces intf1: name of interface 1 intf2: name of interface 2 (override this class method [and possibly delete()] to change link type)""" makeIntfPair( intf1, intf2 ) def delete( self ): "Delete this link" self.intf1.delete() self.intf2.delete() def __str__( self ): return '%s<->%s' % ( self.intf1, self.intf2 ) class TCLink( Link ): "Link with symmetric TC interfaces configured via opts" def __init__( self, node1, node2, port1=None, port2=None, intfName1=None, intfName2=None, **params ): Link.__init__( self, node1, node2, port1=port1, port2=port2, intfName1=intfName1, intfName2=intfName2, cls1=TCIntf, cls2=TCIntf, params1=params, params2=params) mininet-2.1.0/mininet/log.py000066400000000000000000000136721221655600400157650ustar00rootroot00000000000000"Logging functions for Mininet." import logging from logging import Logger import types # Create a new loglevel, 'CLI info', which enables a Mininet user to see only # the output of the commands they execute, plus any errors or warnings. This # level is in between info and warning. CLI info-level commands should not be # printed during regression tests. OUTPUT = 25 LEVELS = { 'debug': logging.DEBUG, 'info': logging.INFO, 'output': OUTPUT, 'warning': logging.WARNING, 'error': logging.ERROR, 'critical': logging.CRITICAL } # change this to logging.INFO to get printouts when running unit tests LOGLEVELDEFAULT = OUTPUT #default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' LOGMSGFORMAT = '%(message)s' # Modified from python2.5/__init__.py class StreamHandlerNoNewline( logging.StreamHandler ): """StreamHandler that doesn't print newlines by default. Since StreamHandler automatically adds newlines, define a mod to more easily support interactive mode when we want it, or errors-only logging for running unit tests.""" def emit( self, record ): """Emit a record. If a formatter is specified, it is used to format the record. The record is then written to the stream with a trailing newline [ N.B. this may be removed depending on feedback ]. If exception information is present, it is formatted using traceback.printException and appended to the stream.""" try: msg = self.format( record ) fs = '%s' # was '%s\n' if not hasattr( types, 'UnicodeType' ): # if no unicode support... self.stream.write( fs % msg ) else: try: self.stream.write( fs % msg ) except UnicodeError: self.stream.write( fs % msg.encode( 'UTF-8' ) ) self.flush() except ( KeyboardInterrupt, SystemExit ): raise except: self.handleError( record ) class Singleton( type ): """Singleton pattern from Wikipedia See http://en.wikipedia.org/wiki/SingletonPattern#Python Intended to be used as a __metaclass_ param, as shown for the class below.""" def __init__( cls, name, bases, dict_ ): super( Singleton, cls ).__init__( name, bases, dict_ ) cls.instance = None def __call__( cls, *args, **kw ): if cls.instance is None: cls.instance = super( Singleton, cls ).__call__( *args, **kw ) return cls.instance class MininetLogger( Logger, object ): """Mininet-specific logger Enable each mininet .py file to with one import: from mininet.log import [lg, info, error] ...get a default logger that doesn't require one newline per logging call. Inherit from object to ensure that we have at least one new-style base class, and can then use the __metaclass__ directive, to prevent this error: TypeError: Error when calling the metaclass bases a new-style class can't have only classic bases If Python2.5/logging/__init__.py defined Filterer as a new-style class, via Filterer( object ): rather than Filterer, we wouldn't need this. Use singleton pattern to ensure only one logger is ever created.""" __metaclass__ = Singleton def __init__( self ): Logger.__init__( self, "mininet" ) # create console handler ch = StreamHandlerNoNewline() # create formatter formatter = logging.Formatter( LOGMSGFORMAT ) # add formatter to ch ch.setFormatter( formatter ) # add ch to lg self.addHandler( ch ) self.setLogLevel() def setLogLevel( self, levelname=None ): """Setup loglevel. Convenience function to support lowercase names. levelName: level name from LEVELS""" level = LOGLEVELDEFAULT if levelname is not None: if levelname not in LEVELS: raise Exception( 'unknown levelname seen in setLogLevel' ) else: level = LEVELS.get( levelname, level ) self.setLevel( level ) self.handlers[ 0 ].setLevel( level ) # pylint: disable-msg=E0202 # "An attribute inherited from mininet.log hide this method" # Not sure why this is occurring - this function definitely gets called. # See /usr/lib/python2.5/logging/__init__.py; modified from warning() def output( self, msg, *args, **kwargs ): """Log 'msg % args' with severity 'OUTPUT'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.warning("Houston, we have a %s", "cli output", exc_info=1) """ if self.manager.disable >= OUTPUT: return if self.isEnabledFor( OUTPUT ): self._log( OUTPUT, msg, args, kwargs ) # pylint: enable-msg=E0202 lg = MininetLogger() # Make things a bit more convenient by adding aliases # (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' ) # In the future we may wish to make things more efficient by only # doing the join (and calling the function) unless the logging level # is high enough. def makeListCompatible( fn ): """Return a new function allowing fn( 'a 1 b' ) to be called as newfn( 'a', 1, 'b' )""" def newfn( *args ): "Generated function. Closure-ish." if len( args ) == 1: return fn( *args ) args = ' '.join( [ str( arg ) for arg in args ] ) return fn( args ) # Fix newfn's name and docstring setattr( newfn, '__name__', fn.__name__ ) setattr( newfn, '__doc__', fn.__doc__ ) return newfn info, output, warn, error, debug = ( lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [ makeListCompatible( f ) for f in lg.info, lg.output, lg.warn, lg.error, lg.debug ] setLogLevel = lg.setLogLevel mininet-2.1.0/mininet/moduledeps.py000066400000000000000000000046171221655600400173440ustar00rootroot00000000000000"Module dependency utility functions for Mininet." 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 ] ) OF_KMOD = 'ofdatapath' OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+ TUN = 'tun' def moduleDeps( subtract=None, add=None ): """Handle module dependencies. subtract: string or list of module names to remove, if already loaded add: string or list of module names to add, if not already loaded""" subtract = subtract if subtract is not None else [] add = add if add is not None else [] if type( subtract ) is str: subtract = [ subtract ] if type( add ) is str: add = [ add ] for mod in subtract: if mod in lsmod(): info( '*** Removing ' + mod + '\n' ) rmmodOutput = rmmod( mod ) if rmmodOutput: error( 'Error removing ' + mod + ': "%s">\n' % rmmodOutput ) exit( 1 ) if mod in lsmod(): error( 'Failed to remove ' + mod + '; still there!\n' ) exit( 1 ) for mod in add: if mod not in lsmod(): info( '*** Loading ' + mod + '\n' ) modprobeOutput = modprobe( mod ) if modprobeOutput: error( 'Error inserting ' + mod + ' - is it installed and available via modprobe?\n' + 'Error was: "%s"\n' % modprobeOutput ) if mod not in lsmod(): error( 'Failed to insert ' + mod + ' - quitting.\n' ) exit( 1 ) else: debug( '*** ' + mod + ' already loaded\n' ) def pathCheck( *args, **kwargs ): "Make sure each program in *args can be found in $PATH." moduleName = kwargs.get( 'moduleName', 'it' ) for arg in args: if not quietRun( 'which ' + arg ): error( 'Cannot find required executable %s.\n' % arg + 'Please make sure that %s is installed ' % moduleName + 'and available in your $PATH:\n(%s)\n' % environ[ 'PATH' ] ) exit( 1 ) mininet-2.1.0/mininet/net.py000077500000000000000000000744531221655600400160010ustar00rootroot00000000000000""" Mininet: A simple networking testbed for OpenFlow/SDN! author: Bob Lantz (rlantz@cs.stanford.edu) author: Brandon Heller (brandonh@stanford.edu) Mininet creates scalable OpenFlow test networks by using process-based virtualization and network namespaces. Simulated hosts are created as processes in separate network namespaces. This allows a complete OpenFlow network to be simulated on top of a single Linux kernel. Each host has: A virtual console (pipes to a shell) A virtual interfaces (half of a veth pair) A parent shell (and possibly some child processes) in a namespace Hosts have a network interface which is configured via ifconfig/ip link/etc. This version supports both the kernel and user space datapaths from the OpenFlow reference implementation (openflowswitch.org) as well as OpenVSwitch (openvswitch.org.) In kernel datapath mode, the controller and switches are simply processes in the root namespace. Kernel OpenFlow datapaths are instantiated using dpctl(8), and are attached to the one side of a veth pair; the other side resides in the host namespace. In this mode, switch processes can simply connect to the controller via the loopback interface. In user datapath mode, the controller and switches can be full-service nodes that live in their own network namespaces and have management interfaces and IP addresses on a control network (e.g. 192.168.123.1, currently routed although it could be bridged.) In addition to a management interface, user mode switches also have several switch interfaces, halves of veth pairs whose other halves reside in the host nodes that the switches are connected to. Consistent, straightforward naming is important in order to easily identify hosts, switches and controllers, both from the CLI and from program code. Interfaces are named to make it easy to identify which interfaces belong to which node. The basic naming scheme is as follows: Host nodes are named h1-hN Switch nodes are named s1-sN Controller nodes are named c0-cN Interfaces are named {nodename}-eth0 .. {nodename}-ethN Note: If the network topology is created using mininet.topo, then node numbers are unique among hosts and switches (e.g. we have h1..hN and SN..SN+M) and also correspond to their default IP addresses of 10.x.y.z/8 where x.y.z is the base-256 representation of N for hN. This mapping allows easy determination of a node's IP address from its name, e.g. h1 -> 10.0.0.1, h257 -> 10.0.1.1. Note also that 10.0.0.1 can often be written as 10.1 for short, e.g. "ping 10.1" is equivalent to "ping 10.0.0.1". Currently we wrap the entire network in a 'mininet' object, which constructs a simulated network based on a network topology created using a topology object (e.g. LinearTopo) from mininet.topo or mininet.topolib, and a Controller which the switches will connect to. Several configuration options are provided for functions such as automatically setting MAC addresses, populating the ARP table, or even running a set of terminals to allow direct interaction with nodes. After the network is created, it can be started using start(), and a variety of useful tasks maybe performed, including basic connectivity and bandwidth tests and running the mininet CLI. Once the network is up and running, test code can easily get access to host and switch objects which can then be used for arbitrary experiments, typically involving running a series of commands on the hosts. After all desired tests or activities have been completed, the stop() method may be called to shut down the network. """ import os import re import select import signal from time import sleep from itertools import chain from mininet.cli import CLI from mininet.log import info, error, debug, output from mininet.node import Host, OVSKernelSwitch, Controller from mininet.link import Link, Intf from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd from mininet.term import cleanUpScreens, makeTerms # Mininet version: should be consistent with README and LICENSE VERSION = "2.1.0" class Mininet( object ): "Network emulation with hosts spawned in network namespaces." def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host, controller=Controller, link=Link, intf=Intf, build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8', inNamespace=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, listenPort=None ): """Create Mininet object. topo: Topo (topology) object or None switch: default Switch class host: default Host class/constructor controller: default Controller class/constructor link: default Link class/constructor intf: default Intf class/constructor ipBase: base IP address for hosts, build: build now from topo? xterms: if build now, spawn xterms? cleanup: if build now, cleanup before creating? inNamespace: spawn switches and controller in net namespaces? autoSetMacs: set MAC addrs automatically like IP addresses? autoStaticArp: set all-pairs static MAC addrs? autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)? listenPort: base listening port to open; will be incremented for each additional switch in the net if inNamespace=False""" self.topo = topo self.switch = switch self.host = host self.controller = controller self.link = link self.intf = intf self.ipBase = ipBase self.ipBaseNum, self.prefixLen = netParse( self.ipBase ) self.nextIP = 1 # start for address allocation self.inNamespace = inNamespace self.xterms = xterms self.cleanup = cleanup self.autoSetMacs = autoSetMacs self.autoStaticArp = autoStaticArp self.autoPinCpus = autoPinCpus self.numCores = numCores() self.nextCore = 0 # next core for pinning hosts to CPUs self.listenPort = listenPort self.hosts = [] self.switches = [] self.controllers = [] self.nameToNode = {} # name to Node (Host/Switch) objects self.terms = [] # list of spawned xterm processes Mininet.init() # Initialize Mininet if necessary self.built = False if topo and build: self.build() def addHost( self, name, cls=None, **params ): """Add host. name: name of host to add cls: custom host class/constructor (optional) params: parameters for host returns: added host""" # Default IP and MAC addresses defaults = { 'ip': ipAdd( self.nextIP, ipBaseNum=self.ipBaseNum, prefixLen=self.prefixLen ) + '/%s' % self.prefixLen } if self.autoSetMacs: defaults[ 'mac'] = macColonHex( self.nextIP ) if self.autoPinCpus: defaults[ 'cores' ] = self.nextCore self.nextCore = ( self.nextCore + 1 ) % self.numCores self.nextIP += 1 defaults.update( params ) if not cls: cls = self.host h = cls( name, **defaults ) self.hosts.append( h ) self.nameToNode[ name ] = h return h def addSwitch( self, name, cls=None, **params ): """Add switch. name: name of switch to add cls: custom switch class/constructor (optional) returns: added switch side effect: increments listenPort ivar .""" defaults = { 'listenPort': self.listenPort, 'inNamespace': self.inNamespace } defaults.update( params ) if not cls: cls = self.switch sw = cls( name, **defaults ) if not self.inNamespace and self.listenPort: self.listenPort += 1 self.switches.append( sw ) self.nameToNode[ name ] = sw return sw def addController( self, name='c0', controller=None, **params ): """Add controller. controller: Controller class""" # Get controller class if not controller: controller = self.controller # Construct new controller if one is not given if isinstance(name, Controller): controller_new = name # Pylint thinks controller is a str() # pylint: disable=E1103 name = controller_new.name # pylint: enable=E1103 else: controller_new = controller( name, **params ) # Add new controller to net if controller_new: # allow controller-less setups self.controllers.append( controller_new ) self.nameToNode[ name ] = controller_new return controller_new # BL: We now have four ways to look up nodes # This may (should?) be cleaned up in the future. def getNodeByName( self, *args ): "Return node(s) with given name(s)" if len( args ) == 1: return self.nameToNode[ args[ 0 ] ] return [ self.nameToNode[ n ] for n in args ] def get( self, *args ): "Convenience alias for getNodeByName" return self.getNodeByName( *args ) # Even more convenient syntax for node lookup and iteration def __getitem__( self, key ): """net [ name ] operator: Return node(s) with given name(s)""" return self.nameToNode[ key ] def __iter__( self ): "return iterator over node names" for node in chain( self.hosts, self.switches, self.controllers ): yield node.name def __len__( self ): "returns number of nodes in net" return ( len( self.hosts ) + len( self.switches ) + len( self.controllers ) ) def __contains__( self, item ): "returns True if net contains named node" return item in self.nameToNode def keys( self ): "return a list of all node names or net's keys" return list( self ) def values( self ): "return a list of all nodes or net's values" return [ self[name] for name in self ] def items( self ): "return (key,value) tuple list for every node in net" return zip( self.keys(), self.values() ) def addLink( self, node1, node2, port1=None, port2=None, cls=None, **params ): """"Add a link from node1 to node2 node1: source node node2: dest node port1: source port port2: dest port returns: link object""" defaults = { 'port1': port1, 'port2': port2, 'intf': self.intf } defaults.update( params ) if not cls: cls = self.link return cls( node1, node2, **defaults ) def configHosts( self ): "Configure a set of hosts." for host in self.hosts: info( host.name + ' ' ) intf = host.defaultIntf() if intf: host.configDefault() else: # Don't configure nonexistent intf host.configDefault( ip=None, mac=None ) # You're low priority, dude! # BL: do we want to do this here or not? # May not make sense if we have CPU lmiting... # quietRun( 'renice +18 -p ' + repr( host.pid ) ) # This may not be the right place to do this, but # it needs to be done somewhere. host.cmd( 'ifconfig lo up' ) info( '\n' ) def buildFromTopo( self, topo=None ): """Build mininet from a topology object At the end of this function, everything should be connected and up.""" # Possibly we should clean up here and/or validate # the topo if self.cleanup: pass info( '*** Creating network\n' ) if not self.controllers and self.controller: # Add a default controller info( '*** Adding controller\n' ) classes = self.controller if type( classes ) is not list: classes = [ classes ] for i, cls in enumerate( classes ): self.addController( 'c%d' % i, cls ) info( '*** Adding hosts:\n' ) for hostName in topo.hosts(): self.addHost( hostName, **topo.nodeInfo( hostName ) ) info( hostName + ' ' ) info( '\n*** Adding switches:\n' ) for switchName in topo.switches(): self.addSwitch( switchName, **topo.nodeInfo( switchName) ) info( switchName + ' ' ) info( '\n*** Adding links:\n' ) for srcName, dstName in topo.links(sort=True): src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ] params = topo.linkInfo( srcName, dstName ) srcPort, dstPort = topo.port( srcName, dstName ) self.addLink( src, dst, srcPort, dstPort, **params ) info( '(%s, %s) ' % ( src.name, dst.name ) ) info( '\n' ) def configureControlNetwork( self ): "Control net config hook: override in subclass" raise Exception( 'configureControlNetwork: ' 'should be overriden in subclass', self ) def build( self ): "Build mininet." if self.topo: self.buildFromTopo( self.topo ) if ( self.inNamespace ): self.configureControlNetwork() info( '*** Configuring hosts\n' ) self.configHosts() if self.xterms: self.startTerms() if self.autoStaticArp: self.staticArp() self.built = True def startTerms( self ): "Start a terminal for each node." if 'DISPLAY' not in os.environ: error( "Error starting terms: Cannot connect to display\n" ) return info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] ) cleanUpScreens() self.terms += makeTerms( self.controllers, 'controller' ) self.terms += makeTerms( self.switches, 'switch' ) self.terms += makeTerms( self.hosts, 'host' ) def stopXterms( self ): "Kill each xterm." for term in self.terms: os.kill( term.pid, signal.SIGKILL ) cleanUpScreens() def staticArp( self ): "Add all-pairs ARP entries to remove the need to handle broadcast." for src in self.hosts: for dst in self.hosts: if src != dst: src.setARP( ip=dst.IP(), mac=dst.MAC() ) def start( self ): "Start controller and switches." if not self.built: self.build() info( '*** Starting controller\n' ) for controller in self.controllers: controller.start() info( '*** Starting %s switches\n' % len( self.switches ) ) for switch in self.switches: info( switch.name + ' ') switch.start( self.controllers ) info( '\n' ) def stop( self ): "Stop the controller(s), switches and hosts" if self.terms: info( '*** Stopping %i terms\n' % len( self.terms ) ) self.stopXterms() info( '*** Stopping %i switches\n' % len( self.switches ) ) for switch in self.switches: info( switch.name + ' ' ) switch.stop() info( '\n' ) info( '*** Stopping %i hosts\n' % len( self.hosts ) ) for host in self.hosts: info( host.name + ' ' ) host.terminate() info( '\n' ) info( '*** Stopping %i controllers\n' % len( self.controllers ) ) for controller in self.controllers: info( controller.name + ' ' ) controller.stop() info( '\n*** Done\n' ) def run( self, test, *args, **kwargs ): "Perform a complete start/test/stop cycle." self.start() info( '*** Running test\n' ) result = test( *args, **kwargs ) self.stop() return result def monitor( self, hosts=None, timeoutms=-1 ): """Monitor a set of hosts (or all hosts by default), and return their output, a line at a time. hosts: (optional) set of hosts to monitor timeoutms: (optional) timeout value in ms returns: iterator which returns host, line""" if hosts is None: hosts = self.hosts poller = select.poll() Node = hosts[ 0 ] # so we can call class method fdToNode for host in hosts: poller.register( host.stdout ) while True: ready = poller.poll( timeoutms ) for fd, event in ready: host = Node.fdToNode( fd ) if event & select.POLLIN: line = host.readline() if line is not None: yield host, line # Return if non-blocking if not ready and timeoutms >= 0: yield None, None # XXX These test methods should be moved out of this class. # Probably we should create a tests.py for them @staticmethod def _parsePing( pingOutput ): "Parse ping output and return packets sent, received." # Check for downed link if 'connect: Network is unreachable' in pingOutput: return (1, 0) r = r'(\d+) packets transmitted, (\d+) received' m = re.search( r, pingOutput ) if m is None: error( '*** Error: could not parse ping output: %s\n' % pingOutput ) return (1, 0) sent, received = int( m.group( 1 ) ), int( m.group( 2 ) ) return sent, received def ping( self, hosts=None, timeout=None ): """Ping between all specified hosts. hosts: list of hosts timeout: time to wait for a response, as string returns: ploss packet loss percentage""" # should we check if running? packets = 0 lost = 0 ploss = None if not hosts: hosts = self.hosts output( '*** Ping: testing ping reachability\n' ) for node in hosts: output( '%s -> ' % node.name ) for dest in hosts: if node != dest: opts = '' if timeout: opts = '-W %s' % timeout result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) ) sent, received = self._parsePing( result ) packets += sent if received > sent: error( '*** Error: received too many packets' ) error( '%s' % result ) node.cmdPrint( 'route' ) exit( 1 ) lost += sent - received output( ( '%s ' % dest.name ) if received else 'X ' ) output( '\n' ) if packets > 0: ploss = 100 * lost / packets received = packets - lost output( "*** Results: %i%% dropped (%d/%d received)\n" % ( ploss, received, packets ) ) else: ploss = 0 output( "*** Warning: No packets sent\n" ) return ploss @staticmethod def _parsePingFull( pingOutput ): "Parse ping output and return all data." errorTuple = (1, 0, 0, 0, 0, 0) # Check for downed link r = r'[uU]nreachable' m = re.search( r, pingOutput ) if m is not None: return errorTuple r = r'(\d+) packets transmitted, (\d+) received' m = re.search( r, pingOutput ) if m is None: error( '*** Error: could not parse ping output: %s\n' % pingOutput ) return errorTuple sent, received = int( m.group( 1 ) ), int( m.group( 2 ) ) r = r'rtt min/avg/max/mdev = ' r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms' m = re.search( r, pingOutput ) if m is None: error( '*** Error: could not parse ping output: %s\n' % pingOutput ) return errorTuple rttmin = float( m.group( 1 ) ) rttavg = float( m.group( 2 ) ) rttmax = float( m.group( 3 ) ) rttdev = float( m.group( 4 ) ) return sent, received, rttmin, rttavg, rttmax, rttdev def pingFull( self, hosts=None, timeout=None ): """Ping between all specified hosts and return all data. hosts: list of hosts timeout: time to wait for a response, as string returns: all ping data; see function body.""" # should we check if running? # Each value is a tuple: (src, dsd, [all ping outputs]) all_outputs = [] if not hosts: hosts = self.hosts output( '*** Ping: testing ping reachability\n' ) for node in hosts: output( '%s -> ' % node.name ) for dest in hosts: if node != dest: opts = '' if timeout: opts = '-W %s' % timeout result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) ) outputs = self._parsePingFull( result ) sent, received, rttmin, rttavg, rttmax, rttdev = outputs all_outputs.append( (node, dest, outputs) ) output( ( '%s ' % dest.name ) if received else 'X ' ) output( '\n' ) output( "*** Results: \n" ) for outputs in all_outputs: src, dest, ping_outputs = outputs sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs output( " %s->%s: %s/%s, " % (src, dest, sent, received ) ) output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" % (rttmin, rttavg, rttmax, rttdev) ) return all_outputs def pingAll( self ): """Ping between all hosts. returns: ploss packet loss percentage""" return self.ping() def pingPair( self ): """Ping between first two hosts, useful for testing. returns: ploss packet loss percentage""" hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ] return self.ping( hosts=hosts ) def pingAllFull( self ): """Ping between all hosts. returns: ploss packet loss percentage""" return self.pingFull() def pingPairFull( self ): """Ping between first two hosts, useful for testing. returns: ploss packet loss percentage""" hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ] return self.pingFull( hosts=hosts ) @staticmethod def _parseIperf( iperfOutput ): """Parse iperf output and return bandwidth. iperfOutput: string returns: result string""" r = r'([\d\.]+ \w+/sec)' m = re.findall( r, iperfOutput ) if m: return m[-1] else: # was: raise Exception(...) error( 'could not parse iperf output: ' + iperfOutput ) return '' # XXX This should be cleaned up def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ): """Run iperf between two hosts. hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] returns: results two-element array of server and client speeds""" if not quietRun( 'which telnet' ): error( 'Cannot find telnet in $PATH - required for iperf test' ) return if not hosts: hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ] else: assert len( hosts ) == 2 client, server = hosts output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' ) output( "%s and %s\n" % ( client.name, server.name ) ) server.cmd( 'killall -9 iperf' ) iperfArgs = 'iperf ' bwArgs = '' if l4Type == 'UDP': iperfArgs += '-u ' bwArgs = '-b ' + udpBw + ' ' elif l4Type != 'TCP': raise Exception( 'Unexpected l4 type: %s' % l4Type ) server.sendCmd( iperfArgs + '-s', printPid=True ) servout = '' while server.lastPid is None: servout += server.monitor() if l4Type == 'TCP': while 'Connected' not in client.cmd( 'sh -c "echo A | telnet -e A %s 5001"' % server.IP()): output('waiting for iperf to start up...') sleep(.5) cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' + bwArgs ) debug( 'Client output: %s\n' % cliout ) server.sendInt() servout += server.waitOutput() debug( 'Server output: %s\n' % servout ) result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ] if l4Type == 'UDP': result.insert( 0, udpBw ) output( '*** Results: %s\n' % result ) return result def runCpuLimitTest( self, cpu, duration=5 ): """run CPU limit test with 'while true' processes. cpu: desired CPU fraction of each host duration: test duration in seconds returns a single list of measured CPU fractions as floats. """ pct = cpu * 100 info('*** Testing CPU %.0f%% bandwidth limit\n' % pct) hosts = self.hosts for h in hosts: h.cmd( 'while true; do a=1; done &' ) pids = [h.cmd( 'echo $!' ).strip() for h in hosts] pids_str = ",".join(["%s" % pid for pid in pids]) cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str # It's a shame that this is what pylint prefers outputs = [] for _ in range( duration ): sleep( 1 ) outputs.append( quietRun( cmd ).strip() ) for h in hosts: h.cmd( 'kill %1' ) cpu_fractions = [] for test_output in outputs: # Split by line. Ignore first line, which looks like this: # PID %CPU COMMAND\n for line in test_output.split('\n')[1:]: r = r'\d+\s*(\d+\.\d+)' m = re.search( r, line ) if m is None: error( '*** Error: could not extract CPU fraction: %s\n' % line ) return None cpu_fractions.append( float( m.group( 1 ) ) ) output( '*** Results: %s\n' % cpu_fractions ) return cpu_fractions # BL: I think this can be rewritten now that we have # a real link class. def configLinkStatus( self, src, dst, status ): """Change status of src <-> dst links. src: node name dst: node name status: string {up, down}""" if src not in self.nameToNode: error( 'src not in network: %s\n' % src ) elif dst not in self.nameToNode: error( 'dst not in network: %s\n' % dst ) else: if type( src ) is str: src = self.nameToNode[ src ] if type( dst ) is str: dst = self.nameToNode[ dst ] connections = src.connectionsTo( dst ) if len( connections ) == 0: error( 'src and dst not connected: %s %s\n' % ( src, dst) ) for srcIntf, dstIntf in connections: result = srcIntf.ifconfig( status ) if result: error( 'link src status change failed: %s\n' % result ) result = dstIntf.ifconfig( status ) if result: error( 'link dst status change failed: %s\n' % result ) def interact( self ): "Start network and run our simple CLI." self.start() result = CLI( self ) self.stop() return result inited = False @classmethod def init( cls ): "Initialize Mininet" if cls.inited: return ensureRoot() fixLimits() cls.inited = True class MininetWithControlNet( Mininet ): """Control network support: Create an explicit control network. Currently this is only used/usable with the user datapath. Notes: 1. If the controller and switches are in the same (e.g. root) namespace, they can just use the loopback connection. 2. If we can get unix domain sockets to work, we can use them instead of an explicit control network. 3. Instead of routing, we could bridge or use 'in-band' control. 4. Even if we dispense with this in general, it could still be useful for people who wish to simulate a separate control network (since real networks may need one!) 5. Basically nobody ever used this code, so it has been moved into its own class. 6. Ultimately we may wish to extend this to allow us to create a control network which every node's control interface is attached to.""" def configureControlNetwork( self ): "Configure control network." self.configureRoutedControlNetwork() # We still need to figure out the right way to pass # in the control network location. def configureRoutedControlNetwork( self, ip='192.168.123.1', prefixLen=16 ): """Configure a routed control network on controller and switches. For use with the user datapath only right now.""" controller = self.controllers[ 0 ] info( controller.name + ' <->' ) cip = ip snum = ipParse( ip ) for switch in self.switches: info( ' ' + switch.name ) link = self.link( switch, controller, port1=0 ) sintf, cintf = link.intf1, link.intf2 switch.controlIntf = sintf snum += 1 while snum & 0xff in [ 0, 255 ]: snum += 1 sip = ipStr( snum ) cintf.setIP( cip, prefixLen ) sintf.setIP( sip, prefixLen ) controller.setHostRoute( sip, cintf ) switch.setHostRoute( cip, sintf ) info( '\n' ) info( '*** Testing control network\n' ) while not cintf.isUp(): info( '*** Waiting for', cintf, 'to come up\n' ) sleep( 1 ) for switch in self.switches: while not sintf.isUp(): info( '*** Waiting for', sintf, 'to come up\n' ) sleep( 1 ) if self.ping( hosts=[ switch, controller ] ) != 0: error( '*** Error: control network test failed\n' ) exit( 1 ) info( '\n' ) mininet-2.1.0/mininet/node.py000066400000000000000000001336511221655600400161310ustar00rootroot00000000000000""" Node objects for Mininet. Nodes provide a simple abstraction for interacting with hosts, switches and controllers. Local nodes are simply one or more processes on the local machine. Node: superclass for all (primarily local) network nodes. Host: a virtual host. By default, a host is simply a shell; commands may be sent using Cmd (which waits for output), or using sendCmd(), which returns immediately, allowing subsequent monitoring using monitor(). Examples of how to run experiments using this functionality are provided in the examples/ directory. CPULimitedHost: a virtual host whose CPU bandwidth is limited by RT or CFS bandwidth limiting. Switch: superclass for switch nodes. UserSwitch: a switch using the user-space switch from the OpenFlow reference implementation. KernelSwitch: a switch using the kernel switch from the OpenFlow reference implementation. OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch implementation (openvswitch.org). Controller: superclass for OpenFlow controllers. The default controller is controller(8) from the reference implementation. NOXController: a controller node using NOX (noxrepo.org). RemoteController: a remote controller node, which may use any arbitrary OpenFlow-compatible controller, and which is not created or managed by mininet. Future enhancements: - Possibly make Node, Switch and Controller more abstract so that they can be used for both local and remote nodes - Create proxy objects for remote nodes (Mininet: Cluster Edition) """ import os import re import signal import select from subprocess import Popen, PIPE, STDOUT from operator import or_ from time import sleep from mininet.log import info, error, warn, debug from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin, numCores, retry, mountCgroups ) from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN from mininet.link import Link, Intf, TCIntf 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? params: Node parameters (see config() for details)""" # Make sure class actually works self.checkSetup() self.name = name self.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() # 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 ): "Start a shell process for running commands" if self.shell: error( "%s: shell is already running" ) return # mnexec: (c)lose descriptors, (d)etach from tty, # (p)rint pid, and run in (n)amespace opts = '-cdp' if self.inNamespace: opts += 'n' # bash -m: enable job control # -s: pass $* to shell, and make process easy to find in ps cmd = [ 'mnexec', opts, 'bash', '-ms', 'mininet:' + self.name ] self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True ) self.stdin = self.shell.stdin self.stdout = self.shell.stdout 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 = '' self.waiting = False def cleanup( self ): "Help python collect its garbage." # Intfs may end up in root NS for intfName in self.intfNames(): if self.name in intfName: quietRun( 'ip link del ' + intfName ) self.shell = None # Subshell I/O, commands and control def read( self, maxbytes=1024 ): """Buffered read from node, non-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, non-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." if self.shell: os.kill( self.pid, signal.SIGKILL ) self.cleanup() def stop( self ): "Stop node." self.terminate() def waitReadable( self, timeoutms=None ): """Wait until node's output is readable. timeoutms: timeout in ms or None to wait indefinitely.""" if len( self.readbuf ) == 0: 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?""" assert not self.waiting printPid = kwargs.get( 'printPid', True ) # Allow sendCmd( [ list ] ) if len( args ) == 1 and type( args[ 0 ] ) is 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 printPid = printPid and not isShellBuiltin( cmd ) if len( cmd ) > 0 and cmd[ -1 ] == '&': # print ^A{pid}\n{sentinel} cmd += ' printf "\\001%d\n\\177" $! \n' else: # print sentinel cmd += '; printf "\\177"' if printPid and not isShellBuiltin( cmd ): cmd = 'mnexec -p ' + cmd self.write( cmd + '\n' ) self.lastPid = None self.waiting = True def sendInt( self, sig=signal.SIGINT ): "Interrupt running command." if self.lastPid: try: os.kill( self.lastPid, sig ) except OSError: pass def monitor( self, timeoutms=None ): """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.""" self.waitReadable( timeoutms ) data = self.read( 1024 ) # Look for PID marker = chr( 1 ) + r'\d+\n' if chr( 1 ) in data: 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 ): """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() 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 ) ) self.sendCmd( *args, **kwargs ) return self.waitOutput( verbose ) 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 type( args[ 0 ] ) is list: # popen([cmd, arg1, arg2...]) cmd = args[ 0 ] elif type( args[ 0 ] ) is str: # 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 mncmd = defaults[ 'mncmd' ] del defaults[ 'mncmd' ] cmd = mncmd + cmd # Shell requires a string, not a list! if defaults.get( 'shell', False ): cmd = ' '.join( cmd ) return Popen( cmd, **defaults ) def pexec( self, *args, **kwargs ): """Execute a command using popen returns: out, err, exitcode""" popen = self.popen( *args, **kwargs) 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 ): """Add an interface. intf: interface port: port number (optional, typically OpenFlow port number)""" 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' ) moveIntf( intf.name, self ) 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='' ): """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 type( intf ) is str: 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 via ...}""" # Note setParam won't call us if intf is none if type( intf ) is str and ' ' in intf: params = intf else: params = 'dev %s' % intf self.cmd( 'ip route del default' ) return self.cmd( '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 ): """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""" # This should probably be rethought if '/' not in ip: ip = '%s/%s' % ( ip, prefixLen ) return self.intf( intf ).setIP( ip ) 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 ] f = getattr( self, method, None ) if not f or value is None: return if type( value ) is list: result = f( *value ) elif type( value ) is 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 ): "CPU limited host" def __init__( self, name, sched='cfs', **kwargs ): Host.__init__( self, name, **kwargs ) # Initialize class if necessary if not CPULimitedHost.inited: CPULimitedHost.init() # Create a cgroup and move shell into it self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name errFail( 'cgcreate -g ' + self.cgroup ) # We don't add ourselves to a cpuset because you must # specify the cpu and memory placement first errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) ) # BL: Setting the correct period/quota is tricky, particularly # for RT. RT allows very small quotas, but the overhead # seems to be high. CFS has a mininimum quota of 1 ms, but # still does better with larger period values. self.period_us = kwargs.get( 'period_us', 100000 ) self.sched = sched self.rtprio = 20 def cgroupSet( self, param, value, resource='cpu' ): "Set a cgroup parameter and return its value" cmd = 'cgset -r %s.%s=%s /%s' % ( resource, param, value, self.name ) quietRun( cmd ) nvalue = int( self.cgroupGet( param, resource ) ) if nvalue != value: error( '*** error: cgroupSet: %s set to %s instead of %s\n' % ( param, nvalue, value ) ) return nvalue def cgroupGet( self, param, resource='cpu' ): "Return value of cgroup parameter" cmd = 'cgget -r %s.%s /%s' % ( resource, param, self.name ) return int( quietRun( cmd ).split()[ -1 ] ) def cgroupDel( self ): "Clean up our cgroup" # info( '*** deleting cgroup', self.cgroup, '\n' ) _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup ) return exitcode != 0 def popen( self, *args, **kwargs ): """Return a Popen() object in node's namespace args: Popen() args, single list, or string kwargs: Popen() keyword args""" # Tell mnexec to execute command in our cgroup mncmd = [ 'mnexec', '-da', str( self.pid ), '-g', self.name ] if self.sched == 'rt': mncmd += [ '-r', str( self.rtprio ) ] return Host.popen( self, *args, mncmd=mncmd, **kwargs ) def cleanup( self ): "Clean up Node, then clean up our cgroup" super( CPULimitedHost, self ).cleanup() retry( retries=3, delaySecs=1, fn=self.cgroupDel ) def chrt( self ): "Set RT scheduling priority" 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 ] if lastword != 'SCHED_RR': error( '*** error: could not assign SCHED_RR to %s\n' % self.name ) return lastword def rtInfo( self, f ): "Internal method: return parameters for RT bandwidth" pstr, qstr = 'rt_period_us', 'rt_runtime_us' # RT uses wall clock time for period and quota quota = int( self.period_us * f * numCores() ) return pstr, qstr, self.period_us, quota def cfsInfo( self, f): "Internal method: return parameters for CFS bandwidth" pstr, qstr = 'cfs_period_us', 'cfs_quota_us' # CFS uses wall clock time for period and CPU time for quota. quota = int( self.period_us * f * numCores() ) period = self.period_us if f > 0 and quota < 1000: debug( '(cfsInfo: increasing default period) ' ) quota = 1000 period = int( quota / f / numCores() ) return pstr, qstr, period, quota # BL comment: # This may not be the right API, # since it doesn't specify CPU bandwidth in "absolute" # units the way link bandwidth is specified. # We should use MIPS or SPECINT or something instead. # Alternatively, we should change from system fraction # to CPU seconds per second, essentially assuming that # all CPUs are the same. def setCPUFrac( self, f=-1, sched=None): """Set overall CPU fraction for this host f: CPU bandwidth limit (fraction) sched: 'rt' or 'cfs' Note 'cfs' requires CONFIG_CFS_BANDWIDTH""" if not f: return if not sched: sched = self.sched if sched == 'rt': pstr, qstr, period, quota = self.rtInfo( f ) elif sched == 'cfs': pstr, qstr, period, quota = self.cfsInfo( f ) else: return if quota < 0: # Reset to unlimited quota = -1 # Set cgroup's period and quota self.cgroupSet( pstr, period ) self.cgroupSet( qstr, quota ) if sched == 'rt': # Set RT priority if necessary self.chrt() info( '(%s %d/%dus) ' % ( sched, quota, period ) ) def setCPUs( self, cores, mems=0 ): "Specify (real) cores that our cgroup can run on" if type( cores ) is list: cores = ','.join( [ str( c ) for c in cores ] ) self.cgroupSet( resource='cpuset', param='cpus', value=cores ) # Memory placement is probably not relevant, but we # must specify it anyway self.cgroupSet( resource='cpuset', param='mems', value=mems) # We have to do this here after we've specified # cpus and mems errFail( 'cgclassify -g cpuset:/%s %s' % ( self.name, self.pid ) ) def config( self, cpu=None, 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 inited = False @classmethod def init( cls ): "Initialization for CPULimitedHost class" mountCgroups() cls.inited = True # Some important things to note: # # The "IP" address which setIP() assigns to the switch is not # an "IP address for the switch" in the sense of IP routing. # Rather, it is the IP address for the control interface, # on the control network, and it is only relevant to the # controller. If you are running in the root namespace # (which is the only way to run OVS at the moment), the # control interface is the loopback interface, and you # normally never want to change its IP address! # # In general, you NEVER want to attempt to use Linux's # network stack (i.e. ifconfig) to "assign" an IP address or # MAC address to a switch data port. Instead, you "assign" # the IP and MAC addresses in the controller by specifying # packets that you want to receive or send. The "MAC" address # reported by ifconfig for a switch data port is essentially # meaningless. It is important to understand this if you # want to create a functional router using OpenFlow. class Switch( Node ): """A Switch is a Node that is running (or has execed?) an OpenFlow switch.""" portBase = 1 # Switches start with port 1 in OpenFlow dpidLen = 16 # digits in dpid passed to switch def __init__( self, name, dpid=None, opts='', listenPort=None, **params): """dpid: dpid for switch (or None to derive from name, e.g. s1 -> 1) opts: additional switch options listenPort: port to listen on for dpctl connections""" Node.__init__( self, name, **params ) self.dpid = dpid if dpid else self.defaultDpid() self.opts = opts self.listenPort = listenPort if not self.inNamespace: self.controlIntf = Intf( 'lo', self, port=0 ) def defaultDpid( self ): "Derive dpid from switch name, s1 -> 1" try: dpid = int( re.findall( r'\d+', self.name )[ 0 ] ) dpid = hex( dpid )[ 2: ] dpid = '0' * ( self.dpidLen - len( dpid ) ) + dpid return dpid except IndexError: raise Exception( 'Unable to derive default datapath ID - ' 'please either specify a dpid or use a ' 'canonical switch name such as s23.' ) def defaultIntf( self ): "Return control interface" if self.controlIntf: return self.controlIntf else: return Node.defaultIntf( self ) def sendCmd( self, *cmd, **kwargs ): """Send command to Node. cmd: string""" kwargs.setdefault( 'printPid', False ) if not self.execed: return Node.sendCmd( self, *cmd, **kwargs ) else: error( '*** Error: %s has execed and cannot accept commands' % self.name ) def connected( self ): "Is the switch connected to a controller? (override this method)" return False and self # satisfy pylint 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 ) class UserSwitch( Switch ): "User-space switch." dpidLen = 12 def __init__( self, name, dpopts='--no-slicing', **kwargs ): """Init. name: name for the switch dpopts: additional arguments to ofdatapath (--no-slicing)""" Switch.__init__( self, name, **kwargs ) pathCheck( 'ofdatapath', 'ofprotocol', moduleName='the OpenFlow reference user switch' + '(openflow.org)' ) if self.listenPort: self.opts += ' --listen=ptcp:%i ' % self.listenPort self.dpopts = dpopts @classmethod def setup( cls ): "Ensure any dependencies are loaded; if not, try to load them." if not os.path.exists( '/dev/net/tun' ): moduleDeps( add=TUN ) def dpctl( self, *args ): "Run dpctl command" if not self.listenPort: return "can't run dpctl without passive listening port" return self.cmd( 'dpctl ' + ' '.join( args ) + ' tcp:127.0.0.1:%i' % self.listenPort ) def connected( self ): "Is the switch connected to a controller?" return 'remote.is-connected=true' in self.dpctl( 'status' ) @staticmethod def TCReapply( intf ): """Unfortunately user switch and Mininet are fighting over tc queuing disciplines. To resolve the conflict, we re-create the user switch's configuration, but as a leaf of the TCIntf-created configuration.""" if type( intf ) is TCIntf: ifspeed = 10000000000 # 10 Gbps minspeed = ifspeed * 0.001 res = intf.config( **intf.params ) parent = res['parent'] # Re-add qdisc, root, and default classes user switch created, but # with new parent, as setup by Mininet's TCIntf intf.tc( "%s qdisc add dev %s " + parent + " handle 1: htb default 0xfffe" ) intf.tc( "%s class add dev %s classid 1:0xffff parent 1: htb rate " + str(ifspeed) ) intf.tc( "%s class add dev %s classid 1:0xfffe parent 1:0xffff " + "htb rate " + str(minspeed) + " ceil " + str(ifspeed) ) def start( self, controllers ): """Start OpenFlow reference user datapath. Log to /tmp/sN-{ofd,ofp}.log. controllers: list of controller objects""" # Add controllers clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port ) for c in controllers ] ) ofdlog = '/tmp/' + self.name + '-ofd.log' ofplog = '/tmp/' + self.name + '-ofp.log' self.cmd( 'ifconfig lo up' ) intfs = [ str( i ) for i in self.intfList() if not i.IP() ] self.cmd( 'ofdatapath -i ' + ','.join( intfs ) + ' punix:/tmp/' + self.name + ' -d %s ' % self.dpid + self.dpopts + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' ) self.cmd( 'ofprotocol unix:/tmp/' + self.name + ' ' + clist + ' --fail=closed ' + self.opts + ' 1> ' + ofplog + ' 2>' + ofplog + ' &' ) if "no-slicing" not in self.dpopts: # Only TCReapply if slicing is enable sleep(1) # Allow ofdatapath to start before re-arranging qdisc's for intf in self.intfList(): if not intf.IP(): self.TCReapply( intf ) def stop( self ): "Stop OpenFlow reference user datapath." self.cmd( 'kill %ofdatapath' ) self.cmd( 'kill %ofprotocol' ) self.deleteIntfs() class OVSLegacyKernelSwitch( Switch ): """Open VSwitch legacy kernel-space switch using ovs-openflowd. Currently only works in the root namespace.""" def __init__( self, name, dp=None, **kwargs ): """Init. name: name for switch dp: netlink id (0, 1, 2, ...) defaultMAC: default MAC as unsigned int; random value if None""" Switch.__init__( self, name, **kwargs ) self.dp = dp if dp else self.name self.intf = self.dp if self.inNamespace: error( "OVSKernelSwitch currently only works" " in the root namespace.\n" ) exit( 1 ) @classmethod def setup( cls ): "Ensure any dependencies are loaded; if not, try to load them." pathCheck( 'ovs-dpctl', 'ovs-openflowd', moduleName='Open vSwitch (openvswitch.org)') moduleDeps( subtract=OF_KMOD, add=OVS_KMOD ) def start( self, controllers ): "Start up kernel datapath." ofplog = '/tmp/' + self.name + '-ofp.log' quietRun( 'ifconfig lo up' ) # Delete local datapath if it exists; # then create a new one monitoring the given interfaces self.cmd( 'ovs-dpctl del-dp ' + self.dp ) self.cmd( 'ovs-dpctl add-dp ' + self.dp ) intfs = [ str( i ) for i in self.intfList() if not i.IP() ] self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) ) # Run protocol daemon clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port ) for c in controllers ] ) self.cmd( 'ovs-openflowd ' + self.dp + ' ' + clist + ' --fail=secure ' + self.opts + ' --datapath-id=' + self.dpid + ' 1>' + ofplog + ' 2>' + ofplog + '&' ) self.execed = False def stop( self ): "Terminate kernel datapath." quietRun( 'ovs-dpctl del-dp ' + self.dp ) self.cmd( 'kill %ovs-openflowd' ) self.deleteIntfs() class OVSSwitch( Switch ): "Open vSwitch switch. Depends on ovs-vsctl." def __init__( self, name, failMode='secure', datapath='kernel', **params ): """Init. name: name for switch failMode: controller loss behavior (secure|open) datapath: userspace or kernel mode (kernel|user)""" Switch.__init__( self, name, **params ) self.failMode = failMode self.datapath = datapath @classmethod def setup( cls ): "Make sure Open vSwitch is installed and working" pathCheck( 'ovs-vsctl', moduleName='Open vSwitch (openvswitch.org)') # This should no longer be needed, and it breaks # with OVS 1.7 which has renamed the kernel module: # moduleDeps( subtract=OF_KMOD, add=OVS_KMOD ) out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' ) if exitcode: error( out + err + 'ovs-vsctl exited with code %d\n' % exitcode + '*** Error connecting to ovs-db with ovs-vsctl\n' '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' ) exit( 1 ) def dpctl( self, *args ): "Run ovs-ofctl command" return self.cmd( 'ovs-ofctl', args[ 0 ], self, *args[ 1: ] ) @staticmethod def TCReapply( intf ): """Unfortunately OVS and Mininet are fighting over tc queuing disciplines. As a quick hack/ workaround, we clear OVS's and reapply our own.""" if type( intf ) is TCIntf: intf.config( **intf.params ) def attach( self, intf ): "Connect a data port" self.cmd( 'ovs-vsctl add-port', self, intf ) self.cmd( 'ifconfig', intf, 'up' ) self.TCReapply( intf ) def detach( self, intf ): "Disconnect a data port" self.cmd( 'ovs-vsctl del-port', self, intf ) def controllerUUIDs( self ): "Return ovsdb UUIDs for our controllers" uuids = [] controllers = self.cmd( 'ovs-vsctl -- get Bridge', self, 'Controller' ).strip() if controllers.startswith( '[' ) and controllers.endswith( ']' ): controllers = controllers[ 1 : -1 ] uuids = [ c.strip() for c in controllers.split( ',' ) ] return uuids def connected( self ): "Are we connected to at least one of our controllers?" results = [ 'true' in self.cmd( 'ovs-vsctl -- get Controller', uuid, 'is_connected' ) for uuid in self.controllerUUIDs() ] return reduce( or_, results, False ) def start( self, controllers ): "Start up a new OVS OpenFlow switch using ovs-vsctl" if self.inNamespace: raise Exception( 'OVS kernel switch does not work in a namespace' ) # We should probably call config instead, but this # requires some rethinking... self.cmd( 'ifconfig lo up' ) # Annoyingly, --if-exists option seems not to work self.cmd( 'ovs-vsctl del-br', self ) self.cmd( 'ovs-vsctl add-br', self ) if self.datapath == 'user': self.cmd( 'ovs-vsctl set bridge', self,'datapath_type=netdev' ) int( self.dpid, 16 ) # DPID must be a hex string self.cmd( 'ovs-vsctl -- set Bridge', self, 'other_config:datapath-id=' + self.dpid ) self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode ) for intf in self.intfList(): if not intf.IP(): self.attach( intf ) # Add controllers clist = ' '.join( [ 'tcp:%s:%d' % ( c.IP(), c.port ) for c in controllers ] ) if self.listenPort: clist += ' ptcp:%s' % self.listenPort self.cmd( 'ovs-vsctl set-controller', self, clist ) # Reconnect quickly to controllers (1s vs. 15s max_backoff) for uuid in self.controllerUUIDs(): if uuid.count( '-' ) != 4: # Doesn't look like a UUID continue uuid = uuid.strip() self.cmd( 'ovs-vsctl set Controller', uuid, 'max_backoff=1000' ) def stop( self ): "Terminate OVS switch." self.cmd( 'ovs-vsctl del-br', self ) if self.datapath == 'user': self.cmd( 'ip link del', self ) self.deleteIntfs() OVSKernelSwitch = OVSSwitch class IVSSwitch(Switch): """IVS virtual switch""" def __init__( self, name, **kwargs ): Switch.__init__( self, name, **kwargs ) @classmethod def setup( cls ): "Make sure IVS is installed" pathCheck( 'ivs-ctl', 'ivs', moduleName="Indigo Virtual Switch (projectfloodlight.org)" ) out, err, exitcode = errRun( 'ivs-ctl show' ) if exitcode: error( out + err + 'ivs-ctl exited with code %d\n' % exitcode + '*** The openvswitch kernel module might ' 'not be loaded. Try modprobe openvswitch.\n' ) exit( 1 ) def start( self, controllers ): "Start up a new IVS switch" args = ['ivs'] args.extend( ['--name', self.name] ) args.extend( ['--dpid', self.dpid] ) args.extend( ['--verbose'] ) for intf in self.intfs.values(): if not intf.IP(): args.extend( ['-i', intf.name] ) for c in controllers: args.extend( ['-c', '%s:%d' % (c.IP(), c.port)] ) if self.listenPort: args.extend( ['--listen', '127.0.0.1:%i' % self.listenPort] ) args.append( self.opts ) logfile = '/tmp/ivs.%s.log' % self.name self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 on controller. Log to /tmp/cN.log""" pathCheck( self.command ) cout = '/tmp/' + self.name + '.log' if self.cdir is not None: self.cmd( 'cd ' + self.cdir ) self.cmd( self.command + ' ' + self.cargs % self.port + ' 1>' + cout + ' 2>' + cout + '&' ) self.execed = False def stop( self ): "Stop controller." self.cmd( 'kill %' + self.command ) self.terminate() def IP( self, intf=None ): "Return IP address of the Controller" if self.intfs: ip = Node.IP( self, intf ) else: ip = self.ip return ip def __repr__( self ): "More informative string representation" return '<%s %s: %s:%s pid=%s> ' % ( self.__class__.__name__, self.name, self.IP(), self.port, self.pid ) class OVSController( Controller ): "Open vSwitch controller" def __init__( self, name, command='ovs-controller', **kwargs ): Controller.__init__( self, name, command=command, **kwargs ) class NOX( Controller ): "Controller to run a NOX application." def __init__( self, name, *noxArgs, **kwargs ): """Init. name: name to give controller noxArgs: arguments (strings) to pass to NOX""" if not noxArgs: warn( 'warning: no NOX modules specified; ' 'running packetdump only\n' ) noxArgs = [ 'packetdump' ] elif type( noxArgs ) not in ( list, tuple ): noxArgs = [ noxArgs ] if 'NOX_CORE_DIR' not in os.environ: exit( 'exiting; please set missing NOX_CORE_DIR env var' ) noxCoreDir = os.environ[ 'NOX_CORE_DIR' ] Controller.__init__( self, name, command=noxCoreDir + '/nox_core', cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' + ' '.join( noxArgs ), cdir=noxCoreDir, **kwargs ) class RemoteController( Controller ): "Controller running outside of Mininet's control." def __init__( self, name, ip='127.0.0.1', port=6633, **kwargs): """Init. name: name to give controller ip: the IP address where the remote controller is listening port: the port where the remote controller is listening""" Controller.__init__( self, name, ip=ip, port=port, **kwargs ) def start( self ): "Overridden to do nothing." return def stop( self ): "Overridden to do nothing." return def checkListening( self ): "Warn if remote controller is not accessible" listening = self.cmd( "echo A | telnet -e A %s %d" % ( self.ip, self.port ) ) if 'Connected' not in listening: warn( "Unable to contact the remote controller" " at %s:%d\n" % ( self.ip, self.port ) ) mininet-2.1.0/mininet/term.py000066400000000000000000000052401221655600400161430ustar00rootroot00000000000000""" Terminal creation and cleanup. Utility functions to run a terminal (connected via socat(1)) on each host. Requires socat(1) and xterm(1). Optionally uses gnome-terminal. """ from os import environ from mininet.log import error from mininet.util import quietRun, errRun def tunnelX11( node, display=None): """Create an X11 tunnel from node:6000 to the root host display: display on root host (optional) returns: node $DISPLAY, Popen object for tunnel""" if display is None and 'DISPLAY' in environ: display = environ[ 'DISPLAY' ] if display is None: error( "Error: Cannot connect to display\n" ) return None, None host, screen = display.split( ':' ) # Unix sockets should work if not host or host == 'unix': # GDM3 doesn't put credentials in .Xauthority, # so allow root to just connect quietRun( 'xhost +si:localuser:root' ) return display, None else: # Create a tunnel for the TCP connection port = 6000 + int( float( screen ) ) connection = r'TCP\:%s\:%s' % ( host, port ) cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port, "EXEC:'mnexec -a 1 socat STDIO %s'" % connection ] return 'localhost:' + screen, node.popen( cmd ) def makeTerm( node, title='Node', term='xterm', display=None ): """Create an X11 tunnel to the node and start up a terminal. node: Node object title: base title term: 'xterm' or 'gterm' returns: two Popen objects, tunnel and terminal""" title += ': ' + node.name if not node.inNamespace: title += ' (root)' cmds = { 'xterm': [ 'xterm', '-title', title, '-display' ], 'gterm': [ 'gnome-terminal', '--title', title, '--display' ] } if term not in cmds: error( 'invalid terminal type: %s' % term ) return display, tunnel = tunnelX11( node, display ) if display is None: return [] term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] ) return [ tunnel, term ] if tunnel else [ term ] def runX11( node, cmd ): "Run an X11 client on a node" _display, tunnel = tunnelX11( node ) if _display is None: return [] popen = node.popen( cmd ) return [ tunnel, popen ] def cleanUpScreens(): "Remove moldy socat X11 tunnels." errRun( "pkill -9 -f mnexec.*socat" ) def makeTerms( nodes, title='Node', term='xterm' ): """Create terminals. nodes: list of Node objects title: base title for each returns: list of created tunnel/terminal processes""" terms = [] for node in nodes: terms += makeTerm( node, title, term ) return terms mininet-2.1.0/mininet/test/000077500000000000000000000000001221655600400156005ustar00rootroot00000000000000mininet-2.1.0/mininet/test/runner.py000077500000000000000000000015511221655600400174700ustar00rootroot00000000000000#!/usr/bin/env python """ Run all mininet core tests -v : verbose output -quick : skip tests that take more than ~30 seconds """ import unittest import os import sys from mininet.util import ensureRoot from mininet.clean import cleanup from mininet.log import setLogLevel def runTests( testDir, verbosity=1 ): "discover and run all tests in testDir" # ensure root and cleanup before starting tests ensureRoot() cleanup() # discover all tests in testDir testSuite = unittest.defaultTestLoader.discover( testDir ) # run tests unittest.TextTestRunner( verbosity=verbosity ).run( testSuite ) if __name__ == '__main__': setLogLevel( 'warning' ) # get the directory containing example tests testDir = os.path.dirname( os.path.realpath( __file__ ) ) verbosity = 2 if '-v' in sys.argv else 1 runTests( testDir, verbosity ) mininet-2.1.0/mininet/test/test_hifi.py000077500000000000000000000143721221655600400201420ustar00rootroot00000000000000#!/usr/bin/env python """Package: mininet Test creation and pings for topologies with link and/or CPU options.""" import unittest from functools import partial from mininet.net import Mininet from mininet.node import OVSSwitch, UserSwitch, IVSSwitch from mininet.node import CPULimitedHost from mininet.link import TCLink from mininet.topo import Topo from mininet.log import setLogLevel from mininet.util import quietRun # Number of hosts for each test N = 2 class SingleSwitchOptionsTopo(Topo): "Single switch connected to n hosts." def __init__(self, n=2, hopts=None, lopts=None): if not hopts: hopts = {} if not lopts: lopts = {} Topo.__init__(self, hopts=hopts, lopts=lopts) switch = self.addSwitch('s1') for h in range(n): host = self.addHost('h%s' % (h + 1)) self.addLink(host, switch) # Tell pylint not to complain about calls to other class # pylint: disable=E1101 class testOptionsTopoCommon( object ): """Verify ability to create networks with host and link options (common code).""" switchClass = None # overridden in subclasses def runOptionsTopoTest( self, n, hopts=None, lopts=None ): "Generic topology-with-options test runner." mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts, lopts=lopts ), host=CPULimitedHost, link=TCLink, switch=self.switchClass ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) def assertWithinTolerance(self, measured, expected, tolerance_frac): """Check that a given value is within a tolerance of expected tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance. """ self.assertTrue( float(measured) >= float(expected) * tolerance_frac ) self.assertTrue( float(measured) >= float(expected) * tolerance_frac ) def testCPULimits( self ): "Verify topology creation with CPU limits set for both schedulers." CPU_FRACTION = 0.1 CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail hopts = { 'cpu': CPU_FRACTION } #self.runOptionsTopoTest( N, hopts=hopts ) mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ), host=CPULimitedHost, switch=self.switchClass ) mn.start() results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) mn.stop() for cpu in results: self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE ) def testLinkBandwidth( self ): "Verify that link bandwidths are accurate within a bound." BW = 5 # Mbps BW_TOLERANCE = 0.8 # BW fraction below which test should fail # Verify ability to create limited-link topo first; lopts = { 'bw': BW, 'use_htb': True } # Also verify correctness of limit limitng within a bound. mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), link=TCLink, switch=self.switchClass ) bw_strs = mn.run( mn.iperf ) for bw_str in bw_strs: bw = float( bw_str.split(' ')[0] ) self.assertWithinTolerance( bw, BW, BW_TOLERANCE ) def testLinkDelay( self ): "Verify that link delays are accurate within a bound." DELAY_MS = 15 DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True } mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), link=TCLink, switch=self.switchClass ) ping_delays = mn.run( mn.pingFull ) test_outputs = ping_delays[0] # Ignore unused variables below # pylint: disable-msg=W0612 node, dest, ping_outputs = test_outputs sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs self.assertEqual( sent, received ) # pylint: enable-msg=W0612 for rttval in [rttmin, rttavg, rttmax]: # Multiply delay by 4 to cover there & back on two links self.assertWithinTolerance( rttval, DELAY_MS * 4.0, DELAY_TOLERANCE) def testLinkLoss( self ): "Verify that we see packet drops with a high configured loss rate." LOSS_PERCENT = 99 REPS = 1 lopts = { 'loss': LOSS_PERCENT, 'use_htb': True } mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ), host=CPULimitedHost, link=TCLink, switch=self.switchClass ) # Drops are probabilistic, but the chance of no dropped packets is # 1 in 100 million with 4 hops for a link w/99% loss. dropped_total = 0 mn.start() for _ in range(REPS): dropped_total += mn.ping(timeout='1') mn.stop() self.assertTrue(dropped_total > 0) def testMostOptions( self ): "Verify topology creation with most link options and CPU limits." lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True } hopts = { 'cpu': 0.5 / N } self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts ) # pylint: enable=E1101 class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ): """Verify ability to create networks with host and link options (OVS kernel switch).""" switchClass = OVSSwitch @unittest.skip( 'Skipping OVS user switch test for now' ) class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ): """Verify ability to create networks with host and link options (OVS user switch).""" switchClass = partial( OVSSwitch, datapath='user' ) @unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' ) class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ): "Verify ability to create networks with host and link options (IVS)." switchClass = IVSSwitch @unittest.skipUnless( quietRun( 'which ofprotocol' ), 'Reference user switch is not installed' ) class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ): "Verify ability to create networks with host and link options (UserSwitch)." switchClass = UserSwitch if __name__ == '__main__': setLogLevel( 'warning' ) unittest.main() mininet-2.1.0/mininet/test/test_nets.py000077500000000000000000000065711221655600400201760ustar00rootroot00000000000000#!/usr/bin/env python """Package: mininet Test creation and all-pairs ping for each included mininet topo type.""" import unittest from functools import partial from mininet.net import Mininet from mininet.node import Host, Controller from mininet.node import UserSwitch, OVSSwitch, IVSSwitch from mininet.topo import SingleSwitchTopo, LinearTopo from mininet.log import setLogLevel from mininet.util import quietRun # Tell pylint not to complain about calls to other class # pylint: disable=E1101 class testSingleSwitchCommon( object ): "Test ping with single switch topology (common code)." switchClass = None # overridden in subclasses def testMinimal( self ): "Ping test on minimal topology" mn = Mininet( SingleSwitchTopo(), self.switchClass, Host, Controller ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) def testSingle5( self ): "Ping test on 5-host single-switch topology" mn = Mininet( SingleSwitchTopo( k=5 ), self.switchClass, Host, Controller ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) # pylint: enable=E1101 class testSingleSwitchOVSKernel( testSingleSwitchCommon, unittest.TestCase ): "Test ping with single switch topology (OVS kernel switch)." switchClass = OVSSwitch class testSingleSwitchOVSUser( testSingleSwitchCommon, unittest.TestCase ): "Test ping with single switch topology (OVS user switch)." switchClass = partial( OVSSwitch, datapath='user' ) @unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' ) class testSingleSwitchIVS( testSingleSwitchCommon, unittest.TestCase ): "Test ping with single switch topology (IVS switch)." switchClass = IVSSwitch @unittest.skipUnless( quietRun( 'which ofprotocol' ), 'Reference user switch is not installed' ) class testSingleSwitchUserspace( testSingleSwitchCommon, unittest.TestCase ): "Test ping with single switch topology (Userspace switch)." switchClass = UserSwitch # Tell pylint not to complain about calls to other class # pylint: disable=E1101 class testLinearCommon( object ): "Test all-pairs ping with LinearNet (common code)." switchClass = None # overridden in subclasses def testLinear5( self ): "Ping test on a 5-switch topology" mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) # pylint: enable=E1101 class testLinearOVSKernel( testLinearCommon, unittest.TestCase ): "Test all-pairs ping with LinearNet (OVS kernel switch)." switchClass = OVSSwitch class testLinearOVSUser( testLinearCommon, unittest.TestCase ): "Test all-pairs ping with LinearNet (OVS user switch)." switchClass = partial( OVSSwitch, datapath='user' ) @unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' ) class testLinearIVS( testLinearCommon, unittest.TestCase ): "Test all-pairs ping with LinearNet (IVS switch)." switchClass = IVSSwitch @unittest.skipUnless( quietRun( 'which ofprotocol' ), 'Reference user switch is not installed' ) class testLinearUserspace( testLinearCommon, unittest.TestCase ): "Test all-pairs ping with LinearNet (Userspace switch)." switchClass = UserSwitch if __name__ == '__main__': setLogLevel( 'warning' ) unittest.main() mininet-2.1.0/mininet/test/test_walkthrough.py000077500000000000000000000273561221655600400215700ustar00rootroot00000000000000#!/usr/bin/env python """ Tests for the Mininet Walkthrough TODO: missing xterm test """ import unittest import pexpect import os from time import sleep from mininet.util import quietRun class testWalkthrough( unittest.TestCase ): prompt = 'mininet>' # PART 1 def testHelp( self ): "Check the usage message" p = pexpect.spawn( 'mn -h' ) index = p.expect( [ 'Usage: mn', pexpect.EOF ] ) self.assertEqual( index, 0 ) def testWireshark( self ): "Use tshark to test the of dissector" tshark = pexpect.spawn( 'tshark -i lo -R of' ) tshark.expect( 'Capturing on lo' ) mn = pexpect.spawn( 'mn --test pingall' ) mn.expect( '0% dropped' ) tshark.expect( 'OFP 74 Hello' ) tshark.sendintr() def testBasic( self ): "Test basic CLI commands (help, nodes, net, dump)" p = pexpect.spawn( 'mn' ) p.expect( self.prompt ) # help command p.sendline( 'help' ) index = p.expect( [ 'commands', self.prompt ] ) self.assertEqual( index, 0, 'No output for "help" command') # nodes command p.sendline( 'nodes' ) p.expect( '([chs]\d ?){4}' ) nodes = p.match.group( 0 ).split() self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command') p.expect( self.prompt ) # net command p.sendline( 'net' ) expected = [ x for x in nodes ] while len( expected ) > 0: index = p.expect( expected ) node = p.match.group( 0 ) expected.remove( node ) p.expect( '\n' ) self.assertEqual( len( expected ), 0, '"nodes" and "net" differ') p.expect( self.prompt ) # dump command p.sendline( 'dump' ) expected = [ '<\w+ (%s)' % n for n in nodes ] actual = [] for _ in nodes: index = p.expect( expected ) node = p.match.group( 1 ) actual.append( node ) p.expect( '\n' ) self.assertEqual( actual.sort(), nodes.sort(), '"nodes" and "dump" differ' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testHostCommands( self ): "Test ifconfig and ps on h1 and s1" p = pexpect.spawn( 'mn' ) p.expect( self.prompt ) interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ] # h1 ifconfig p.sendline( 'h1 ifconfig -a' ) ifcount = 0 while True: index = p.expect( interfaces ) if index == 0 or index == 3: ifcount += 1 elif index == 1: self.fail( 's1 interface displayed in "h1 ifconfig"' ) elif index == 2: self.fail( 'eth0 displayed in "h1 ifconfig"' ) else: break self.assertEqual( ifcount, 2, 'Missing interfaces on h1') # s1 ifconfig p.sendline( 's1 ifconfig -a' ) ifcount = 0 while True: index = p.expect( interfaces ) if index == 0: self.fail( 'h1 interface displayed in "s1 ifconfig"' ) elif index == 1 or index == 2 or index == 3: ifcount += 1 else: break self.assertEqual( ifcount, 3, 'Missing interfaces on s1') # h1 ps p.sendline( 'h1 ps -a' ) p.expect( self.prompt ) h1Output = p.before # s1 ps p.sendline( 's1 ps -a' ) p.expect( self.prompt ) s1Output = p.before # strip command from ps output h1Output = h1Output.split( '\n', 1 )[ 1 ] s1Output = s1Output.split( '\n', 1 )[ 1 ] self.assertEqual( h1Output, s1Output, 'h1 and s1 "ps" output differs') p.sendline( 'exit' ) p.wait() def testConnectivity( self ): "Test ping and pingall" p = pexpect.spawn( 'mn' ) p.expect( self.prompt ) p.sendline( 'h1 ping -c 1 h2' ) p.expect( '1 packets transmitted, 1 received' ) p.expect( self.prompt ) p.sendline( 'pingall' ) p.expect( '0% dropped' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testSimpleHTTP( self ): "Start an HTTP server on h1 and wget from h2" p = pexpect.spawn( 'mn' ) p.expect( self.prompt ) p.sendline( 'h1 python -m SimpleHTTPServer 80 &' ) p.expect( self.prompt ) p.sendline( ' h2 wget -O - h1' ) p.expect( '200 OK' ) p.expect( self.prompt ) p.sendline( 'h1 kill %python' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() # PART 2 def testRegressionRun( self ): "Test pingpair (0% drop) and iperf (bw > 0) regression tests" # test pingpair p = pexpect.spawn( 'mn --test pingpair' ) p.expect( '0% dropped' ) p.expect( pexpect.EOF ) # test iperf p = pexpect.spawn( 'mn --test iperf' ) p.expect( "Results: \['([\d\.]+) .bits/sec'," ) bw = float( p.match.group( 1 ) ) self.assertTrue( bw > 0 ) p.expect( pexpect.EOF ) def testTopoChange( self ): "Test pingall on single,3 and linear,4 topos" # testing single,3 p = pexpect.spawn( 'mn --test pingall --topo single,3' ) p.expect( '(\d+)/(\d+) received') received = int( p.match.group( 1 ) ) sent = int( p.match.group( 2 ) ) self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' ) self.assertEqual( sent, received, 'Dropped packets in single,3') p.expect( pexpect.EOF ) # testing linear,4 p = pexpect.spawn( 'mn --test pingall --topo linear,4' ) p.expect( '(\d+)/(\d+) received') received = int( p.match.group( 1 ) ) sent = int( p.match.group( 2 ) ) self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' ) self.assertEqual( sent, received, 'Dropped packets in linear,4') p.expect( pexpect.EOF ) def testLinkChange( self ): "Test TCLink bw and delay" p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' ) # test bw p.expect( self.prompt ) p.sendline( 'iperf' ) p.expect( "Results: \['([\d\.]+) Mbits/sec'," ) bw = float( p.match.group( 1 ) ) self.assertTrue( bw < 10.1, 'Bandwidth > 10 Mb/s') self.assertTrue( bw > 9.0, 'Bandwidth < 9 Mb/s') p.expect( self.prompt ) # test delay p.sendline( 'h1 ping -c 4 h2' ) p.expect( 'rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' ) delay = float( p.match.group( 2 ) ) self.assertTrue( delay > 40, 'Delay < 40ms' ) self.assertTrue( delay < 45, 'Delay > 40ms' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testVerbosity( self ): "Test debug and output verbosity" # test output p = pexpect.spawn( 'mn -v output' ) p.expect( self.prompt ) self.assertEqual( len( p.before ), 0, 'Too much output for "output"' ) p.sendline( 'exit' ) p.wait() # test debug p = pexpect.spawn( 'mn -v debug --test none' ) p.expect( pexpect.EOF ) lines = p.before.split( '\n' ) self.assertTrue( len( lines ) > 100, "Debug output is too short" ) def testCustomTopo( self ): "Start Mininet using a custom topo, then run pingall" custom = os.path.dirname( os.path.realpath( __file__ ) ) custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' ) custom = os.path.normpath( custom ) p = pexpect.spawn( 'mn --custom %s --topo mytopo --test pingall' % custom ) p.expect( '0% dropped' ) p.expect( pexpect.EOF ) def testStaticMAC( self ): "Verify that MACs are set to easy to read numbers" p = pexpect.spawn( 'mn --mac' ) p.expect( self.prompt ) for i in range( 1, 3 ): p.sendline( 'h%d ifconfig' % i ) p.expect( 'HWaddr 00:00:00:00:00:0%d' % i ) p.expect( self.prompt ) def testSwitches( self ): "Run iperf test using user and ovsk switches" switches = [ 'user', 'ovsk' ] for sw in switches: p = pexpect.spawn( 'mn --switch %s --test iperf' % sw ) p.expect( "Results: \['([\d\.]+) .bits/sec'," ) bw = float( p.match.group( 1 ) ) self.assertTrue( bw > 0 ) p.expect( pexpect.EOF ) def testBenchmark( self ): "Run benchmark and verify that it takes less than 2 seconds" p = pexpect.spawn( 'mn --test none' ) p.expect( 'completed in ([\d\.]+) seconds' ) time = float( p.match.group( 1 ) ) self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' ) def testOwnNamespace( self ): "Test running user switch in its own namespace" p = pexpect.spawn( 'mn --innamespace --switch user' ) p.expect( self.prompt ) interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ] p.sendline( 's1 ifconfig -a' ) ifcount = 0 while True: index = p.expect( interfaces ) if index == 1 or index == 3: ifcount += 1 elif index == 0: self.fail( 'h1 interface displayed in "s1 ifconfig"' ) elif index == 2: self.fail( 'eth0 displayed in "s1 ifconfig"' ) else: break self.assertEqual( ifcount, 2, 'Missing interfaces on s1' ) # verify that all hosts a reachable p.sendline( 'pingall' ) p.expect( '(\d+)% dropped' ) dropped = int( p.match.group( 1 ) ) self.assertEqual( dropped, 0, 'pingall failed') p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() # PART 3 def testPythonInterpreter( self ): "Test py and px by checking IP for h1 and adding h3" p = pexpect.spawn( 'mn' ) p.expect( self.prompt ) # test host IP p.sendline( 'py h1.IP()' ) p.expect( '10.0.0.1' ) p.expect( self.prompt ) # test adding host p.sendline( "px net.addHost('h3')" ) p.expect( self.prompt ) p.sendline( "px net.addLink(s1, h3)" ) p.expect( self.prompt ) p.sendline( 'net' ) p.expect( 'h3' ) p.expect( self.prompt ) p.sendline( 'py h3.MAC()' ) p.expect( '([a-f0-9]{2}:?){6}' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() def testLink( self ): "Test link CLI command using ping" p = pexpect.spawn( 'mn' ) p.expect( self.prompt ) p.sendline( 'link s1 h1 down' ) p.expect( self.prompt ) p.sendline( 'h1 ping -c 1 h2' ) p.expect( 'unreachable' ) p.expect( self.prompt ) p.sendline( 'link s1 h1 up' ) p.expect( self.prompt ) p.sendline( 'h1 ping -c 1 h2' ) p.expect( '0% packet loss' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait() @unittest.skipUnless( os.path.exists( '/tmp/pox' ) or '1 received' in quietRun( 'ping -c 1 github.com' ), 'Github is not reachable; cannot download Pox' ) def testRemoteController( self ): "Test Mininet using Pox controller" if not os.path.exists( '/tmp/pox' ): p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox' ) p.expect( pexpect.EOF ) pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' ) net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' ) net.expect( '0% dropped' ) net.expect( pexpect.EOF ) pox.sendintr() pox.wait() if __name__ == '__main__': unittest.main()mininet-2.1.0/mininet/topo.py000066400000000000000000000204041221655600400161540ustar00rootroot00000000000000#!/usr/bin/env python '''@package topo Network topology creation. @author Brandon Heller (brandonh@stanford.edu) This package includes code to represent network topologies. A Topo object can be a topology database for NOX, can represent a physical setup for testing, and can even be emulated with the Mininet package. ''' from mininet.util import irange, natural, naturalSeq class MultiGraph( object ): "Utility class to track nodes and edges - replaces networkx.Graph" def __init__( self ): self.data = {} def add_node( self, node ): "Add node to graph" self.data.setdefault( node, [] ) def add_edge( self, src, dest ): "Add edge to graph" src, dest = sorted( ( src, dest ) ) self.add_node( src ) self.add_node( dest ) self.data[ src ].append( dest ) def nodes( self ): "Return list of graph nodes" return self.data.keys() def edges( self ): "Iterator: return graph edges" for src in self.data.keys(): for dest in self.data[ src ]: yield ( src, dest ) def __getitem__( self, node ): "Return link dict for the given node" return self.data[node] class Topo(object): "Data center network representation for structured multi-trees." def __init__(self, hopts=None, sopts=None, lopts=None): """Topo object: hinfo: default host options sopts: default switch options lopts: default link options""" self.g = MultiGraph() self.node_info = {} self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects self.hopts = {} if hopts is None else hopts self.sopts = {} if sopts is None else sopts self.lopts = {} if lopts is None else lopts self.ports = {} # ports[src][dst] is port on src that connects to dst def addNode(self, name, **opts): """Add Node to graph. name: name opts: node options returns: node name""" self.g.add_node(name) self.node_info[name] = opts return name def addHost(self, name, **opts): """Convenience method: Add host to graph. name: host name opts: host options returns: host name""" if not opts and self.hopts: opts = self.hopts return self.addNode(name, **opts) def addSwitch(self, name, **opts): """Convenience method: Add switch to graph. name: switch name opts: switch options returns: switch name""" if not opts and self.sopts: opts = self.sopts result = self.addNode(name, isSwitch=True, **opts) return result def addLink(self, node1, node2, port1=None, port2=None, **opts): """node1, node2: nodes to link together port1, port2: ports (optional) opts: link options (optional) returns: link info key""" if not opts and self.lopts: opts = self.lopts self.addPort(node1, node2, port1, port2) key = tuple(self.sorted([node1, node2])) self.link_info[key] = opts self.g.add_edge(*key) return key def addPort(self, src, dst, sport=None, dport=None): '''Generate port mapping for new edge. @param src source switch name @param dst destination switch name ''' self.ports.setdefault(src, {}) self.ports.setdefault(dst, {}) # New port: number of outlinks + base src_base = 1 if self.isSwitch(src) else 0 dst_base = 1 if self.isSwitch(dst) else 0 if sport is None: sport = len(self.ports[src]) + src_base if dport is None: dport = len(self.ports[dst]) + dst_base self.ports[src][dst] = sport self.ports[dst][src] = dport def nodes(self, sort=True): "Return nodes in graph" if sort: return self.sorted( self.g.nodes() ) else: return self.g.nodes() def isSwitch(self, n): '''Returns true if node is a switch.''' info = self.node_info[n] return info and info.get('isSwitch', False) def switches(self, sort=True): '''Return switches. sort: sort switches alphabetically @return dpids list of dpids ''' return [n for n in self.nodes(sort) if self.isSwitch(n)] def hosts(self, sort=True): '''Return hosts. sort: sort hosts alphabetically @return dpids list of dpids ''' return [n for n in self.nodes(sort) if not self.isSwitch(n)] def links(self, sort=True): '''Return links. sort: sort links alphabetically @return links list of name pairs ''' if not sort: return self.g.edges() else: links = [tuple(self.sorted(e)) for e in self.g.edges()] return sorted( links, key=naturalSeq ) def port(self, src, dst): '''Get port number. @param src source switch name @param dst destination switch name @return tuple (src_port, dst_port): src_port: port on source switch leading to the destination switch dst_port: port on destination switch leading to the source switch ''' if src in self.ports and dst in self.ports[src]: assert dst in self.ports and src in self.ports[dst] return (self.ports[src][dst], self.ports[dst][src]) def linkInfo( self, src, dst ): "Return link metadata" src, dst = self.sorted([src, dst]) return self.link_info[(src, dst)] def setlinkInfo( self, src, dst, info ): "Set link metadata" src, dst = self.sorted([src, dst]) self.link_info[(src, dst)] = info def nodeInfo( self, name ): "Return metadata (dict) for node" info = self.node_info[ name ] return info if info is not None else {} def setNodeInfo( self, name, info ): "Set metadata (dict) for node" self.node_info[ name ] = info @staticmethod def sorted( items ): "Items sorted in natural (i.e. alphabetical) order" return sorted(items, key=natural) class SingleSwitchTopo(Topo): '''Single switch connected to k hosts.''' def __init__(self, k=2, **opts): '''Init. @param k number of hosts @param enable_all enables all nodes and switches? ''' super(SingleSwitchTopo, self).__init__(**opts) self.k = k switch = self.addSwitch('s1') for h in irange(1, k): host = self.addHost('h%s' % h) self.addLink(host, switch) class SingleSwitchReversedTopo(Topo): '''Single switch connected to k hosts, with reversed ports. The lowest-numbered host is connected to the highest-numbered port. Useful to verify that Mininet properly handles custom port numberings. ''' def __init__(self, k=2, **opts): '''Init. @param k number of hosts @param enable_all enables all nodes and switches? ''' super(SingleSwitchReversedTopo, self).__init__(**opts) self.k = k switch = self.addSwitch('s1') for h in irange(1, k): host = self.addHost('h%s' % h) self.addLink(host, switch, port1=0, port2=(k - h + 1)) class LinearTopo(Topo): "Linear topology of k switches, with n hosts per switch." def __init__(self, k=2, n=1, **opts): """Init. k: number of switches n: number of hosts per switch hconf: host configuration options lconf: link configuration options""" super(LinearTopo, self).__init__(**opts) self.k = k self.n = n if n == 1: genHostName = lambda i, j: 'h%s' % i else: genHostName = lambda i, j: 'h%ss%d' % (j, i) lastSwitch = None for i in irange(1, k): # Add switch switch = self.addSwitch('s%s' % i) # Add hosts to switch for j in irange(1, n): host = self.addHost(genHostName(i, j)) self.addLink(host, switch) # Connect switch to previous if lastSwitch: self.addLink(switch, lastSwitch) lastSwitch = switch mininet-2.1.0/mininet/topolib.py000066400000000000000000000021771221655600400166520ustar00rootroot00000000000000"Library of potentially useful topologies for Mininet" from mininet.topo import Topo from mininet.net import Mininet class TreeTopo( Topo ): "Topology for a tree network with a given depth and fanout." def __init__( self, depth=1, fanout=2 ): super( TreeTopo, self ).__init__() # Numbering: h1..N, s1..M self.hostNum = 1 self.switchNum = 1 # Build topology self.addTree( depth, fanout ) def addTree( self, depth, fanout ): """Add a subtree starting with node n. returns: last node added""" isSwitch = depth > 0 if isSwitch: node = self.addSwitch( 's%s' % self.switchNum ) self.switchNum += 1 for _ in range( fanout ): child = self.addTree( depth - 1, fanout ) self.addLink( node, child ) else: node = self.addHost( 'h%s' % self.hostNum ) self.hostNum += 1 return node def TreeNet( depth=1, fanout=2, **kwargs ): "Convenience function for creating tree networks." topo = TreeTopo( depth, fanout ) return Mininet( topo, **kwargs ) mininet-2.1.0/mininet/util.py000066400000000000000000000423061221655600400161550ustar00rootroot00000000000000"Utility functions for Mininet." from mininet.log import output, info, error, warn, 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 from fcntl import fcntl, F_GETFL, F_SETFL from os import O_NONBLOCK import os # Command execution support def run( cmd ): """Simple interface to subprocess.call() cmd: list of command params""" return call( cmd.split( ' ' ) ) def checkRun( cmd ): """Simple interface to subprocess.check_call() cmd: list of command params""" return check_call( cmd.split( ' ' ) ) # pylint doesn't understand explicit type checking # pylint: disable-msg=E1103 def oldQuietRun( *cmd ): """Run a command, routing stderr to stdout, and return the output. cmd: list of command params""" if len( cmd ) == 1: cmd = cmd[ 0 ] if isinstance( cmd, str ): cmd = cmd.split( ' ' ) popen = Popen( cmd, stdout=PIPE, stderr=STDOUT ) # We can't use Popen.communicate() because it uses # select(), which can't handle # high file descriptor numbers! poll() can, however. out = '' readable = poll() readable.register( popen.stdout ) while True: while readable.poll(): data = popen.stdout.read( 1024 ) if len( data ) == 0: break out += data popen.poll() if popen.returncode is not None: break return out # This is a bit complicated, but it enables us to # monitor command output as it is happening def errRun( *cmd, **kwargs ): """Run a command and return stdout, stderr and return code cmd: string or list of command and args stderr: STDOUT to merge stderr with stdout shell: run command using shell echo: monitor output to console""" # Allow passing in a list or a string if len( cmd ) == 1: cmd = cmd[ 0 ] if isinstance( cmd, str ): cmd = cmd.split( ' ' ) cmd = [ str( arg ) for arg in cmd ] # By default we separate stderr, don't run in a shell, and don't echo stderr = kwargs.get( 'stderr', PIPE ) shell = kwargs.get( 'shell', False ) echo = kwargs.get( 'echo', False ) if echo: # cmd goes to stderr, output goes to stdout info( cmd, '\n' ) popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell ) # We use poll() because select() doesn't work with large fd numbers, # and thus communicate() doesn't work either out, err = '', '' poller = poll() poller.register( popen.stdout, POLLIN ) fdtofile = { popen.stdout.fileno(): popen.stdout } outDone, errDone = False, True if popen.stderr: fdtofile[ popen.stderr.fileno() ] = popen.stderr poller.register( popen.stderr, POLLIN ) errDone = False while not outDone or not errDone: readable = poller.poll() for fd, _event in readable: f = fdtofile[ fd ] data = f.read( 1024 ) if echo: output( data ) if f == popen.stdout: out += data if data == '': outDone = True elif f == popen.stderr: err += data if data == '': errDone = True returncode = popen.wait() return out, err, returncode def errFail( *cmd, **kwargs ): "Run a command using errRun and raise exception on nonzero exit" out, err, ret = errRun( *cmd, **kwargs ) if ret: raise Exception( "errFail: %s failed with return code %s: %s" % ( cmd, ret, err ) ) return out, err, ret def quietRun( cmd, **kwargs ): "Run a command and return merged stdout and stderr" return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ] # pylint: enable-msg=E1103 # pylint: disable-msg=E1101 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 # pylint: enable-msg=E1101 # 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 ): """Make a veth pair connecting intf1 and intf2. intf1: string, interface intf2: string, interface returns: success boolean""" # Delete any old interfaces with the same names quietRun( 'ip link del ' + intf1 ) quietRun( 'ip link del ' + intf2 ) # Create new pair cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 return quietRun( cmd ) def retry( retries, delaySecs, fn, *args, **keywords ): """Try something several times before giving up. n: number of times to retry delaySecs: wait this long between tries fn: function to call args: args to apply to function call""" tries = 0 while not fn( *args, **keywords ) and tries < retries: sleep( delaySecs ) tries += 1 if tries >= retries: error( "*** gave up after %i retries\n" % tries ) exit( 1 ) def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ): """Move interface to node, without retrying. intf: string, interface dstNode: destination Node srcNode: source Node or None (default) for root ns printError: if true, print error""" intf = str( intf ) cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid ) if srcNode: srcNode.cmd( cmd ) else: quietRun( cmd ) links = dstNode.cmd( 'ip link show' ) if not ( ' %s:' % intf ) in links: if printError: error( '*** Error: moveIntf: ' + intf + ' not successfully moved to ' + dstNode.name + '\n' ) return False return True def moveIntf( intf, dstNode, srcNode=None, printError=False, retries=3, delaySecs=0.001 ): """Move interface to node, retrying on failure. intf: string, interface dstNode: destination Node srcNode: source Node or None (default) for root ns printError: if true, print error""" retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode, srcNode=srcNode, printError=printError ) # Support for dumping network def dumpNodeConnections( nodes ): "Dump connections to/from nodes." def dumpConnections( node ): "Helper function: dump connections to node" for intf in node.intfList(): output( ' %s:' % intf ) if intf.link: intfs = [ intf.link.intf1, intf.link.intf2 ] intfs.remove( intf ) output( intfs[ 0 ] ) else: output( ' ' ) for node in nodes: output( node.name ) dumpConnections( node ) output( '\n' ) def dumpNetConnections( net ): "Dump connections in network" nodes = net.controllers + net.switches + net.hosts dumpNodeConnections( nodes ) # IP and Mac address formatting and parsing def _colonHex( val, bytecount ): """Generate colon-hex string. val: input as unsigned int bytecount: number of bytes to convert returns: chStr colon-hex string""" pieces = [] for i in range( bytecount - 1, -1, -1 ): piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 ) pieces.append( '%02x' % piece ) chStr = ':'.join( pieces ) return chStr def macColonHex( mac ): """Generate MAC colon-hex string from unsigned int. mac: MAC address as unsigned int returns: macStr MAC colon-hex string""" return _colonHex( mac, 6 ) def ipStr( ip ): """Generate IP address string from an unsigned int. ip: unsigned int of form w << 24 | x << 16 | y << 8 | z returns: ip address string w.x.y.z""" w = ( ip >> 24 ) & 0xff x = ( ip >> 16 ) & 0xff y = ( ip >> 8 ) & 0xff z = ip & 0xff return "%i.%i.%i.%i" % ( w, x, y, z ) def ipNum( w, x, y, z ): """Generate unsigned int from components of IP address returns: w << 24 | x << 16 | y << 8 | z""" return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ): """Return IP address string from ints i: int to be added to ipbase prefixLen: optional IP prefix length ipBaseNum: option base IP address as int returns IP address as string""" imax = 0xffffffff >> prefixLen assert i <= imax mask = 0xffffffff ^ imax ipnum = ( ipBaseNum & mask ) + i return ipStr( ipnum ) def ipParse( ip ): "Parse an IP address and return an unsigned int." args = [ int( arg ) for arg in ip.split( '.' ) ] return ipNum( *args ) def netParse( ipstr ): """Parse an IP network specification, returning address and prefix len as unsigned ints""" prefixLen = 0 if '/' in ipstr: ip, pf = ipstr.split( '/' ) prefixLen = int( pf ) return ipParse( ip ), prefixLen def checkInt( s ): "Check if input string is an int" try: int( s ) return True except ValueError: return False def checkFloat( s ): "Check if input string is a float" try: float( s ) return True except ValueError: return False def makeNumeric( s ): "Convert string to int or float if numeric." if checkInt( s ): return int( s ) elif checkFloat( s ): return float( s ) else: return s # Popen support def pmonitor(popens, timeoutms=500, readline=True, readmax=1024 ): """Monitor dict of hosts to popen objects a line at a time timeoutms: timeout for poll() readline: return single line of output yields: host, line/output (if any) terminates: when all EOFs received""" poller = poll() fdToHost = {} for host, popen in popens.iteritems(): fd = popen.stdout.fileno() fdToHost[ fd ] = host poller.register( fd, POLLIN ) if not readline: # Use non-blocking reads flags = fcntl( fd, F_GETFL ) fcntl( fd, F_SETFL, flags | O_NONBLOCK ) while popens: fds = poller.poll( timeoutms ) if fds: for fd, event in fds: host = fdToHost[ fd ] popen = popens[ host ] if event & POLLIN: if readline: # Attempt to read a line of output # This blocks until we receive a newline! line = popen.stdout.readline() else: line = popen.stdout.read( readmax ) yield host, line # Check for EOF elif event & POLLHUP: poller.unregister( fd ) del popens[ host ] 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 type( limit ) is 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 ) except: warn( "*** Error setting resource limits. " "Mininet's performance may be affected.\n" ) 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 ): "Convert text segment to int if necessary" return int( s ) if s.isdigit() else s return [ num( s ) for s in re.split( r'(\d+)', text ) ] 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.) irange(1,5) -> 1, 2, 3, 4, 5""" return range( start, end + 1 ) def custom( cls, **params ): "Returns customized constructor for class cls." # Note: we may wish to see if we can use functools.partial() here # and in customConstructor def customized( *args, **kwargs): "Customized constructor" kwargs = kwargs.copy() kwargs.update( params ) return cls( *args, **kwargs ) customized.__name__ = 'custom(%s,%s)' % ( cls, params ) return customized def splitArgs( argstr ): """Split argument string into usable python arguments argstr: argument string with format fn,arg2,kw1=arg3... returns: fn, args, kwargs""" split = argstr.split( ',' ) fn = split[ 0 ] params = split[ 1: ] # Convert int and float args; removes the need for function # to be flexible with input arg formats. args = [ makeNumeric( s ) for s in params if '=' not in s ] kwargs = {} for s in [ p for p in params if '=' in p ]: key, val = s.split( '=', 1 ) kwargs[ key ] = makeNumeric( val ) return fn, args, kwargs def customConstructor( constructors, argStr ): """Return custom constructor based on argStr The args and key/val pairs in argsStr will be automatically applied when the generated constructor is later used. """ cname, newargs, kwargs = splitArgs( argStr ) constructor = constructors.get( cname, None ) if not constructor: raise Exception( "error: %s is unknown - please specify one of %s" % ( cname, constructors.keys() ) ) def customized( name, *args, **params ): "Customized constructor, useful for Node, Link, and other classes" params = params.copy() params.update( kwargs ) if not newargs: return constructor( name, *args, **params ) if args: warn( 'warning: %s replacing %s with %s\n' % ( constructor, args, newargs ) ) return constructor( name, *newargs, **params ) customized.__name__ = 'customConstructor(%s)' % argStr return customized def buildTopo( topos, topoStr ): """Create topology from string with format (object, arg1, arg2,...). input topos is a dict of topo names to constructors, possibly w/args. """ topo, args, kwargs = splitArgs( topoStr ) if topo not in topos: raise Exception( 'Invalid topo name %s' % topo ) return topos[ topo ]( *args, **kwargs ) def ensureRoot(): """Ensure that we are running as root. Probably we should only sudo when needed as per Big Switch's patch. """ if os.getuid() != 0: print "*** Mininet must run as root." exit( 1 ) return mininet-2.1.0/mnexec.c000066400000000000000000000111471221655600400146050ustar00rootroot00000000000000/* mnexec: execution utility for mininet * * Starts up programs and does things that are slow or * difficult in Python, including: * * - closing all file descriptors except stdin/out/error * - detaching from a controlling tty using setsid * - running in a network namespace * - printing out the pid of a process so we can identify it later * - attaching to a namespace and cgroup * - setting RT scheduling * * Partially based on public domain setsid(1) */ #include #include #include #include #include #include #include #include #include #if !defined(VERSION) #define VERSION "(devel)" #endif void usage(char *name) { printf("Execution utility for Mininet\n\n" "Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n" "Options:\n" " -c: close all file descriptors except stdin/out/error\n" " -d: detach from tty by calling setsid()\n" " -n: run in new network namespace\n" " -p: print ^A + pid\n" " -a pid: attach to pid's network namespace\n" " -g group: add to cgroup\n" " -r rtprio: run with SCHED_RR (usually requires -g)\n" " -v: print version\n", name); } int setns(int fd, int nstype) { return syscall(__NR_setns, fd, nstype); } /* Validate alphanumeric path foo1/bar2/baz */ void validate(char *path) { char *s; for (s=path; *s; s++) { if (!isalnum(*s) && *s != '/') { fprintf(stderr, "invalid path: %s\n", path); exit(1); } } } /* Add our pid to cgroup */ int cgroup(char *gname) { static char path[PATH_MAX]; static char *groups[] = { "cpu", "cpuacct", "cpuset", NULL }; char **gptr; pid_t pid = getpid(); int count = 0; validate(gname); for (gptr = groups; *gptr; gptr++) { FILE *f; snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks", *gptr, gname); f = fopen(path, "w"); if (f) { count++; fprintf(f, "%d\n", pid); fclose(f); } } if (!count) { fprintf(stderr, "cgroup: could not add to cgroup %s\n", gname); exit(1); } } int main(int argc, char *argv[]) { int c; int fd; char path[PATH_MAX]; int nsid; int pid; static struct sched_param sp; while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1) switch(c) { case 'c': /* close file descriptors except stdin/out/error */ for (fd = getdtablesize(); fd > 2; fd--) close(fd); break; case 'd': /* detach from tty */ if (getpgrp() == getpid()) { switch(fork()) { case -1: perror("fork"); return 1; case 0: /* child */ break; default: /* parent */ return 0; } } setsid(); break; case 'n': /* run in network namespace */ if (unshare(CLONE_NEWNET) == -1) { perror("unshare"); return 1; } break; case 'p': /* print pid */ printf("\001%d\n", getpid()); fflush(stdout); break; case 'a': /* Attach to pid's network namespace */ pid = atoi(optarg); sprintf(path, "/proc/%d/ns/net", pid ); nsid = open(path, O_RDONLY); if (nsid < 0) { perror(path); return 1; } if (setns(nsid, 0) != 0) { perror("setns"); return 1; } break; case 'g': /* Attach to cgroup */ cgroup(optarg); break; case 'r': /* Set RT scheduling priority */ sp.sched_priority = atoi(optarg); if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) { perror("sched_setscheduler"); return 1; } break; case 'v': printf("%s\n", VERSION); exit(0); case 'h': usage(argv[0]); exit(0); default: usage(argv[0]); exit(1); } if (optind < argc) { execvp(argv[optind], &argv[optind]); perror(argv[optind]); return 1; } usage(argv[0]); } mininet-2.1.0/setup.py000066400000000000000000000023011221655600400146640ustar00rootroot00000000000000#!/usr/bin/env python "Setuptools params" from setuptools import setup, find_packages from os.path import join # Get version number from source tree import sys sys.path.append( '.' ) from mininet.net import VERSION scripts = [ join( 'bin', filename ) for filename in [ 'mn' ] ] modname = distname = 'mininet' setup( name=distname, version=VERSION, description='Process-based OpenFlow emulator', author='Bob Lantz', author_email='rlantz@cs.stanford.edu', packages=[ 'mininet', 'mininet.examples' ], long_description=""" Mininet is a network emulator which uses lightweight virtualization to create virtual networks for rapid prototyping of Software-Defined Network (SDN) designs using OpenFlow. http://mininet.org """, classifiers=[ "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: System :: Emulators", ], keywords='networking emulator protocol Internet OpenFlow SDN', license='BSD', install_requires=[ 'setuptools' ], scripts=scripts, ) mininet-2.1.0/util/000077500000000000000000000000001221655600400141335ustar00rootroot00000000000000mininet-2.1.0/util/build-ovs-packages.sh000077500000000000000000000055641221655600400201640ustar00rootroot00000000000000#!/bin/bash # Attempt to build debian packages for OVS set -e # exit on error set -u # exit on undefined variable kvers=`uname -r` ksrc=/lib/modules/$kvers/build dist=`lsb_release -is | tr [A-Z] [a-z]` release=`lsb_release -rs` arch=`uname -m` buildsuffix=-2 if [ "$arch" = "i686" ]; then arch=i386; fi if [ "$arch" = "x86_64" ]; then arch=amd64; fi overs=1.4.0 ovs=openvswitch-$overs ovstgz=$ovs.tar.gz ovsurl=http://openvswitch.org/releases/$ovstgz install='sudo apt-get install -y' echo "*** Installing debian/ubuntu build system" $install build-essential devscripts ubuntu-dev-tools debhelper dh-make $install diff patch cdbs quilt gnupg fakeroot lintian pbuilder piuparts $install module-assistant echo "*** Installing OVS dependencies" $install pkg-config gcc make python-dev libssl-dev libtool $install dkms ipsec-tools echo "*** Installing headers for $kvers" $install linux-headers-$kvers echo "*** Retrieving OVS source" wget -c $ovsurl tar xzf $ovstgz cd $ovs echo "*** Patching OVS source" # Not sure why this fails, but off it goes! sed -i -e 's/dh_strip/# dh_strip/' debian/rules if [ "$release" = "10.04" ]; then # Lucid doesn't seem to have all the packages for ovsdbmonitor echo "*** Patching debian/rules to remove dh_python2" sed -i -e 's/dh_python2/dh_pysupport/' debian/rules echo "*** Not building ovsdbmonitor since it's too hard on 10.04" mv debian/ovsdbmonitor.install debian/ovsdbmonitor.install.backup sed -i -e 's/ovsdbmonitor.install/ovsdbmonitor.install.backup/' Makefile.in else # Install a bag of hurt for ovsdbmonitor $install python-pyside.qtcore pyqt4-dev-tools python-twisted python-twisted-bin \ python-twisted-core python-twisted-conch python-anyjson python-zope.interface fi # init script was written to assume that commands complete sed -i -e 's/^set -e/#set -e/' debian/openvswitch-controller.init echo "*** Building OVS user packages" opts=--with-linux=/lib/modules/`uname -r`/build fakeroot make -f debian/rules DATAPATH_CONFIGURE_OPTS=$opts binary echo "*** Building OVS datapath kernel module package" # Still looking for the "right" way to do this... sudo mkdir -p /usr/src/linux ln -sf _debian/openvswitch.tar.gz . sudo make -f debian/rules.modules KSRC=$ksrc KVERS=$kvers binary-modules echo "*** Built the following packages:" cd ~ ls -l *deb archive=ovs-$overs-core-$dist-$release-$arch$buildsuffix.tar ovsbase='common pki switch brcompat controller datapath-dkms' echo "*** Packing up $ovsbase .debs into:" echo " $archive" pkgs="" for component in $ovsbase; do if echo $component | egrep 'dkms|pki'; then # Architecture-independent packages deb=(openvswitch-${component}_$overs*all.deb) else deb=(openvswitch-${component}_$overs*$arch.deb) fi pkgs="$pkgs $deb" done rm -rf $archive tar cf $archive $pkgs echo "*** Contents of archive $archive:" tar tf $archive echo "*** Done (hopefully)" mininet-2.1.0/util/colorfilters000066400000000000000000000026211221655600400165660ustar00rootroot00000000000000# DO NOT EDIT THIS FILE! It was created by Wireshark @Bad TCP@tcp.analysis.flags@[0,0,0][65535,24383,24383] @HSRP State Change@hsrp.state != 8 && hsrp.state != 16@[0,0,0][65535,63222,0] @Spanning Tree Topology Change@stp.type == 0x80@[0,0,0][65535,63222,0] @OSPF State Change@ospf.msg != 1@[0,0,0][65535,63222,0] @ICMP errors@icmp.type eq 3 || icmp.type eq 4 || icmp.type eq 5 || icmp.type eq 11@[0,0,0][0,65535,3616] @ARP@arp@[55011,59486,65534][0,0,0] @ICMP@icmp@[49680,49737,65535][0,0,0] @TCP RST@tcp.flags.reset eq 1@[37008,0,0][65535,63121,32911] @TTL low or unexpected@( ! ip.dst == 224.0.0.0/4 && ip.ttl < 5) || (ip.dst == 224.0.0.0/24 && ip.ttl != 1)@[37008,0,0][65535,65535,65535] @of@of@[0,5,65535][65535,65535,65535] @Checksum Errors@cdp.checksum_bad==1 || edp.checksum_bad==1 || ip.checksum_bad==1 || tcp.checksum_bad==1 || udp.checksum_bad==1@[0,0,0][65535,24383,24383] @SMB@smb || nbss || nbns || nbipx || ipxsap || netbios@[65534,64008,39339][0,0,0] @HTTP@http || tcp.port == 80@[36107,65535,32590][0,0,0] @IPX@ipx || spx@[65534,58325,58808][0,0,0] @DCERPC@dcerpc@[51199,38706,65533][0,0,0] @Routing@hsrp || eigrp || ospf || bgp || cdp || vrrp || gvrp || igmp || ismp@[65534,62325,54808][0,0,0] @TCP SYN/FIN@tcp.flags & 0x02 || tcp.flags.fin == 1@[41026,41026,41026][0,0,0] @TCP@tcp@[59345,58980,65534][0,0,0] @UDP@udp@[28834,57427,65533][0,0,0] @Broadcast@eth[0] & 1@[65535,65535,65535][32768,32768,32768] mininet-2.1.0/util/doxify.py000077500000000000000000000037401221655600400160160ustar00rootroot00000000000000#!/usr/bin/python """ Convert simple documentation to epydoc/pydoctor-compatible markup """ from sys import stdin, stdout, argv import os from tempfile import mkstemp from subprocess import call import re spaces = re.compile( r'\s+' ) singleLineExp = re.compile( r'\s+"([^"]+)"' ) commentStartExp = re.compile( r'\s+"""' ) commentEndExp = re.compile( r'"""$' ) returnExp = re.compile( r'\s+(returns:.*)' ) lastindent = '' comment = False def fixParam( line ): "Change foo: bar to @foo bar" result = re.sub( r'(\w+):', r'@param \1', line ) result = re.sub( r' @', r'@', result) return result def fixReturns( line ): "Change returns: foo to @return foo" return re.sub( 'returns:', r'@returns', line ) def fixLine( line ): global comment match = spaces.match( line ) if not match: return line else: indent = match.group(0) if singleLineExp.match( line ): return re.sub( '"', '"""', line ) if commentStartExp.match( line ): comment = True if comment: line = fixReturns( line ) line = fixParam( line ) if commentEndExp.search( line ): comment = False return line def test(): "Test transformations" assert fixLine(' "foo"') == ' """foo"""' assert fixParam( 'foo: bar' ) == '@param foo bar' assert commentStartExp.match( ' """foo"""') def funTest(): testFun = ( 'def foo():\n' ' "Single line comment"\n' ' """This is a test"""\n' ' bar: int\n' ' baz: string\n' ' returns: junk"""\n' ' if True:\n' ' print "OK"\n' ).splitlines( True ) fixLines( testFun ) def fixLines( lines, fid ): for line in lines: os.write( fid, fixLine( line ) ) if __name__ == '__main__': if False: funTest() infile = open( argv[1] ) outfid, outname = mkstemp() fixLines( infile.readlines(), outfid ) infile.close() os.close( outfid ) call( [ 'doxypy', outname ] ) mininet-2.1.0/util/install.sh000077500000000000000000000600011221655600400161350ustar00rootroot00000000000000#!/usr/bin/env bash # Mininet install script for Ubuntu (and Debian Lenny) # Brandon Heller (brandonh@stanford.edu) # Fail on error set -e # Fail on unset var usage set -o nounset # Get directory containing mininet folder MININET_DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )" # Set up build directory, which by default is the working directory # unless the working directory is a subdirectory of mininet, # in which case we use the directory containing mininet BUILD_DIR="$(pwd -P)" case $BUILD_DIR in $MININET_DIR/*) BUILD_DIR=$MININET_DIR;; # currect directory is a subdirectory *) BUILD_DIR=$BUILD_DIR;; esac # Location of CONFIG_NET_NS-enabled kernel(s) KERNEL_LOC=http://www.openflow.org/downloads/mininet # Attempt to identify Linux release DIST=Unknown RELEASE=Unknown CODENAME=Unknown ARCH=`uname -m` if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi if [ "$ARCH" = "i686" ]; then ARCH="i386"; fi test -e /etc/debian_version && DIST="Debian" grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu" if [ "$DIST" = "Ubuntu" ] || [ "$DIST" = "Debian" ]; then install='sudo apt-get -y install' remove='sudo apt-get -y remove' pkginst='sudo dpkg -i' # Prereqs for this script if ! which lsb_release &> /dev/null; then $install lsb-release fi fi test -e /etc/fedora-release && DIST="Fedora" if [ "$DIST" = "Fedora" ]; then install='sudo yum -y install' remove='sudo yum -y erase' pkginst='sudo rpm -ivh' # Prereqs for this script if ! which lsb_release &> /dev/null; then $install redhat-lsb-core fi fi if which lsb_release &> /dev/null; then DIST=`lsb_release -is` RELEASE=`lsb_release -rs` CODENAME=`lsb_release -cs` fi echo "Detected Linux distribution: $DIST $RELEASE $CODENAME $ARCH" # Kernel params if [ "$DIST" = "Ubuntu" ]; then if [ "$RELEASE" = "10.04" ]; then KERNEL_NAME='3.0.0-15-generic' else KERNEL_NAME=`uname -r` fi KERNEL_HEADERS=linux-headers-${KERNEL_NAME} elif [ "$DIST" = "Debian" ] && [ "$ARCH" = "i386" ] && [ "$CODENAME" = "lenny" ]; then KERNEL_NAME=2.6.33.1-mininet KERNEL_HEADERS=linux-headers-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb KERNEL_IMAGE=linux-image-${KERNEL_NAME}_${KERNEL_NAME}-10.00.Custom_i386.deb elif [ "$DIST" = "Fedora" ]; then KERNEL_NAME=`uname -r` KERNEL_HEADERS=kernel-headers-${KERNEL_NAME} else echo "Install.sh currently only supports Ubuntu, Debian Lenny i386 and Fedora." exit 1 fi # More distribution info DIST_LC=`echo $DIST | tr [A-Z] [a-z]` # as lower case # Kernel Deb pkg to be removed: KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic DRIVERS_DIR=/lib/modules/${KERNEL_NAME}/kernel/drivers/net OVS_RELEASE=1.4.0 OVS_PACKAGE_LOC=https://github.com/downloads/mininet/mininet OVS_BUILDSUFFIX=-ignore # was -2 OVS_PACKAGE_NAME=ovs-$OVS_RELEASE-core-$DIST_LC-$RELEASE-$ARCH$OVS_BUILDSUFFIX.tar OVS_TAG=v$OVS_RELEASE IVS_TAG=v0.3 # Command-line versions overrides that simplify custom VM creation # To use, pass in the vars on the cmd line before install.sh, e.g. # WS_DISSECTOR_REV=pre-ws-1.10.0 install.sh -w WS_DISSECTOR_REV=${WS_DISSECTOR_REV:-""} OF13_SWITCH_REV=${OF13_SWITCH_REV:-""} function kernel { echo "Install Mininet-compatible kernel if necessary" sudo apt-get update if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then $install linux-image-$KERNEL_NAME elif [ "$DIST" = "Debian" ]; then # The easy approach: download pre-built linux-image and linux-headers packages: wget -c $KERNEL_LOC/$KERNEL_HEADERS wget -c $KERNEL_LOC/$KERNEL_IMAGE # Install custom linux headers and image: $pkginst $KERNEL_IMAGE $KERNEL_HEADERS # The next two steps are to work around a bug in newer versions of # kernel-package, which fails to add initrd images with the latest kernels. # See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=525032 # Generate initrd image if the .deb didn't install it: if ! test -e /boot/initrd.img-${KERNEL_NAME}; then sudo update-initramfs -c -k ${KERNEL_NAME} fi # Ensure /boot/grub/menu.lst boots with initrd image: sudo update-grub # The default should be the new kernel. Otherwise, you may need to modify # /boot/grub/menu.lst to set the default to the entry corresponding to the # kernel you just installed. fi } function kernel_clean { echo "Cleaning kernel..." # To save disk space, remove previous kernel if ! $remove $KERNEL_IMAGE_OLD; then echo $KERNEL_IMAGE_OLD not installed. fi # Also remove downloaded packages: rm -f $HOME/linux-headers-* $HOME/linux-image-* } # Install Mininet deps function mn_deps { echo "Installing Mininet dependencies" if [ "$DIST" = "Fedora" ]; then $install gcc make socat psmisc xterm openssh-clients iperf \ iproute telnet python-setuptools libcgroup-tools \ ethtool help2man pyflakes pylint python-pep8 else $install gcc make socat psmisc xterm ssh iperf iproute telnet \ python-setuptools cgroup-bin ethtool help2man \ pyflakes pylint pep8 fi echo "Installing Mininet core" pushd $MININET_DIR/mininet sudo make install popd } # Install Mininet developer dependencies function mn_dev { echo "Installing Mininet developer dependencies" $install doxygen doxypy texlive-fonts-recommended } # The following will cause a full OF install, covering: # -user switch # The instructions below are an abbreviated version from # http://www.openflowswitch.org/wk/index.php/Debian_Install # ... modified to use Debian Lenny rather than unstable. function of { echo "Installing OpenFlow reference implementation..." cd $BUILD_DIR/ $install autoconf automake libtool make gcc if [ "$DIST" = "Fedora" ]; then $install git pkgconfig glibc-devel else $install git-core autotools-dev pkg-config libc6-dev fi git clone git://openflowswitch.org/openflow.git cd $BUILD_DIR/openflow # Patch controller to handle more than 16 switches patch -p1 < $MININET_DIR/mininet/util/openflow-patches/controller.patch # Resume the install: ./boot.sh ./configure make sudo make install cd $BUILD_DIR } function of13 { echo "Installing OpenFlow 1.3 soft switch implementation..." cd $BUILD_DIR/ $install git-core autoconf automake autotools-dev pkg-config \ make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \ unzip libpcre3-dev flex bison libboost-dev if [ ! -d "ofsoftswitch13" ]; then git clone https://github.com/CPqD/ofsoftswitch13.git if [[ -n "$OF13_SWITCH_REV" ]]; then cd ofsoftswitch13 git checkout ${OF13_SWITCH_REV} cd .. fi fi # Install netbee NBEESRC="nbeesrc-jan-10-2013" NBEEURL=${NBEEURL:-http://www.nbee.org/download/} wget -nc ${NBEEURL}${NBEESRC}.zip unzip ${NBEESRC}.zip cd ${NBEESRC}/src cmake . make cd $BUILD_DIR/ sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib sudo ldconfig sudo cp -R ${NBEESRC}/include/ /usr/ # Resume the install: cd $BUILD_DIR/ofsoftswitch13 ./boot.sh ./configure make sudo make install cd $BUILD_DIR } function wireshark_version_check { # Check Wireshark version WS=$(which wireshark) WS_VER_PATCH=(1 10) # targetting wireshark 1.10.0 WS_VER=($($WS --version | sed 's/[a-z ]*\([0-9]*\).\([0-9]*\).\([0-9]*\).*/\1 \2 \3/')) if [ "${WS_VER[0]}" -lt "${WS_VER_PATCH[0]}" ] || [[ "${WS_VER[0]}" -le "${WS_VER_PATCH[0]}" && "${WS_VER[1]}" -lt "${WS_VER_PATCH[1]}" ]] then # pre-1.10.0 wireshark echo "Setting revision: pre-ws-1.10.0" WS_DISSECTOR_REV="pre-ws-1.10.0" fi } function wireshark { echo "Installing Wireshark dissector..." if [ "$DIST" = "Fedora" ]; then # Just install Fedora's wireshark RPMS # Fedora's wirehark >= 1.10.2-2 includes an OF dissector # (it has been backported from the future Wireshark 1.12 code base) $install wireshark wireshark-gnome return fi $install wireshark tshark libgtk2.0-dev if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" != "10.04" ]; then # Install newer version $install scons mercurial libglib2.0-dev $install libwiretap-dev libwireshark-dev cd $BUILD_DIR hg clone https://bitbucket.org/barnstorm/of-dissector if [[ -z "$WS_DISSECTOR_REV" ]]; then wireshark_version_check fi cd of-dissector if [[ -n "$WS_DISSECTOR_REV" ]]; then hg checkout ${WS_DISSECTOR_REV} fi # Build dissector cd src export WIRESHARK=/usr/include/wireshark scons # libwireshark0/ on 11.04; libwireshark1/ on later WSDIR=`find /usr/lib -type d -name 'libwireshark*' | head -1` WSPLUGDIR=$WSDIR/plugins/ sudo cp openflow.so $WSPLUGDIR echo "Copied openflow plugin to $WSPLUGDIR" else # Install older version from reference source cd $BUILD_DIR/openflow/utilities/wireshark_dissectors/openflow make sudo make install fi # Copy coloring rules: OF is white-on-blue: mkdir -p $HOME/.wireshark cp $MININET_DIR/mininet/util/colorfilters $HOME/.wireshark } # Install Open vSwitch # Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files. function ovs { echo "Installing Open vSwitch..." if [ "$DIST" = "Fedora" ]; then # Just install Fedora's openvswitch RPMS $install openvswitch openvswitch-controller return fi OVS_SRC=$BUILD_DIR/openvswitch OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko}) # Required for module build/dkms install $install $KERNEL_HEADERS ovspresent=0 # First see if we have packages # XXX wget -c seems to fail from github/amazon s3 cd /tmp if wget $OVS_PACKAGE_LOC/$OVS_PACKAGE_NAME 2> /dev/null; then $install patch dkms fakeroot python-argparse tar xf $OVS_PACKAGE_NAME orig=`tar tf $OVS_PACKAGE_NAME` # Now install packages in reasonable dependency order order='dkms common pki openvswitch-switch brcompat controller' pkgs="" for p in $order; do pkg=`echo "$orig" | grep $p` # Annoyingly, things seem to be missing without this flag $pkginst --force-confmiss $pkg done ovspresent=1 fi # Otherwise try distribution's OVS packages if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 11.10` = 1 ]; then if ! dpkg --get-selections | grep openvswitch-datapath; then # If you've already installed a datapath, assume you # know what you're doing and don't need dkms datapath. # Otherwise, install it. $install openvswitch-datapath-dkms fi if $install openvswitch-switch openvswitch-controller; then echo "Ignoring error installing openvswitch-controller" fi ovspresent=1 fi # Switch can run on its own, but # Mininet should control the controller if [ -e /etc/init.d/openvswitch-controller ]; then if sudo service openvswitch-controller stop; then echo "Stopped running controller" fi sudo update-rc.d openvswitch-controller disable fi if [ $ovspresent = 1 ]; then echo "Done (hopefully) installing packages" cd $BUILD_DIR return fi # Otherwise attempt to install from source $install pkg-config gcc make python-dev libssl-dev libtool if [ "$DIST" = "Debian" ]; then if [ "$CODENAME" = "lenny" ]; then $install git-core # Install Autoconf 2.63+ backport from Debian Backports repo: # Instructions from http://backports.org/dokuwiki/doku.php?id=instructions sudo su -c "echo 'deb http://www.backports.org/debian lenny-backports main contrib non-free' >> /etc/apt/sources.list" sudo apt-get update sudo apt-get -y --force-yes install debian-backports-keyring sudo apt-get -y --force-yes -t lenny-backports install autoconf fi else $install git fi # Install OVS from release cd $BUILD_DIR/ git clone git://openvswitch.org/openvswitch $OVS_SRC cd $OVS_SRC git checkout $OVS_TAG ./boot.sh BUILDDIR=/lib/modules/${KERNEL_NAME}/build if [ ! -e $BUILDDIR ]; then echo "Creating build sdirectory $BUILDDIR" sudo mkdir -p $BUILDDIR fi opts="--with-linux=$BUILDDIR" mkdir -p $OVS_BUILD cd $OVS_BUILD ../configure $opts make sudo make install modprobe } function remove_ovs { pkgs=`dpkg --get-selections | grep openvswitch | awk '{ print $1;}'` echo "Removing existing Open vSwitch packages:" echo $pkgs if ! $remove $pkgs; then echo "Not all packages removed correctly" fi # For some reason this doesn't happen if scripts=`ls /etc/init.d/*openvswitch* 2>/dev/null`; then echo $scripts for s in $scripts; do s=$(basename $s) echo SCRIPT $s sudo service $s stop sudo rm -f /etc/init.d/$s sudo update-rc.d -f $s remove done fi echo "Done removing OVS" } function ivs { echo "Installing Indigo Virtual Switch..." IVS_SRC=$BUILD_DIR/ivs # Install dependencies $install git pkg-config gcc make libnl-3-dev libnl-route-3-dev libnl-genl-3-dev # Install IVS from source cd $BUILD_DIR git clone git://github.com/floodlight/ivs $IVS_SRC -b $IVS_TAG --recursive cd $IVS_SRC make sudo make install } # Install NOX with tutorial files function nox { echo "Installing NOX w/tutorial files..." # Install NOX deps: $install autoconf automake g++ libtool python python-twisted \ swig libssl-dev make if [ "$DIST" = "Debian" ]; then $install libboost1.35-dev elif [ "$DIST" = "Ubuntu" ]; then $install python-dev libboost-dev $install libboost-filesystem-dev $install libboost-test-dev fi # Install NOX optional deps: $install libsqlite3-dev python-simplejson # Fetch NOX destiny cd $BUILD_DIR/ git clone https://github.com/noxrepo/nox-classic.git noxcore cd noxcore if ! git checkout -b destiny remotes/origin/destiny ; then echo "Did not check out a new destiny branch - assuming current branch is destiny" fi # Apply patches git checkout -b tutorial-destiny git am $MININET_DIR/mininet/util/nox-patches/*tutorial-port-nox-destiny*.patch if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then git am $MININET_DIR/mininet/util/nox-patches/*nox-ubuntu12-hacks.patch fi # Build ./boot.sh mkdir build cd build ../configure make -j3 #make check # Add NOX_CORE_DIR env var: sed -i -e 's|# for examples$|&\nexport NOX_CORE_DIR=$BUILD_DIR/noxcore/build/src|' ~/.bashrc # To verify this install: #cd ~/noxcore/build/src #./nox_core -v -i ptcp: } # Install NOX Classic/Zaku for OpenFlow 1.3 function nox13 { echo "Installing NOX w/tutorial files..." # Install NOX deps: $install autoconf automake g++ libtool python python-twisted \ swig libssl-dev make if [ "$DIST" = "Debian" ]; then $install libboost1.35-dev elif [ "$DIST" = "Ubuntu" ]; then $install python-dev libboost-dev $install libboost-filesystem-dev $install libboost-test-dev fi # Fetch NOX destiny cd $BUILD_DIR/ git clone https://github.com/CPqD/nox13oflib.git cd nox13oflib # Build ./boot.sh mkdir build cd build ../configure make -j3 #make check # To verify this install: #cd ~/nox13oflib/build/src #./nox_core -v -i ptcp: } # "Install" POX function pox { echo "Installing POX into $BUILD_DIR/pox..." cd $BUILD_DIR git clone https://github.com/noxrepo/pox.git } # Install OFtest function oftest { echo "Installing oftest..." # Install deps: $install tcpdump python-scapy # Install oftest: cd $BUILD_DIR/ git clone git://github.com/floodlight/oftest } # Install cbench function cbench { echo "Installing cbench..." if [ "$DIST" = "Fedora" ]; then $install net-snmp-devel libpcap-devel libconfig-devel else $install libsnmp-dev libpcap-dev libconfig-dev fi cd $BUILD_DIR/ git clone git://openflow.org/oflops.git cd oflops sh boot.sh || true # possible error in autoreconf, so run twice sh boot.sh ./configure --with-openflow-src-dir=$BUILD_DIR/openflow make sudo make install || true # make install fails; force past this } function vm_other { echo "Doing other Mininet VM setup tasks..." # Remove avahi-daemon, which may cause unwanted discovery packets to be # sent during tests, near link status changes: echo "Removing avahi-daemon" $remove avahi-daemon # was: Disable IPv6. Add to /etc/modprobe.d/blacklist: #echo "Attempting to disable IPv6" #if [ "$DIST" = "Ubuntu" ]; then # BLACKLIST=/etc/modprobe.d/blacklist.conf #else # BLACKLIST=/etc/modprobe.d/blacklist #fi #sudo sh -c "echo 'blacklist net-pf-10\nblacklist ipv6' >> $BLACKLIST" # Disable IPv6 if ! grep 'disable IPv6' /etc/sysctl.conf; then echo 'Disabling IPv6' echo ' # Mininet: disable IPv6 net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null fi # Disabling IPv6 breaks X11 forwarding via ssh line='AddressFamily inet' file='/etc/ssh/sshd_config' echo "Adding $line to $file" if ! grep "$line" $file > /dev/null; then echo "$line" | sudo tee -a $file > /dev/null fi # Enable command auto completion using sudo; modify ~/.bashrc: sed -i -e 's|# for examples$|&\ncomplete -cf sudo|' ~/.bashrc # Install tcpdump, cmd-line packet dump tool. Also install gitk, # a graphical git history viewer. $install tcpdump gitk # Install common text editors $install vim nano emacs # Install NTP $install ntp # Set git to colorize everything. git config --global color.diff auto git config --global color.status auto git config --global color.branch auto # Reduce boot screen opt-out delay. Modify timeout in /boot/grub/menu.lst to 1: if [ "$DIST" = "Debian" ]; then sudo sed -i -e 's/^timeout.*$/timeout 1/' /boot/grub/menu.lst fi # Clean unneeded debs: rm -f ~/linux-headers-* ~/linux-image-* } # Script to copy built OVS kernel module to where modprobe will # find them automatically. Removes the need to keep an environment variable # for insmod usage, and works nicely with multiple kernel versions. # # The downside is that after each recompilation of OVS you'll need to # re-run this script. If you're using only one kernel version, then it may be # a good idea to use a symbolic link in place of the copy below. function modprobe { echo "Setting up modprobe for OVS kmod..." set +o nounset if [ -z "$OVS_KMODS" ]; then echo "OVS_KMODS not set. Aborting." else sudo cp $OVS_KMODS $DRIVERS_DIR sudo depmod -a ${KERNEL_NAME} fi set -o nounset } function all { if [ "$DIST" = "Fedora" ]; then printf "\nFedora 18+ support (still work in progress):\n" printf " * Fedora 18+ has kernel 3.10 RPMS in the updates repositories\n" printf " * Fedora 18+ has openvswitch 1.10 RPMS in the updates repositories\n" printf " * the install.sh script options [-bfnpvw] should work.\n" printf " * for a basic setup just try:\n" printf " install.sh -fnpv\n\n" exit 3 fi echo "Installing all packages except for -eix (doxypy, ivs, nox-classic)..." kernel mn_deps # Skip mn_dev (doxypy/texlive/fonts/etc.) because it's huge # mn_dev of wireshark ovs # We may add ivs once it's more mature # ivs # NOX-classic is deprecated, but you can install it manually if desired. # nox pox oftest cbench echo "Enjoy Mininet!" } # Restore disk space and remove sensitive files before shipping a VM. function vm_clean { echo "Cleaning VM..." sudo apt-get clean sudo apt-get autoremove sudo rm -rf /tmp/* sudo rm -rf openvswitch*.tar.gz # Remove sensistive files history -c # note this won't work if you have multiple bash sessions rm -f ~/.bash_history # need to clear in memory and remove on disk rm -f ~/.ssh/id_rsa* ~/.ssh/known_hosts sudo rm -f ~/.ssh/authorized_keys* # Remove Mininet files #sudo rm -f /lib/modules/python2.5/site-packages/mininet* #sudo rm -f /usr/bin/mnexec # Clear optional dev script for SSH keychain load on boot rm -f ~/.bash_profile # Clear git changes git config --global user.name "None" git config --global user.email "None" # Note: you can shrink the .vmdk in vmware using # vmware-vdiskmanager -k *.vmdk echo "Zeroing out disk blocks for efficient compaction..." time sudo dd if=/dev/zero of=/tmp/zero bs=1M sync ; sleep 1 ; sync ; sudo rm -f /tmp/zero } function usage { printf '\nUsage: %s [-abcdfhikmnprtvwx03]\n\n' $(basename $0) >&2 printf 'This install script attempts to install useful packages\n' >&2 printf 'for Mininet. It should (hopefully) work on Ubuntu 11.10+\n' >&2 printf 'If you run into trouble, try\n' >&2 printf 'installing one thing at a time, and looking at the \n' >&2 printf 'specific installation function in this script.\n\n' >&2 printf 'options:\n' >&2 printf -- ' -a: (default) install (A)ll packages - good luck!\n' >&2 printf -- ' -b: install controller (B)enchmark (oflops)\n' >&2 printf -- ' -c: (C)lean up after kernel install\n' >&2 printf -- ' -d: (D)elete some sensitive files from a VM image\n' >&2 printf -- ' -e: install Mininet d(E)veloper dependencies\n' >&2 printf -- ' -f: install Open(F)low\n' >&2 printf -- ' -h: print this (H)elp message\n' >&2 printf -- ' -i: install (I)ndigo Virtual Switch\n' >&2 printf -- ' -k: install new (K)ernel\n' >&2 printf -- ' -m: install Open vSwitch kernel (M)odule from source dir\n' >&2 printf -- ' -n: install Mini(N)et dependencies + core files\n' >&2 printf -- ' -p: install (P)OX OpenFlow Controller\n' >&2 printf -- ' -r: remove existing Open vSwitch packages\n' >&2 printf -- ' -s : place dependency (S)ource/build trees in \n' >&2 printf -- ' -t: complete o(T)her Mininet VM setup tasks\n' >&2 printf -- ' -v: install Open (V)switch\n' >&2 printf -- ' -w: install OpenFlow (W)ireshark dissector\n' >&2 printf -- ' -x: install NO(X) Classic OpenFlow controller\n' >&2 printf -- ' -0: (default) -0[fx] installs OpenFlow 1.0 versions\n' >&2 printf -- ' -3: -3[fx] installs OpenFlow 1.3 versions\n' >&2 exit 2 } OF_VERSION=1.0 if [ $# -eq 0 ] then all else while getopts 'abcdefhikmnprs:tvwx03' OPTION do case $OPTION in a) all;; b) cbench;; c) kernel_clean;; d) vm_clean;; e) mn_dev;; f) case $OF_VERSION in 1.0) of;; 1.3) of13;; *) echo "Invalid OpenFlow version $OF_VERSION";; esac;; h) usage;; i) ivs;; k) kernel;; m) modprobe;; n) mn_deps;; p) pox;; r) remove_ovs;; s) mkdir -p $OPTARG; # ensure the directory is created BUILD_DIR="$( cd -P "$OPTARG" && pwd )"; # get the full path echo "Dependency installation directory: $BUILD_DIR";; t) vm_other;; v) ovs;; w) wireshark;; x) case $OF_VERSION in 1.0) nox;; 1.3) nox13;; *) echo "Invalid OpenFlow version $OF_VERSION";; esac;; 0) OF_VERSION=1.0;; 3) OF_VERSION=1.3;; ?) usage;; esac done shift $(($OPTIND - 1)) fi mininet-2.1.0/util/kbuild/000077500000000000000000000000001221655600400154055ustar00rootroot00000000000000mininet-2.1.0/util/kbuild/kbuild000077500000000000000000000036121221655600400166070ustar00rootroot00000000000000#!/bin/bash # Script to build new Debian kernel packages for 2.6.33.1 # # Caveats: # # Since kernel-package in debian-stable doesn't work with # 2.6.33.1, we attempt to patch it in place. This may not be the # right thing to do. A possibly better alternative is to install # a later version of kernel-package, although that could potentially # cause problems with upgrades, etc.. # # The patch to tun.c is a workaround rather than a real fix. # # Building a full Debian kernel package with all drivers takes a long # time, 60-80 minutes on my laptop. # # Re-running a make-kpkg may not work without running 'make-kpkg clean' # Season to taste # export PATH=/usr/lib/ccache:$PATH export CONCURRENCY_LEVEL=3 debversion=2.6.26-2-686-bigmem image=linux-image-$debversion echo "*** Installing $image" sudo aptitude install $image newversion=2.6.33.1 archive=linux-$newversion.tar.bz2 location=http://www.kernel.org/pub/linux/kernel/v2.6 echo "*** Fetching $location/$archive" wget -c $location/$archive tree=linux-$newversion if [ -e $tree ]; then echo "*** $tree already exists" else echo "*** Extracting $archive" tar xjf $archive fi echo "*** Patching tun driver" patch $tree/drivers/net/tun.c < tun.patch echo "*** Patching debian build script" sudo patch /usr/share/kernel-package/ruleset/misc/version_vars.mk < version_vars.patch config=/boot/config-$debversion echo "*** Copying $config to $tree/.config" cp $config $tree/.config echo "*** Updating config" cd $tree yes '' | make oldconfig 1> /dev/null sed 's/# CONFIG_NET_NS is not set/CONFIG_NET_NS=y/' .config > .config-new mv .config-new .config echo "*** Result: " `grep CONFIG_NET_NS .config` echo "*** Building kernel" time fakeroot make-kpkg --initrd --append-to-version=-mininet kernel_image kernel_headers cd .. echo "*** Done - package should be in current directory" ls *$newversion*.deb echo "To install:" echo "# dpkg -i " *$newversion*.deb mininet-2.1.0/util/kbuild/tun.patch000066400000000000000000000011001221655600400172240ustar00rootroot00000000000000--- linux-2.6.33.1/drivers/net/tun.c 2010-03-24 22:47:32.000000000 -0700 +++ tun-new.c 2010-03-24 22:45:00.000000000 -0700 @@ -1006,7 +1006,9 @@ if (err < 0) goto err_free_sk; - if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) || + /* BL hack: check for null parent kobj */ + if (!tun->dev->dev.kobj.sd || + device_create_file(&tun->dev->dev, &dev_attr_tun_flags) || device_create_file(&tun->dev->dev, &dev_attr_owner) || device_create_file(&tun->dev->dev, &dev_attr_group)) printk(KERN_ERR "Failed to create tun sysfs files\n"); mininet-2.1.0/util/kbuild/version_vars.patch000066400000000000000000000020171221655600400211460ustar00rootroot00000000000000--- /usr/share/kernel-package/ruleset/misc/version_vars.mk 2010-03-25 18:14:41.000000000 -0700 +++ version_vars.mk 2010-03-03 06:46:59.000000000 -0800 @@ -138,11 +138,13 @@ EXTRAV_ARG := endif -UTS_RELEASE_HEADER=$(call doit,if [ -f include/linux/utsrelease.h ]; then \ +UTS_RELEASE_HEADER=$(call doit, if [ -f include/generated/utsrelease.h ]; then \ + echo include/generated/utsrelease.h; \ + else if [ -f include/linux/utsrelease.h ]; then \ echo include/linux/utsrelease.h; \ else \ echo include/linux/version.h ; \ - fi) + fi fi) UTS_RELEASE_VERSION=$(call doit,if [ -f $(UTS_RELEASE_HEADER) ]; then \ grep 'define UTS_RELEASE' $(UTS_RELEASE_HEADER) | \ perl -nle 'm/^\s*\#define\s+UTS_RELEASE\s+("?)(\S+)\1/g && print $$2;';\ mininet-2.1.0/util/m000077500000000000000000000014371221655600400143220ustar00rootroot00000000000000#!/bin/bash # Attach to a Mininet host and run a command if [ -z $1 ]; then echo "usage: $0 host cmd [args...]" exit 1 else host=$1 fi pid=`ps ax | grep "mininet:$host$" | grep bash | awk '{print $1};'` if echo $pid | grep -q ' '; then echo "Error: found multiple mininet:$host processes" exit 2 fi if [ "$pid" == "" ]; then echo "Could not find Mininet host $host" exit 3 fi if [ -z $2 ]; then cmd='bash' else shift cmd=$* fi cgroup=/sys/fs/cgroup/cpu/$host if [ -d "$cgroup" ]; then cg="-g $host" fi # Check whether host should be running in a chroot dir rootdir="/var/run/mn/$host/root" if [ -d $rootdir -a -x $rootdir/bin/bash ]; then cmd="'cd `pwd`; exec $cmd'" cmd="chroot $rootdir /bin/bash -c $cmd" fi cmd="exec sudo mnexec -a $pid $cg $cmd" eval $cmd mininet-2.1.0/util/nox-patches/000077500000000000000000000000001221655600400163645ustar00rootroot00000000000000mininet-2.1.0/util/nox-patches/0001-OpenFlow-tutorial-port-nox-destiny.patch000066400000000000000000000231401221655600400266160ustar00rootroot00000000000000From 5c9610ffb88c89b0f36359ad3c7547831482a3ff Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Fri, 3 Feb 2012 14:48:58 -0800 Subject: [PATCH] OpenFlow tutorial port nox-destiny. --- src/nox/coreapps/examples/Makefile.am | 2 +- src/nox/coreapps/examples/tutorial/Makefile.am | 25 ++++ src/nox/coreapps/examples/tutorial/meta.json | 12 ++ src/nox/coreapps/examples/tutorial/pytutorial.py | 67 +++++++++++ src/nox/coreapps/examples/tutorial/tutorial.cc | 134 ++++++++++++++++++++++ 5 files changed, 239 insertions(+), 1 deletions(-) create mode 100644 src/nox/coreapps/examples/tutorial/Makefile.am create mode 100644 src/nox/coreapps/examples/tutorial/__init__.py create mode 100644 src/nox/coreapps/examples/tutorial/meta.json create mode 100644 src/nox/coreapps/examples/tutorial/pytutorial.py create mode 100644 src/nox/coreapps/examples/tutorial/tutorial.cc diff --git a/src/nox/coreapps/examples/Makefile.am b/src/nox/coreapps/examples/Makefile.am index 126f32e..1a0458c 100644 --- a/src/nox/coreapps/examples/Makefile.am +++ b/src/nox/coreapps/examples/Makefile.am @@ -1,6 +1,6 @@ include ../../../Make.vars -SUBDIRS = t +SUBDIRS = tutorial t EXTRA_DIST =\ meta.json\ diff --git a/src/nox/coreapps/examples/tutorial/Makefile.am b/src/nox/coreapps/examples/tutorial/Makefile.am new file mode 100644 index 0000000..51cf921 --- /dev/null +++ b/src/nox/coreapps/examples/tutorial/Makefile.am @@ -0,0 +1,25 @@ +include ../../../../Make.vars + +EXTRA_DIST =\ + meta.xml \ + __init__.py \ + pytutorial.py + +if PY_ENABLED +AM_CPPFLAGS += $(PYTHON_CPPFLAGS) +endif # PY_ENABLED + +pkglib_LTLIBRARIES = \ + tutorial.la + +tutorial_la_CPPFLAGS = $(AM_CPPFLAGS) -I $(top_srcdir)/src/nox -I $(top_srcdir)/src/nox/coreapps/ +tutorial_la_SOURCES = tutorial.cc +tutorial_la_LDFLAGS = -module -export-dynamic + +NOX_RUNTIMEFILES = meta.json \ + __init__.py \ + pytutorial.py + +all-local: nox-all-local +clean-local: nox-clean-local +install-exec-hook: nox-install-local diff --git a/src/nox/coreapps/examples/tutorial/__init__.py b/src/nox/coreapps/examples/tutorial/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nox/coreapps/examples/tutorial/meta.json b/src/nox/coreapps/examples/tutorial/meta.json new file mode 100644 index 0000000..7a9f227 --- /dev/null +++ b/src/nox/coreapps/examples/tutorial/meta.json @@ -0,0 +1,12 @@ +{ + "components": [ + { + "name": "tutorial", + "library": "tutorial" + }, + { + "name": "pytutorial", + "python": "nox.coreapps.examples.tutorial.pytutorial" + } + ] +} diff --git a/src/nox/coreapps/examples/tutorial/pytutorial.py b/src/nox/coreapps/examples/tutorial/pytutorial.py new file mode 100644 index 0000000..1e21c0b --- /dev/null +++ b/src/nox/coreapps/examples/tutorial/pytutorial.py @@ -0,0 +1,67 @@ +# Tutorial Controller +# Starts as a hub, and your job is to turn this into a learning switch. + +import logging + +from nox.lib.core import * +import nox.lib.openflow as openflow +from nox.lib.packet.ethernet import ethernet +from nox.lib.packet.packet_utils import mac_to_str, mac_to_int + +log = logging.getLogger('nox.coreapps.tutorial.pytutorial') + + +class pytutorial(Component): + + def __init__(self, ctxt): + Component.__init__(self, ctxt) + # Use this table to store MAC addresses in the format of your choice; + # Functions already imported, including mac_to_str, and mac_to_int, + # should prove useful for converting the byte array provided by NOX + # for packet MAC destination fields. + # This table is initialized to empty when your module starts up. + self.mac_to_port = {} # key: MAC addr; value: port + + def learn_and_forward(self, dpid, inport, packet, buf, bufid): + """Learn MAC src port mapping, then flood or send unicast.""" + + # Initial hub behavior: flood packet out everything but input port. + # Comment out the line below when starting the exercise. + self.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport) + + # Starter psuedocode for learning switch exercise below: you'll need to + # replace each pseudocode line with more specific Python code. + + # Learn the port for the source MAC + #self.mac_to_port = + #if (destination MAC of the packet is known): + # Send unicast packet to known output port + #self.send_openflow( ) + # Later, only after learning controller works: + # push down flow entry and remove the send_openflow command above. + #self.install_datapath_flow( ) + #else: + #flood packet out everything but the input port + #self.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport) + + def packet_in_callback(self, dpid, inport, reason, len, bufid, packet): + """Packet-in handler""" + if not packet.parsed: + log.debug('Ignoring incomplete packet') + else: + self.learn_and_forward(dpid, inport, packet, packet.arr, bufid) + + return CONTINUE + + def install(self): + self.register_for_packet_in(self.packet_in_callback) + + def getInterface(self): + return str(pytutorial) + +def getFactory(): + class Factory: + def instance(self, ctxt): + return pytutorial(ctxt) + + return Factory() diff --git a/src/nox/coreapps/examples/tutorial/tutorial.cc b/src/nox/coreapps/examples/tutorial/tutorial.cc new file mode 100644 index 0000000..e7240cc --- /dev/null +++ b/src/nox/coreapps/examples/tutorial/tutorial.cc @@ -0,0 +1,134 @@ +#include "component.hh" +#include "config.h" +#include "packet-in.hh" +#include "flow.hh" +#include "assert.hh" +#include "netinet++/ethernetaddr.hh" +#include "netinet++/ethernet.hh" +#include +#include +#ifdef LOG4CXX_ENABLED +#include +#include "log4cxx/logger.h" +#else +#include "vlog.hh" +#endif + +using namespace std; +using namespace vigil; +using namespace vigil::container; + +namespace +{ + static Vlog_module lg("tutorial"); + + /** Learning switch. + */ + class tutorial + : public Component + { + public: + /** Constructor. + */ + tutorial(const Context* c, const json_object* node) + : Component(c) + { } + + /** Configuration. + * Add handler for packet-in event. + */ + void configure(const Configuration*) + { + register_handler + (boost::bind(&tutorial::handle, this, _1)); + } + + /** Just simply install. + */ + void install() + { + lg.dbg(" Install called "); + } + + /** Function to setup flow. + */ + void setup_flow(Flow& flow, datapathid datapath_id , + uint32_t buffer_id, uint16_t out_port) + { + ofp_flow_mod* ofm; + size_t size = sizeof *ofm + sizeof(ofp_action_output); + boost::shared_array raw_of(new char[size]); + ofm = (ofp_flow_mod*) raw_of.get(); + + ofm->header.version = OFP_VERSION; + ofm->header.type = OFPT_FLOW_MOD; + ofm->header.length = htons(size); + ofm->match.wildcards = htonl(0); + ofm->match.in_port = htons(flow.in_port); + ofm->match.dl_vlan = flow.dl_vlan; + memcpy(ofm->match.dl_src, flow.dl_src.octet, sizeof ofm->match.dl_src); + memcpy(ofm->match.dl_dst, flow.dl_dst.octet, sizeof ofm->match.dl_dst); + ofm->match.dl_type = flow.dl_type; + ofm->match.nw_src = flow.nw_src; + ofm->match.nw_dst = flow.nw_dst; + ofm->match.nw_proto = flow.nw_proto; + ofm->match.tp_src = flow.tp_src; + ofm->match.tp_dst = flow.tp_dst; + ofm->command = htons(OFPFC_ADD); + ofm->buffer_id = htonl(buffer_id); + ofm->idle_timeout = htons(5); + ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); + ofm->priority = htons(OFP_DEFAULT_PRIORITY); + ofp_action_output& action = *((ofp_action_output*)ofm->actions); + memset(&action, 0, sizeof(ofp_action_output)); + action.type = htons(OFPAT_OUTPUT); + action.len = htons(sizeof(ofp_action_output)); + action.max_len = htons(0); + action.port = htons(out_port); + send_openflow_command(datapath_id, &ofm->header, true); + } + + /** Function to handle packets. + * @param datapath_id datapath id of switch + * @param in_port port packet is received + * @param buffer_id buffer id of packet + * @param source source mac address in host order + * @param destination destination mac address in host order + */ + void handle_packet(datapathid datapath_id, uint16_t in_port, uint32_t buffer_id, + uint64_t source, uint64_t destination) + { + send_openflow_packet(datapath_id, buffer_id, OFPP_FLOOD, + in_port, true); + } + + /** Packet-on handler. + */ + Disposition handle(const Event& e) + { + const Packet_in_event& pi = assert_cast(e); + uint32_t buffer_id = pi.buffer_id; + Flow flow(pi.in_port, *pi.get_buffer()); + + // drop LLDP packets + if (flow.dl_type == ethernet::LLDP) + return CONTINUE; + + // pass handle of unicast packet, else flood + if (!flow.dl_src.is_multicast()) + handle_packet(pi.datapath_id, pi.in_port, buffer_id, + flow.dl_src.hb_long(), flow.dl_dst.hb_long()); + else + send_openflow_packet(pi.datapath_id, buffer_id, OFPP_FLOOD, + pi.in_port, true); + + return CONTINUE; + } + + private: +}; + +REGISTER_COMPONENT(container::Simple_component_factory, + tutorial); + +} // unnamed namespace -- 1.7.5.4 mininet-2.1.0/util/nox-patches/0002-nox-ubuntu12-hacks.patch000066400000000000000000000150401221655600400233420ustar00rootroot00000000000000From 166693d7cb640d4a41251b87e92c52d9c688196b Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Mon, 14 May 2012 15:30:44 -0700 Subject: [PATCH] Hacks to get NOX classic/destiny to compile under Ubuntu 12.04 Thanks to Srinivasu R. Kanduru for the initial patch. Apologies for the hacks - it is my hope that this will be fixed upstream eventually. --- config/ac_pkg_swig.m4 | 7 ++++--- src/Make.vars | 2 +- src/nox/coreapps/pyrt/deferredcallback.cc | 2 +- src/nox/coreapps/pyrt/pyglue.cc | 2 +- src/nox/coreapps/pyrt/pyrt.cc | 2 +- src/nox/netapps/authenticator/auth.i | 2 ++ src/nox/netapps/authenticator/flow_util.i | 1 + src/nox/netapps/routing/routing.i | 2 ++ .../switch_management/pyswitch_management.i | 2 ++ src/nox/netapps/tests/tests.cc | 2 +- src/nox/netapps/topology/pytopology.i | 2 ++ 11 files changed, 18 insertions(+), 8 deletions(-) diff --git a/config/ac_pkg_swig.m4 b/config/ac_pkg_swig.m4 index d12556e..9b608f2 100644 --- a/config/ac_pkg_swig.m4 +++ b/config/ac_pkg_swig.m4 @@ -78,9 +78,10 @@ AC_DEFUN([AC_PROG_SWIG],[ if test -z "$available_patch" ; then [available_patch=0] fi - if test $available_major -ne $required_major \ - -o $available_minor -ne $required_minor \ - -o $available_patch -lt $required_patch ; then + major_done=`test $available_major -gt $required_major` + minor_done=`test $available_minor -gt $required_minor` + if test !$major_done -a !$minor_done \ + -a $available_patch -lt $required_patch ; then AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org]) SWIG='' else diff --git a/src/Make.vars b/src/Make.vars index d70d6aa..93b2879 100644 --- a/src/Make.vars +++ b/src/Make.vars @@ -53,7 +53,7 @@ AM_LDFLAGS += -export-dynamic endif # set python runtimefiles to be installed in the same directory as pkg -pkglib_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES) +pkgdata_SCRIPTS = $(NOX_RUNTIMEFILES) $(NOX_PYBUILDFILES) BUILT_SOURCES = $(NOX_PYBUILDFILES) # Runtime-files build and clean rules diff --git a/src/nox/coreapps/pyrt/deferredcallback.cc b/src/nox/coreapps/pyrt/deferredcallback.cc index 3a40fa7..111a586 100644 --- a/src/nox/coreapps/pyrt/deferredcallback.cc +++ b/src/nox/coreapps/pyrt/deferredcallback.cc @@ -69,7 +69,7 @@ DeferredCallback::get_instance(const Callback& c) DeferredCallback* cb = new DeferredCallback(c); // flag as used in *_wrap.cc....correct? - return SWIG_Python_NewPointerObj(cb, s, SWIG_POINTER_OWN | 0); + return SWIG_Python_NewPointerObj(m, cb, s, SWIG_POINTER_OWN | 0); } bool diff --git a/src/nox/coreapps/pyrt/pyglue.cc b/src/nox/coreapps/pyrt/pyglue.cc index 48b9716..317fd04 100644 --- a/src/nox/coreapps/pyrt/pyglue.cc +++ b/src/nox/coreapps/pyrt/pyglue.cc @@ -874,7 +874,7 @@ to_python(const Flow& flow) if (!s) { throw std::runtime_error("Could not find Flow SWIG type_info"); } - return SWIG_Python_NewPointerObj(f, s, SWIG_POINTER_OWN | 0); + return SWIG_Python_NewPointerObj(m, f, s, SWIG_POINTER_OWN | 0); // PyObject* dict = PyDict_New(); // if (!dict) { diff --git a/src/nox/coreapps/pyrt/pyrt.cc b/src/nox/coreapps/pyrt/pyrt.cc index fbda461..8ec05d6 100644 --- a/src/nox/coreapps/pyrt/pyrt.cc +++ b/src/nox/coreapps/pyrt/pyrt.cc @@ -776,7 +776,7 @@ Python_event_manager::create_python_context(const Context* ctxt, pretty_print_python_exception()); } - PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0); + PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0); Py_INCREF(pyctxt); // XXX needed? //Py_DECREF(m); diff --git a/src/nox/netapps/authenticator/auth.i b/src/nox/netapps/authenticator/auth.i index 1de1a17..bfa04e2 100644 --- a/src/nox/netapps/authenticator/auth.i +++ b/src/nox/netapps/authenticator/auth.i @@ -18,6 +18,8 @@ %module "nox.netapps.authenticator.pyauth" +// Hack to get it to compile -BL +%include "std_list.i" %{ #include "core_events.hh" #include "pyrt/pycontext.hh" diff --git a/src/nox/netapps/authenticator/flow_util.i b/src/nox/netapps/authenticator/flow_util.i index f67c3ef..2a314e2 100644 --- a/src/nox/netapps/authenticator/flow_util.i +++ b/src/nox/netapps/authenticator/flow_util.i @@ -32,6 +32,7 @@ using namespace vigil::applications; %} %include "common-defs.i" +%include "std_list.i" %import "netinet/netinet.i" %import "pyrt/event.i" diff --git a/src/nox/netapps/routing/routing.i b/src/nox/netapps/routing/routing.i index 44ccb3d..f9221a2 100644 --- a/src/nox/netapps/routing/routing.i +++ b/src/nox/netapps/routing/routing.i @@ -17,6 +17,8 @@ */ %module "nox.netapps.routing.pyrouting" +// Hack to get it to compile -BL +%include "std_list.i" %{ #include "pyrouting.hh" #include "routing.hh" diff --git a/src/nox/netapps/switch_management/pyswitch_management.i b/src/nox/netapps/switch_management/pyswitch_management.i index 72bfed4..ad2c90d 100644 --- a/src/nox/netapps/switch_management/pyswitch_management.i +++ b/src/nox/netapps/switch_management/pyswitch_management.i @@ -18,6 +18,8 @@ %module "nox.netapps.pyswitch_management" +// Hack to get it to compile -BL +%include "std_list.i" %{ #include "switch_management_proxy.hh" #include "pyrt/pycontext.hh" diff --git a/src/nox/netapps/tests/tests.cc b/src/nox/netapps/tests/tests.cc index 20e900d..f027028 100644 --- a/src/nox/netapps/tests/tests.cc +++ b/src/nox/netapps/tests/tests.cc @@ -306,7 +306,7 @@ private: throw runtime_error("Could not find PyContext SWIG type_info."); } - PyObject* pyctxt = SWIG_Python_NewPointerObj(p, s, 0); + PyObject* pyctxt = SWIG_Python_NewPointerObj(m, p, s, 0); assert(pyctxt); Py_DECREF(m); diff --git a/src/nox/netapps/topology/pytopology.i b/src/nox/netapps/topology/pytopology.i index 94a9f4b..7a8cd94 100644 --- a/src/nox/netapps/topology/pytopology.i +++ b/src/nox/netapps/topology/pytopology.i @@ -18,6 +18,8 @@ %module "nox.netapps.topology" +// Hack to get it to compile -BL +%include "std_list.i" %{ #include "pytopology.hh" #include "pyrt/pycontext.hh" -- 1.7.5.4 mininet-2.1.0/util/nox-patches/README000066400000000000000000000002161221655600400172430ustar00rootroot000000000000000001: This patch adds the OpenFlow tutorial module source code to nox-destiny. 0002: This patch hacks nox-destiny to compile on Ubuntu 12.04. mininet-2.1.0/util/openflow-patches/000077500000000000000000000000001221655600400174115ustar00rootroot00000000000000mininet-2.1.0/util/openflow-patches/README000066400000000000000000000003111221655600400202640ustar00rootroot00000000000000Patches for OpenFlow Reference Implementation controller.patch: patch controller to support up to 4096 switches (up from 16!) datapath.patch: patch to kernel datapath to compile with CONFIG_NET_NS=y mininet-2.1.0/util/openflow-patches/controller.patch000066400000000000000000000006001221655600400226110ustar00rootroot00000000000000diff --git a/controller/controller.c b/controller/controller.c index 41f2547..6eec590 100644 --- a/controller/controller.c +++ b/controller/controller.c @@ -58,8 +58,8 @@ #include "vlog.h" #define THIS_MODULE VLM_controller -#define MAX_SWITCHES 16 -#define MAX_LISTENERS 16 +#define MAX_SWITCHES 4096 +#define MAX_LISTENERS 4096 struct switch_ { struct lswitch *lswitch; mininet-2.1.0/util/openflow-patches/datapath.patch000066400000000000000000000014751221655600400222270ustar00rootroot00000000000000diff --git a/datapath/datapath.c b/datapath/datapath.c index 4a4d3a2..365aa25 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -47,6 +47,9 @@ #include "compat.h" +#ifdef CONFIG_NET_NS +#include +#endif /* Strings to describe the manufacturer, hardware, and software. This data * is queriable through the switch description stats message. */ @@ -259,6 +262,10 @@ send_openflow_skb(const struct datapath *dp, struct sk_buff *skb, const struct sender *sender) { return (sender - ? genlmsg_unicast(skb, sender->pid) +#ifdef CONFIG_NET_NS + ? genlmsg_unicast(&init_net, skb, sender->pid) +#else + ? genlmsg_unicast(skb, sender->pid) +#endif : genlmsg_multicast(skb, 0, dp_mc_group(dp), GFP_ATOMIC)); } mininet-2.1.0/util/sch_htb-ofbuf/000077500000000000000000000000001221655600400166445ustar00rootroot00000000000000mininet-2.1.0/util/sch_htb-ofbuf/Makefile000066400000000000000000000007431221655600400203100ustar00rootroot00000000000000obj-m = sch_htb.o KVERSION = $(shell uname -r) all: make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules install: test -e /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak || mv /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko.bak cp sch_htb.ko /lib/modules/$(KVERSION)/kernel/net/sched/sch_htb.ko rmmod sch_htb modprobe sch_htb clean: make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean mininet-2.1.0/util/sch_htb-ofbuf/README000066400000000000000000000003261221655600400175250ustar00rootroot00000000000000Modified sch_htb implementation with ofbuf support. To compile, just type make. To use this module instead of regular sch_htb, do: 0. make 1. rmmod sch_htb 2. insmod ./sch_htb.ko To revert, just rmmod sch_htb. mininet-2.1.0/util/sch_htb-ofbuf/sch_htb.c000066400000000000000000001256151221655600400204340ustar00rootroot00000000000000#define OFBUF (1) /* * net/sched/sch_htb.c Hierarchical token bucket, feed tree version * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Martin Devera, * * Credits (in time order) for older HTB versions: * Stef Coene * HTB support at LARTC mailing list * Ondrej Kraus, * found missing INIT_QDISC(htb) * Vladimir Smelhaus, Aamer Akhter, Bert Hubert * helped a lot to locate nasty class stall bug * Andi Kleen, Jamal Hadi, Bert Hubert * code review and helpful comments on shaping * Tomasz Wrona, * created test case so that I was able to fix nasty bug * Wilfried Weissmann * spotted bug in dequeue code and helped with fix * Jiri Fojtasek * fixed requeue routine * and many others. thanks. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* HTB algorithm. Author: devik@cdi.cz ======================================================================== HTB is like TBF with multiple classes. It is also similar to CBQ because it allows to assign priority to each class in hierarchy. In fact it is another implementation of Floyd's formal sharing. Levels: Each class is assigned level. Leaf has ALWAYS level 0 and root classes have level TC_HTB_MAXDEPTH-1. Interior nodes has level one less than their parent. */ static int htb_hysteresis __read_mostly = 0; /* whether to use mode hysteresis for speedup */ #define HTB_VER 0x30011 /* major must be matched with number suplied by TC as version */ #if HTB_VER >> 16 != TC_HTB_PROTOVER #error "Mismatched sch_htb.c and pkt_sch.h" #endif /* Module parameter and sysfs export */ module_param (htb_hysteresis, int, 0640); MODULE_PARM_DESC(htb_hysteresis, "Hysteresis mode, less CPU load, less accurate"); /* used internaly to keep status of single class */ enum htb_cmode { HTB_CANT_SEND, /* class can't send and can't borrow */ HTB_MAY_BORROW, /* class can't send but may borrow */ HTB_CAN_SEND /* class can send */ }; /* interior & leaf nodes; props specific to leaves are marked L: */ struct htb_class { struct Qdisc_class_common common; /* general class parameters */ struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; struct gnet_stats_rate_est rate_est; struct tc_htb_xstats xstats; /* our special stats */ int refcnt; /* usage count of this class */ /* topology */ int level; /* our level (see above) */ unsigned int children; struct htb_class *parent; /* parent class */ int prio; /* these two are used only by leaves... */ int quantum; /* but stored for parent-to-leaf return */ union { struct htb_class_leaf { struct Qdisc *q; int deficit[TC_HTB_MAXDEPTH]; struct list_head drop_list; } leaf; struct htb_class_inner { struct rb_root feed[TC_HTB_NUMPRIO]; /* feed trees */ struct rb_node *ptr[TC_HTB_NUMPRIO]; /* current class ptr */ /* When class changes from state 1->2 and disconnects from * parent's feed then we lost ptr value and start from the * first child again. Here we store classid of the * last valid ptr (used when ptr is NULL). */ u32 last_ptr_id[TC_HTB_NUMPRIO]; } inner; } un; struct rb_node node[TC_HTB_NUMPRIO]; /* node for self or feed tree */ struct rb_node pq_node; /* node for event queue */ psched_time_t pq_key; int prio_activity; /* for which prios are we active */ enum htb_cmode cmode; /* current mode of the class */ /* class attached filters */ struct tcf_proto *filter_list; int filter_cnt; /* token bucket parameters */ struct qdisc_rate_table *rate; /* rate table of the class itself */ struct qdisc_rate_table *ceil; /* ceiling rate (limits borrows too) */ long buffer, cbuffer; /* token bucket depth/rate */ psched_tdiff_t mbuffer; /* max wait time */ long tokens, ctokens; /* current number of tokens */ psched_time_t t_c; /* checkpoint time */ }; struct htb_sched { struct Qdisc_class_hash clhash; struct list_head drops[TC_HTB_NUMPRIO];/* active leaves (for drops) */ /* self list - roots of self generating tree */ struct rb_root row[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO]; int row_mask[TC_HTB_MAXDEPTH]; struct rb_node *ptr[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO]; u32 last_ptr_id[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO]; /* self wait list - roots of wait PQs per row */ struct rb_root wait_pq[TC_HTB_MAXDEPTH]; /* time of nearest event per level (row) */ psched_time_t near_ev_cache[TC_HTB_MAXDEPTH]; int defcls; /* class where unclassified flows go to */ /* filters for qdisc itself */ struct tcf_proto *filter_list; int rate2quantum; /* quant = rate / rate2quantum */ psched_time_t now; /* cached dequeue time */ struct qdisc_watchdog watchdog; /* non shaped skbs; let them go directly thru */ struct sk_buff_head direct_queue; int direct_qlen; /* max qlen of above */ long direct_pkts; #if OFBUF /* overflow buffer */ struct sk_buff_head ofbuf; int ofbuf_queued; /* # packets queued in above */ #endif #define HTB_WARN_TOOMANYEVENTS 0x1 unsigned int warned; /* only one warning */ struct work_struct work; }; /* find class in global hash table using given handle */ static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch) { struct htb_sched *q = qdisc_priv(sch); struct Qdisc_class_common *clc; clc = qdisc_class_find(&q->clhash, handle); if (clc == NULL) return NULL; return container_of(clc, struct htb_class, common); } /** * htb_classify - classify a packet into class * * It returns NULL if the packet should be dropped or -1 if the packet * should be passed directly thru. In all other cases leaf class is returned. * We allow direct class selection by classid in priority. The we examine * filters in qdisc and in inner nodes (if higher filter points to the inner * node). If we end up with classid MAJOR:0 we enqueue the skb into special * internal fifo (direct). These packets then go directly thru. If we still * have no valid leaf we try to use MAJOR:default leaf. It still unsuccessful * then finish and return direct queue. */ #define HTB_DIRECT ((struct htb_class *)-1L) static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl; struct tcf_result res; struct tcf_proto *tcf; int result; /* allow to select class by setting skb->priority to valid classid; * note that nfmark can be used too by attaching filter fw with no * rules in it */ if (skb->priority == sch->handle) return HTB_DIRECT; /* X:0 (direct flow) selected */ cl = htb_find(skb->priority, sch); if (cl && cl->level == 0) return cl; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; tcf = q->filter_list; while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_QUEUED: case TC_ACT_STOLEN: *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return NULL; } #endif cl = (void *)res.class; if (!cl) { if (res.classid == sch->handle) return HTB_DIRECT; /* X:0 (direct flow) */ cl = htb_find(res.classid, sch); if (!cl) break; /* filter selected invalid classid */ } if (!cl->level) return cl; /* we hit leaf; return it */ /* we have got inner class; apply inner filter chain */ tcf = cl->filter_list; } /* classification failed; try to use default class */ cl = htb_find(TC_H_MAKE(TC_H_MAJ(sch->handle), q->defcls), sch); if (!cl || cl->level) return HTB_DIRECT; /* bad default .. this is safe bet */ return cl; } /** * htb_add_to_id_tree - adds class to the round robin list * * Routine adds class to the list (actually tree) sorted by classid. * Make sure that class is not already on such list for given prio. */ static void htb_add_to_id_tree(struct rb_root *root, struct htb_class *cl, int prio) { struct rb_node **p = &root->rb_node, *parent = NULL; while (*p) { struct htb_class *c; parent = *p; c = rb_entry(parent, struct htb_class, node[prio]); if (cl->common.classid > c->common.classid) p = &parent->rb_right; else p = &parent->rb_left; } rb_link_node(&cl->node[prio], parent, p); rb_insert_color(&cl->node[prio], root); } /** * htb_add_to_wait_tree - adds class to the event queue with delay * * The class is added to priority event queue to indicate that class will * change its mode in cl->pq_key microseconds. Make sure that class is not * already in the queue. */ static void htb_add_to_wait_tree(struct htb_sched *q, struct htb_class *cl, long delay) { struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL; cl->pq_key = q->now + delay; if (cl->pq_key == q->now) cl->pq_key++; /* update the nearest event cache */ if (q->near_ev_cache[cl->level] > cl->pq_key) q->near_ev_cache[cl->level] = cl->pq_key; while (*p) { struct htb_class *c; parent = *p; c = rb_entry(parent, struct htb_class, pq_node); if (cl->pq_key >= c->pq_key) p = &parent->rb_right; else p = &parent->rb_left; } rb_link_node(&cl->pq_node, parent, p); rb_insert_color(&cl->pq_node, &q->wait_pq[cl->level]); } /** * htb_next_rb_node - finds next node in binary tree * * When we are past last key we return NULL. * Average complexity is 2 steps per call. */ static inline void htb_next_rb_node(struct rb_node **n) { *n = rb_next(*n); } /** * htb_add_class_to_row - add class to its row * * The class is added to row at priorities marked in mask. * It does nothing if mask == 0. */ static inline void htb_add_class_to_row(struct htb_sched *q, struct htb_class *cl, int mask) { q->row_mask[cl->level] |= mask; while (mask) { int prio = ffz(~mask); mask &= ~(1 << prio); htb_add_to_id_tree(q->row[cl->level] + prio, cl, prio); } } /* If this triggers, it is a bug in this code, but it need not be fatal */ static void htb_safe_rb_erase(struct rb_node *rb, struct rb_root *root) { if (RB_EMPTY_NODE(rb)) { WARN_ON(1); } else { rb_erase(rb, root); RB_CLEAR_NODE(rb); } } /** * htb_remove_class_from_row - removes class from its row * * The class is removed from row at priorities marked in mask. * It does nothing if mask == 0. */ static inline void htb_remove_class_from_row(struct htb_sched *q, struct htb_class *cl, int mask) { int m = 0; while (mask) { int prio = ffz(~mask); mask &= ~(1 << prio); if (q->ptr[cl->level][prio] == cl->node + prio) htb_next_rb_node(q->ptr[cl->level] + prio); htb_safe_rb_erase(cl->node + prio, q->row[cl->level] + prio); if (!q->row[cl->level][prio].rb_node) m |= 1 << prio; } q->row_mask[cl->level] &= ~m; } /** * htb_activate_prios - creates active classe's feed chain * * The class is connected to ancestors and/or appropriate rows * for priorities it is participating on. cl->cmode must be new * (activated) mode. It does nothing if cl->prio_activity == 0. */ static void htb_activate_prios(struct htb_sched *q, struct htb_class *cl) { struct htb_class *p = cl->parent; long m, mask = cl->prio_activity; while (cl->cmode == HTB_MAY_BORROW && p && mask) { m = mask; while (m) { int prio = ffz(~m); m &= ~(1 << prio); if (p->un.inner.feed[prio].rb_node) /* parent already has its feed in use so that * reset bit in mask as parent is already ok */ mask &= ~(1 << prio); htb_add_to_id_tree(p->un.inner.feed + prio, cl, prio); } p->prio_activity |= mask; cl = p; p = cl->parent; } if (cl->cmode == HTB_CAN_SEND && mask) htb_add_class_to_row(q, cl, mask); } /** * htb_deactivate_prios - remove class from feed chain * * cl->cmode must represent old mode (before deactivation). It does * nothing if cl->prio_activity == 0. Class is removed from all feed * chains and rows. */ static void htb_deactivate_prios(struct htb_sched *q, struct htb_class *cl) { struct htb_class *p = cl->parent; long m, mask = cl->prio_activity; while (cl->cmode == HTB_MAY_BORROW && p && mask) { m = mask; mask = 0; while (m) { int prio = ffz(~m); m &= ~(1 << prio); if (p->un.inner.ptr[prio] == cl->node + prio) { /* we are removing child which is pointed to from * parent feed - forget the pointer but remember * classid */ p->un.inner.last_ptr_id[prio] = cl->common.classid; p->un.inner.ptr[prio] = NULL; } htb_safe_rb_erase(cl->node + prio, p->un.inner.feed + prio); if (!p->un.inner.feed[prio].rb_node) mask |= 1 << prio; } p->prio_activity &= ~mask; cl = p; p = cl->parent; } if (cl->cmode == HTB_CAN_SEND && mask) htb_remove_class_from_row(q, cl, mask); } static inline long htb_lowater(const struct htb_class *cl) { if (htb_hysteresis) return cl->cmode != HTB_CANT_SEND ? -cl->cbuffer : 0; else return 0; } static inline long htb_hiwater(const struct htb_class *cl) { if (htb_hysteresis) return cl->cmode == HTB_CAN_SEND ? -cl->buffer : 0; else return 0; } /** * htb_class_mode - computes and returns current class mode * * It computes cl's mode at time cl->t_c+diff and returns it. If mode * is not HTB_CAN_SEND then cl->pq_key is updated to time difference * from now to time when cl will change its state. * Also it is worth to note that class mode doesn't change simply * at cl->{c,}tokens == 0 but there can rather be hysteresis of * 0 .. -cl->{c,}buffer range. It is meant to limit number of * mode transitions per time unit. The speed gain is about 1/6. */ static inline enum htb_cmode htb_class_mode(struct htb_class *cl, long *diff) { long toks; if ((toks = (cl->ctokens + *diff)) < htb_lowater(cl)) { *diff = -toks; return HTB_CANT_SEND; } if ((toks = (cl->tokens + *diff)) >= htb_hiwater(cl)) return HTB_CAN_SEND; *diff = -toks; return HTB_MAY_BORROW; } /** * htb_change_class_mode - changes classe's mode * * This should be the only way how to change classe's mode under normal * cirsumstances. Routine will update feed lists linkage, change mode * and add class to the wait event queue if appropriate. New mode should * be different from old one and cl->pq_key has to be valid if changing * to mode other than HTB_CAN_SEND (see htb_add_to_wait_tree). */ static void htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, long *diff) { enum htb_cmode new_mode = htb_class_mode(cl, diff); if (new_mode == cl->cmode) return; if (cl->prio_activity) { /* not necessary: speed optimization */ if (cl->cmode != HTB_CANT_SEND) htb_deactivate_prios(q, cl); cl->cmode = new_mode; if (new_mode != HTB_CANT_SEND) htb_activate_prios(q, cl); } else cl->cmode = new_mode; } /** * htb_activate - inserts leaf cl into appropriate active feeds * * Routine learns (new) priority of leaf and activates feed chain * for the prio. It can be called on already active leaf safely. * It also adds leaf into droplist. */ static inline void htb_activate(struct htb_sched *q, struct htb_class *cl) { WARN_ON(cl->level || !cl->un.leaf.q || !cl->un.leaf.q->q.qlen); if (!cl->prio_activity) { cl->prio_activity = 1 << cl->prio; htb_activate_prios(q, cl); list_add_tail(&cl->un.leaf.drop_list, q->drops + cl->prio); } } /** * htb_deactivate - remove leaf cl from active feeds * * Make sure that leaf is active. In the other words it can't be called * with non-active leaf. It also removes class from the drop list. */ static inline void htb_deactivate(struct htb_sched *q, struct htb_class *cl) { WARN_ON(!cl->prio_activity); htb_deactivate_prios(q, cl); cl->prio_activity = 0; list_del_init(&cl->un.leaf.drop_list); } static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch) { int uninitialized_var(ret); struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = htb_classify(skb, sch, &ret); #if OFBUF if(cl != HTB_DIRECT && cl) skb_get(skb); #endif if (cl == HTB_DIRECT) { /* enqueue to helper queue */ if (q->direct_queue.qlen < q->direct_qlen) { __skb_queue_tail(&q->direct_queue, skb); q->direct_pkts++; } else { kfree_skb(skb); sch->qstats.drops++; return NET_XMIT_DROP; } #ifdef CONFIG_NET_CLS_ACT } else if (!cl) { if (ret & __NET_XMIT_BYPASS) sch->qstats.drops++; kfree_skb(skb); return ret; #endif } else if ((ret = qdisc_enqueue(skb, cl->un.leaf.q)) != NET_XMIT_SUCCESS) { /* We shouldn't drop this, but enqueue it into ofbuf */ // TODO: is skb actually valid? // Ans: looks like qdisc_enqueue will end up freeing the packet // if enqueue failed. So we should incr refcnt before calling qdisc_enqueue... #if OFBUF __skb_queue_tail(&q->ofbuf, skb); q->ofbuf_queued++; #else if (net_xmit_drop_count(ret)) { sch->qstats.drops++; cl->qstats.drops++; } return ret; #endif } else { bstats_update(&cl->bstats, skb); htb_activate(q, cl); #if OFBUF kfree_skb(skb); #endif } sch->q.qlen++; return NET_XMIT_SUCCESS; } static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, long diff) { long toks = diff + cl->tokens; if (toks > cl->buffer) toks = cl->buffer; toks -= (long) qdisc_l2t(cl->rate, bytes); if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; cl->tokens = toks; } static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, long diff) { long toks = diff + cl->ctokens; if (toks > cl->cbuffer) toks = cl->cbuffer; toks -= (long) qdisc_l2t(cl->ceil, bytes); if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; cl->ctokens = toks; } /** * htb_charge_class - charges amount "bytes" to leaf and ancestors * * Routine assumes that packet "bytes" long was dequeued from leaf cl * borrowing from "level". It accounts bytes to ceil leaky bucket for * leaf and all ancestors and to rate bucket for ancestors at levels * "level" and higher. It also handles possible change of mode resulting * from the update. Note that mode can also increase here (MAY_BORROW to * CAN_SEND) because we can use more precise clock that event queue here. * In such case we remove class from event queue first. */ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl, int level, struct sk_buff *skb) { int bytes = qdisc_pkt_len(skb); enum htb_cmode old_mode; long diff; while (cl) { diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer); if (cl->level >= level) { if (cl->level == level) cl->xstats.lends++; htb_accnt_tokens(cl, bytes, diff); } else { cl->xstats.borrows++; cl->tokens += diff; /* we moved t_c; update tokens */ } htb_accnt_ctokens(cl, bytes, diff); cl->t_c = q->now; old_mode = cl->cmode; diff = 0; htb_change_class_mode(q, cl, &diff); if (old_mode != cl->cmode) { if (old_mode != HTB_CAN_SEND) htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level); if (cl->cmode != HTB_CAN_SEND) htb_add_to_wait_tree(q, cl, diff); } /* update basic stats except for leaves which are already updated */ if (cl->level) bstats_update(&cl->bstats, skb); cl = cl->parent; } } /** * htb_do_events - make mode changes to classes at the level * * Scans event queue for pending events and applies them. Returns time of * next pending event (0 for no event in pq, q->now for too many events). * Note: Applied are events whose have cl->pq_key <= q->now. */ static psched_time_t htb_do_events(struct htb_sched *q, int level, unsigned long start) { /* don't run for longer than 2 jiffies; 2 is used instead of * 1 to simplify things when jiffy is going to be incremented * too soon */ unsigned long stop_at = start + 2; while (time_before(jiffies, stop_at)) { struct htb_class *cl; long diff; struct rb_node *p = rb_first(&q->wait_pq[level]); if (!p) return 0; cl = rb_entry(p, struct htb_class, pq_node); if (cl->pq_key > q->now) return cl->pq_key; htb_safe_rb_erase(p, q->wait_pq + level); diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer); htb_change_class_mode(q, cl, &diff); if (cl->cmode != HTB_CAN_SEND) htb_add_to_wait_tree(q, cl, diff); } /* too much load - let's continue after a break for scheduling */ if (!(q->warned & HTB_WARN_TOOMANYEVENTS)) { pr_warning("htb: too many events!\n"); q->warned |= HTB_WARN_TOOMANYEVENTS; } return q->now; } /* Returns class->node+prio from id-tree where classe's id is >= id. NULL * is no such one exists. */ static struct rb_node *htb_id_find_next_upper(int prio, struct rb_node *n, u32 id) { struct rb_node *r = NULL; while (n) { struct htb_class *cl = rb_entry(n, struct htb_class, node[prio]); if (id > cl->common.classid) { n = n->rb_right; } else if (id < cl->common.classid) { r = n; n = n->rb_left; } else { return n; } } return r; } /** * htb_lookup_leaf - returns next leaf class in DRR order * * Find leaf where current feed pointers points to. */ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio, struct rb_node **pptr, u32 * pid) { int i; struct { struct rb_node *root; struct rb_node **pptr; u32 *pid; } stk[TC_HTB_MAXDEPTH], *sp = stk; BUG_ON(!tree->rb_node); sp->root = tree->rb_node; sp->pptr = pptr; sp->pid = pid; for (i = 0; i < 65535; i++) { if (!*sp->pptr && *sp->pid) { /* ptr was invalidated but id is valid - try to recover * the original or next ptr */ *sp->pptr = htb_id_find_next_upper(prio, sp->root, *sp->pid); } *sp->pid = 0; /* ptr is valid now so that remove this hint as it * can become out of date quickly */ if (!*sp->pptr) { /* we are at right end; rewind & go up */ *sp->pptr = sp->root; while ((*sp->pptr)->rb_left) *sp->pptr = (*sp->pptr)->rb_left; if (sp > stk) { sp--; if (!*sp->pptr) { WARN_ON(1); return NULL; } htb_next_rb_node(sp->pptr); } } else { struct htb_class *cl; cl = rb_entry(*sp->pptr, struct htb_class, node[prio]); if (!cl->level) return cl; (++sp)->root = cl->un.inner.feed[prio].rb_node; sp->pptr = cl->un.inner.ptr + prio; sp->pid = cl->un.inner.last_ptr_id + prio; } } WARN_ON(1); return NULL; } /* dequeues packet at given priority and level; call only if * you are sure that there is active class at prio/level */ static struct sk_buff *htb_dequeue_tree(struct htb_sched *q, int prio, int level) { struct sk_buff *skb = NULL; struct htb_class *cl, *start; /* look initial class up in the row */ start = cl = htb_lookup_leaf(q->row[level] + prio, prio, q->ptr[level] + prio, q->last_ptr_id[level] + prio); do { next: if (unlikely(!cl)) return NULL; /* class can be empty - it is unlikely but can be true if leaf * qdisc drops packets in enqueue routine or if someone used * graft operation on the leaf since last dequeue; * simply deactivate and skip such class */ if (unlikely(cl->un.leaf.q->q.qlen == 0)) { struct htb_class *next; htb_deactivate(q, cl); /* row/level might become empty */ if ((q->row_mask[level] & (1 << prio)) == 0) return NULL; next = htb_lookup_leaf(q->row[level] + prio, prio, q->ptr[level] + prio, q->last_ptr_id[level] + prio); if (cl == start) /* fix start if we just deleted it */ start = next; cl = next; goto next; } skb = cl->un.leaf.q->dequeue(cl->un.leaf.q); if (likely(skb != NULL)) break; qdisc_warn_nonwc("htb", cl->un.leaf.q); htb_next_rb_node((level ? cl->parent->un.inner.ptr : q-> ptr[0]) + prio); cl = htb_lookup_leaf(q->row[level] + prio, prio, q->ptr[level] + prio, q->last_ptr_id[level] + prio); } while (cl != start); if (likely(skb != NULL)) { cl->un.leaf.deficit[level] -= qdisc_pkt_len(skb); if (cl->un.leaf.deficit[level] < 0) { cl->un.leaf.deficit[level] += cl->quantum; htb_next_rb_node((level ? cl->parent->un.inner.ptr : q-> ptr[0]) + prio); } /* this used to be after charge_class but this constelation * gives us slightly better performance */ if (!cl->un.leaf.q->q.qlen) htb_deactivate(q, cl); htb_charge_class(q, cl, level, skb); } return skb; } static struct sk_buff *htb_dequeue(struct Qdisc *sch) { struct sk_buff *skb; struct htb_sched *q = qdisc_priv(sch); int level; psched_time_t next_event; unsigned long start_at; u32 r, i; struct sk_buff *pkt; /* try to dequeue direct packets as high prio (!) to minimize cpu work */ skb = __skb_dequeue(&q->direct_queue); if (skb != NULL) { ok: qdisc_bstats_update(sch, skb); qdisc_unthrottled(sch); sch->q.qlen--; #if OFBUF if(q->ofbuf_queued > 0) { i = 0; r = net_random() % q->ofbuf_queued; // enqueue the rth packet and drop the rest while((pkt = __skb_dequeue(&q->ofbuf)) != NULL) { if(i == r) { // the chosen one htb_enqueue(pkt, sch); } else { kfree_skb(pkt); } i++; } q->ofbuf_queued = 0; } #endif return skb; } if (!sch->q.qlen) goto fin; q->now = psched_get_time(); start_at = jiffies; next_event = q->now + 5 * PSCHED_TICKS_PER_SEC; for (level = 0; level < TC_HTB_MAXDEPTH; level++) { /* common case optimization - skip event handler quickly */ int m; psched_time_t event; if (q->now >= q->near_ev_cache[level]) { event = htb_do_events(q, level, start_at); if (!event) event = q->now + PSCHED_TICKS_PER_SEC; q->near_ev_cache[level] = event; } else event = q->near_ev_cache[level]; if (next_event > event) next_event = event; m = ~q->row_mask[level]; while (m != (int)(-1)) { int prio = ffz(m); m |= 1 << prio; skb = htb_dequeue_tree(q, prio, level); if (likely(skb != NULL)) goto ok; } } sch->qstats.overlimits++; if (likely(next_event > q->now)) qdisc_watchdog_schedule(&q->watchdog, next_event); else schedule_work(&q->work); fin: return skb; } /* try to drop from each class (by prio) until one succeed */ static unsigned int htb_drop(struct Qdisc *sch) { struct htb_sched *q = qdisc_priv(sch); int prio; for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) { struct list_head *p; list_for_each(p, q->drops + prio) { struct htb_class *cl = list_entry(p, struct htb_class, un.leaf.drop_list); unsigned int len; if (cl->un.leaf.q->ops->drop && (len = cl->un.leaf.q->ops->drop(cl->un.leaf.q))) { sch->q.qlen--; if (!cl->un.leaf.q->q.qlen) htb_deactivate(q, cl); return len; } } } return 0; } /* reset all classes */ /* always caled under BH & queue lock */ static void htb_reset(struct Qdisc *sch) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl; struct hlist_node *n; unsigned int i; for (i = 0; i < q->clhash.hashsize; i++) { hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) { if (cl->level) memset(&cl->un.inner, 0, sizeof(cl->un.inner)); else { if (cl->un.leaf.q) qdisc_reset(cl->un.leaf.q); INIT_LIST_HEAD(&cl->un.leaf.drop_list); } cl->prio_activity = 0; cl->cmode = HTB_CAN_SEND; } } qdisc_watchdog_cancel(&q->watchdog); __skb_queue_purge(&q->direct_queue); sch->q.qlen = 0; #if OFBUF __skb_queue_purge(&q->ofbuf); q->ofbuf_queued = 0; #endif memset(q->row, 0, sizeof(q->row)); memset(q->row_mask, 0, sizeof(q->row_mask)); memset(q->wait_pq, 0, sizeof(q->wait_pq)); memset(q->ptr, 0, sizeof(q->ptr)); for (i = 0; i < TC_HTB_NUMPRIO; i++) INIT_LIST_HEAD(q->drops + i); } static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = { [TCA_HTB_PARMS] = { .len = sizeof(struct tc_htb_opt) }, [TCA_HTB_INIT] = { .len = sizeof(struct tc_htb_glob) }, [TCA_HTB_CTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, [TCA_HTB_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, }; static void htb_work_func(struct work_struct *work) { struct htb_sched *q = container_of(work, struct htb_sched, work); struct Qdisc *sch = q->watchdog.qdisc; __netif_schedule(qdisc_root(sch)); } static int htb_init(struct Qdisc *sch, struct nlattr *opt) { struct htb_sched *q = qdisc_priv(sch); struct nlattr *tb[TCA_HTB_INIT + 1]; struct tc_htb_glob *gopt; int err; int i; if (!opt) return -EINVAL; err = nla_parse_nested(tb, TCA_HTB_INIT, opt, htb_policy); if (err < 0) return err; if (tb[TCA_HTB_INIT] == NULL) { pr_err("HTB: hey probably you have bad tc tool ?\n"); return -EINVAL; } gopt = nla_data(tb[TCA_HTB_INIT]); if (gopt->version != HTB_VER >> 16) { pr_err("HTB: need tc/htb version %d (minor is %d), you have %d\n", HTB_VER >> 16, HTB_VER & 0xffff, gopt->version); return -EINVAL; } err = qdisc_class_hash_init(&q->clhash); if (err < 0) return err; for (i = 0; i < TC_HTB_NUMPRIO; i++) INIT_LIST_HEAD(q->drops + i); qdisc_watchdog_init(&q->watchdog, sch); INIT_WORK(&q->work, htb_work_func); skb_queue_head_init(&q->direct_queue); #if OFBUF skb_queue_head_init(&q->ofbuf); q->ofbuf_queued = 0; #endif q->direct_qlen = qdisc_dev(sch)->tx_queue_len; if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */ q->direct_qlen = 2; if ((q->rate2quantum = gopt->rate2quantum) < 1) q->rate2quantum = 1; q->defcls = gopt->defcls; return 0; } static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) { spinlock_t *root_lock = qdisc_root_sleeping_lock(sch); struct htb_sched *q = qdisc_priv(sch); struct nlattr *nest; struct tc_htb_glob gopt; spin_lock_bh(root_lock); gopt.direct_pkts = q->direct_pkts; gopt.version = HTB_VER; gopt.rate2quantum = q->rate2quantum; gopt.defcls = q->defcls; gopt.debug = 0; nest = nla_nest_start(skb, TCA_OPTIONS); if (nest == NULL) goto nla_put_failure; NLA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt); nla_nest_end(skb, nest); spin_unlock_bh(root_lock); return skb->len; nla_put_failure: spin_unlock_bh(root_lock); nla_nest_cancel(skb, nest); return -1; } static int htb_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb, struct tcmsg *tcm) { struct htb_class *cl = (struct htb_class *)arg; spinlock_t *root_lock = qdisc_root_sleeping_lock(sch); struct nlattr *nest; struct tc_htb_opt opt; spin_lock_bh(root_lock); tcm->tcm_parent = cl->parent ? cl->parent->common.classid : TC_H_ROOT; tcm->tcm_handle = cl->common.classid; if (!cl->level && cl->un.leaf.q) tcm->tcm_info = cl->un.leaf.q->handle; nest = nla_nest_start(skb, TCA_OPTIONS); if (nest == NULL) goto nla_put_failure; memset(&opt, 0, sizeof(opt)); opt.rate = cl->rate->rate; opt.buffer = cl->buffer; opt.ceil = cl->ceil->rate; opt.cbuffer = cl->cbuffer; opt.quantum = cl->quantum; opt.prio = cl->prio; opt.level = cl->level; NLA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt); nla_nest_end(skb, nest); spin_unlock_bh(root_lock); return skb->len; nla_put_failure: spin_unlock_bh(root_lock); nla_nest_cancel(skb, nest); return -1; } static int htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d) { struct htb_class *cl = (struct htb_class *)arg; if (!cl->level && cl->un.leaf.q) cl->qstats.qlen = cl->un.leaf.q->q.qlen; cl->xstats.tokens = cl->tokens; cl->xstats.ctokens = cl->ctokens; if (gnet_stats_copy_basic(d, &cl->bstats) < 0 || gnet_stats_copy_rate_est(d, NULL, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, &cl->qstats) < 0) return -1; return gnet_stats_copy_app(d, &cl->xstats, sizeof(cl->xstats)); } static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct Qdisc **old) { struct htb_class *cl = (struct htb_class *)arg; if (cl->level) return -EINVAL; if (new == NULL && (new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, cl->common.classid)) == NULL) return -ENOBUFS; sch_tree_lock(sch); *old = cl->un.leaf.q; cl->un.leaf.q = new; if (*old != NULL) { qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); } sch_tree_unlock(sch); return 0; } static struct Qdisc *htb_leaf(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; return !cl->level ? cl->un.leaf.q : NULL; } static void htb_qlen_notify(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; if (cl->un.leaf.q->q.qlen == 0) htb_deactivate(qdisc_priv(sch), cl); } static unsigned long htb_get(struct Qdisc *sch, u32 classid) { struct htb_class *cl = htb_find(classid, sch); if (cl) cl->refcnt++; return (unsigned long)cl; } static inline int htb_parent_last_child(struct htb_class *cl) { if (!cl->parent) /* the root class */ return 0; if (cl->parent->children > 1) /* not the last child */ return 0; return 1; } static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl, struct Qdisc *new_q) { struct htb_class *parent = cl->parent; WARN_ON(cl->level || !cl->un.leaf.q || cl->prio_activity); if (parent->cmode != HTB_CAN_SEND) htb_safe_rb_erase(&parent->pq_node, q->wait_pq + parent->level); parent->level = 0; memset(&parent->un.inner, 0, sizeof(parent->un.inner)); INIT_LIST_HEAD(&parent->un.leaf.drop_list); parent->un.leaf.q = new_q ? new_q : &noop_qdisc; parent->tokens = parent->buffer; parent->ctokens = parent->cbuffer; parent->t_c = psched_get_time(); parent->cmode = HTB_CAN_SEND; } static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl) { if (!cl->level) { WARN_ON(!cl->un.leaf.q); qdisc_destroy(cl->un.leaf.q); } gen_kill_estimator(&cl->bstats, &cl->rate_est); qdisc_put_rtab(cl->rate); qdisc_put_rtab(cl->ceil); tcf_destroy_chain(&cl->filter_list); kfree(cl); } static void htb_destroy(struct Qdisc *sch) { struct htb_sched *q = qdisc_priv(sch); struct hlist_node *n, *next; struct htb_class *cl; unsigned int i; cancel_work_sync(&q->work); qdisc_watchdog_cancel(&q->watchdog); /* This line used to be after htb_destroy_class call below * and surprisingly it worked in 2.4. But it must precede it * because filter need its target class alive to be able to call * unbind_filter on it (without Oops). */ tcf_destroy_chain(&q->filter_list); for (i = 0; i < q->clhash.hashsize; i++) { hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) tcf_destroy_chain(&cl->filter_list); } for (i = 0; i < q->clhash.hashsize; i++) { hlist_for_each_entry_safe(cl, n, next, &q->clhash.hash[i], common.hnode) htb_destroy_class(sch, cl); } qdisc_class_hash_destroy(&q->clhash); __skb_queue_purge(&q->direct_queue); #if OFBUF __skb_queue_purge(&q->ofbuf); q->ofbuf_queued = 0; #endif } static int htb_delete(struct Qdisc *sch, unsigned long arg) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)arg; unsigned int qlen; struct Qdisc *new_q = NULL; int last_child = 0; // TODO: why don't allow to delete subtree ? references ? does // tc subsys quarantee us that in htb_destroy it holds no class // refs so that we can remove children safely there ? if (cl->children || cl->filter_cnt) return -EBUSY; if (!cl->level && htb_parent_last_child(cl)) { new_q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, cl->parent->common.classid); last_child = 1; } sch_tree_lock(sch); if (!cl->level) { qlen = cl->un.leaf.q->q.qlen; qdisc_reset(cl->un.leaf.q); qdisc_tree_decrease_qlen(cl->un.leaf.q, qlen); } /* delete from hash and active; remainder in destroy_class */ qdisc_class_hash_remove(&q->clhash, &cl->common); if (cl->parent) cl->parent->children--; if (cl->prio_activity) htb_deactivate(q, cl); if (cl->cmode != HTB_CAN_SEND) htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level); if (last_child) htb_parent_to_leaf(q, cl, new_q); BUG_ON(--cl->refcnt == 0); /* * This shouldn't happen: we "hold" one cops->get() when called * from tc_ctl_tclass; the destroy method is done from cops->put(). */ sch_tree_unlock(sch); return 0; } static void htb_put(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; if (--cl->refcnt == 0) htb_destroy_class(sch, cl); } static int htb_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **tca, unsigned long *arg) { int err = -EINVAL; struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)*arg, *parent; struct nlattr *opt = tca[TCA_OPTIONS]; struct qdisc_rate_table *rtab = NULL, *ctab = NULL; struct nlattr *tb[__TCA_HTB_MAX]; struct tc_htb_opt *hopt; /* extract all subattrs from opt attr */ if (!opt) goto failure; err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy); if (err < 0) goto failure; err = -EINVAL; if (tb[TCA_HTB_PARMS] == NULL) goto failure; parent = parentid == TC_H_ROOT ? NULL : htb_find(parentid, sch); hopt = nla_data(tb[TCA_HTB_PARMS]); rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]); ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]); if (!rtab || !ctab) goto failure; if (!cl) { /* new class */ struct Qdisc *new_q; int prio; struct { struct nlattr nla; struct gnet_estimator opt; } est = { .nla = { .nla_len = nla_attr_size(sizeof(est.opt)), .nla_type = TCA_RATE, }, .opt = { /* 4s interval, 16s averaging constant */ .interval = 2, .ewma_log = 2, }, }; /* check for valid classid */ if (!classid || TC_H_MAJ(classid ^ sch->handle) || htb_find(classid, sch)) goto failure; /* check maximal depth */ if (parent && parent->parent && parent->parent->level < 2) { pr_err("htb: tree is too deep\n"); goto failure; } err = -ENOBUFS; cl = kzalloc(sizeof(*cl), GFP_KERNEL); if (!cl) goto failure; err = gen_new_estimator(&cl->bstats, &cl->rate_est, qdisc_root_sleeping_lock(sch), tca[TCA_RATE] ? : &est.nla); if (err) { kfree(cl); goto failure; } cl->refcnt = 1; cl->children = 0; INIT_LIST_HEAD(&cl->un.leaf.drop_list); RB_CLEAR_NODE(&cl->pq_node); for (prio = 0; prio < TC_HTB_NUMPRIO; prio++) RB_CLEAR_NODE(&cl->node[prio]); /* create leaf qdisc early because it uses kmalloc(GFP_KERNEL) * so that can't be used inside of sch_tree_lock * -- thanks to Karlis Peisenieks */ new_q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid); sch_tree_lock(sch); if (parent && !parent->level) { unsigned int qlen = parent->un.leaf.q->q.qlen; /* turn parent into inner node */ qdisc_reset(parent->un.leaf.q); qdisc_tree_decrease_qlen(parent->un.leaf.q, qlen); qdisc_destroy(parent->un.leaf.q); if (parent->prio_activity) htb_deactivate(q, parent); /* remove from evt list because of level change */ if (parent->cmode != HTB_CAN_SEND) { htb_safe_rb_erase(&parent->pq_node, q->wait_pq); parent->cmode = HTB_CAN_SEND; } parent->level = (parent->parent ? parent->parent->level : TC_HTB_MAXDEPTH) - 1; memset(&parent->un.inner, 0, sizeof(parent->un.inner)); } /* leaf (we) needs elementary qdisc */ cl->un.leaf.q = new_q ? new_q : &noop_qdisc; cl->common.classid = classid; cl->parent = parent; /* set class to be in HTB_CAN_SEND state */ cl->tokens = hopt->buffer; cl->ctokens = hopt->cbuffer; cl->mbuffer = 60 * PSCHED_TICKS_PER_SEC; /* 1min */ cl->t_c = psched_get_time(); cl->cmode = HTB_CAN_SEND; /* attach to the hash list and parent's family */ qdisc_class_hash_insert(&q->clhash, &cl->common); if (parent) parent->children++; } else { if (tca[TCA_RATE]) { err = gen_replace_estimator(&cl->bstats, &cl->rate_est, qdisc_root_sleeping_lock(sch), tca[TCA_RATE]); if (err) return err; } sch_tree_lock(sch); } /* it used to be a nasty bug here, we have to check that node * is really leaf before changing cl->un.leaf ! */ if (!cl->level) { cl->quantum = rtab->rate.rate / q->rate2quantum; if (!hopt->quantum && cl->quantum < 1000) { pr_warning( "HTB: quantum of class %X is small. Consider r2q change.\n", cl->common.classid); cl->quantum = 1000; } if (!hopt->quantum && cl->quantum > 200000) { pr_warning( "HTB: quantum of class %X is big. Consider r2q change.\n", cl->common.classid); cl->quantum = 200000; } if (hopt->quantum) cl->quantum = hopt->quantum; if ((cl->prio = hopt->prio) >= TC_HTB_NUMPRIO) cl->prio = TC_HTB_NUMPRIO - 1; } cl->buffer = hopt->buffer; cl->cbuffer = hopt->cbuffer; if (cl->rate) qdisc_put_rtab(cl->rate); cl->rate = rtab; if (cl->ceil) qdisc_put_rtab(cl->ceil); cl->ceil = ctab; sch_tree_unlock(sch); qdisc_class_hash_grow(sch, &q->clhash); *arg = (unsigned long)cl; return 0; failure: if (rtab) qdisc_put_rtab(rtab); if (ctab) qdisc_put_rtab(ctab); return err; } static struct tcf_proto **htb_find_tcf(struct Qdisc *sch, unsigned long arg) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)arg; struct tcf_proto **fl = cl ? &cl->filter_list : &q->filter_list; return fl; } static unsigned long htb_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { struct htb_class *cl = htb_find(classid, sch); /*if (cl && !cl->level) return 0; * The line above used to be there to prevent attaching filters to * leaves. But at least tc_index filter uses this just to get class * for other reasons so that we have to allow for it. * ---- * 19.6.2002 As Werner explained it is ok - bind filter is just * another way to "lock" the class - unlike "get" this lock can * be broken by class during destroy IIUC. */ if (cl) cl->filter_cnt++; return (unsigned long)cl; } static void htb_unbind_filter(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; if (cl) cl->filter_cnt--; } static void htb_walk(struct Qdisc *sch, struct qdisc_walker *arg) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl; struct hlist_node *n; unsigned int i; if (arg->stop) return; for (i = 0; i < q->clhash.hashsize; i++) { hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) { if (arg->count < arg->skip) { arg->count++; continue; } if (arg->fn(sch, (unsigned long)cl, arg) < 0) { arg->stop = 1; return; } arg->count++; } } } static const struct Qdisc_class_ops htb_class_ops = { .graft = htb_graft, .leaf = htb_leaf, .qlen_notify = htb_qlen_notify, .get = htb_get, .put = htb_put, .change = htb_change_class, .delete = htb_delete, .walk = htb_walk, .tcf_chain = htb_find_tcf, .bind_tcf = htb_bind_filter, .unbind_tcf = htb_unbind_filter, .dump = htb_dump_class, .dump_stats = htb_dump_class_stats, }; static struct Qdisc_ops htb_qdisc_ops __read_mostly = { .cl_ops = &htb_class_ops, .id = "htb", .priv_size = sizeof(struct htb_sched), .enqueue = htb_enqueue, .dequeue = htb_dequeue, .peek = qdisc_peek_dequeued, .drop = htb_drop, .init = htb_init, .reset = htb_reset, .destroy = htb_destroy, .dump = htb_dump, .owner = THIS_MODULE, }; static int __init htb_module_init(void) { return register_qdisc(&htb_qdisc_ops); } static void __exit htb_module_exit(void) { unregister_qdisc(&htb_qdisc_ops); } module_init(htb_module_init) module_exit(htb_module_exit) MODULE_LICENSE("GPL"); mininet-2.1.0/util/sysctl_addon000066400000000000000000000007731221655600400165530ustar00rootroot00000000000000# Mininet: Increase open file limit fs.file-max = 100000 # Mininet: increase network buffer space net.core.wmem_max = 16777216 net.core.rmem_max = 16777216 net.ipv4.tcp_rmem = 10240 87380 16777216 net.ipv4.tcp_rmem = 10240 87380 16777216 net.core.netdev_max_backlog = 5000 # Mininet: increase arp cache size net.ipv4.neigh.default.gc_thresh1 = 4096 net.ipv4.neigh.default.gc_thresh2 = 8192 net.ipv4.neigh.default.gc_thresh3 = 16384 # Mininet: increase routing table size net.ipv4.route.max_size=32768 mininet-2.1.0/util/unpep8000077500000000000000000000154341221655600400153070ustar00rootroot00000000000000#!/usr/bin/python """ Translate from PEP8 Python style to Mininet (i.e. Arista-like) Python style usage: unpep8 < old.py > new.py - Reinstates CapWords for methods and instance variables - Gets rid of triple single quotes - Eliminates triple quotes on single lines - Inserts extra spaces to improve readability - Fixes Doxygen (or doxypy) ugliness Does the following translations: ClassName.method_name(foo = bar) -> ClassName.methodName( foo=bar ) Triple-single-quotes -> triple-double-quotes @param foo description -> foo: description @return description -> returns: description @author me -> author: me @todo(me) -> TODO(me) Bugs/Limitations: - Hack to restore strings is ugly - Multiline strings get mangled - Comments are mangled (which is arguably the "right thing" to do, except that, for example, the left hand sides of the above would get translated!) - Doesn't eliminate unnecessary backslashes - Has no opinion on tab size - complicated indented docstrings get flattened - We don't (yet) have a filter to generate Doxygen/Doxypy - Currently leaves indents on blank comment lines - May lead to namespace collisions (e.g. some_thing and someThing) Bob Lantz, rlantz@cs.stanford.edu 1/24/2010 """ import re, sys def fixUnderscoreTriplet( match ): "Translate a matched triplet of the form a_b to aB." triplet = match.group() return triplet[ :-2 ] + triplet[ -1 ].capitalize() def reinstateCapWords( text ): underscoreTriplet = re.compile( r'[A-Za-z0-9]_[A-Za-z0-9]' ) return underscoreTriplet.sub( fixUnderscoreTriplet, text ) def replaceTripleApostrophes( text ): "Replace triple apostrophes with triple quotes." return text.replace( "'''", '"""') def simplifyTripleQuotes( text ): "Fix single-line doc strings." r = re.compile( r'"""([^\"\n]+)"""' ) return r.sub( r'"\1"', text ) def insertExtraSpaces( text ): "Insert extra spaces inside of parentheses and brackets/curly braces." lparen = re.compile( r'\((?![\s\)])' ) text = lparen.sub( r'( ', text ) rparen = re.compile( r'([^\s\(])(?=\))' ) text = rparen.sub( r'\1 ', text) # brackets lbrack = re.compile( r'\[(?![\s\]])' ) text = lbrack.sub( r'[ ', text ) rbrack = re.compile( r'([^\s\[])(?=\])' ) text = rbrack.sub( r'\1 ', text) # curly braces lcurly = re.compile( r'\{(?![\s\}])' ) text = lcurly.sub( r'{ ', text ) rcurly = re.compile( r'([^\s\{])(?=\})' ) text = rcurly.sub( r'\1 ', text) return text def fixDoxygen( text ): """Translate @param foo to foo:, @return bar to returns: bar, and @author me to author: me""" param = re.compile( r'@param (\w+)' ) text = param.sub( r'\1:', text ) returns = re.compile( r'@return' ) text = returns.sub( r'returns:', text ) author = re.compile( r'@author' ) text = author.sub( r'author:', text) # @todo -> TODO text = text.replace( '@todo', 'TODO' ) return text def removeCommentFirstBlankLine( text ): "Remove annoying blank lines after first line in comments." line = re.compile( r'("""[^\n]*\n)\s*\n', re.MULTILINE ) return line.sub( r'\1', text ) def fixArgs( match, kwarg = re.compile( r'(\w+) = ' ) ): "Replace foo = bar with foo=bar." return kwarg.sub( r'\1=', match.group() ) def fixKeywords( text ): "Change keyword argumentsfrom foo = bar to foo=bar." args = re.compile( r'\(([^\)]+)\)', re.MULTILINE ) return args.sub( fixArgs, text ) # Unfortunately, Python doesn't natively support balanced or recursive # regular expressions. We could use PyParsing, but that opens another can # of worms. For now, we just have a cheap hack to restore strings, # so we don't end up accidentally mangling things like messages, search strings, # and regular expressions. def lineIter( text ): "Simple iterator over lines in text." for line in text.splitlines(): yield line def stringIter( strList ): "Yield strings in strList." for s in strList: yield s def restoreRegex( regex, old, new ): "Find regexes in old and restore them into new." oldStrs = regex.findall( old ) # Sanity check - count should be the same! newStrs = regex.findall( new ) assert len( oldStrs ) == len( newStrs ) # Replace newStrs with oldStrs siter = stringIter( oldStrs ) reps = lambda dummy: siter.next() return regex.sub( reps, new ) # This is a cheap hack, and it may not work 100%, since # it doesn't handle multiline strings. # However, it should be mostly harmless... def restoreStrings( oldText, newText ): "Restore strings from oldText into newText, returning result." oldLines, newLines = lineIter( oldText ), lineIter( newText ) quoteStrings = re.compile( r'("[^"]*")' ) tickStrings = re.compile( r"('[^']*')" ) result = '' # It would be nice if we could blast the whole file, but for # now it seems to work line-by-line for newLine in newLines: oldLine = oldLines.next() newLine = restoreRegex( quoteStrings, oldLine, newLine ) newLine = restoreRegex( tickStrings, oldLine, newLine ) result += newLine + '\n' return result # This might be slightly controversial, since it uses # three spaces to line up multiline comments. However, # I much prefer it. Limitations: if you have deeper # indents in comments, they will be eliminated. ;-( def fixComment( match, indentExp=re.compile( r'\n([ ]*)(?=[^/s])', re.MULTILINE ), trailingQuotes=re.compile( r'\s+"""' ) ): "Re-indent comment, and join trailing quotes." originalIndent = match.group( 1 ) comment = match.group( 2 ) indent = '\n' + originalIndent # Exception: leave unindented things unindented! if len( originalIndent ) is not 0: indent += ' ' comment = indentExp.sub( indent, comment ) return originalIndent + trailingQuotes.sub( '"""', comment ) def fixCommentIndents( text ): "Fix multiline comment indentation." comments = re.compile( r'^([ ]*)("""[^"]*""")$', re.MULTILINE ) return comments.sub( fixComment, text ) def removeBogusLinefeeds( text ): "Remove extra linefeeds at the end of single-line comments." bogusLfs = re.compile( r'"([^"\n]*)\n"', re.MULTILINE ) return bogusLfs.sub( '"\1"', text) def convertFromPep8( program ): oldProgram = program # Program text transforms program = reinstateCapWords( program ) program = fixKeywords( program ) program = insertExtraSpaces( program ) # Undo string damage program = restoreStrings( oldProgram, program ) # Docstring transforms program = replaceTripleApostrophes( program ) program = simplifyTripleQuotes( program ) program = fixDoxygen( program ) program = fixCommentIndents( program ) program = removeBogusLinefeeds( program ) # Destructive transforms (these can delete lines) program = removeCommentFirstBlankLine( program ) return program if __name__ == '__main__': print convertFromPep8( sys.stdin.read() )mininet-2.1.0/util/versioncheck.py000077500000000000000000000012311221655600400171700ustar00rootroot00000000000000#!/usr/bin/python from subprocess import check_output as co from sys import exit # Actually run bin/mn rather than importing via python path version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version', shell=True ) version = version.strip() # Find all Mininet path references lines = co( "grep -or 'Mininet \w\.\w\.\w\w*' *", shell=True ) error = False for line in lines.split( '\n' ): if line and 'Binary' not in line: fname, fversion = line.split( ':' ) if version != fversion: print "%s: incorrect version '%s' (should be '%s')" % ( fname, fversion, version ) error = True if error: exit( 1 ) mininet-2.1.0/util/vm/000077500000000000000000000000001221655600400145555ustar00rootroot00000000000000mininet-2.1.0/util/vm/.bash_profile000066400000000000000000000010621221655600400172120ustar00rootroot00000000000000SSH_ENV="$HOME/.ssh/environment" function start_agent { echo "Initialising new SSH agent..." /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}" echo succeeded chmod 600 "${SSH_ENV}" . "${SSH_ENV}" > /dev/null /usr/bin/ssh-add; } # Source SSH settings, if applicable if [ -f "${SSH_ENV}" ]; then . "${SSH_ENV}" > /dev/null #ps ${SSH_AGENT_PID} doesn't work under cywgin ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || { start_agent; } else start_agent; fi source ~/.bashrc mininet-2.1.0/util/vm/build.py000077500000000000000000000747261221655600400162510ustar00rootroot00000000000000#!/usr/bin/python """ build.py: build a Mininet VM Basic idea: prepare -> create base install image if it's missing - download iso if it's missing - install from iso onto image build -> create cow disk for new VM, based on base image -> boot it in qemu/kvm with text /serial console -> install Mininet test -> sudo mn --test pingall -> make test release -> shut down VM -> shrink-wrap VM -> upload to storage """ import os from os import stat, path from stat import ST_MODE, ST_SIZE from os.path import abspath from sys import exit, stdout, argv, modules import re from glob import glob from subprocess import check_output, call, Popen from tempfile import mkdtemp, NamedTemporaryFile from time import time, strftime, localtime import argparse from distutils.spawn import find_executable import inspect pexpect = None # For code check - imported dynamically # boot can be slooooow!!!! need to debug/optimize somehow TIMEOUT=600 # Some configuration options # Possibly change this to use the parsed arguments instead! LogToConsole = False # VM output to console rather than log file SaveQCOW2 = False # Save QCOW2 image rather than deleting it NoKVM = False # Don't use kvm and use emulation instead Branch = None # Branch to update and check out before testing Zip = False # Archive .ovf and .vmdk into a .zip file VMImageDir = os.environ[ 'HOME' ] + '/vm-images' Prompt = '\$ ' # Shell prompt that pexpect will wait for isoURLs = { 'precise32server': 'http://mirrors.kernel.org/ubuntu-releases/12.04/' 'ubuntu-12.04.3-server-i386.iso', 'precise64server': 'http://mirrors.kernel.org/ubuntu-releases/12.04/' 'ubuntu-12.04.3-server-amd64.iso', 'quantal32server': 'http://mirrors.kernel.org/ubuntu-releases/12.10/' 'ubuntu-12.10-server-i386.iso', 'quantal64server': 'http://mirrors.kernel.org/ubuntu-releases/12.10/' 'ubuntu-12.10-server-amd64.iso', 'raring32server': 'http://mirrors.kernel.org/ubuntu-releases/13.04/' 'ubuntu-13.04-server-i386.iso', 'raring64server': 'http://mirrors.kernel.org/ubuntu-releases/13.04/' 'ubuntu-13.04-server-amd64.iso', 'saucy32server': 'http://mirrors.kernel.org/ubuntu-releases/13.10/' 'ubuntu-13.10-server-i386.iso', 'saucy64server': 'http://mirrors.kernel.org/ubuntu-releases/13.10/' 'ubuntu-13.10-server-amd64.iso', } def OSVersion( flavor ): "Return full OS version string for build flavor" urlbase = path.basename( isoURLs.get( flavor, 'unknown' ) ) return path.splitext( urlbase )[ 0 ] LogStartTime = time() LogFile = None def log( *args, **kwargs ): """Simple log function: log( message along with local and elapsed time cr: False/0 for no CR""" cr = kwargs.get( 'cr', True ) elapsed = time() - LogStartTime clocktime = strftime( '%H:%M:%S', localtime() ) msg = ' '.join( str( arg ) for arg in args ) output = '%s [ %.3f ] %s' % ( clocktime, elapsed, msg ) if cr: print output else: print output, # Optionally mirror to LogFile if type( LogFile ) is file: if cr: output += '\n' LogFile.write( output ) LogFile.flush() def run( cmd, **kwargs ): "Convenient interface to check_output" log( '-', cmd ) cmd = cmd.split() arg0 = cmd[ 0 ] if not find_executable( arg0 ): raise Exception( 'Cannot find executable "%s";' % arg0 + 'you might try %s --depend' % argv[ 0 ] ) return check_output( cmd, **kwargs ) def srun( cmd, **kwargs ): "Run + sudo" return run( 'sudo ' + cmd, **kwargs ) # BL: we should probably have a "checkDepend()" which # checks to make sure all dependencies are satisfied! def depend(): "Install package dependencies" log( '* Installing package dependencies' ) run( 'sudo apt-get -y update' ) run( 'sudo apt-get install -y' ' kvm cloud-utils genisoimage qemu-kvm qemu-utils' ' e2fsprogs ' ' landscape-client' ' python-setuptools mtools zip' ) run( 'sudo easy_install pexpect' ) def popen( cmd ): "Convenient interface to popen" log( cmd ) cmd = cmd.split() return Popen( cmd ) def remove( fname ): "Remove a file, ignoring errors" try: os.remove( fname ) except OSError: pass def findiso( flavor ): "Find iso, fetching it if it's not there already" url = isoURLs[ flavor ] name = path.basename( url ) iso = path.join( VMImageDir, name ) if not path.exists( iso ) or ( stat( iso )[ ST_MODE ] & 0777 != 0444 ): log( '* Retrieving', url ) run( 'curl -C - -o %s %s' % ( iso, url ) ) if 'ISO' not in run( 'file ' + iso ): os.remove( iso ) raise Exception( 'findiso: could not download iso from ' + url ) # Write-protect iso, signaling it is complete log( '* Write-protecting iso', iso) os.chmod( iso, 0444 ) log( '* Using iso', iso ) return iso def attachNBD( cow, flags='' ): """Attempt to attach a COW disk image and return its nbd device flags: additional flags for qemu-nbd (e.g. -r for readonly)""" # qemu-nbd requires an absolute path cow = abspath( cow ) log( '* Checking for unused /dev/nbdX device ' ) for i in range ( 0, 63 ): nbd = '/dev/nbd%d' % i # Check whether someone's already messing with that device if call( [ 'pgrep', '-f', nbd ] ) == 0: continue srun( 'modprobe nbd max-part=64' ) srun( 'qemu-nbd %s -c %s %s' % ( flags, nbd, cow ) ) print return nbd raise Exception( "Error: could not find unused /dev/nbdX device" ) def detachNBD( nbd ): "Detatch an nbd device" srun( 'qemu-nbd -d ' + nbd ) def extractKernel( image, flavor, imageDir=VMImageDir ): "Extract kernel and initrd from base image" kernel = path.join( imageDir, flavor + '-vmlinuz' ) initrd = path.join( imageDir, flavor + '-initrd' ) if path.exists( kernel ) and ( stat( image )[ ST_MODE ] & 0777 ) == 0444: # If kernel is there, then initrd should also be there return kernel, initrd log( '* Extracting kernel to', kernel ) nbd = attachNBD( image, flags='-r' ) print srun( 'partx ' + nbd ) # Assume kernel is in partition 1/boot/vmlinuz*generic for now part = nbd + 'p1' mnt = mkdtemp() srun( 'mount -o ro %s %s' % ( part, mnt ) ) kernsrc = glob( '%s/boot/vmlinuz*generic' % mnt )[ 0 ] initrdsrc = glob( '%s/boot/initrd*generic' % mnt )[ 0 ] srun( 'cp %s %s' % ( initrdsrc, initrd ) ) srun( 'chmod 0444 ' + initrd ) srun( 'cp %s %s' % ( kernsrc, kernel ) ) srun( 'chmod 0444 ' + kernel ) srun( 'umount ' + mnt ) run( 'rmdir ' + mnt ) detachNBD( nbd ) return kernel, initrd def findBaseImage( flavor, size='8G' ): "Return base VM image and kernel, creating them if needed" image = path.join( VMImageDir, flavor + '-base.qcow2' ) if path.exists( image ): # Detect race condition with multiple builds perms = stat( image )[ ST_MODE ] & 0777 if perms != 0444: raise Exception( 'Error - %s is writable ' % image + '; are multiple builds running?' ) else: # We create VMImageDir here since we are called first run( 'mkdir -p %s' % VMImageDir ) iso = findiso( flavor ) log( '* Creating image file', image ) run( 'qemu-img create -f qcow2 %s %s' % ( image, size ) ) installUbuntu( iso, image ) # Write-protect image, also signaling it is complete log( '* Write-protecting image', image) os.chmod( image, 0444 ) kernel, initrd = extractKernel( image, flavor ) log( '* Using base image', image, 'and kernel', kernel ) return image, kernel, initrd # Kickstart and Preseed files for Ubuntu/Debian installer # # Comments: this is really clunky and painful. If Ubuntu # gets their act together and supports kickstart a bit better # then we can get rid of preseed and even use this as a # Fedora installer as well. # # Another annoying thing about Ubuntu is that it can't just # install a normal system from the iso - it has to download # junk from the internet, making this house of cards even # more precarious. KickstartText =""" #Generated by Kickstart Configurator #platform=x86 #System language lang en_US #Language modules to install langsupport en_US #System keyboard keyboard us #System mouse mouse #System timezone timezone America/Los_Angeles #Root password rootpw --disabled #Initial user user mininet --fullname "mininet" --password "mininet" #Use text mode install text #Install OS instead of upgrade install #Use CDROM installation media cdrom #System bootloader configuration bootloader --location=mbr #Clear the Master Boot Record zerombr yes #Partition clearing information clearpart --all --initlabel #Automatic partitioning autopart #System authorization infomation auth --useshadow --enablemd5 #Firewall configuration firewall --disabled #Do not configure the X Window System skipx """ # Tell the Ubuntu/Debian installer to stop asking stupid questions PreseedText = """ d-i mirror/country string manual d-i mirror/http/hostname string mirrors.kernel.org d-i mirror/http/directory string /ubuntu d-i mirror/http/proxy string d-i partman/confirm_write_new_label boolean true d-i partman/choose_partition select finish d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true d-i user-setup/allow-password-weak boolean true d-i finish-install/reboot_in_progress note d-i debian-installer/exit/poweroff boolean true """ def makeKickstartFloppy(): "Create and return kickstart floppy, kickstart, preseed" kickstart = 'ks.cfg' with open( kickstart, 'w' ) as f: f.write( KickstartText ) preseed = 'ks.preseed' with open( preseed, 'w' ) as f: f.write( PreseedText ) # Create floppy and copy files to it floppy = 'ksfloppy.img' run( 'qemu-img create %s 1440k' % floppy ) run( 'mkfs -t msdos ' + floppy ) run( 'mcopy -i %s %s ::/' % ( floppy, kickstart ) ) run( 'mcopy -i %s %s ::/' % ( floppy, preseed ) ) return floppy, kickstart, preseed def archFor( filepath ): "Guess architecture for file path" name = path.basename( filepath ) if 'amd64' in name or 'x86_64' in name: arch = 'x86_64' # Beware of version 64 of a 32-bit OS elif 'i386' in name or '32' in name or 'x86' in name: arch = 'i386' elif '64' in name: arch = 'x86_64' else: log( "Error: can't discern CPU for name", name ) exit( 1 ) return arch def installUbuntu( iso, image, logfilename='install.log', memory=1024 ): "Install Ubuntu from iso onto image" kvm = 'qemu-system-' + archFor( iso ) floppy, kickstart, preseed = makeKickstartFloppy() # Mount iso so we can use its kernel mnt = mkdtemp() srun( 'mount %s %s' % ( iso, mnt ) ) kernel = path.join( mnt, 'install/vmlinuz' ) initrd = path.join( mnt, 'install/initrd.gz' ) if NoKVM: accel = 'tcg' else: accel = 'kvm' cmd = [ 'sudo', kvm, '-machine', 'accel=%s' % accel, '-nographic', '-netdev', 'user,id=mnbuild', '-device', 'virtio-net,netdev=mnbuild', '-m', str( memory ), '-k', 'en-us', '-fda', floppy, '-drive', 'file=%s,if=virtio' % image, '-cdrom', iso, '-kernel', kernel, '-initrd', initrd, '-append', ' ks=floppy:/' + kickstart + ' preseed/file=floppy://' + preseed + ' console=ttyS0' ] ubuntuStart = time() log( '* INSTALLING UBUNTU FROM', iso, 'ONTO', image ) log( ' '.join( cmd ) ) log( '* logging to', abspath( logfilename ) ) params = {} if not LogToConsole: logfile = open( logfilename, 'w' ) params = { 'stdout': logfile, 'stderr': logfile } vm = Popen( cmd, **params ) log( '* Waiting for installation to complete') vm.wait() if not LogToConsole: logfile.close() elapsed = time() - ubuntuStart # Unmount iso and clean up srun( 'umount ' + mnt ) run( 'rmdir ' + mnt ) if vm.returncode != 0: raise Exception( 'Ubuntu installation returned error %d' % vm.returncode ) log( '* UBUNTU INSTALLATION COMPLETED FOR', image ) log( '* Ubuntu installation completed in %.2f seconds' % elapsed ) def boot( cow, kernel, initrd, logfile, memory=1024 ): """Boot qemu/kvm with a COW disk and local/user data store cow: COW disk path kernel: kernel path logfile: log file for pexpect object memory: memory size in MB returns: pexpect object to qemu process""" # pexpect might not be installed until after depend() is called global pexpect import pexpect arch = archFor( kernel ) log( '* Detected kernel architecture', arch ) if NoKVM: accel = 'tcg' else: accel = 'kvm' cmd = [ 'sudo', 'qemu-system-' + arch, '-machine accel=%s' % accel, '-nographic', '-netdev user,id=mnbuild', '-device virtio-net,netdev=mnbuild', '-m %s' % memory, '-k en-us', '-kernel', kernel, '-initrd', initrd, '-drive file=%s,if=virtio' % cow, '-append "root=/dev/vda1 init=/sbin/init console=ttyS0" ' ] cmd = ' '.join( cmd ) log( '* BOOTING VM FROM', cow ) log( cmd ) vm = pexpect.spawn( cmd, timeout=TIMEOUT, logfile=logfile ) return vm def login( vm ): "Log in to vm (pexpect object)" log( '* Waiting for login prompt' ) vm.expect( 'login: ' ) log( '* Logging in' ) vm.sendline( 'mininet' ) log( '* Waiting for password prompt' ) vm.expect( 'Password: ' ) log( '* Sending password' ) vm.sendline( 'mininet' ) log( '* Waiting for login...' ) def sanityTest( vm ): "Run Mininet sanity test (pingall) in vm" vm.sendline( 'sudo mn --test pingall' ) if vm.expect( [ ' 0% dropped', pexpect.TIMEOUT ], timeout=45 ) == 0: log( '* Sanity check OK' ) else: log( '* Sanity check FAILED' ) log( '* Sanity check output:' ) log( vm.before ) def coreTest( vm, prompt=Prompt ): "Run core tests (make test) in VM" log( '* Making sure cgroups are mounted' ) vm.sendline( 'sudo service cgroup-lite restart' ) vm.expect( prompt ) vm.sendline( 'sudo cgroups-mount' ) vm.expect( prompt ) log( '* Running make test' ) vm.sendline( 'cd ~/mininet; sudo make test' ) # We should change "make test" to report the number of # successful and failed tests. For now, we have to # know the time for each test, which means that this # script will have to change as we add more tests. for test in range( 0, 2 ): if vm.expect( [ 'OK', 'FAILED', pexpect.TIMEOUT ], timeout=180 ) == 0: log( '* Test', test, 'OK' ) else: log( '* Test', test, 'FAILED' ) log( '* Test', test, 'output:' ) log( vm.before ) def examplesquickTest( vm, prompt=Prompt ): "Quick test of mininet examples" vm.sendline( 'sudo apt-get install python-pexpect' ) vm.expect( prompt ) vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v -quick' ) def examplesfullTest( vm, prompt=Prompt ): "Full (slow) test of mininet examples" vm.sendline( 'sudo apt-get install python-pexpect' ) vm.expect( prompt ) vm.sendline( 'sudo python ~/mininet/examples/test/runner.py -v' ) def walkthroughTest( vm, prompt=Prompt ): "Test mininet walkthrough" vm.sendline( 'sudo apt-get install python-pexpect' ) vm.expect( prompt ) vm.sendline( 'sudo python ~/mininet/mininet/test/test_walkthrough.py -v' ) def checkOutBranch( vm, branch, prompt=Prompt ): vm.sendline( 'cd ~/mininet; git fetch; git pull --rebase; git checkout ' + branch ) vm.expect( prompt ) vm.sendline( 'sudo make install' ) def interact( vm, tests, pre='', post='', prompt=Prompt ): "Interact with vm, which is a pexpect object" login( vm ) log( '* Waiting for login...' ) vm.expect( prompt ) log( '* Sending hostname command' ) vm.sendline( 'hostname' ) log( '* Waiting for output' ) vm.expect( prompt ) log( '* Fetching Mininet VM install script' ) vm.sendline( 'wget ' 'https://raw.github.com/mininet/mininet/master/util/vm/' 'install-mininet-vm.sh' ) vm.expect( prompt ) log( '* Running VM install script' ) vm.sendline( 'bash install-mininet-vm.sh' ) vm.expect ( 'password for mininet: ' ) vm.sendline( 'mininet' ) log( '* Waiting for script to complete... ' ) # Gigantic timeout for now ;-( vm.expect( 'Done preparing Mininet', timeout=3600 ) log( '* Completed successfully' ) vm.expect( prompt ) version = getMininetVersion( vm ) vm.expect( prompt ) log( '* Mininet version: ', version ) log( '* Testing Mininet' ) runTests( vm, tests=tests, pre=pre, post=post ) log( '* Shutting down' ) vm.sendline( 'sync; sudo shutdown -h now' ) log( '* Waiting for EOF/shutdown' ) vm.read() log( '* Interaction complete' ) return version def cleanup(): "Clean up leftover qemu-nbd processes and other junk" call( [ 'sudo', 'pkill', '-9', 'qemu-nbd' ] ) def convert( cow, basename ): """Convert a qcow2 disk to a vmdk and put it a new directory basename: base name for output vmdk file""" vmdk = basename + '.vmdk' log( '* Converting qcow2 to vmdk' ) run( 'qemu-img convert -f qcow2 -O vmdk %s %s' % ( cow, vmdk ) ) return vmdk # Template for OVF - a very verbose format! # In the best of all possible worlds, we might use an XML # library to generate this, but a template is easier and # possibly more concise! # Warning: XML file cannot begin with a newline! OVFTemplate = """ Virtual disk information The list of logical networks The nat network A Mininet Virtual Machine (%s) mininet-vm Virtual hardware requirements hertz * 10^6 Number of Virtual CPUs 1 virtual CPU(s) 1 3 1 byte * 2^20 Memory Size %dMB of memory 2 4 %d 0 scsiController0 SCSI Controller scsiController0 4 lsilogic 6 0 disk1 ovf:/disk/vmdisk1 11 4 17 2 true nat E1000 ethernet adapter on nat ethernet0 12 E1000 10 0 usb USB Controller usb 9 23 """ def generateOVF( name, diskname, disksize, mem=1024 ): """Generate (and return) OVF file "name.ovf" name: root name of OVF file to generate diskname: name of disk file disksize: size of virtual disk in bytes mem: VM memory size in MB""" ovf = name + '.ovf' filesize = stat( diskname )[ ST_SIZE ] # OVFTemplate uses the memory size twice in a row xmltext = OVFTemplate % ( diskname, filesize, disksize, name, mem, mem ) with open( ovf, 'w+' ) as f: f.write( xmltext ) return ovf def qcow2size( qcow2 ): "Return virtual disk size (in bytes) of qcow2 image" output = check_output( [ 'file', qcow2 ] ) assert 'QCOW' in output bytes = int( re.findall( '(\d+) bytes', output )[ 0 ] ) return bytes def build( flavor='raring32server', tests=None, pre='', post='', memory=1024 ): """Build a Mininet VM; return vmdk and vdisk size tests: tests to run pre: command line to run in VM before tests post: command line to run in VM after tests prompt: shell prompt (default '$ ') memory: memory size in MB""" global LogFile, Zip start = time() lstart = localtime() date = strftime( '%y%m%d-%H-%M-%S', lstart) ovfdate = strftime( '%y%m%d', lstart ) dir = 'mn-%s-%s' % ( flavor, date ) try: os.mkdir( dir ) except: raise Exception( "Failed to create build directory %s" % dir ) os.chdir( dir ) LogFile = open( 'build.log', 'w' ) log( '* Logging to', abspath( LogFile.name ) ) log( '* Created working directory', dir ) image, kernel, initrd = findBaseImage( flavor ) basename = 'mininet-' + flavor volume = basename + '.qcow2' run( 'qemu-img create -f qcow2 -b %s %s' % ( image, volume ) ) log( '* VM image for', flavor, 'created as', volume ) if LogToConsole: logfile = stdout else: logfile = open( flavor + '.log', 'w+' ) log( '* Logging results to', abspath( logfile.name ) ) vm = boot( volume, kernel, initrd, logfile, memory=memory ) version = interact( vm, tests=tests, pre=pre, post=post ) size = qcow2size( volume ) vmdk = convert( volume, basename='mininet-vm-' + archFor( flavor ) ) if not SaveQCOW2: log( '* Removing qcow2 volume', volume ) os.remove( volume ) log( '* Converted VM image stored as', abspath( vmdk ) ) ovfname = 'mininet-%s-%s-%s' % ( version, ovfdate, OSVersion( flavor ) ) ovf = generateOVF( diskname=vmdk, disksize=size, name=ovfname ) log( '* Generated OVF descriptor file', ovf ) if Zip: log( '* Generating .zip file' ) run( 'zip %s-ovf.zip %s %s' % ( ovfname, ovf, vmdk ) ) end = time() elapsed = end - start log( '* Results logged to', abspath( logfile.name ) ) log( '* Completed in %.2f seconds' % elapsed ) log( '* %s VM build DONE!!!!! :D' % flavor ) os.chdir( '..' ) def runTests( vm, tests=None, pre='', post='', prompt=Prompt ): "Run tests (list) in vm (pexpect object)" if not tests: tests = [] if pre: log( '* Running command', pre ) vm.sendline( pre ) vm.expect( prompt ) testfns = testDict() if tests: log( '* Running tests' ) for test in tests: if test not in testfns: raise Exception( 'Unknown test: ' + test ) log( '* Running test', test ) fn = testfns[ test ] fn( vm ) vm.expect( prompt ) if post: log( '* Running post-test command', post ) vm.sendline( post ) vm.expect( prompt ) def getMininetVersion( vm ): "Run mn to find Mininet version in VM" vm.sendline( '~/mininet/bin/mn --version' ) # Eat command line echo, then read output line vm.readline() version = vm.readline().strip() return version def bootAndRunTests( image, tests=None, pre='', post='', prompt=Prompt, memory=1024 ): """Boot and test VM tests: list of tests to run pre: command line to run in VM before tests post: command line to run in VM after tests prompt: shell prompt (default '$ ') memory: VM memory size in MB""" bootTestStart = time() basename = path.basename( image ) image = abspath( image ) tmpdir = mkdtemp( prefix='test-' + basename ) log( '* Using tmpdir', tmpdir ) cow = path.join( tmpdir, basename + '.qcow2' ) log( '* Creating COW disk', cow ) run( 'qemu-img create -f qcow2 -b %s %s' % ( image, cow ) ) log( '* Extracting kernel and initrd' ) kernel, initrd = extractKernel( image, flavor=basename, imageDir=tmpdir ) if LogToConsole: logfile = stdout else: logfile = NamedTemporaryFile( prefix=basename, suffix='.testlog', delete=False ) log( '* Logging VM output to', logfile.name ) vm = boot( cow=cow, kernel=kernel, initrd=initrd, logfile=logfile, memory=memory ) login( vm ) log( '* Waiting for prompt after login' ) vm.expect( prompt ) if Branch: checkOutBranch( vm, branch=Branch ) vm.expect( prompt ) runTests( vm, tests=tests, pre=pre, post=post ) # runTests eats its last prompt, but maybe it shouldn't... log( '* Shutting down' ) vm.sendline( 'sudo shutdown -h now ' ) log( '* Waiting for shutdown' ) vm.wait() log( '* Removing temporary dir', tmpdir ) srun( 'rm -rf ' + tmpdir ) elapsed = time() - bootTestStart log( '* Boot and test completed in %.2f seconds' % elapsed ) def buildFlavorString(): "Return string listing valid build flavors" return 'valid build flavors: ( %s )' % ' '.join( sorted( isoURLs ) ) def testDict(): "Return dict of tests in this module" suffix = 'Test' trim = len( suffix ) fdict = dict( [ ( fname[ : -trim ], f ) for fname, f in inspect.getmembers( modules[ __name__ ], inspect.isfunction ) if fname.endswith( suffix ) ] ) return fdict def testString(): "Return string listing valid tests" return 'valid tests: ( %s )' % ' '.join( testDict().keys() ) def parseArgs(): "Parse command line arguments and run" global LogToConsole, NoKVM, Branch, Zip, TIMEOUT parser = argparse.ArgumentParser( description='Mininet VM build script', epilog=buildFlavorString() + ' ' + testString() ) parser.add_argument( '-v', '--verbose', action='store_true', help='send VM output to console rather than log file' ) parser.add_argument( '-d', '--depend', action='store_true', help='install dependencies for this script' ) parser.add_argument( '-l', '--list', action='store_true', help='list valid build flavors and tests' ) parser.add_argument( '-c', '--clean', action='store_true', help='clean up leftover build junk (e.g. qemu-nbd)' ) parser.add_argument( '-q', '--qcow2', action='store_true', help='save qcow2 image rather than deleting it' ) parser.add_argument( '-n', '--nokvm', action='store_true', help="Don't use kvm - use tcg emulation instead" ) parser.add_argument( '-m', '--memory', metavar='MB', type=int, default=1024, help='VM memory size in MB' ) parser.add_argument( '-i', '--image', metavar='image', default=[], action='append', help='Boot and test an existing VM image' ) parser.add_argument( '-t', '--test', metavar='test', default=[], action='append', help='specify a test to run' ) parser.add_argument( '-w', '--timeout', metavar='timeout', type=int, default=0, help='set expect timeout' ) parser.add_argument( '-r', '--run', metavar='cmd', default='', help='specify a command line to run before tests' ) parser.add_argument( '-p', '--post', metavar='cmd', default='', help='specify a command line to run after tests' ) parser.add_argument( '-b', '--branch', metavar='branch', help='For an existing VM image, check out and install' ' this branch before testing' ) parser.add_argument( 'flavor', nargs='*', help='VM flavor(s) to build (e.g. raring32server)' ) parser.add_argument( '-z', '--zip', action='store_true', help='archive .ovf and .vmdk into .zip file' ) args = parser.parse_args() if args.depend: depend() if args.list: print buildFlavorString() if args.clean: cleanup() if args.verbose: LogToConsole = True if args.nokvm: NoKVM = True if args.branch: Branch = args.branch if args.zip: Zip = True if args.timeout: TIMEOUT = args.timeout if not args.test and not args.run and not args.post: args.test = [ 'sanity', 'core' ] for flavor in args.flavor: if flavor not in isoURLs: print "Unknown build flavor:", flavor print buildFlavorString() break try: build( flavor, tests=args.test, pre=args.run, post=args.post, memory=args.memory ) except Exception as e: log( '* BUILD FAILED with exception: ', e ) exit( 1 ) for image in args.image: bootAndRunTests( image, tests=args.test, pre=args.run, post=args.post, memory=args.memory) if not ( args.depend or args.list or args.clean or args.flavor or args.image ): parser.print_help() if __name__ == '__main__': parseArgs() mininet-2.1.0/util/vm/install-mininet-vm.sh000077500000000000000000000024361221655600400206500ustar00rootroot00000000000000#!/bin/bash # This script is intended to install Mininet into # a brand-new Ubuntu virtual machine, # to create a fully usable "tutorial" VM. set -e echo `whoami` ALL=NOPASSWD: ALL | sudo tee -a /etc/sudoers > /dev/null sudo sed -i -e 's/Default/#Default/' /etc/sudoers echo mininet-vm | sudo tee /etc/hostname > /dev/null sudo sed -i -e 's/ubuntu/mininet-vm/g' /etc/hosts sudo hostname `cat /etc/hostname` sudo sed -i -e 's/quiet splash/text/' /etc/default/grub sudo update-grub # 12.10 and earlier sudo sed -i -e 's/us.archive.ubuntu.com/mirrors.kernel.org/' \ /etc/apt/sources.list # 13.04 and later sudo sed -i -e 's/\/archive.ubuntu.com/\/mirrors.kernel.org/' \ /etc/apt/sources.list sudo apt-get update # Clean up vmware easy install junk if present if [ -e /etc/issue.backup ]; then sudo mv /etc/issue.backup /etc/issue fi if [ -e /etc/rc.local.backup ]; then sudo mv /etc/rc.local.backup /etc/rc.local fi # Install Mininet sudo apt-get -y install git-core openssh-server git clone git://github.com/mininet/mininet cd mininet cd time mininet/util/install.sh # Finalize VM time mininet/util/install.sh -tc # Ignoring this since NOX classic is deprecated #if ! grep NOX_CORE_DIR .bashrc; then # echo "export NOX_CORE_DIR=~/noxcore/build/src/" >> .bashrc #fi echo "Done preparing Mininet VM."