Using latest version of mininet

refs: #2851

Change-Id: Ica4936cb98245a0b1a22d8ceada4e7a0f6dbddc8
This commit is contained in:
ashu authored and Ashlesh Gawande committed 2015-06-16 16:02:05 -05:00
1 parent cb20c547af
commit 2ad32e2a61
72 files changed
+237 -10279

No files matched your search

+10 -17
View File
@@ -1,21 +1,14 @@
*.pyc
mnexec
*.pyc
*~
*.1
*.xcodeproj
*.xcworkspace
\#*\#
mininet.egg-info
Mini_NDN.egg-info
build
dist
doc/html
doc/latex
trunk
data
trash
measures
debian
.fuse*
.project
.pydev*
NFD
NLSR
crypto
mininet
ndn-cxx
ndn-tools
openflow
examples
util
+17 -29
View File
@@ -10,7 +10,7 @@ extra packages and also to execute **Mini-NDN**.
### Installing NDN
Each node in **Mini-NDN** will run the official implementation of NDN. Let's get it.
Each node in **Mini-NDN** will run the official implementation of NDN. The following dependencies are needed:
Mini-NDN uses NFD, NLSR, and ndn-tlv-ping.
@@ -20,45 +20,33 @@ http://named-data.net/doc/NFD/current/INSTALL.html
To install NLSR:
http://named-data.net/doc/NLSR/current/INSTALL.html
To install ndn-tlv-ping:
https://github.com/named-data/ndn-tlv-ping
To install ndn-tools:
https://github.com/named-data/ndn-tools
### Downloading and installing **Mini-NDN**
### Installing Mininet
If you don't have it yet, you'll need to have _git_ installed first. In Ubuntu, that would be:
**Mini-NDN** is based on Mininet. To install Mininet:
https://github.com/mininet/mininet/INSTALL
sudo apt-get install git
### Installing **Mini-NDN**
Now, let's get the source code of **Mini-NDN**.
Go to your home directory and use the following command:
If you have all the dependencies installed simply clone this repository and run:
git clone https://github.com/named-data/mini-ndn
sudo ./install.sh -i
As a result, there will be a directory named _mini-ndn_ in your home directory, containing all the source code.
else if you don't have the dependencies:
Still in your home directory, use the utility install script with _-fnv_ options:
sudo ./mini-ndn/util/install.sh -fnv
where
-f: install open(F)low
-n: install mini(N)et dependencies + core files
-v: install open (V)switch
Prerequisite packages will be downloaded and installed during the process.
sudo ./install.sh -mrfti
### Verification
Once everything is installed, the following command can be issued for verification from the home folder:
Once everything is installed, the following command can be issued for verification
sudo minindn --pingall 50 --ctime 180 mini-ndn/ndn_utils/hyperbolic_conf_file/minindn.caida.conf
where:
--pingall: Will schedule and collect the specified number of pings from each node to every other node
--ctime: Convergence time for NLSR, provide according to the size of the topology
Note: The configuration file contains hyperbolic coordinates but hyperbolic routing will only be
activated if --hr is provided
sudo minindn --pingall 50 --ctime 180 ndn_utils/hyperbolic_conf_file/minindn.caida.conf
All the ping logs will be stored under /tmp/node-name/ping-data and the command will provide a
command line interface at the end.
When the "mininet>" CLI prompt appears, press CTRL+D to terminate the experiment.
Then, execute `ls /tmp/*/ping-data/*.txt | wc -l`, and expect to see "90".
Execute `cat /tmp/*/ping-data/*.txt | grep loss`, and expect to see "0% packet loss" on every line.
-123
View File
@@ -1,123 +0,0 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.0.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 <http://openflow.org/mininet>
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 on Ubuntu 11.10+
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`, or `noxcosre`.
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 (and to add some
stuff to `/etc/sysctl.conf` which may or may not be useful) you may
use:
mininet/util/install.sh -a
This takes about 4 minutes on our test system.
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
---
-33
View File
@@ -1,33 +0,0 @@
Mininet 2.0.0 License
Copyright (c) 2012 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.
-66
View File
@@ -1,66 +0,0 @@
MININET = mininet/*.py
TEST = mininet/test/*.py
EXAMPLES = examples/*.py
MN = bin/mn
BIN = $(MN)
NDN = ndn/**/*.py
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN) $(NDN)
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
-134
View File
@@ -1,134 +0,0 @@
Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Version 2.0.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.0.0
Mininet 2.0.0 is a major upgrade and provides
a number of enhancements and new features, including:
* "Mininet-HiFi" functionality:
* Link bandwidth limits using `tc` (`TCIntf` and `TCLink` classes)
* CPU isolation and bandwidth limits (`CPULimitedHost` class)
* Support for Open vSwitch 1.4+ (including Ubuntu OVS packages)
* Debian packaging (and `apt-get install mininet` in Ubuntu 12.10)
* First-class Interface (`Intf`) and Link (`Link`) classes for easier
extensibility
* An upgraded Topology (`Topo`) class which supports node and link
customization
* Man pages for the `mn` and `mnexec` utilities.
[Since the API (most notably the topology) has changed, existing code
that runs in Mininet 1.0 will need to be changed to run with Mininet
2.0. This is the primary reason for the major version number change.]
### 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://openflow.org/mininet).
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:
<https://mailman.stanford.edu/mailman/listinfo/mininet-discuss>
### Contributing
Mininet is an open-source project and is currently hosted at
<https://github.com/mininet>. You are encouraged to download the code,
examine it, modify it, and submit bug reports, bug fixes, feature
requests, and enhancements!
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
### Credits
The Mininet Team:
* Bob Lantz
* Brandon Heller
* Nikhil Handigol
* Vimal Jeyakumar
+19 -5
View File
@@ -5,12 +5,13 @@ from mininet.net import Mininet
from mininet.log import setLogLevel, output, info
from mininet.cli import CLI
from mininet.link import TCLink
from mininet.conf_parser import parse_hosts, parse_links
from mininet.util import ipStr, ipParse
from ndn.experiments.multiple_failure_experiment import MultipleFailureExperiment
from ndn.experiments.pingall_experiment import PingallExperiment
from ndn.experiments.failure_experiment import FailureExperiment
from ndn.ndn_host import NdnHost, CpuLimitedNdnHost
from ndn.conf_parser import parse_hosts, parse_links
import os.path, time
import optparse
@@ -114,15 +115,12 @@ class NdnTopo(Topo):
def execute(template_file='minindn.conf', testbed=False, pingall=None, ctime=None, hr=False, faces=3, failure=False, isMultipleFailure=False, isCliEnabled=True):
"Create a network based on template_file"
home = expanduser("~")
if template_file == '':
template_file='minindn.conf'
if os.path.exists(template_file) == False:
info('No template file given and default template file minindn.conf not found. Exiting...\n')
quit()
topo = NdnTopo(template_file)
t = datetime.datetime.now()
@@ -144,6 +142,22 @@ def execute(template_file='minindn.conf', testbed=False, pingall=None, ctime=Non
net.start()
# Giving proper IPs to intf so neighbor nodes can communicate
# This is one way of giving connectivity, another way could be
# to insert a switch between each pair of neighbors
ndnNetBase = "1.0.0.0"
interfaces = []
for host in net.hosts:
for intf in host.intfList():
link = intf.link
node1, node2 = link.intf1.node, link.intf2.node
if link.intf1 not in interfaces and link.intf2 not in interfaces:
interfaces.append(link.intf1)
interfaces.append(link.intf2)
node1.setIP(ipStr(ipParse(ndnNetBase) + 1) + '/30', intf=link.intf1)
node2.setIP(ipStr(ipParse(ndnNetBase) + 2) + '/30', intf=link.intf2)
ndnNetBase = ipStr(ipParse(ndnNetBase) + 4)
nodes = "" # Used later to check prefix name in checkFIB
# NLSR initialization
@@ -160,7 +174,7 @@ def execute(template_file='minindn.conf', testbed=False, pingall=None, ctime=Non
host.nlsrParameters["hyperbolic-state"] = "on"
# Generate NLSR configuration file
configGenerator = NlsrConfigGenerator(host, home)
configGenerator = NlsrConfigGenerator(host)
configGenerator.createConfigFile()
# Start NLSR
+1 -1
View File
@@ -42,7 +42,7 @@ from mininet.log import info, error, debug, output, setLogLevel
from mininet.net import Mininet, VERSION
from mininet.util import ipStr, netParse, ipAdd, quietRun
from mininet.util import buildTopo
from mininet.util import custom, customConstructor
from mininet.util import custom
from mininet.term import makeTerm, cleanUpScreens
from mininet.node import Controller, RemoteController, NOX, OVSController
from mininet.node import CPULimitedHost, Host, Node
-102
View File
@@ -1,102 +0,0 @@
Mininet 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 and adds multiple controllers to it.
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.
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.)
treeping64.py:
This example creates a 64-host tree network, and attempts to check full
connectivity using ping, for different switch/datapath types.
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.)
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/python
"This example doesn't use OpenFlow, but attempts to run sshd in a namespace."
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"
h1.cmd( '/usr/sbin/sshd -o "Banner /tmp/%s.banner"' % h1.name )
print "*** You may now ssh into", h1.name, "at", h1.IP()
-456
View File
@@ -1,456 +0,0 @@
#!/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( '<Return>', self.handleReturn )
self.text.bind( '<Control-c>', self.handleInt )
self.text.bind( '<KeyPress>', 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+) Mbits/sec', output )
if not m:
return
self.updates += 1
self.bw += .001 * float( m.group( 1 ) )
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:
console.node.cmd( 'iperf -sD' )
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()
-64
View File
@@ -1,64 +0,0 @@
#!/usr/bin/python
"""
This example creates a multi-controller network from
semi-scratch; note a topo object could also be used and
would be passed into the Mininet() constructor.
"""
from mininet.net import Mininet
from mininet.node import Controller, OVSKernelSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel
Switch = OVSKernelSwitch
def addHost( net, N ):
"Create host hN and add to net."
name = 'h%d' % N
ip = '10.0.0.%d' % N
return net.addHost( name, ip=ip )
def multiControllerNet():
"Create a network with multiple controllers."
net = Mininet( controller=Controller, switch=Switch)
print "*** Creating 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 = [ addHost( net, n ) for n in 3, 4 ]
hosts2 = [ addHost( net, n ) for n in 5, 6 ]
print "*** Creating links"
for h in hosts1:
s1.linkTo( h )
for h in hosts2:
s2.linkTo( h )
s1.linkTo( 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()
-81
View File
@@ -1,81 +0,0 @@
#!/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 )
-44
View File
@@ -1,44 +0,0 @@
#!/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' )
h1.linkTo( s3 )
h2.linkTo( s3 )
info( '*** Starting network\n')
net.start()
info( '*** Running CLI\n' )
CLI( net )
info( '*** Stopping network' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
emptyNet()
-48
View File
@@ -1,48 +0,0 @@
#!/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
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' )
intfName = 'eth1'
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()
-53
View File
@@ -1,53 +0,0 @@
#!/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()
-110
View File
@@ -1,110 +0,0 @@
#!/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 )
-771
View File
@@ -1,771 +0,0 @@
#!/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
"""
import optparse
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
def parse_args():
usage="""Usage: miniccnxedit [template_file]
If no template_file is given, generated template will be written
to the file topo.miniccnx in the current directory.
"""
parser = optparse.OptionParser(usage)
#parser.add_option("-t", "--template", help="template file name")
_, arg = parser.parse_args()
return arg
class MiniEdit( Frame ):
"A simple network editor for Mininet."
def __init__( self, parent=None, cheight=200, cwidth=500, template_file='topo.miniccnx' ):
Frame.__init__( self, parent )
self.action = None
self.appName = 'Mini-CCNx'
self.template_file = template_file
# 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( '<Control-q>', lambda event: self.quit() )
self.bind( '<KeyPress-Delete>', self.deleteSelection )
self.bind( '<KeyPress-BackSpace>', 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 Mini-CCNx', 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( '<Configure>', lambda event: self.updateScrollRegion() )
# Mouse bindings
canvas.bind( '<ButtonPress-1>', self.clickCanvas )
canvas.bind( '<B1-Motion>', self.dragCanvas )
canvas.bind( '<ButtonRelease-1>', 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=50, 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' )
for cmd, color in [ ( 'Generate', '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 doGenerate( self ):
"Generate template."
self.activate( 'Select' )
for tool in self.tools:
self.buttons[ tool ].config( state='disabled' )
self.buildTemplate()
for tool in self.tools:
self.buttons[ tool ].config( state='normal' )
def doStop( self ):
"Stop command."
self.stop()
for tool in self.tools:
self.buttons[ tool ].config( state='normal' )
def buildTemplate( self ):
"Generate template"
template = open(self.template_file, 'w')
# hosts
template.write('[hosts]\n')
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
if 'Host' in tags:
template.write(name + ':\n')
# switches/routers
template.write('[routers]\n')
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
if 'Switch' in tags:
template.write(name + ':\n')
# Make links
template.write('[links]\n')
for link in self.links.values():
( src, dst ) = link
srcName, dstName = src[ 'text' ], dst[ 'text' ]
template.write(srcName + ':' + dstName + '\n')
template.close()
# 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 = {
'<ButtonPress-1>': self.clickNode,
'<B1-Motion>': self.dragNode,
'<ButtonRelease-1>': self.releaseNode,
'<Enter>': self.enterNode,
'<Leave>': self.leaveNode,
'<Double-ButtonPress-1>': 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, '<Enter>', highlight )
self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
self.canvas.tag_bind( self.link, '<ButtonPress-1>', 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 Mini-CCNx - based on Miniedit '
warning = 'Development version - not entirely functional!'
author = 'Carlos Cabral, Jan 2013'
author2 = 'Miniedit by Bob Lantz <rlantz@cs>, 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 )
line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
line1.pack( padx=20, pady=10 )
line2.pack(pady=10 )
line3.pack(pady=10 )
line4.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:
net.addHost( name, ip=ipStr( nodeNum ) )
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.append( 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' ),
'Switch' : PhotoImage( data=r"""
R0lGODlhOgArAMIEAB8aFwB7tQCb343L8P///////////////yH+GlNvZnR3YXJlOiBNaWNyb3NvZnQgT2ZmaWNlACwAAAAAOgArAAAD/ki63P4wykmrvTjr3YYfQigKH7d5Y6qmnjmBayyHg8vAAqDPaUTbowaA13OIahqcyEgEQEbIi7LIGA1FzsaSQK0QfbnH10sMa83VsqX53HLL7sgUTudR5s367F7PEq4CYDJRcngqfgshiAqAMwF3AYdWTCERjSoBjy+ZVItvMg6XIZmaEgOkSmJwlKOkkKSRlaqraaewr7ABhnqBNLmZuL+6vCzCrpvGsB9EH8m5wc7R0sbQ09bT1dOEBLbXwMjeEN7HpuO6Dt3hFObi7Ovj7d7bEOnYD+4V8PfqF/wN/lKsxZPmop6wBwaFzTsRbVvCWzYQmlMW0UKzZCUqatzICLGjx48gKyYAADs=
"""),
'Host' : PhotoImage( data=r"""
R0lGODlhKQAyAMIHAJyeoK+wsrW2uMHCxM7P0Ozt7fn5+f///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACwAAAAAKQAyAAAD63i63P4wykmrvS4cwLv/IEhxRxGeKGBM3pa+X9QeBmxrT3gMNpyLrt6rgcJgisKXgIFMopaLpjMEVUinn2pQ1ImSrN8uGKCVegHn8bl8CqbV7jFbJ47H650592sX4zl6MX9ocIOBLYNvhkxtiYV8eYx0kJSEi2d7WFmSmZqRmIKeHoddoqOcoaZkqIiqq6CtqqQkrq9jnaKzaLW6Wy8DBMHCp7ClPT+ArMY2t1u9Qs3Et6k+W87KtMfW0r6x1d7P2uDYu+LLtt3nQ9ufxeXM7MkOuCnR7UTe6/jyEOqeWj/SYQEowxXBfgYPJAAAOw==
"""),
'Hosti': 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=
""" ),
'Switchi': 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' )
temp_file = parse_args()[0]
app = MiniEdit(template_file=temp_file)
app.mainloop()
-86
View File
@@ -1,86 +0,0 @@
#!/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.append( '10.0.0.200' )
targetips = ' '.join( targetips )
# BL: Not sure why loopback intf isn't up!
host.cmd( 'ifconfig lo up' )
# 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 ]
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 )
-81
View File
@@ -1,81 +0,0 @@
#!/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()
-35
View File
@@ -1,35 +0,0 @@
#!/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()
-36
View File
@@ -1,36 +0,0 @@
#!/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 )
-33
View File
@@ -1,33 +0,0 @@
#!/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()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

-68
View File
@@ -1,68 +0,0 @@
#!/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()
-73
View File
@@ -1,73 +0,0 @@
#!/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()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

-49
View File
@@ -1,49 +0,0 @@
#!/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()
-71
View File
@@ -1,71 +0,0 @@
#!/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
"""
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.log import lg
from mininet.node import Node, OVSKernelSwitch
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, prefixLen, 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
prefixLen: IP address prefix length (e.g. 8, 16, 24)
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, prefixLen, 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' ):
"Start a network, connect it to root ns, and run sshd on all hosts."
switch = network.switches[ 0 ] # switch to use
ip = '10.123.123.1' # our IP address on host network
routes = [ '10.0.0.0/8' ] # host networks to route to
connectToRootNS( network, switch, ip, 8, 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, switch=OVSKernelSwitch )
sshd( net )
-18
View File
@@ -1,18 +0,0 @@
#!/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 )
-32
View File
@@ -1,32 +0,0 @@
#!/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()
Executable
+182
View File
@@ -0,0 +1,182 @@
#!/bin/bash
test -e /etc/debian_version && DIST="Debian"
grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
if [[ $DIST == Ubuntu || $DIST == Debian ]]; then
update='sudo apt-get update'
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
update='sudo yum update'
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
function forwarder {
if [[ $cxx != true ]]; then
ndncxx
cxx="true"
fi
if [[ $DIST == Ubuntu || $DIST == Debian ]]; then
$install libpcap-dev pkg-config
fi
if [[ $DIST == Fedora ]]; then
$install libpcap-devel
fi
git clone --depth 1 https://github.com/named-data/NFD
cd NFD
./waf configure --without-websocket
./waf
sudo ./waf install
cd ../
}
function routing {
if [[ $cxx != true ]]; then
ndncxx
cxx="true"
fi
if [[ $DIST == Ubuntu ]]; then
$install liblog4cxx10-dev libprotobuf-dev protobuf-compiler
fi
if [[ $DIST == Fedora ]]; then
$install log4cxx log4cxx-devel openssl-devel protobuf-devel
fi
git clone --depth 1 https://github.com/named-data/NLSR
cd NLSR
./waf configure
./waf
sudo ./waf install
cd ../
}
function ndncxx {
if [[ updated != true ]]; then
$update
updated="true"
fi
if [[ $DIST == Ubuntu || $DIST == Debian ]]; then
$install git libsqlite3-dev libboost-all-dev make g++
crypto
fi
if [[ $DIST == Fedora ]]; then
$install gcc-c++ sqlite-devel boost-devel
fi
git clone --depth 1 https://github.com/named-data/ndn-cxx
cd ndn-cxx
./waf configure
./waf
sudo ./waf install
cd ../
}
function crypto {
mkdir crypto
cd crypto
$install unzip
wget http://www.cryptopp.com/cryptopp562.zip
unzip cryptopp562.zip
make
sudo make install
cd ../
}
function tools {
if [[ $cxx != true ]]; then
ndncxx
cxx="true"
fi
git clone --depth 1 https://github.com/named-data/ndn-tools
cd ndn-tools
./waf configure
./waf
sudo ./waf install
cd ../
}
function mininet {
if [[ updated != true ]]; then
$update
updated="true"
fi
if [[ $pysetup != true ]]; then
pysetup="true"
fi
git clone --depth 1 https://github.com/mininet/mininet
cd mininet
sudo ./util/install.sh -fnv
cd ../
}
function minindn {
if [[ updated != true ]]; then
$update
updated="true"
fi
if [[ $pysetup != true ]]; then
$install python-setuptools
pysetup="true"
fi
sudo mkdir -p /usr/local/etc/mini-ndn/
sudo cp ndn_utils/client.conf.sample /usr/local/etc/mini-ndn/
sudo cp ndn_utils/nfd.conf /usr/local/etc/mini-ndn/
sudo cp ndn_utils/nlsr.conf /usr/local/etc/mini-ndn/
sudo python setup.py install
}
function usage {
printf '\nUsage: %s [-mfrti]\n\n' $(basename $0) >&2
printf 'options:\n' >&2
printf -- ' -f: install NFD\n' >&2
printf -- ' -i: install mini-ndn\n' >&2
printf -- ' -m: install mininet and dependencies\n' >&2
printf -- ' -r: install NLSR\n' >&2
printf -- ' -t: install tools\n' >&2
exit 2
}
if [[ $# -eq 0 ]]; then
usage
else
while getopts 'mfrti' OPTION
do
case $OPTION in
f) forwarder;;
i) minindn;;
m) mininet;;
r) routing;;
t) tools;;
?) usage;;
esac
done
shift $(($OPTIND - 1))
fi
-1
View File
@@ -1 +0,0 @@
"Docstring to silence pylint; ignores --ignore option for __init__.py"
-60
View File
@@ -1,60 +0,0 @@
"""
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
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 udpbwtest'
# 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.
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
info( "*** Removing junk from /tmp\n" )
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
info( "*** Removing old screen sessions\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 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( "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" )
-376
View File
@@ -1,376 +0,0 @@
"""
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
from mininet.util import quietRun, isShellBuiltin, dumpNodeConnections
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
prompt = 'minindn> '
def __init__( self, mininet, stdin=sys.stdin, script=None ):
self.mn = mininet
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
self.nodemap = {} # map names to Node objects
for node in self.nodelist:
self.nodemap[ node.name ] = node
# Local variable bindings for py command
self.locals = { 'net': mininet }
self.locals.update( self.nodemap )
# 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.nodelist:
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
# Disable pylint "Unused argument: 'arg's'" messages, as well as
# "method could be a function" warning, since each CLI function
# must have the same interface
# pylint: disable-msg=R0201
helpStr = (
'You may also send a command to a node using:\n'
' <node> command {args}\n'
'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( [ node.name for node in sorted( self.nodelist ) ] )
output( 'available nodes are: \n%s\n' % nodes )
def do_net( self, _line ):
"List network connections."
dumpNodeConnections( self.nodelist )
def do_sh( self, line ):
"Run an external shell command"
call( line, shell=True )
# do_py() needs to catch any exception during eval()
# pylint: disable-msg=W0703
def do_py( self, line ):
"""Evaluate a Python expression.
Node names may be used, e.g.: h1.cmd('ls')"""
try:
result = eval( line, globals(), self.locals )
if not result:
return
elif isinstance( result, str ):
output( result + '\n' )
else:
output( repr( result ) + '\n' )
except Exception, e:
output( str( e ) + '\n' )
# pylint: enable-msg=W0703
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.nodemap:
err = True
error( "node '%s' not in network\n" % arg )
else:
hosts.append( self.nodemap[ 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.nodemap:
err = True
error( "node '%s' not in network\n" % arg )
else:
hosts.append( self.nodemap[ 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.nodelist:
output( '%s: %s\n' %
( node.name, ','.join( node.intfNames() ) ) )
def do_ccndump(self, _line):
"Dump FIB entries"
for node in self.nodelist:
if 'fib' in node.params:
output(node.name + ': ')
for name in node.params['fib']:
output(str(name) + ' ')
output('\n')
def do_dump( self, _line ):
"Dump node info."
for node in self.nodelist:
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.nodemap:
error( "node '%s' not in network\n" % arg )
else:
node = self.nodemap[ arg ]
self.mn.terms += makeTerms( [ node ], term = term )
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 <file>\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 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 not args:
return
if args and len(args) > 0 and args[ -1 ] == '\n':
args = args[ :-1 ]
rest = args.split( ' ' )
if first in self.nodemap:
node = self.nodemap[ first ]
# Substitute IP addresses for node names in command
rest = [ self.nodemap[ arg ].IP()
if arg in self.nodemap 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' % first )
# 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
-399
View File
@@ -1,399 +0,0 @@
"""
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
from time import sleep
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 )
# Does it help to sleep to let things run?
sleep( 0.001 )
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 1:0 hfsc default 1',
'%s class add dev %s parent 1:0 classid 1: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 1: tbf ' +
'rate %fMbit burst 15000 latency %fms' %
( bw, latency_ms ) ]
else:
cmds += [ '%s qdisc add dev %s root handle 1:0 htb default 1',
'%s class add dev %s parent 1:0 classid 1:1 htb ' +
'rate %fMbit burst 15k' % bw ]
parent = ' parent 1:1 '
# ECN or RED
if enable_ecn:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'min 30000 max 35000 avpkt 1500 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1 ecn' % bw ]
parent = ' parent 10: '
elif enable_red:
cmds += [ '%s qdisc add dev %s' + parent +
'handle 10: red limit 1000000 ' +
'min 30000 max 35000 avpkt 1500 ' +
'burst 20 ' +
'bandwidth %fmbit probability 1' % bw ]
parent = ' parent 10: '
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 ]
return cmds
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
cmds += self.delayCmds( delay=delay, jitter=jitter, loss=loss,
max_queue_size=max_queue_size,
parent=parent )
# 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
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)
-178
View File
@@ -1,178 +0,0 @@
"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.
Changed cls first args to mcs to satisfy pylint."""
def __init__( mcs, name, bases, dict_ ):
super( Singleton, mcs ).__init__( name, bases, dict_ )
mcs.instance = None
def __call__( mcs, *args, **kw ):
if mcs.instance is None:
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
return mcs.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
-68
View File
@@ -1,68 +0,0 @@
"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 )
-775
View File
@@ -1,775 +0,0 @@
"""
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 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, nextCCNnet
from mininet.term import cleanUpScreens, makeTerms
import pdb
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.0.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.ccnNetBase = '1.0.0.0'
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 isNdnhost(self, node):
if 'fib' in node.params:
return True
else:
return False
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
#pdb.set_trace()
#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 = {}
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"""
if not controller:
controller = self.controller
controller_new = controller( name, **params )
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ name ] = controller_new
return controller_new
# BL: is this better than just using nameToNode[] ?
# Should it have a better name?
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 )
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 self.isNdnhost(host):
host.configNdn()
host.configDefault(ip=None,mac=None)
elif intf:
host.configDefault( defaultRoute=intf )
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:
# 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():
#pdb.set_trace()
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 )
if self.isNdnhost(src):
src.setIP(ipStr(ipParse(self.ccnNetBase) + 1) + '/30', intf=src.name + '-eth' + str(srcPort))
dst.setIP(ipStr(ipParse(self.ccnNetBase) + 2) + '/30', intf=dst.name + '-eth' + str(dstPort))
self.ccnNetBase=nextCCNnet(self.ccnNetBase)
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."
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 hosts\n' % len( self.hosts ) )
for host in self.hosts:
info( host.name + ' ' )
host.terminate()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ' )
switch.stop()
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' )
ploss = 100 * lost / packets
output( "*** Results: %i%% dropped (%d/%d lost)\n" %
( ploss, lost, packets ) )
return ploss
@staticmethod
def _parsePingFull( pingOutput ):
"Parse ping output and return all data."
# 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, 0, 0, 0, 0)
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 )
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' )
-1107
View File
File diff suppressed because it is too large. Load diff
-60
View File
@@ -1,60 +0,0 @@
"""
Terminal creation and cleanup.
Utility functions to run a term (connected via screen(1)) on each host.
Requires GNU screen(1) and xterm(1).
Optionally uses gnome-terminal.
"""
import re
from subprocess import Popen
from mininet.log import error
from mininet.util import quietRun
def quoteArg( arg ):
"Quote an argument if it contains spaces."
return repr( arg ) if ' ' in arg else arg
def makeTerm( node, title='Node', term='xterm' ):
"""Run screen on a node, and hook up a terminal.
node: Node object
title: base title
term: 'xterm' or 'gterm'
returns: process created"""
title += ': ' + node.name
if not node.inNamespace:
title += ' (root)'
cmds = {
'xterm': [ 'xterm', '-title', title, '-e' ],
'gterm': [ 'gnome-terminal', '--title', title, '-e' ]
}
if term not in cmds:
error( 'invalid terminal type: %s' % term )
return
if not node.execed:
node.cmd( 'screen -dmS ' + 'mininet.' + node.name)
args = [ 'screen', '-D', '-RR', '-S', 'mininet.' + node.name ]
else:
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
if term == 'gterm':
# Compress these for gnome-terminal, which expects one token
# to follow the -e option
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
return Popen( cmds[ term ] + args )
def cleanUpScreens():
"Remove moldy old screen sessions."
r = r'(\d+\.mininet\.[hsc]\d+)'
output = quietRun( 'screen -ls' ).split( '\n' )
for line in output:
m = re.search( r, line )
if m:
quietRun( 'screen -S ' + m.group( 1 ) + ' -X quit' )
def makeTerms( nodes, title='Node', term='xterm' ):
"""Create terminals.
nodes: list of Node objects
title: base title for each
returns: list of created terminal processes"""
return [ makeTerm( node, title, term ) for node in nodes ]
-127
View File
@@ -1,127 +0,0 @@
#!/usr/bin/env python
"""Package: mininet
Test creation and pings for topologies with link and/or CPU options."""
import unittest
from mininet.net import Mininet
from mininet.node import OVSKernelSwitch
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel
SWITCH = OVSKernelSwitch
# 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)
class testOptionsTopo( unittest.TestCase ):
"Verify ability to create networks with host and link options."
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 )
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 )
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 )
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 )
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 )
# 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 )
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
-50
View File
@@ -1,50 +0,0 @@
#!/usr/bin/env python
"""Package: mininet
Test creation and all-pairs ping for each included mininet topo type."""
import unittest
from mininet.net import Mininet
from mininet.node import Host, Controller
from mininet.node import UserSwitch, OVSKernelSwitch
from mininet.topo import SingleSwitchTopo, LinearTopo
from mininet.log import setLogLevel
SWITCHES = { 'user': UserSwitch,
'ovsk': OVSKernelSwitch,
}
class testSingleSwitch( unittest.TestCase ):
"For each datapath type, test ping with single switch topologies."
def testMinimal( self ):
"Ping test with both datapaths on minimal topology"
for switch in SWITCHES.values():
mn = Mininet( SingleSwitchTopo(), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
def testSingle5( self ):
"Ping test with both datapaths on 5-host single-switch topology"
for switch in SWITCHES.values():
mn = Mininet( SingleSwitchTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
class testLinear( unittest.TestCase ):
"For each datapath type, test all-pairs ping with LinearNet."
def testLinear5( self ):
"Ping test with both datapaths on a 5-switch topology"
for switch in SWITCHES.values():
mn = Mininet( LinearTopo( k=5 ), switch, Host, Controller )
dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 )
if __name__ == '__main__':
setLogLevel( 'warning' )
unittest.main()
-237
View File
@@ -1,237 +0,0 @@
#!/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.
'''
# BL: we may have to fix compatibility here.
# networkx is also a fairly heavyweight dependency
# from networkx.classes.graph import Graph
from networkx import Graph
from mininet.util import irange, natural, naturalSeq
import pdb
class Topo(object):
"Data center network representation for structured multi-trees."
def __init__(self, hopts=None, sopts=None, lopts=None, ropts=None):
"""Topo object:
hinfo: default host options
sopts: default switch options
lopts: default link options"""
self.g = Graph()
self.node_info = {}
self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects
self.hopts = {} if hopts is None else hopts
self.ropts = {} if ropts is None else ropts
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"""
#pdb.set_trace()
if not opts:
if self.hopts:
opts = self.hopts
elif self.ropts:
opts = self.ropts
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.'''
#pdb.set_trace()
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 one host per switch."
def __init__(self, k=2, **opts):
"""Init.
k: number of switches (and hosts)
hconf: host configuration options
lconf: link configuration options"""
super(LinearTopo, self).__init__(**opts)
self.k = k
lastSwitch = None
for i in irange(1, k):
host = self.addHost('h%s' % i)
switch = self.addSwitch('s%s' % i)
self.addLink( host, switch)
if lastSwitch:
self.addLink( switch, lastSwitch)
lastSwitch = switch
-36
View File
@@ -1,36 +0,0 @@
"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 )
-473
View File
@@ -1,473 +0,0 @@
"Utility functions for Mininet."
from mininet.log import output, info, error, warn
from time import sleep
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
from select import poll, POLLIN
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, node, printError=False ):
"""Move interface to node, without retrying.
intf: string, interface
node: Node object
printError: if true, print error"""
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
quietRun( cmd )
links = node.cmd( 'ip link show' )
if not ( ' %s:' % intf ) in links:
if printError:
error( '*** Error: moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
return True
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
"""Move interface to node, retrying on failure.
intf: string, interface
node: Node object
printError: if true, print error"""
retry( retries, delaySecs, moveIntfNoRetry, intf, node, 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, or 10.x.y.z if w==0"""
w = ( ip >> 24 ) & 0xff
w = 10 if w == 0 else w
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 nextCCNnet(curCCNnet):
netNum = ipParse(curCCNnet)
return ipStr(netNum+4)
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"""
# Ugly but functional
assert i < ( 1 << ( 32 - prefixLen ) )
mask = 0xffffffff ^ ( ( 1 << prefixLen ) - 1 )
ipnum = i + ( ipBaseNum & mask )
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 True:
fds = poller.poll( timeoutms )
if fds:
for fd, _event in fds:
host = fdToHost[ fd ]
popen = popens[ host ]
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
if not line:
popen.poll()
if popen.returncode is not None:
poller.unregister( fd )
del popens[ host ]
if not popens:
return
else:
yield None, ''
# Other stuff we use
def fixLimits():
"Fix ridiculously small resource limits."
setrlimit( RLIMIT_NPROC, ( 8192, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 16384 ) )
def mountCgroups():
"Make sure cgroups file system is mounted"
mounts = quietRun( 'mount' )
cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset'
if ('cgroup on %s' % cgdir not in mounts and
'cgroups on %s' % cgdir not in mounts):
raise Exception( "cgroups not mounted on " + cgdir )
if 'cpuset on %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( '=' )
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
-179
View File
@@ -1,179 +0,0 @@
/* 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 <stdio.h>
#include <linux/sched.h>
#include <unistd.h>
#include <limits.h>
#include <syscall.h>
#include <fcntl.h>
#include <stdlib.h>
#include <limits.h>
#include <sched.h>
#if !defined(VERSION)
#define VERSION "(devel)"
#endif
void usage(char *name)
{
printf("Execution utility for Mininet\n\n"
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
"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(308, 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[])
{
char 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]);
}
File renamed without changes.
+3 -3
View File
@@ -21,8 +21,8 @@ class Nfd:
self.ndnFolder = "%s/.ndn" % self.homeFolder
self.clientConf = "%s/client.conf" % self.ndnFolder
# Copy nfd.conf file from mini-ndn/ndn_utils to the node's home
node.cmd("sudo cp ~/mini-ndn/ndn_utils/nfd.conf %s" % self.confFile)
# Copy nfd.conf file from /usr/local/etc/mini-ndn to the node's home
node.cmd("sudo cp /usr/local/etc/mini-ndn/nfd.conf %s" % self.confFile)
# Open the conf file and change socket file name
node.cmd("sudo sed -i 's|nfd.sock|%s.sock|g' %s" % (node.name, self.confFile))
@@ -31,7 +31,7 @@ class Nfd:
node.cmd("sudo mkdir %s" % self.ndnFolder)
# Copy the client.conf file and change the unix socket
node.cmd("sudo cp ~/mini-ndn/ndn_utils/client.conf.sample %s" % self.clientConf)
node.cmd("sudo cp /usr/local/etc/mini-ndn/client.conf.sample %s" % self.clientConf)
node.cmd("sudo sed -i 's|nfd.sock|%s.sock|g' %s" % (node.name, self.clientConf))
# Change home folder
+2 -2
View File
@@ -42,8 +42,8 @@ class NlsrConfigGenerator:
ROUTING_LINK_STATE = "ls"
ROUTING_HYPERBOLIC = "hr"
def __init__(self, node, home):
node.cmd("sudo cp %s/mini-ndn/ndn_utils/nlsr.conf nlsr.conf" % home)
def __init__(self, node):
node.cmd("sudo cp /usr/local/etc/mini-ndn/nlsr.conf nlsr.conf")
self.node = node
parameters = node.nlsrParameters
+3 -40
View File
@@ -1,46 +1,9 @@
#!/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', 'minindn', 'minindnedit' ] ]
modname = distname = 'mininet'
setup(
name=distname,
version=VERSION,
description='Process-based OpenFlow emulator with NDN extension',
author='Bob Lantz, Carlos Cabral',
author_email='rlantz@cs.stanford.edu, cabral@dca.fee.unicamp.br',
packages=find_packages(exclude='test'),
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://openflow.org/mininet.
This also includes an extension for using Content Centric
Networks based on the Named Data Networking (NDN) model.
""",
classifiers=[
"License :: OSI Approved :: BSD License",
"Programming Language :: Python",
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Topic :: Internet",
],
keywords='networking emulator protocol Internet OpenFlow SDN NDN NFD NLSR',
license='BSD',
install_requires=[
'setuptools',
'networkx'
],
scripts=scripts,
name = "Mini-NDN",
packages = find_packages(),
scripts = ['bin/minindn', 'bin/minindnedit'],
)
-94
View File
@@ -1,94 +0,0 @@
#!/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)"
-21
View File
@@ -1,21 +0,0 @@
# 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]
-89
View File
@@ -1,89 +0,0 @@
#!/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 ] )
-562
View File
@@ -1,562 +0,0 @@
#!/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
# 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
if ! which bc &> /dev/null; then
$install bc
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
else
echo "Install.sh currently only supports Ubuntu and Debian Lenny i386."
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_SRC=~/openvswitch
OVS_TAG=v$OVS_RELEASE
OVS_BUILD=$OVS_SRC/build-$KERNEL_NAME
OVS_KMODS=($OVS_BUILD/datapath/linux/{openvswitch_mod.ko,brcompat_mod.ko})
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 ~/linux-headers-* ~/linux-image-*
}
# Install Mininet deps
function mn_deps {
echo "Installing Mininet dependencies"
$install gcc make screen psmisc xterm ssh iperf iproute telnet \
python-setuptools python-networkx cgroup-bin ethtool help2man \
pyflakes pylint pep8
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" = "10.04" ]; then
echo "Upgrading networkx to avoid deprecation warning"
sudo easy_install --upgrade networkx
fi
# Add sysctl parameters as noted in the INSTALL file to increase kernel
# limits to support larger setups:
sudo su -c "cat $HOME/mini-ndn/util/sysctl_addon >> /etc/sysctl.conf"
# Load new sysctl settings:
sudo sysctl -p
echo "Installing Mininet/Mini-CCNx core"
pushd ~/mini-ndn
sudo make install
popd
}
# 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 ~/
$install git-core autoconf automake autotools-dev pkg-config \
make gcc libtool libc6-dev
git clone git://openflowswitch.org/openflow.git
cd ~/openflow
# Patch controller to handle more than 16 switches
patch -p1 < ~/mini-ndn/util/openflow-patches/controller.patch
# Resume the install:
./boot.sh
./configure
make
sudo make install
# Remove avahi-daemon, which may cause unwanted discovery packets to be
# sent during tests, near link status changes:
$remove avahi-daemon
# Disable IPv6. Add to /etc/modprobe.d/blacklist:
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"
cd ~
}
function wireshark {
echo "Installing Wireshark dissector..."
sudo apt-get install -y wireshark libgtk2.0-dev
if [ "$DIST" = "Ubuntu" ] && [ "$RELEASE" != "10.04" ]; then
# Install newer version
sudo apt-get install -y scons mercurial libglib2.0-dev
sudo apt-get install -y libwiretap-dev libwireshark-dev
cd ~
hg clone https://bitbucket.org/barnstorm/of-dissector
cd of-dissector/src
export WIRESHARK=/usr/include/wireshark
scons
# libwireshark0/ on 11.04; libwireshark1/ on later
WSDIR=`ls -d /usr/lib/wireshark/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 ~/openflow/utilities/wireshark_dissectors/openflow
make
sudo make install
fi
# Copy coloring rules: OF is white-on-blue:
mkdir -p ~/.wireshark
cp ~/mini-ndn/util/colorfilters ~/.wireshark
}
# Install Open vSwitch
# Instructions derived from OVS INSTALL, INSTALL.OpenFlow and README files.
function ovs {
echo "Installing Open vSwitch..."
# 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 ~
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 ~/
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"
}
# 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 ~/
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 ~/mini-ndn/util/nox-patches/*tutorial-port-nox-destiny*.patch
if [ "$DIST" = "Ubuntu" ] && [ `expr $RELEASE '>=' 12.04` = 1 ]; then
git am ~/mini-ndn/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=~/noxcore/build/src|' ~/.bashrc
# To verify this install:
#cd ~/noxcore/build/src
#./nox_core -v -i ptcp:
}
# "Install" POX
function pox {
echo "Installing POX into $HOME/pox..."
cd ~
git clone https://github.com/noxrepo/pox.git
}
# Install OFtest
function oftest {
echo "Installing oftest..."
# Install deps:
$install tcpdump python-scapy
# Install oftest:
cd ~/
git clone git://github.com/floodlight/oftest
cd oftest
cd tools/munger
sudo make install
}
# Install cbench
function cbench {
echo "Installing cbench..."
$install libsnmp-dev libpcap-dev libconfig-dev
cd ~/
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=$HOME/openflow
make
sudo make install || true # make install fails; force past this
}
function other {
echo "Doing other setup tasks..."
# Enable command auto completion using sudo; modify ~/.bashrc:
sed -i -e 's|# for examples$|&\ncomplete -cf sudo|' ~/.bashrc
# Install tcpdump and tshark, cmd-line packet dump tools. Also install gitk,
# a graphical git history viewer.
$install tcpdump tshark 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..."
sudo cp $OVS_KMODS $DRIVERS_DIR
sudo depmod -a ${KERNEL_NAME}
}
function all {
echo "Running all commands..."
kernel
mn_deps
of
wireshark
ovs
# NOX-classic is deprecated, but you can install it manually if desired.
# nox
pox
oftest
cbench
other
echo "Please reboot, then run ./mininet/util/install.sh -c to remove unneeded packages."
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 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"
# Remove mininet install script
rm -f install-mininet.sh
}
function usage {
printf 'Usage: %s [-acdfhkmntvxy]\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 10.04, 11.10\n' >&2
printf 'and Debian 5.0 (Lenny). 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 -- ' -f: install open(F)low\n' >&2
printf -- ' -h: print this (H)elp message\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 -- ' -r: remove existing Open vSwitch packages\n' >&2
printf -- ' -t: install o(T)her stuff\n' >&2
printf -- ' -v: install open (V)switch\n' >&2
printf -- ' -w: install OpenFlow (w)ireshark dissector\n' >&2
printf -- ' -x: install NO(X) OpenFlow controller\n' >&2
printf -- ' -y: install (A)ll packages\n' >&2
exit 2
}
if [ $# -eq 0 ]
then
all
else
while getopts 'abcdfhkmnprtvwx' OPTION
do
case $OPTION in
a) all;;
b) cbench;;
c) kernel_clean;;
d) vm_clean;;
f) of;;
h) usage;;
k) kernel;;
m) modprobe;;
n) mn_deps;;
p) pox;;
r) remove_ovs;;
t) other;;
v) ovs;;
w) wireshark;;
x) nox;;
?) usage;;
esac
done
shift $(($OPTIND - 1))
fi
-71
View File
@@ -1,71 +0,0 @@
#!/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
-13
View File
@@ -1,13 +0,0 @@
--- 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");
-18
View File
@@ -1,18 +0,0 @@
--- /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;';\
@@ -1,298 +0,0 @@
From 5c9610ffb88c89b0f36359ad3c7547831482a3ff Mon Sep 17 00:00:00 2001
From: Bob Lantz <rlantz@cs.stanford.edu>
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 = <fill in>
+ #if (destination MAC of the packet is known):
+ # Send unicast packet to known output port
+ #self.send_openflow( <fill in params> )
+ # Later, only after learning controller works:
+ # push down flow entry and remove the send_openflow command above.
+ #self.install_datapath_flow( <fill in params> )
+ #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 <boost/shared_array.hpp>
+#include <boost/bind.hpp>
+#ifdef LOG4CXX_ENABLED
+#include <boost/format.hpp>
+#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<Packet_in_event>
+ (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<char> 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<const Packet_in_event&>(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>,
+ tutorial);
+
+} // unnamed namespace
--
1.7.5.4
@@ -1,175 +0,0 @@
From 166693d7cb640d4a41251b87e92c52d9c688196b Mon Sep 17 00:00:00 2001
From: Bob Lantz <rlantz@cs.stanford.edu>
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
-2
View File
@@ -1,2 +0,0 @@
0001: This patch adds the OpenFlow tutorial module source code to nox-destiny.
0002: This patch hacks nox-destiny to compile on Ubuntu 12.04.
-5
View File
@@ -1,5 +0,0 @@
Patches 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
-15
View File
@@ -1,15 +0,0 @@
diff --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;
-26
View File
@@ -1,26 +0,0 @@
diff --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 <net/net_namespace.h>
+#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));
}
-11
View File
@@ -1,11 +0,0 @@
obj-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
-10
View File
@@ -1,10 +0,0 @@
Modified 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.
File diff suppressed because it is too large. Load diff
-17
View File
@@ -1,17 +0,0 @@
# 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
-198
View File
@@ -1,198 +0,0 @@
#!/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() )
-24
View File
@@ -1,24 +0,0 @@
#!/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 )
-24
View File
@@ -1,24 +0,0 @@
SSH_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
-40
View File
@@ -1,40 +0,0 @@
#!/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
sudo sed -i -e 's/Default/#Default/' /etc/sudoers
sudo sed -i -e 's/ubuntu/mininet-vm/' /etc/hostname
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
sudo sed -i -e 's/us.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
# 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 <<EOF
You may need to reboot and then:
sudo dpkg-reconfigure openvswitch-datapath-dkms
sudo service openvswitch-switch start
EOF