Compare commits

...
21 Commits
Author SHA1 Message Date
Bob Lantz 7ce07884f4 Install cleanup callback on load. 2015-06-12 13:06:51 -07:00
Bob Lantz be53ac31d6 Fix CLI for switches, and rename cleanup (in superclass!!) 2015-06-11 00:17:19 -07:00
Bob Lantz 50bfbd0566 Add cleanup callback 2015-06-11 00:17:19 -07:00
Bob Lantz a4d8049661 Rename pid files to make them more distinctive and to facilitate pkill
If there are leftover ovsdb/vswitchd processes, you can kill them with
sudo pkill -9 -f mn.pid
2015-06-11 00:17:19 -07:00
Bob Lantz da4c53b3c9 Add OVSLinkNSh and share shell by default
Note: this changes the semantics somewhat, but it drastically reduces
the number of bash processes that we start up.
2015-06-11 00:17:19 -07:00
Bob Lantz 1b96ff3539 Use 172.123.123.0/24 for control network so we can ping data network 2015-06-11 00:17:19 -07:00
Bob Lantz 93d04123c6 Rename group option to n (number of OVS instances per ovsdb) 2015-06-11 00:17:19 -07:00
Bob Lantz 22655cf9fd We can now use --nat flush=0 2015-06-11 00:17:19 -07:00
Bob Lantz fe82d2c04a Minor changes + allow --switch ovsm,group=16 2015-06-11 00:17:18 -07:00
Bob Lantz 0534932bf3 First version of OVS in shared namespaces 2015-06-11 00:17:18 -07:00
Bob Lantz 5f68be2273 Remove debug print 2015-06-11 00:16:55 -07:00
Bob Lantz 68ae67dc58 Support custom links in custom file 2015-06-10 20:35:53 -07:00
Bob Lantz c4a85ab1d1 Add flush option to disable flushing iptables 2015-06-10 20:09:29 -07:00
Bob Lantz ef3c885630 Add help regarding --nat [option=val...] 2015-06-10 20:08:44 -07:00
Bob Lantz bb35d04102 Fix TorusTopo for UserSwitch
UserSwitch requires a 12-digit dpid.
2015-06-07 22:10:18 -07:00
Bob Lantz ba05fd363b Warn if bridge netfilter (firewall) is enabled
Newer linux kernels enable filtering on the linux bridge;
this can prevent it from working in mininet!
2015-06-05 16:10:21 -07:00
Brian O'Connor e988b0f660 Merge pull request #522 from mininet/devel/torus
Adding number of hosts per switch option to torus topo
2015-06-05 01:45:01 -07:00
Brian O'Connor b27cce08af Adding number of hosts per switch option to torus topo 2015-05-27 23:45:19 -07:00
Bob Lantz a38896c254 Avoid expanding a string into a list of chars 2015-05-16 08:24:15 -07:00
Bob Lantz 63ae13fcf9 Fix #520 2015-05-12 23:50:37 -07:00
Bob Lantz c5f6d0ff17 Restore use of self.intf (if present) in addLink
fixes #515
2015-05-02 13:16:48 -07:00
6 changed files with 325 additions and 27 deletions

No files matched your search

+2 -2
View File
@@ -158,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 )
@@ -238,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"
+275
View File
@@ -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()
+2
View File
@@ -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() )
+3 -1
View File
@@ -168,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
@@ -1143,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 ):
+30 -20
View File
@@ -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()
+13 -4
View File
@@ -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 ):