Compare commits

...

10 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
+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()