Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ce07884f4 | |||
| be53ac31d6 | |||
| 50bfbd0566 | |||
| a4d8049661 | |||
| da4c53b3c9 | |||
| 1b96ff3539 | |||
| 93d04123c6 | |||
| 22655cf9fd | |||
| fe82d2c04a | |||
| 0534932bf3 | |||
| 5f68be2273 | |||
| 68ae67dc58 | |||
| c4a85ab1d1 | |||
| ef3c885630 | |||
| bb35d04102 | |||
| ba05fd363b | |||
| e988b0f660 | |||
| b27cce08af | |||
| a38896c254 | |||
| 63ae13fcf9 | |||
| c5f6d0ff17 | |||
| ab8c4e9198 | |||
| 8daa4193c7 | |||
| 5c895eaad8 | |||
| 435d0d68b3 | |||
| 66e9845f05 | |||
| fe3340077c | |||
| c589660e17 | |||
| d9117c77e1 | |||
| 6cb68f26c9 | |||
| c60764c34f | |||
| ba723826b7 | |||
| 0165d7bdff | |||
| 1df1f9a1c5 | |||
| 2c916acc06 | |||
| 0a810b226c | |||
| 57c8f5934f | |||
| 125e669723 | |||
| a2486a6d66 | |||
| 15f2898f26 | |||
| 1c4adde1b6 | |||
| 4d22991245 | |||
| 90ea6c6bd1 | |||
| 269cecd3ee | |||
| c2be20f09e | |||
| df56fa2716 | |||
| f49e2539b7 | |||
| 4da7b3b7f0 | |||
| e5a5cd0070 | |||
| c5779deeb6 | |||
| 79c944aef4 | |||
| a23c6a2871 | |||
| 7acc693277 | |||
| 889698f7bd | |||
| f6f6d9282b | |||
| 5224884e5e | |||
| f063b023bd | |||
| a1edb167b1 | |||
| 613fac4bab | |||
| e0cd11ab21 | |||
| 5b818ad75c |
+4
-2
@@ -29,14 +29,16 @@ Andrew Ferguson
|
||||
Eder Leao Fernandes
|
||||
Gregory Gee
|
||||
Jon Hall
|
||||
Roan Huang
|
||||
Vitaly Ivanov
|
||||
Babis Kaidos
|
||||
Rich Lane
|
||||
Rémy Léone
|
||||
Zi Shen Lim
|
||||
David Mahler
|
||||
Murphy McCauley
|
||||
José Pedro Oliveira
|
||||
James Page
|
||||
Rich Lane
|
||||
Rémy Léone
|
||||
Angad Singh
|
||||
Piyush Srivastava
|
||||
Ed Swierk
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.2.1d1
|
||||
Mininet 2.2.1
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Mininet 2.2.1d1 License
|
||||
Mininet 2.2.1 License
|
||||
|
||||
Copyright (c) 2013-2014 Open Networking Laboratory
|
||||
Copyright (c) 2013-2015 Open Networking Laboratory
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
|
||||
The Leland Stanford Junior University
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
|
||||
|
||||
*The best way to emulate almost any network on your laptop!*
|
||||
|
||||
Mininet 2.2.1d1
|
||||
Mininet 2.2.1
|
||||
|
||||
### What is Mininet?
|
||||
|
||||
@@ -68,44 +68,25 @@ Mininet includes:
|
||||
|
||||
### New features in this release
|
||||
|
||||
This release provides a number of bug fixes as well as
|
||||
several new features, including:
|
||||
This is primarily a performance improvement and bug fix release.
|
||||
|
||||
* Improved OpenFlow 1.3 support
|
||||
- Batch startup has been implemented for Open vSwitch, improving
|
||||
startup performance.
|
||||
|
||||
- `mn --switch ovs,protocols=openflow13` starts OVS in 1.3 mode
|
||||
- `install.sh -w` installs a 1.3-compatible Wireshark dissector using
|
||||
Loxigen
|
||||
- `install.sh -y` installs the Ryu 1.3-compatible controller
|
||||
- OVS patch links have been implemented via OVSLink and --link ovs
|
||||
|
||||
* A new `nodelib.py` node library, and new `Node` types including
|
||||
`LinuxBridge`, `OVSBridge`, `LinuxRouter` (see `examples/`)
|
||||
and `NAT`
|
||||
Warning! These links have *serious limitations* compared to
|
||||
virtual Ethernet pairs: they are not attached to real Linux
|
||||
interfaces so you cannot use tcpdump or wireshark with them;
|
||||
they also cannot be used in long chains - we don't recommend more
|
||||
than 64 OVSLinks, for example --linear,64. However, they can offer
|
||||
significantly better performance than veth pairs, for certain
|
||||
configurations.
|
||||
|
||||
* A `--nat` option which connects a Mininet network to your LAN using NAT
|
||||
(For this to work correctly, Mininet's `--ipbase` subnet should not
|
||||
overlap with any external or internet IP addresses you wish to use)
|
||||
- You can now easily install Mininet on a Raspberry Pi ;-)
|
||||
|
||||
* An improved MiniEdit GUI (`examples/miniedit.py`) - thanks to
|
||||
Gregory Gee
|
||||
|
||||
* Support for multiple `--custom` arguments to `mn`
|
||||
|
||||
* Experimental cluster support - consult the
|
||||
[documentation](http://docs.mininet.org) for details -
|
||||
as well as `examples/cluster.py` and an experimental `--cluster`
|
||||
option for topologies built with the default `Host` and `OVSSwitch`
|
||||
classes:
|
||||
|
||||
`mn --cluster localhost,server1,server2`
|
||||
|
||||
Note that examples contain experimental features which might
|
||||
"graduate" into mainline Mininet in the future, but they should
|
||||
not be considered a stable part of the Mininet API!
|
||||
|
||||
A number of bugs have also been fixed, most notably multiple link
|
||||
support in `Topo()`. See github issues and the release notes on
|
||||
the Mininet wiki for additional information.
|
||||
- Additional information for this release and previous releases
|
||||
may be found in the release notes on docs.mininet.org
|
||||
|
||||
### Installation
|
||||
|
||||
@@ -129,6 +110,8 @@ Mininet mailing list, `mininet-discuss` at:
|
||||
|
||||
### Join Us
|
||||
|
||||
Thanks again to all of the Mininet contributors!
|
||||
|
||||
Mininet is an open source project and is currently hosted
|
||||
at <https://github.com/mininet>. You are encouraged to download
|
||||
the code, examine it, modify it, and submit bug reports, bug fixes,
|
||||
@@ -142,12 +125,5 @@ hard work that Mininet continues to grow and improve.
|
||||
Best wishes, and we look forward to seeing what you can do with
|
||||
Mininet to change the networking world!
|
||||
|
||||
The Mininet Core Team:
|
||||
|
||||
* Bob Lantz
|
||||
* Brian O'Connor
|
||||
* Cody Burkard
|
||||
|
||||
Thanks again to all of the Mininet contributors, particularly Gregory
|
||||
Gee for his work on MiniEdit.
|
||||
|
||||
Bob Lantz
|
||||
Mininet Core Team
|
||||
|
||||
@@ -25,15 +25,16 @@ from mininet.cli import CLI
|
||||
from mininet.log import lg, LEVELS, info, debug, warn, error
|
||||
from mininet.net import Mininet, MininetWithControlNet, VERSION
|
||||
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
RYU, NOX, RemoteController, findController,
|
||||
DefaultController,
|
||||
Ryu, NOX, RemoteController, findController,
|
||||
DefaultController, NullController,
|
||||
UserSwitch, OVSSwitch, OVSBridge,
|
||||
OVSLegacyKernelSwitch, IVSSwitch )
|
||||
IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink, OVSLink
|
||||
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
|
||||
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
|
||||
SingleSwitchReversedTopo, MinimalTopo )
|
||||
from mininet.topolib import TreeTopo, TorusTopo
|
||||
from mininet.util import customConstructor, splitArgs
|
||||
from mininet.util import customClass, specialClass, splitArgs
|
||||
from mininet.util import buildTopo
|
||||
|
||||
from functools import partial
|
||||
@@ -49,7 +50,7 @@ PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
|
||||
|
||||
# built in topologies, created only when run
|
||||
TOPODEF = 'minimal'
|
||||
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
|
||||
TOPOS = { 'minimal': MinimalTopo,
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo,
|
||||
@@ -62,24 +63,24 @@ SWITCHES = { 'user': UserSwitch,
|
||||
'ovsbr' : OVSBridge,
|
||||
# Keep ovsk for compatibility with 2.0
|
||||
'ovsk': OVSSwitch,
|
||||
'ovsl': OVSLegacyKernelSwitch,
|
||||
'ivs': IVSSwitch,
|
||||
'lxbr': LinuxBridge,
|
||||
'default': OVSSwitch }
|
||||
|
||||
HOSTDEF = 'proc'
|
||||
HOSTS = { 'proc': Host,
|
||||
'rt': partial( CPULimitedHost, sched='rt' ),
|
||||
'cfs': partial( CPULimitedHost, sched='cfs' ) }
|
||||
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
|
||||
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
|
||||
|
||||
CONTROLLERDEF = 'default'
|
||||
|
||||
CONTROLLERS = { 'ref': Controller,
|
||||
'ovsc': OVSController,
|
||||
'nox': NOX,
|
||||
'remote': RemoteController,
|
||||
'ryu': RYU,
|
||||
'ryu': Ryu,
|
||||
'default': DefaultController, # Note: replaced below
|
||||
'none': lambda name: None }
|
||||
'none': NullController }
|
||||
|
||||
LINKDEF = 'default'
|
||||
LINKS = { 'default': Link,
|
||||
@@ -97,24 +98,21 @@ ALTSPELLING = { 'pingall': 'pingAll',
|
||||
'iperfUDP': 'iperfUdp' }
|
||||
|
||||
|
||||
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
|
||||
def addDictOption( opts, choicesDict, default, name, **kwargs ):
|
||||
"""Convenience function to add choices dicts to OptionParser.
|
||||
opts: OptionParser instance
|
||||
choicesDict: dictionary of valid choices, must include default
|
||||
default: default choice key
|
||||
name: long option name
|
||||
help: string"""
|
||||
if default not in choicesDict:
|
||||
raise Exception( 'Invalid default %s for choices dict: %s' %
|
||||
( default, name ) )
|
||||
if not helpStr:
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
opts.add_option( '--' + name,
|
||||
type='string',
|
||||
default = default,
|
||||
help = helpStr )
|
||||
|
||||
kwargs: additional arguments to add_option"""
|
||||
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
|
||||
'[,param=value...]' )
|
||||
helpList = [ '%s=%s' % ( k, v.__name__ )
|
||||
for k, v in choicesDict.items() ]
|
||||
helpStr += ' ' + ( ' '.join( helpList ) )
|
||||
params = dict( type='string', default=default, help=helpStr )
|
||||
params.update( **kwargs )
|
||||
opts.add_option( '--' + name, **params )
|
||||
|
||||
def version( *_args ):
|
||||
"Print Mininet version and exit"
|
||||
@@ -160,7 +158,7 @@ class MininetRunner( object ):
|
||||
|
||||
def setCustom( self, name, value ):
|
||||
"Set custom parameters for MininetRunner."
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
|
||||
if name in ( 'topos', 'switches', 'hosts', 'controllers', 'links' ):
|
||||
# Update dictionaries
|
||||
param = name.upper()
|
||||
globals()[ param ].update( value )
|
||||
@@ -199,7 +197,7 @@ class MininetRunner( object ):
|
||||
opts = OptionParser( description=desc, usage=usage )
|
||||
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
|
||||
addDictOption( opts, HOSTS, HOSTDEF, 'host' )
|
||||
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
|
||||
addDictOption( opts, CONTROLLERS, [], 'controller', action='append' )
|
||||
addDictOption( opts, LINKS, LINKDEF, 'link' )
|
||||
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
|
||||
|
||||
@@ -240,7 +238,7 @@ class MininetRunner( object ):
|
||||
default=False, help="pin hosts to CPU cores "
|
||||
"(requires --host cfs or --host rt)" )
|
||||
opts.add_option( '--nat', action='callback', callback=self.setNat,
|
||||
help="adds a NAT to the topology that"
|
||||
help="[option=val...] adds a NAT to the topology that"
|
||||
" connects Mininet hosts to the physical network."
|
||||
" Warning: This may route any traffic on the machine"
|
||||
" that uses Mininet's"
|
||||
@@ -293,28 +291,28 @@ class MininetRunner( object ):
|
||||
|
||||
start = time.time()
|
||||
|
||||
if self.options.controller == 'default':
|
||||
if not self.options.controller:
|
||||
# Update default based on available controllers
|
||||
CONTROLLERS[ 'default' ] = findController()
|
||||
if CONTROLLERS[ 'default' ] is None:
|
||||
self.options.controller = [ 'default' ]
|
||||
if not CONTROLLERS[ 'default' ]:
|
||||
self.options.controller = [ 'none' ]
|
||||
if self.options.switch == 'default':
|
||||
info( '*** No default OpenFlow controller found '
|
||||
'for default switch!\n' )
|
||||
info( '*** Falling back to OVS Bridge\n' )
|
||||
self.options.switch = 'ovsbr'
|
||||
self.options.controller = 'none'
|
||||
elif self.options.switch in ( 'ovsbr', 'lxbr' ):
|
||||
self.options.controller = 'none'
|
||||
else:
|
||||
elif self.options.switch not in ( 'ovsbr', 'lxbr' ):
|
||||
raise Exception( "Could not find a default controller "
|
||||
"for switch %s" %
|
||||
self.options.switch )
|
||||
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
switch = customConstructor( SWITCHES, self.options.switch )
|
||||
host = customConstructor( HOSTS, self.options.host )
|
||||
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||
link = customConstructor( LINKS, self.options.link )
|
||||
switch = customClass( SWITCHES, self.options.switch )
|
||||
host = customClass( HOSTS, self.options.host )
|
||||
controller = [ customClass( CONTROLLERS, c )
|
||||
for c in self.options.controller ]
|
||||
link = customClass( LINKS, self.options.link )
|
||||
|
||||
if self.validate:
|
||||
self.validate( self.options )
|
||||
|
||||
@@ -49,7 +49,7 @@ from mininet.log import info, setLogLevel
|
||||
from mininet.net import Mininet, VERSION
|
||||
from mininet.util import netParse, ipAdd, quietRun
|
||||
from mininet.util import buildTopo
|
||||
from mininet.util import custom, customConstructor
|
||||
from mininet.util import custom, customClass
|
||||
from mininet.term import makeTerm, cleanUpScreens
|
||||
from mininet.node import Controller, RemoteController, NOX, OVSController
|
||||
from mininet.node import CPULimitedHost, Host, Node
|
||||
@@ -3222,7 +3222,7 @@ class MiniEdit( Frame ):
|
||||
return
|
||||
self.newTopology()
|
||||
topo = buildTopo( TOPOS, self.options.topo )
|
||||
link = customConstructor( LINKS, self.options.link )
|
||||
link = customClass( LINKS, self.options.link )
|
||||
importNet = Mininet(topo=topo, build=False, link=link)
|
||||
importNet.build()
|
||||
|
||||
|
||||
Executable
+275
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Multiple ovsdb OVS!!
|
||||
|
||||
We scale up by creating multiple ovsdb instances,
|
||||
each of which is shared by several OVS switches
|
||||
|
||||
The shell may also be shared among switch instances,
|
||||
which causes switch.cmd() and switch.popen() to be
|
||||
delegated to the ovsdb instance.
|
||||
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node, OVSSwitch
|
||||
from mininet.node import OVSBridge
|
||||
from mininet.link import Link, OVSIntf
|
||||
from mininet.topo import LinearTopo, SingleSwitchTopo
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
from mininet.clean import Cleanup, sh
|
||||
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
|
||||
class OVSDB( Node ):
|
||||
"Namespace for an OVSDB instance"
|
||||
|
||||
privateDirs = [ '/etc/openvswitch',
|
||||
'/var/run/openvswitch',
|
||||
'/var/log/openvswitch' ]
|
||||
|
||||
# Control network
|
||||
ipBase = '172.123.123.0/24'
|
||||
cnet = None
|
||||
nat = None
|
||||
|
||||
@classmethod
|
||||
def startControlNet( cls ):
|
||||
"Start control net if necessary and return it"
|
||||
cnet = cls.cnet
|
||||
if not cnet:
|
||||
info( '### Starting control network\n' )
|
||||
cnet = Mininet( ipBase=cls.ipBase )
|
||||
cswitch = cnet.addSwitch( 'ovsbr0', cls=OVSBridge )
|
||||
# Add NAT - note this can conflict with data network NAT
|
||||
info( '### Adding NAT for control and data networks'
|
||||
' (use --nat flush=0 for data network)\n' )
|
||||
cls.cnet = cnet
|
||||
cls.nat = cnet.addNAT( 'ovsdbnat0')
|
||||
cnet.start()
|
||||
info( '### Control network started\n' )
|
||||
return cnet
|
||||
|
||||
def stopControlNet( self ):
|
||||
info( '\n### Stopping control network\n' )
|
||||
cls = self.__class__
|
||||
cls.cnet.stop()
|
||||
info( '### Control network stopped\n' )
|
||||
|
||||
def addSwitch( self, switch ):
|
||||
"Add a switch to our namespace"
|
||||
# Attach first switch to cswitch!
|
||||
self.switches.append( switch )
|
||||
|
||||
def delSwitch( self, switch ):
|
||||
"Delete a switch from our namespace, and terminate if none left"
|
||||
self.switches.remove( switch )
|
||||
if not self.switches:
|
||||
self.stopOVS()
|
||||
|
||||
ovsdbCount = 0
|
||||
|
||||
def startOVS( self ):
|
||||
"Start new OVS instance"
|
||||
self.cmd( 'ovsdb-tool create /etc/openvswitch/conf.db' )
|
||||
self.cmd( 'ovsdb-server /etc/openvswitch/conf.db'
|
||||
' -vfile:emer -vfile:err -vfile:info'
|
||||
' --remote=punix:/var/run/openvswitch/db.sock '
|
||||
' --log-file=/var/log/openvswitch/ovsdb-server.log'
|
||||
' --pidfile=/var/run/openvswitch/ovsdb-server-mn.pid'
|
||||
' --no-chdir'
|
||||
' --detach' )
|
||||
|
||||
self.cmd( 'ovs-vswitchd unix:/var/run/openvswitch/db.sock'
|
||||
' -vfile:emer -vfile:err -vfile:info'
|
||||
' --mlockall --log-file=/var/log/openvswitch/ovs-vswitchd.log'
|
||||
' --pidfile=/var/run/openvswitch/ovs-vswitchd-mn.pid'
|
||||
' --no-chdir'
|
||||
' --detach' )
|
||||
|
||||
def stopOVS( self ):
|
||||
self.cmd( 'kill',
|
||||
'`cat /var/run/openvswitch/ovs-vswitchd-mn.pid`',
|
||||
'`cat /var/run/openvswitch/ovsdb-server-mn.pid`' )
|
||||
self.cmd( 'wait' )
|
||||
self.__class__.ovsdbCount -= 1
|
||||
if self.__class__.ovsdbCount <= 0:
|
||||
self.stopControlNet()
|
||||
|
||||
@classmethod
|
||||
def cleanUpOVS( cls ):
|
||||
"Clean up leftover ovsdb-server/ovs-vswitchd processes"
|
||||
info( '*** Shutting down extra ovsdb-server/ovs-vswitchd processes\n' )
|
||||
sh( 'pkill -f mn.pid' )
|
||||
|
||||
def self( self, *args, **kwargs ):
|
||||
"A fake constructor that sets params and returns self"
|
||||
self.params = kwargs
|
||||
return self
|
||||
|
||||
def __init__( self, **kwargs ):
|
||||
cls = self.__class__
|
||||
cls.ovsdbCount += 1
|
||||
cnet = self.startControlNet()
|
||||
# Create a new ovsdb namespace
|
||||
self.switches = []
|
||||
name = 'ovsdb%d' % cls.ovsdbCount
|
||||
kwargs.update( inNamespace=True )
|
||||
kwargs.setdefault( 'privateDirs', self.privateDirs )
|
||||
super( OVSDB, self ).__init__( name, **kwargs )
|
||||
ovsdb = cnet.addHost( name, cls=self.self, **kwargs )
|
||||
link = cnet.addLink( ovsdb, cnet.switches[ 0 ] )
|
||||
cnet.switches[ 0 ].attach( link.intf2 )
|
||||
ovsdb.configDefault()
|
||||
ovsdb.setDefaultRoute( 'via %s' % self.nat.intfs[ 0 ].IP() )
|
||||
ovsdb.startOVS()
|
||||
|
||||
|
||||
# Install cleanup callback
|
||||
Cleanup.addCleanupCallback( OVSDB.cleanUpOVS )
|
||||
|
||||
|
||||
class OVSSwitchNS( OVSSwitch ):
|
||||
"OVS Switch in shared OVSNS namespace"
|
||||
|
||||
isSetup = False
|
||||
|
||||
@classmethod
|
||||
def batchStartup( cls, switches ):
|
||||
result = []
|
||||
for ovsdb, switchGroup in groupby( switches, attrgetter( 'ovsdb') ):
|
||||
switchGroup = list( switchGroup )
|
||||
info( '(%s)' % ovsdb )
|
||||
result += OVSSwitch.batchStartup( switchGroup, run=ovsdb.cmd )
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def batchShutdown( cls, switches ):
|
||||
result = []
|
||||
for ovsdb, switchGroup in groupby( switches, attrgetter( 'ovsdb') ):
|
||||
switchGroup = list( switchGroup )
|
||||
info( '(%s)' % ovsdb )
|
||||
for switch in switches:
|
||||
if switch.pid == ovsdb.pid:
|
||||
switch.pid = None
|
||||
switch.shell = None
|
||||
result += OVSSwitch.batchShutdown( switchGroup, run=ovsdb.cmd )
|
||||
for switch in switchGroup:
|
||||
switch.ovsdbFree()
|
||||
return result
|
||||
|
||||
# OVSDB allocation
|
||||
groupSize = 64
|
||||
switchCount = 0
|
||||
lastOvsdb = None
|
||||
|
||||
@classmethod
|
||||
def ovsdbAlloc( cls, switch ):
|
||||
"Allocate (possibly new) OVSDB instance for switch"
|
||||
if cls.switchCount % switch.groupSize == 0:
|
||||
cls.lastOvsdb = OVSDB()
|
||||
cls.switchCount += 1
|
||||
cls.lastOvsdb.addSwitch( switch )
|
||||
return cls.lastOvsdb
|
||||
|
||||
def ovsdbFree( self ):
|
||||
"Deallocate OVSDB instance for switch"
|
||||
self.ovsdb.delSwitch( self )
|
||||
|
||||
def startShell( self, *args, **kwargs ):
|
||||
"Start shell in shared OVSDB namespace"
|
||||
ovsdb = self.ovsdbAlloc( self )
|
||||
kwargs.update( mnopts='-da %d ' % ovsdb.pid )
|
||||
self.ns = [ 'net' ]
|
||||
self.ovsdb = ovsdb
|
||||
self._waiting = False
|
||||
if self.privateShell:
|
||||
super( OVSSwitchNS, self ).startShell( *args, **kwargs )
|
||||
else:
|
||||
# Delegate methods and initialize local vars
|
||||
attrs = ( 'cmd', 'cmdPrint', 'sendCmd', 'waitOutput',
|
||||
'monitor', 'write', 'read',
|
||||
'pid', 'shell', 'stdout',)
|
||||
for attr in attrs:
|
||||
setattr( self, attr, getattr( ovsdb, attr ) )
|
||||
self.defaultIntf().updateIP()
|
||||
|
||||
@property
|
||||
def waiting( self ):
|
||||
"Optionally delegated to ovsdb"
|
||||
return self._waiting if self.privateShell else self.ovsdb.waiting
|
||||
|
||||
@waiting.setter
|
||||
def waiting( self, value ):
|
||||
"Optionally delegated to ovsdb (read only!)"
|
||||
if self.privateShell:
|
||||
_waiting = value
|
||||
|
||||
def start( self, controllers ):
|
||||
"Update controller IP addresses if necessary"
|
||||
for controller in controllers:
|
||||
if controller.IP() == '127.0.0.1' and not controller.intfs:
|
||||
controller.intfs[ 0 ] = self.ovsdb.nat.intfs[ 0 ]
|
||||
super( OVSSwitchNS, self ).start( controllers )
|
||||
|
||||
def stop( self, *args, **kwargs ):
|
||||
"Stop and free OVSDB namespace if necessary"
|
||||
self.ovsdbFree()
|
||||
|
||||
def terminate( self, *args, **kwargs ):
|
||||
if self.privateShell:
|
||||
super( OVSSwitchNS, self ).terminate( *args, **kwargs )
|
||||
else:
|
||||
self.pid = None
|
||||
self.shell= None
|
||||
|
||||
def defaultIntf( self ):
|
||||
return self.ovsdb.defaultIntf()
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"""n: number of OVS instances per OVSDB
|
||||
shell: run private shell/bash process? (False)
|
||||
If shell is shared/not private, cmd() and popen() are
|
||||
delegated to the OVSDB instance, which is different than
|
||||
regular OVSSwitch semantics!!"""
|
||||
self.groupSize = kwargs.pop( 'n', self.groupSize )
|
||||
self.privateShell = kwargs.pop( 'shell', False )
|
||||
super( OVSSwitchNS, self ).__init__( *args, **kwargs )
|
||||
|
||||
|
||||
class OVSLinkNS( Link ):
|
||||
"OVSLink that supports OVSSwitchNS"
|
||||
|
||||
def __init__( self, node1, node2, **kwargs ):
|
||||
"See Link.__init__() for options"
|
||||
self.isPatchLink = False
|
||||
if ( isinstance( node1, OVSSwitch ) and
|
||||
isinstance( node2, OVSSwitch ) and
|
||||
getattr( node1, 'ovsdb', None ) ==
|
||||
getattr( node2, 'ovsdb', None ) ):
|
||||
self.isPatchLink = True
|
||||
kwargs.update( cls1=OVSIntf, cls2=OVSIntf )
|
||||
Link.__init__( self, node1, node2, **kwargs )
|
||||
|
||||
switches = { 'ovsns': OVSSwitchNS, 'ovsm': OVSSwitchNS }
|
||||
|
||||
links = { 'ovs': OVSLinkNS }
|
||||
|
||||
def test():
|
||||
"Test OVSNS switch"
|
||||
setLogLevel( 'info' )
|
||||
topo = TreeTopo( depth=4, fanout=2 )
|
||||
net = Mininet( topo=topo, switch=OVSSwitchNS )
|
||||
# Add connectivity to controller which is on LAN or in root NS
|
||||
# net.addNAT().configDefault()
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
+38
-13
@@ -45,6 +45,10 @@ class CLI( Cmd ):
|
||||
prompt = 'mininet> '
|
||||
|
||||
def __init__( self, mininet, stdin=sys.stdin, script=None ):
|
||||
"""Start and run interactive or batch mode CLI
|
||||
mininet: Mininet network object
|
||||
stdin: standard input for CLI
|
||||
script: script to run in batch mode"""
|
||||
self.mn = mininet
|
||||
# Local variable bindings for py command
|
||||
self.locals = { 'net': mininet }
|
||||
@@ -56,33 +60,54 @@ class CLI( Cmd ):
|
||||
Cmd.__init__( self )
|
||||
info( '*** Starting CLI:\n' )
|
||||
|
||||
# Set up history if readline is available
|
||||
try:
|
||||
import readline
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
history_path = os.path.expanduser('~/.mininet_history')
|
||||
if os.path.isfile(history_path):
|
||||
readline.read_history_file(history_path)
|
||||
atexit.register(lambda: readline.write_history_file(history_path))
|
||||
|
||||
if self.inputFile:
|
||||
self.do_source( self.inputFile )
|
||||
return
|
||||
|
||||
self.initReadline()
|
||||
self.run()
|
||||
|
||||
readlineInited = False
|
||||
|
||||
@classmethod
|
||||
def initReadline( cls ):
|
||||
"Set up history if readline is available"
|
||||
# Only set up readline once to prevent multiplying the history file
|
||||
if cls.readlineInited:
|
||||
return
|
||||
cls.readlineInited = True
|
||||
try:
|
||||
from readline import read_history_file, write_history_file
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
history_path = os.path.expanduser( '~/.mininet_history' )
|
||||
if os.path.isfile( history_path ):
|
||||
read_history_file( history_path )
|
||||
atexit.register( lambda: write_history_file( history_path ) )
|
||||
|
||||
def run( self ):
|
||||
"Run our cmdloop(), catching KeyboardInterrupt"
|
||||
while True:
|
||||
try:
|
||||
# Make sure no nodes are still waiting
|
||||
for node in self.mn.values():
|
||||
while node.waiting:
|
||||
info( 'stopping', node, '\n' )
|
||||
node.sendInt()
|
||||
node.waitOutput()
|
||||
if self.isatty():
|
||||
quietRun( 'stty echo sane intr "^C"' )
|
||||
quietRun( 'stty echo sane intr ^C' )
|
||||
self.cmdloop()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
output( '\nInterrupt\n' )
|
||||
# Output a message - unless it's also interrupted
|
||||
# pylint: disable=broad-except
|
||||
try:
|
||||
output( '\nInterrupt\n' )
|
||||
except Exception:
|
||||
pass
|
||||
# pylint: enable=broad-except
|
||||
|
||||
def emptyline( self ):
|
||||
"Don't repeat last command when you hit return."
|
||||
|
||||
+4
-2
@@ -108,7 +108,7 @@ from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
|
||||
from mininet.term import cleanUpScreens, makeTerms
|
||||
|
||||
# Mininet version: should be consistent with README and LICENSE
|
||||
VERSION = "2.2.1d1"
|
||||
VERSION = "2.2.1"
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
@@ -357,6 +357,8 @@ class Mininet( object ):
|
||||
options.setdefault( 'port1', port1 )
|
||||
if port2 is not None:
|
||||
options.setdefault( 'port2', port2 )
|
||||
if self.intf is not None:
|
||||
options.setdefault( 'intf', self.intf )
|
||||
# Set default MAC - this should probably be in Link
|
||||
options.setdefault( 'addr1', self.randMac() )
|
||||
options.setdefault( 'addr2', self.randMac() )
|
||||
@@ -402,7 +404,7 @@ class Mininet( object ):
|
||||
if not isinstance( classes, list ):
|
||||
classes = [ classes ]
|
||||
for i, cls in enumerate( classes ):
|
||||
# Allow Controller objects because nobody understands currying
|
||||
# Allow Controller objects because nobody understands partial()
|
||||
if isinstance( cls, Controller ):
|
||||
self.addController( cls )
|
||||
else:
|
||||
|
||||
+36
-66
@@ -23,20 +23,26 @@ Switch: superclass for switch nodes.
|
||||
UserSwitch: a switch using the user-space switch from the OpenFlow
|
||||
reference implementation.
|
||||
|
||||
KernelSwitch: a switch using the kernel switch from the OpenFlow reference
|
||||
implementation.
|
||||
|
||||
OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
|
||||
OVSSwitch: a switch using the Open vSwitch OpenFlow-compatible switch
|
||||
implementation (openvswitch.org).
|
||||
|
||||
OVSBridge: an Ethernet bridge implemented using Open vSwitch.
|
||||
Supports STP.
|
||||
|
||||
IVSSwitch: OpenFlow switch using the Indigo Virtual Switch.
|
||||
|
||||
Controller: superclass for OpenFlow controllers. The default controller
|
||||
is controller(8) from the reference implementation.
|
||||
|
||||
OVSController: The test controller from Open vSwitch.
|
||||
|
||||
NOXController: a controller node using NOX (noxrepo.org).
|
||||
|
||||
Ryu: The Ryu controller (https://osrg.github.io/ryu/)
|
||||
|
||||
RemoteController: a remote controller node, which may use any
|
||||
arbitrary OpenFlow-compatible controller, and which is not
|
||||
created or managed by mininet.
|
||||
created or managed by Mininet.
|
||||
|
||||
Future enhancements:
|
||||
|
||||
@@ -57,7 +63,7 @@ from time import sleep
|
||||
from mininet.log import info, error, warn, debug
|
||||
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
|
||||
numCores, retry, mountCgroups )
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, TUN
|
||||
from mininet.link import Link, Intf, TCIntf, OVSIntf
|
||||
from re import findall
|
||||
from distutils.version import StrictVersion
|
||||
@@ -162,6 +168,8 @@ class Node( object ):
|
||||
|
||||
def mountPrivateDirs( self ):
|
||||
"mount private directories"
|
||||
# Avoid expanding a string into a list of chars
|
||||
assert not isinstance( self.privateDirs, basestring )
|
||||
for directory in self.privateDirs:
|
||||
if isinstance( directory, tuple ):
|
||||
# mount given private directory
|
||||
@@ -507,15 +515,13 @@ class Node( object ):
|
||||
mac: MAC address as string"""
|
||||
return self.intf( intf ).setMAC( mac )
|
||||
|
||||
def setIP( self, ip, prefixLen=8, intf=None ):
|
||||
def setIP( self, ip, prefixLen=8, intf=None, **kwargs ):
|
||||
"""Set the IP address for an interface.
|
||||
intf: intf or intf name
|
||||
ip: IP address as a string
|
||||
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
|
||||
# This should probably be rethought
|
||||
if '/' not in ip:
|
||||
ip = '%s/%s' % ( ip, prefixLen )
|
||||
return self.intf( intf ).setIP( ip )
|
||||
prefixLen: prefix length, e.g. 8 for /8 or 16M addrs
|
||||
kwargs: any additional arguments for intf.setIP"""
|
||||
return self.intf( intf ).setIP( ip, prefixLen, **kwargs )
|
||||
|
||||
def IP( self, intf=None ):
|
||||
"Return IP address of a node or specific interface."
|
||||
@@ -897,6 +903,12 @@ class Switch( Node ):
|
||||
debug( 'Assuming', repr( self ), 'is connected to a controller\n' )
|
||||
return True
|
||||
|
||||
def stop( self, deleteIntfs=True ):
|
||||
"""Stop switch
|
||||
deleteIntfs: delete interfaces? (True)"""
|
||||
if deleteIntfs:
|
||||
self.deleteIntfs()
|
||||
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
@@ -1003,56 +1015,6 @@ class UserSwitch( Switch ):
|
||||
self.cmd( 'kill %ofprotocol' )
|
||||
super( UserSwitch, self ).stop( deleteIntfs )
|
||||
|
||||
class OVSLegacyKernelSwitch( Switch ):
|
||||
"""Open VSwitch legacy kernel-space switch using ovs-openflowd.
|
||||
Currently only works in the root namespace."""
|
||||
|
||||
def __init__( self, name, dp=None, **kwargs ):
|
||||
"""Init.
|
||||
name: name for switch
|
||||
dp: netlink id (0, 1, 2, ...)
|
||||
defaultMAC: default MAC as unsigned int; random value if None"""
|
||||
Switch.__init__( self, name, **kwargs )
|
||||
self.dp = dp if dp else self.name
|
||||
self.intf = self.dp
|
||||
if self.inNamespace:
|
||||
error( "OVSKernelSwitch currently only works"
|
||||
" in the root namespace.\n" )
|
||||
exit( 1 )
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Ensure any dependencies are loaded; if not, try to load them."
|
||||
pathCheck( 'ovs-dpctl', 'ovs-openflowd',
|
||||
moduleName='Open vSwitch (openvswitch.org)')
|
||||
moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
|
||||
|
||||
def start( self, controllers ):
|
||||
"Start up kernel datapath."
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
# Delete local datapath if it exists;
|
||||
# then create a new one monitoring the given interfaces
|
||||
self.cmd( 'ovs-dpctl del-dp ' + self.dp )
|
||||
self.cmd( 'ovs-dpctl add-dp ' + self.dp )
|
||||
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
|
||||
self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
|
||||
# Run protocol daemon
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
self.cmd( 'ovs-openflowd ' + self.dp +
|
||||
' ' + clist +
|
||||
' --fail=secure ' + self.opts +
|
||||
' --datapath-id=' + self.dpid +
|
||||
' 1>' + ofplog + ' 2>' + ofplog + '&' )
|
||||
self.execed = False
|
||||
|
||||
def stop( self, deleteIntfs=True ):
|
||||
"""Terminate kernel datapath."
|
||||
deleteIntfs: delete interfaces? (True)"""
|
||||
quietRun( 'ovs-dpctl del-dp ' + self.dp )
|
||||
self.cmd( 'kill %ovs-openflowd' )
|
||||
super( OVSLegacyKernelSwitch, self ).stop( deleteIntfs )
|
||||
|
||||
|
||||
class OVSSwitch( Switch ):
|
||||
"Open vSwitch switch. Depends on ovs-vsctl."
|
||||
@@ -1183,7 +1145,7 @@ class OVSSwitch( Switch ):
|
||||
if self.protocols and not self.isOldOVS():
|
||||
opts += ' protocols=%s' % self.protocols
|
||||
if self.stp and self.failMode == 'standalone':
|
||||
opts += ' stp_enable=true' % self
|
||||
opts += ' stp_enable=true'
|
||||
return opts
|
||||
|
||||
def start( self, controllers ):
|
||||
@@ -1289,11 +1251,14 @@ OVSKernelSwitch = OVSSwitch
|
||||
class OVSBridge( OVSSwitch ):
|
||||
"OVSBridge is an OVSSwitch in standalone/bridge mode"
|
||||
|
||||
def __init__( self, args, **kwargs ):
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"""stp: enable Spanning Tree Protocol (False)
|
||||
see OVSSwitch for other options"""
|
||||
kwargs.update( failMode='standalone' )
|
||||
OVSSwitch.__init__( self, args, **kwargs )
|
||||
OVSSwitch.__init__( self, *args, **kwargs )
|
||||
|
||||
def start( self, controllers ):
|
||||
"Start bridge, ignoring controllers argument"
|
||||
OVSSwitch.start( self, controllers=[] )
|
||||
|
||||
def connected( self ):
|
||||
@@ -1488,7 +1453,7 @@ class NOX( Controller ):
|
||||
cdir=noxCoreDir,
|
||||
**kwargs )
|
||||
|
||||
class RYU( Controller ):
|
||||
class Ryu( Controller ):
|
||||
"Controller to run Ryu application"
|
||||
def __init__( self, name, *ryuArgs, **kwargs ):
|
||||
"""Init.
|
||||
@@ -1510,6 +1475,7 @@ class RYU( Controller ):
|
||||
cdir=ryuCoreDir,
|
||||
**kwargs )
|
||||
|
||||
|
||||
class RemoteController( Controller ):
|
||||
"Controller running outside of Mininet's control."
|
||||
|
||||
@@ -1553,3 +1519,7 @@ def DefaultController( name, controllers=DefaultControllers, **kwargs ):
|
||||
if not controller:
|
||||
raise Exception( 'Could not find a default OpenFlow controller' )
|
||||
return controller( name, **kwargs )
|
||||
|
||||
def NullController( *_args, **_kwargs ):
|
||||
"Nonexistent controller - simply returns None"
|
||||
return None
|
||||
|
||||
+30
-20
@@ -7,6 +7,7 @@ This contains additional Node types which you may find to be useful.
|
||||
from mininet.node import Node, Switch
|
||||
from mininet.log import info, warn
|
||||
from mininet.moduledeps import pathCheck
|
||||
from mininet.util import quietRun
|
||||
|
||||
import re
|
||||
|
||||
@@ -59,23 +60,31 @@ class LinuxBridge( Switch ):
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
"Make sure our class dependencies are available"
|
||||
"Check dependencies and warn about firewalling"
|
||||
pathCheck( 'brctl', moduleName='bridge-utils' )
|
||||
# Disable Linux bridge firewalling so that traffic can flow!
|
||||
for table in 'arp', 'ip', 'ip6':
|
||||
cmd = 'sysctl net.bridge.bridge-nf-call-%stables' % table
|
||||
out = quietRun( cmd ).strip()
|
||||
if out.endswith( '1' ):
|
||||
warn( 'Warning: Linux bridge may not work with', out, '\n' )
|
||||
|
||||
|
||||
class NAT( Node ):
|
||||
"NAT: Provides connectivity to external network"
|
||||
|
||||
def __init__( self, name, inetIntf=None, subnet='10.0/8',
|
||||
localIntf=None, **params):
|
||||
localIntf=None, flush=False, **params):
|
||||
"""Start NAT/forwarding between Mininet and external network
|
||||
inetIntf: interface for internet access
|
||||
subnet: Mininet subnet (default 10.0/8)="""
|
||||
subnet: Mininet subnet (default 10.0/8)
|
||||
flush: flush iptables before installing NAT rules"""
|
||||
super( NAT, self ).__init__( name, **params )
|
||||
|
||||
self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
|
||||
self.subnet = subnet
|
||||
self.localIntf = localIntf
|
||||
self.flush = flush
|
||||
|
||||
def config( self, **params ):
|
||||
"""Configure the NAT and iptables"""
|
||||
@@ -86,17 +95,15 @@ class NAT( Node ):
|
||||
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
# Flush any currently active rules
|
||||
# TODO: is this safe?
|
||||
self.cmd( 'iptables -F' )
|
||||
self.cmd( 'iptables -t nat -F' )
|
||||
if self.flush:
|
||||
self.cmd( 'iptables -F' )
|
||||
self.cmd( 'iptables -t nat -F' )
|
||||
# Create default entries for unmatched traffic
|
||||
self.cmd( 'iptables -P INPUT ACCEPT' )
|
||||
self.cmd( 'iptables -P OUTPUT ACCEPT' )
|
||||
self.cmd( 'iptables -P FORWARD DROP' )
|
||||
|
||||
# Create default entries for unmatched traffic
|
||||
self.cmd( 'iptables -P INPUT ACCEPT' )
|
||||
self.cmd( 'iptables -P OUTPUT ACCEPT' )
|
||||
self.cmd( 'iptables -P FORWARD DROP' )
|
||||
|
||||
# Configure NAT
|
||||
# Install NAT rules
|
||||
self.cmd( 'iptables -I FORWARD',
|
||||
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
|
||||
self.cmd( 'iptables -A FORWARD',
|
||||
@@ -136,13 +143,16 @@ class NAT( Node ):
|
||||
return fallback
|
||||
|
||||
def terminate( self ):
|
||||
"""Stop NAT/forwarding between Mininet and external network"""
|
||||
# Flush any currently active rules
|
||||
# TODO: is this safe?
|
||||
self.cmd( 'iptables -F' )
|
||||
self.cmd( 'iptables -t nat -F' )
|
||||
|
||||
"Stop NAT/forwarding between Mininet and external network"
|
||||
# Remote NAT rules
|
||||
self.cmd( 'iptables -D FORWARD',
|
||||
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
|
||||
self.cmd( 'iptables -D FORWARD',
|
||||
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -D FORWARD',
|
||||
'-i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
|
||||
self.cmd( 'iptables -t nat -D POSTROUTING',
|
||||
'-o', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
|
||||
# Instruct the kernel to stop forwarding
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
||||
|
||||
super( NAT, self ).terminate()
|
||||
|
||||
+3
-2
@@ -35,7 +35,7 @@ def tunnelX11( node, display=None):
|
||||
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
|
||||
return 'localhost:' + screen, node.popen( cmd )
|
||||
|
||||
def makeTerm( node, title='Node', term='xterm', display=None ):
|
||||
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
|
||||
"""Create an X11 tunnel to the node and start up a terminal.
|
||||
node: Node object
|
||||
title: base title
|
||||
@@ -54,7 +54,8 @@ def makeTerm( node, title='Node', term='xterm', display=None ):
|
||||
display, tunnel = tunnelX11( node, display )
|
||||
if display is None:
|
||||
return []
|
||||
term = node.popen( cmds[ term ] + [ display, '-e', 'env TERM=ansi bash'] )
|
||||
term = node.popen( cmds[ term ] +
|
||||
[ display, '-e', 'env TERM=ansi %s' % cmd ] )
|
||||
return [ tunnel, term ] if tunnel else [ term ]
|
||||
|
||||
def runX11( node, cmd ):
|
||||
|
||||
@@ -8,8 +8,7 @@ import sys
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Host, Controller
|
||||
from mininet.node import ( UserSwitch, OVSSwitch, OVSLegacyKernelSwitch,
|
||||
IVSSwitch )
|
||||
from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch )
|
||||
from mininet.topo import Topo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.util import quietRun
|
||||
@@ -81,12 +80,6 @@ class testSwitchOVSUser( TestSwitchDpidAssignmentOVS ):
|
||||
"Test dpid assignnment of OVS User Switch."
|
||||
switchClass = OVSUser
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ovs-openflowd' ),
|
||||
'OVS Legacy Kernel switch is not installed' )
|
||||
class testSwitchOVSLegacyKernel( TestSwitchDpidAssignmentOVS ):
|
||||
"Test dpid assignnment of OVS Legacy Kernel Switch."
|
||||
switchClass = OVSLegacyKernelSwitch
|
||||
|
||||
@unittest.skipUnless( quietRun( 'which ivs-ctl' ),
|
||||
'IVS switch is not installed' )
|
||||
class testSwitchIVS( TestSwitchDpidAssignmentOVS ):
|
||||
|
||||
@@ -318,6 +318,12 @@ class SingleSwitchReversedTopo( Topo ):
|
||||
port1=0, port2=( k - h + 1 ) )
|
||||
|
||||
|
||||
class MinimalTopo( SingleSwitchTopo ):
|
||||
"Minimal topology with two hosts and one switch"
|
||||
def build( self ):
|
||||
return SingleSwitchTopo.build( self, k=2 )
|
||||
|
||||
|
||||
class LinearTopo( Topo ):
|
||||
"Linear topology of k switches, with n hosts per switch."
|
||||
|
||||
|
||||
+13
-4
@@ -45,10 +45,18 @@ class TorusTopo( Topo ):
|
||||
without STP turned on! It can be used with STP, e.g.:
|
||||
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
|
||||
|
||||
def build( self, x, y ):
|
||||
def build( self, x, y, n=1 ):
|
||||
"""x: dimension of torus in x-direction
|
||||
y: dimension of torus in y-direction
|
||||
n: number of hosts per switch"""
|
||||
if x < 3 or y < 3:
|
||||
raise Exception( 'Please use 3x3 or greater for compatibility '
|
||||
'with 2.1' )
|
||||
if n == 1:
|
||||
genHostName = lambda loc, k: 'h%s' % ( loc )
|
||||
else:
|
||||
genHostName = lambda loc, k: 'h%sx%d' % ( loc, k )
|
||||
|
||||
hosts, switches, dpid = {}, {}, 0
|
||||
# Create and wire interior
|
||||
for i in range( 0, x ):
|
||||
@@ -57,9 +65,10 @@ class TorusTopo( Topo ):
|
||||
# dpid cannot be zero for OVS
|
||||
dpid = ( i + 1 ) * 256 + ( j + 1 )
|
||||
switch = switches[ i, j ] = self.addSwitch(
|
||||
's' + loc, dpid='%016x' % dpid )
|
||||
host = hosts[ i, j ] = self.addHost( 'h' + loc )
|
||||
self.addLink( host, switch )
|
||||
's' + loc, dpid='%x' % dpid )
|
||||
for k in range( 0, n ):
|
||||
host = hosts[ i, j, k ] = self.addHost( genHostName( loc, k + 1 ) )
|
||||
self.addLink( host, switch )
|
||||
# Connect switches
|
||||
for i in range( 0, x ):
|
||||
for j in range( 0, y ):
|
||||
|
||||
+39
-27
@@ -523,43 +523,55 @@ def splitArgs( argstr ):
|
||||
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.
|
||||
def customClass( classes, argStr ):
|
||||
"""Return customized class based on argStr
|
||||
The args and key/val pairs in argStr will be automatically applied
|
||||
when the generated class is later used.
|
||||
"""
|
||||
cname, newargs, kwargs = splitArgs( argStr )
|
||||
constructor = constructors.get( cname, None )
|
||||
|
||||
if not constructor:
|
||||
cname, args, kwargs = splitArgs( argStr )
|
||||
cls = classes.get( cname, None )
|
||||
if not cls:
|
||||
raise Exception( "error: %s is unknown - please specify one of %s" %
|
||||
( cname, constructors.keys() ) )
|
||||
( cname, classes.keys() ) )
|
||||
if not args and not kwargs:
|
||||
return cls
|
||||
|
||||
if not newargs and not kwargs:
|
||||
return constructor
|
||||
return specialClass( cls, append=args, defaults=kwargs )
|
||||
|
||||
if not isinstance( constructor, type ):
|
||||
raise Exception( "error: invalid arguments %s" % argStr )
|
||||
def specialClass( cls, prepend=None, append=None,
|
||||
defaults=None, override=None ):
|
||||
"""Like functools.partial, but it returns a class
|
||||
prepend: arguments to prepend to argument list
|
||||
append: arguments to append to argument list
|
||||
defaults: default values for keyword arguments
|
||||
override: keyword arguments to override"""
|
||||
|
||||
# Return a customized subclass
|
||||
cls = constructor
|
||||
if prepend is None:
|
||||
prepend = []
|
||||
|
||||
if append is None:
|
||||
append = []
|
||||
|
||||
if defaults is None:
|
||||
defaults = {}
|
||||
|
||||
if override is None:
|
||||
override = {}
|
||||
|
||||
class CustomClass( cls ):
|
||||
"Customized subclass, useful for Node, Link, and other classes"
|
||||
def __init__( self, name, *args, **params ):
|
||||
params = params.copy()
|
||||
params.update( kwargs )
|
||||
if not newargs:
|
||||
cls.__init__( self, name, *args, **params )
|
||||
return
|
||||
if args:
|
||||
warn( 'warning: %s replacing %s with %s\n' %
|
||||
( constructor, args, newargs ) )
|
||||
cls.__init__( self, name, *newargs, **params )
|
||||
"Customized subclass with preset args/params"
|
||||
def __init__( self, *args, **params ):
|
||||
newparams = defaults.copy()
|
||||
newparams.update( params )
|
||||
newparams.update( override )
|
||||
cls.__init__( self, *( list( prepend ) + list( args ) +
|
||||
list( append ) ),
|
||||
**newparams )
|
||||
|
||||
CustomClass.__name__ = '%s%s' % ( cls.__name__, kwargs )
|
||||
CustomClass.__name__ = '%s%s' % ( cls.__name__, defaults )
|
||||
return CustomClass
|
||||
|
||||
|
||||
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.
|
||||
|
||||
+24
-24
@@ -25,20 +25,20 @@ clean=false
|
||||
declare -a hosts=()
|
||||
user=$(whoami)
|
||||
SSHDIR=/tmp/mn/ssh
|
||||
USERDIR=/home/$user/.ssh
|
||||
usage=$'./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
|
||||
USERDIR=$HOME/.ssh
|
||||
usage="./clustersetup.sh [ -p|h|c ] [ host1 ] [ host2 ] ...\n
|
||||
Authenticate yourself and other cluster nodes to each other
|
||||
via ssh for mininet cluster edition. By default, we use a
|
||||
temporary ssh setup. An ssh directory is mounted over
|
||||
/home/user/.ssh on each machine in the cluster.
|
||||
$USERDIR on each machine in the cluster.
|
||||
|
||||
-h: display this help
|
||||
-p: create a persistent ssh setup. This will add
|
||||
new ssh keys and known_hosts to each nodes
|
||||
/home/user/.ssh directory
|
||||
$USERDIR directory
|
||||
-c: method to clean up a temporary ssh setup.
|
||||
Any hosts taken as arguments will be cleaned
|
||||
'
|
||||
"
|
||||
|
||||
persistentSetup() {
|
||||
echo "***creating key pair"
|
||||
@@ -74,40 +74,40 @@ tempSetup() {
|
||||
echo "***creating temporary ssh directory"
|
||||
mkdir -p $SSHDIR
|
||||
echo "***creating key pair"
|
||||
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f /tmp/mn/ssh/id_rsa -N '' &> /dev/null
|
||||
ssh-keygen -t rsa -C "Cluster_Edition_Key" -f $SSHDIR/id_rsa -N '' &> /dev/null
|
||||
|
||||
echo "***mounting temporary ssh directory"
|
||||
sudo mount --bind $SSHDIR /home/$user/.ssh
|
||||
sudo mount --bind $SSHDIR $USERDIR
|
||||
cp $SSHDIR/id_rsa.pub $SSHDIR/authorized_keys
|
||||
|
||||
for host in $hosts; do
|
||||
echo "***copying public key to $host"
|
||||
ssh-copy-id $user@$host &> /dev/null
|
||||
echo "***mounting remote temporary ssh directory for $host"
|
||||
ssh -o ForwardAgent=yes $user@$host "
|
||||
mkdir -p /tmp/mn/ssh
|
||||
cp /home/$user/.ssh/authorized_keys $SSHDIR/authorized_keys
|
||||
sudo mount --bind $SSHDIR /home/$user/.ssh"
|
||||
echo "***copying key pair to $host"
|
||||
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
|
||||
done
|
||||
for host in $hosts; do
|
||||
echo "***copying public key to $host"
|
||||
ssh-copy-id $user@$host &> /dev/null
|
||||
echo "***mounting remote temporary ssh directory for $host"
|
||||
ssh -o ForwardAgent=yes $user@$host "
|
||||
mkdir -p $SSHDIR
|
||||
cp $USERDIR/authorized_keys $SSHDIR/authorized_keys
|
||||
sudo mount --bind $SSHDIR $USERDIR"
|
||||
echo "***copying key pair to $host"
|
||||
scp $SSHDIR/{id_rsa,id_rsa.pub} $user@$host:$SSHDIR
|
||||
done
|
||||
|
||||
for host in $hosts; do
|
||||
echo "***copying known_hosts to $host"
|
||||
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
|
||||
done
|
||||
for host in $hosts; do
|
||||
echo "***copying known_hosts to $host"
|
||||
scp $SSHDIR/known_hosts $user@$host:$SSHDIR
|
||||
done
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
|
||||
for host in $hosts; do
|
||||
echo "***cleaning up $host"
|
||||
ssh $user@$host "sudo umount /home/$user/.ssh
|
||||
ssh $user@$host "sudo umount $USERDIR
|
||||
sudo rm -rf $SSHDIR"
|
||||
done
|
||||
|
||||
echo "**unmounting local directories"
|
||||
sudo umount /home/$user/.ssh
|
||||
sudo umount $USERDIR
|
||||
echo "***removing temporary ssh directory"
|
||||
sudo rm -rf $SSHDIR
|
||||
echo "done!"
|
||||
|
||||
+83
-60
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mininet install script for Ubuntu (and Debian Lenny)
|
||||
# Mininet install script for Ubuntu (and Debian Wheezy+)
|
||||
# Brandon Heller (brandonh@stanford.edu)
|
||||
|
||||
# Fail on error
|
||||
@@ -102,7 +102,10 @@ OF13_SWITCH_REV=${OF13_SWITCH_REV:-""}
|
||||
function kernel {
|
||||
echo "Install Mininet-compatible kernel if necessary"
|
||||
sudo apt-get update
|
||||
$install linux-image-$KERNEL_NAME
|
||||
if ! $install linux-image-$KERNEL_NAME; then
|
||||
echo "Could not install linux-image-$KERNEL_NAME"
|
||||
echo "Skipping - assuming installed kernel is OK."
|
||||
fi
|
||||
}
|
||||
|
||||
function kernel_clean {
|
||||
@@ -189,17 +192,24 @@ function of13 {
|
||||
fi
|
||||
|
||||
# Install netbee
|
||||
NBEESRC="nbeesrc-jan-10-2013"
|
||||
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
|
||||
NBEESRC="nbeesrc-feb-24-2015"
|
||||
NBEEDIR="netbee"
|
||||
else
|
||||
NBEESRC="nbeesrc-jan-10-2013"
|
||||
NBEEDIR="nbeesrc-jan-10-2013"
|
||||
fi
|
||||
|
||||
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
|
||||
wget -nc ${NBEEURL}${NBEESRC}.zip
|
||||
unzip ${NBEESRC}.zip
|
||||
cd ${NBEESRC}/src
|
||||
cd ${NBEEDIR}/src
|
||||
cmake .
|
||||
make
|
||||
cd $BUILD_DIR/
|
||||
sudo cp ${NBEESRC}/bin/libn*.so /usr/local/lib
|
||||
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
|
||||
sudo ldconfig
|
||||
sudo cp -R ${NBEESRC}/include/ /usr/
|
||||
sudo cp -R ${NBEEDIR}/include/ /usr/
|
||||
|
||||
# Resume the install:
|
||||
cd $BUILD_DIR/ofsoftswitch13
|
||||
@@ -258,53 +268,63 @@ function ubuntuOvs {
|
||||
OVS_SRC=$BUILD_DIR/openvswitch
|
||||
OVS_TARBALL_LOC=http://openvswitch.org/releases
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 12.04; then
|
||||
rm -rf $OVS_SRC
|
||||
mkdir -p $OVS_SRC
|
||||
cd $OVS_SRC
|
||||
if ! echo "$DIST" | egrep "Ubuntu|Debian" > /dev/null; then
|
||||
echo "OS must be Ubuntu or Debian"
|
||||
$cd BUILD_DIR
|
||||
return
|
||||
fi
|
||||
if [ "$DIST" = "Ubuntu" ] && ! version_ge $RELEASE 12.04; then
|
||||
echo "Ubuntu version must be >= 12.04"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
if [ "$DIST" = "Debian" ] && ! version_ge $RELEASE 7.0; then
|
||||
echo "Debian version must be >= 7.0"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
|
||||
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
|
||||
tar xzf openvswitch-$OVS_RELEASE.tar.gz
|
||||
else
|
||||
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
rm -rf $OVS_SRC
|
||||
mkdir -p $OVS_SRC
|
||||
cd $OVS_SRC
|
||||
|
||||
# Remove any old packages
|
||||
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
|
||||
openvswitch-pki openvswitch-switch
|
||||
|
||||
# Get build deps
|
||||
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
|
||||
pkg-config bzip2 openssl python-all procps python-qt4 \
|
||||
python-zopeinterface python-twisted-conch dkms
|
||||
|
||||
# Build OVS
|
||||
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
|
||||
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
|
||||
cd ..
|
||||
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
|
||||
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
|
||||
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
|
||||
echo "Ignoring error installing openvswitch-controller"
|
||||
fi
|
||||
|
||||
modinfo openvswitch
|
||||
sudo ovs-vsctl show
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
if wget $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz 2> /dev/null; then
|
||||
tar xzf openvswitch-$OVS_RELEASE.tar.gz
|
||||
else
|
||||
echo "Failed to install Open vSwitch. OS must be Ubuntu >= 12.04"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
echo "Failed to find OVS at $OVS_TARBALL_LOC/openvswitch-$OVS_RELEASE.tar.gz"
|
||||
cd $BUILD_DIR
|
||||
return
|
||||
fi
|
||||
|
||||
# Remove any old packages
|
||||
$remove openvswitch-common openvswitch-datapath-dkms openvswitch-controller \
|
||||
openvswitch-pki openvswitch-switch
|
||||
|
||||
# Get build deps
|
||||
$install build-essential fakeroot debhelper autoconf automake libssl-dev \
|
||||
pkg-config bzip2 openssl python-all procps python-qt4 \
|
||||
python-zopeinterface python-twisted-conch dkms
|
||||
|
||||
# Build OVS
|
||||
cd $BUILD_DIR/openvswitch/openvswitch-$OVS_RELEASE
|
||||
DEB_BUILD_OPTIONS='parallel=2 nocheck' fakeroot debian/rules binary
|
||||
cd ..
|
||||
$pkginst openvswitch-common_$OVS_RELEASE*.deb openvswitch-datapath-dkms_$OVS_RELEASE*.deb \
|
||||
openvswitch-pki_$OVS_RELEASE*.deb openvswitch-switch_$OVS_RELEASE*.deb
|
||||
if $pkginst openvswitch-controller_$OVS_RELEASE*.deb; then
|
||||
echo "Ignoring error installing openvswitch-controller"
|
||||
fi
|
||||
|
||||
modinfo openvswitch
|
||||
sudo ovs-vsctl show
|
||||
# Switch can run on its own, but
|
||||
# Mininet should control the controller
|
||||
# This appears to only be an issue on Ubuntu/Debian
|
||||
if sudo service openvswitch-controller stop; then
|
||||
echo "Stopped running controller"
|
||||
fi
|
||||
if [ -e /etc/init.d/openvswitch-controller ]; then
|
||||
sudo update-rc.d openvswitch-controller disable
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -319,14 +339,17 @@ function ovs {
|
||||
return
|
||||
fi
|
||||
|
||||
# Manually installing openvswitch-datapath may be necessary
|
||||
# for manually built kernel .debs using Debian's defective kernel
|
||||
# packaging, which doesn't yield usable headers.
|
||||
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||
# If you've already installed a datapath, assume you
|
||||
# know what you're doing and don't need dkms datapath.
|
||||
# Otherwise, install it.
|
||||
$install openvswitch-datapath-dkms
|
||||
if [ "$DIST" = "Ubuntu" ] && ! version_ge $RELEASE 14.04; then
|
||||
# Older Ubuntu versions need openvswitch-datapath/-dkms
|
||||
# Manually installing openvswitch-datapath may be necessary
|
||||
# for manually built kernel .debs using Debian's defective kernel
|
||||
# packaging, which doesn't yield usable headers.
|
||||
if ! dpkg --get-selections | grep openvswitch-datapath; then
|
||||
# If you've already installed a datapath, assume you
|
||||
# know what you're doing and don't need dkms datapath.
|
||||
# Otherwise, install it.
|
||||
$install openvswitch-datapath-dkms
|
||||
fi
|
||||
fi
|
||||
|
||||
$install openvswitch-switch
|
||||
@@ -343,7 +366,7 @@ function ovs {
|
||||
else
|
||||
echo "Attempting to install openvswitch-testcontroller"
|
||||
if ! $install openvswitch-testcontroller; then
|
||||
echo "Failed - giving up"
|
||||
echo "Failed - skipping openvswitch-testcontroller"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -567,7 +590,7 @@ net.ipv6.conf.lo.disable_ipv6 = 1' | sudo tee -a /etc/sysctl.conf > /dev/null
|
||||
# Since the above doesn't disable neighbor discovery, also do this:
|
||||
if ! grep 'ipv6.disable' /etc/default/grub; then
|
||||
sudo sed -i -e \
|
||||
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1/' \
|
||||
's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1 /' \
|
||||
/etc/default/grub
|
||||
sudo update-grub
|
||||
fi
|
||||
|
||||
+1
-1
@@ -323,7 +323,7 @@ zerombr yes
|
||||
clearpart --all --initlabel
|
||||
#Automatic partitioning
|
||||
autopart
|
||||
#System authorization infomation
|
||||
#System authorization information
|
||||
auth --useshadow --enablemd5
|
||||
#Firewall configuration
|
||||
firewall --disabled
|
||||
|
||||
Reference in New Issue
Block a user