Compare commits

...

9 Commits

Author SHA1 Message Date
Bob Lantz 703c6b102f Clean up makeIntfPair to prepare for pid namespaces, chroot 2013-07-04 18:38:57 -07:00
Bob Lantz b6a1084326 Don't use mnexec if we're not attaching to a cgroup. 2013-04-26 18:01:11 -07:00
Bob Lantz 93085f1cad Clean up shutdown messages, and remove stale namespaces
Note that we can't actually use ip netns del to remove the namespaces
untill ALL of the processes are gone, necessitating this cleanup.
2013-04-26 14:48:46 -07:00
Bob Lantz f69664cd90 Remove redundant intf delete, and try (!) to delete netns 2013-04-26 14:47:29 -07:00
Bob Lantz 1e41c8342c Change to use ip netns exec 2013-04-26 14:46:59 -07:00
Bob Lantz 99f08f98e2 Look for mininet: and print error rather than exception 2013-04-26 14:46:25 -07:00
Bob Lantz 947337c1dc Let Mininet() explicitly delete links. 2013-04-25 17:24:57 -07:00
Bob Lantz 0a176439ab More robust bash and namespace cleanup 2013-04-25 14:30:16 -07:00
Bob Lantz 0de8d94673 Basic support for using ip netns for mnexec and link creation 2013-04-24 18:44:19 -07:00
5 changed files with 132 additions and 71 deletions
+46 -17
View File
@@ -10,24 +10,30 @@ It may also get rid of 'false positives', but hopefully
nothing irreplaceable! nothing irreplaceable!
""" """
from subprocess import Popen, PIPE from subprocess import Popen, PIPE, STDOUT, check_output as co
from sys import stdout, exit
from time import sleep
from mininet.log import info from mininet.log import info, error
from mininet.term import cleanUpScreens from mininet.term import cleanUpScreens
def sh( cmd ): def sh( cmd ):
"Print a command and send it to the shell" "Run a command in the shell and return non-empty output lines"
info( cmd + '\n' ) info( cmd + '\n' )
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ] output = ( Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE )
.communicate()[ 0 ]
.strip()
.split( '\n' ) )
return [ s for s in output if s ]
def cleanup(): def cleanup():
"""Clean up junk which might be left over from old runs; """Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!""" do fast stuff before slow dp and link removal!"""
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes" info( "*** Removing excess "
"\n") "controllers/ofprotocols/ofdatapaths/pings/noxes\n" )
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core ' zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
zombies += 'ovs-openflowd udpbwtest mnexec' zombies += 'ovs-openflowd ovs-controller udpbwtest mnexec'
# Note: real zombie processes can't actually be killed, since they # Note: real zombie processes can't actually be killed, since they
# are already (un)dead. Then again, # are already (un)dead. Then again,
# you can't connect to them either, so they're mostly harmless. # you can't connect to them either, so they're mostly harmless.
@@ -43,21 +49,44 @@ def cleanup():
cleanUpScreens() cleanUpScreens()
info( "*** Removing excess kernel datapaths\n" ) info( "*** Removing excess kernel datapaths\n" )
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n' ) dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" )
for dp in dps: for dp in dps:
if dp != '': sh( 'dpctl deldp ' + dp )
sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths" ) info( "*** Removing OVS datapaths\n" )
dps = sh("ovs-vsctl list-br").split( '\n' ) dps = sh("ovs-vsctl list-br")
for dp in dps: for dp in dps:
if dp: sh( 'ovs-vsctl del-br ' + dp )
sh( 'ovs-vsctl del-br ' + dp ) if co( 'ovs-vsctl list-br', shell=True ):
raise Excpetion( "Error: could not remove all OVS datapaths" )
info( "*** Removing all links of the pattern foo-ethX\n" ) info( "*** Removing all links of the pattern foo-ethX\n" )
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' ) links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" )
for link in links: for link in links:
if link != '': sh( "ip link del " + link )
sh( "ip link del " + link ) if sh( "ip link show | egrep -o '(\w+-eth\w+)'" ):
raise Exception( "Error could not remove stale links")
info( "*** Killing stale mininet node processes\n" )
sh( 'pkill -9 -f mininet:' )
# Make sure they are gone
while True:
try:
pids = co( 'pgrep -f mininet:'.split() )
except:
pids = ''
if pids:
sh( 'pkill -f 9 mininet:' )
sleep( .5 )
else:
break
info( "*** Removing stale namespaces\n" )
nses = sh( "ip netns list" )
for ns in nses:
sh( "ip netns del " + ns )
if co( "ip netns list", shell=True ):
error( "Error: could not remove all namespaces - exiting\n" )
exit( 1 )
info( "*** Cleanup complete.\n" ) info( "*** Cleanup complete.\n" )
+17 -5
View File
@@ -25,7 +25,7 @@ Link: basic link class for creating veth pairs
""" """
from mininet.log import info, error, debug from mininet.log import info, error, debug
from mininet.util import makeIntfPair from mininet.util import makeIntfPair, errFail, quietRun
from time import sleep from time import sleep
import re import re
@@ -109,7 +109,7 @@ class Intf( object ):
def rename( self, newname ): def rename( self, newname ):
"Rename interface" "Rename interface"
self.ifconfig( 'down' ) self.ifconfig( 'down' )
result = self.cmd( 'ip link set', self.name, 'name', newname ) result = self.cmd( 'ip link set dev', self.name, 'name', newname )
self.name = newname self.name = newname
self.ifconfig( 'up' ) self.ifconfig( 'up' )
return result return result
@@ -347,7 +347,7 @@ class Link( object ):
if not intfName2: if not intfName2:
intfName2 = self.intfName( node2, port2 ) intfName2 = self.intfName( node2, port2 )
self.makeIntfPair( intfName1, intfName2 ) self.makeIntfPair( intfName1, intfName2, node1, node2 )
if not cls1: if not cls1:
cls1 = intf cls1 = intf
@@ -372,13 +372,24 @@ class Link( object ):
return node.name + '-eth' + repr( n ) return node.name + '-eth' + repr( n )
@classmethod @classmethod
def makeIntfPair( cls, intf1, intf2 ): def makeIntfPair( cls, intf1, intf2, node1=None, node2=None ):
"""Create pair of interfaces """Create pair of interfaces
intf1: name of interface 1 intf1: name of interface 1
intf2: name of interface 2 intf2: name of interface 2
(override this class method [and possibly delete()] (override this class method [and possibly delete()]
to change link type)""" to change link type)"""
makeIntfPair( intf1, intf2 ) # To be compatible with pid namespaces and chroot in the future
# we create links in the root namespace and then move
# the ends as needed.
# First, make sure there aren't stale links sitting around
quietRun( 'ip link delete %s type veth' % intf1 )
quietRun( 'ip link delete %s type veth' % intf2 )
cmd = 'ip link add %s type veth peer name %s' % ( intf1, intf2 )
if node2 and node2.inNamespace:
cmd += ' netns %s' % node2
errFail( cmd )
if node1 and node1.inNamespace:
errFail( 'ip link set dev %s netns %s' % ( intf1, node1 ) )
def delete( self ): def delete( self ):
"Delete this link" "Delete this link"
@@ -388,6 +399,7 @@ class Link( object ):
def __str__( self ): def __str__( self ):
return '%s<->%s' % ( self.intf1, self.intf2 ) return '%s<->%s' % ( self.intf1, self.intf2 )
class TCLink( Link ): class TCLink( Link ):
"Link with symmetric TC interfaces configured via opts" "Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None, def __init__( self, node1, node2, port1=None, port2=None,
+29 -12
View File
@@ -97,7 +97,7 @@ from mininet.cli import CLI
from mininet.log import info, error, debug, output from mininet.log import info, error, debug, output
from mininet.node import Host, OVSKernelSwitch, Controller from mininet.node import Host, OVSKernelSwitch, Controller
from mininet.link import Link, Intf from mininet.link import Link, Intf
from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import quietRun, errFail, fixLimits, numCores, ensureRoot
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
from mininet.term import cleanUpScreens, makeTerms from mininet.term import cleanUpScreens, makeTerms
@@ -152,7 +152,8 @@ class Mininet( object ):
self.hosts = [] self.hosts = []
self.switches = [] self.switches = []
self.controllers = [] self.controllers = []
self.links = []
self.nameToNode = {} # name to Node (Host/Switch) objects self.nameToNode = {} # name to Node (Host/Switch) objects
self.terms = [] # list of spawned xterm processes self.terms = [] # list of spawned xterm processes
@@ -252,7 +253,9 @@ class Mininet( object ):
defaults.update( params ) defaults.update( params )
if not cls: if not cls:
cls = self.link cls = self.link
return cls( node1, node2, **defaults ) link = cls( node1, node2, **defaults )
self.links.append( link )
return link
def configHosts( self ): def configHosts( self ):
"Configure a set of hosts." "Configure a set of hosts."
@@ -372,20 +375,34 @@ class Mininet( object ):
if self.terms: if self.terms:
info( '*** Stopping %i terms\n' % len( self.terms ) ) info( '*** Stopping %i terms\n' % len( self.terms ) )
self.stopXterms() self.stopXterms()
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
info( controller.name + ' ' )
controller.stop()
info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) )
for switch in self.switches:
info( switch.name + ' ' )
switch.stop( deleteIntfs=False )
info( '\n' )
info( '*** Removing links\n' )
for link in self.links:
info( '.' )
link.delete()
info( '\n*** Terminating switches\n' )
for switch in self.switches:
info( '.' )
switch.terminate()
info( '\n' )
info( '*** Stopping %i hosts\n' % len( self.hosts ) ) info( '*** Stopping %i hosts\n' % len( self.hosts ) )
for host in self.hosts: for host in self.hosts:
info( host.name + ' ' ) info( host.name + ' ' )
host.terminate() host.terminate()
info( '\n' ) info( '\n' )
info( '*** Stopping %i switches\n' % len( self.switches ) ) nses = quietRun( 'ip netns list').strip().split()
for switch in self.switches: info( '*** Removing namespaces' )
info( switch.name + ' ' ) for ns in nses:
switch.stop() errFail( 'ip netns del ' + ns )
info( '\n' )
info( '*** Stopping %i controllers\n' % len( self.controllers ) )
for controller in self.controllers:
info( controller.name + ' ' )
controller.stop()
info( '\n*** Done\n' ) info( '\n*** Done\n' )
def run( self, test, *args, **kwargs ): def run( self, test, *args, **kwargs ):
+36 -23
View File
@@ -49,6 +49,8 @@ import re
import signal import signal
import select import select
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
from sys import stdout
from time import sleep
from mininet.log import info, error, warn, debug from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin, from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
@@ -107,20 +109,26 @@ class Node( object ):
# Command support via shell process in namespace # Command support via shell process in namespace
def _popen( self, *args, **kwargs ):
"Internal wrapper for Popen"
old = signal.signal( signal.SIGINT, signal.SIG_IGN )
result = Popen( *args, **kwargs )
signal.signal( signal.SIGINT, old )
return result
def startShell( self ): def startShell( self ):
"Start a shell process for running commands" "Start a shell process for running commands"
if self.shell: if self.shell:
error( "%s: shell is already running" ) error( "%s: shell is already running" )
return return
# mnexec: (c)lose descriptors, (d)etach from tty,
# (p)rint pid, and run in (n)amespace
opts = '-cdp'
if self.inNamespace:
opts += 'n'
# bash -m: enable job control # bash -m: enable job control
# -s: pass $* to shell, and make process easy to find in ps # -s: pass $* to shell, and make process easy to find in ps
cmd = [ 'mnexec', opts, 'bash', '-ms', 'mininet:' + self.name ] cmd = [ 'bash', '-ms', 'mininet:' + self.name ]
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT, if self.inNamespace:
quietRun( 'ip netns del ' + self.name )
errFail( 'ip netns add ' + self.name )
cmd = [ 'ip', 'netns', 'exec', self.name ] + cmd
self.shell = self._popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
close_fds=True ) close_fds=True )
self.stdin = self.shell.stdin self.stdin = self.shell.stdin
self.stdout = self.shell.stdout self.stdout = self.shell.stdout
@@ -140,11 +148,14 @@ class Node( object ):
def cleanup( self ): def cleanup( self ):
"Help python collect its garbage." "Help python collect its garbage."
if not self.inNamespace: if self.shell:
for intfName in self.intfNames(): self.shell.terminate()
if self.name in intfName: self.shell.wait()
quietRun( 'ip link del ' + intfName ) self.shell = None
self.shell = None if self.inNamespace:
# Note: this will only work if there are no other
# processes running in namespaces, but we'll do it anyway
quietRun( 'ip netns del ' + self.name )
# Subshell I/O, commands and control # Subshell I/O, commands and control
@@ -312,7 +323,7 @@ class Node( object ):
# Shell requires a string, not a list! # Shell requires a string, not a list!
if defaults.get( 'shell', False ): if defaults.get( 'shell', False ):
cmd = ' '.join( cmd ) cmd = ' '.join( cmd )
return Popen( cmd, **defaults ) return self._popen( cmd, **defaults )
def pexec( self, *args, **kwargs ): def pexec( self, *args, **kwargs ):
"""Execute a command using popen """Execute a command using popen
@@ -346,10 +357,8 @@ class Node( object ):
self.ports[ intf ] = port self.ports[ intf ] = port
self.nameToIntf[ intf.name ] = intf self.nameToIntf[ intf.name ] = intf
debug( '\n' ) debug( '\n' )
assert intf.name in self.cmd( 'ip link show' )
debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) ) debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
if self.inNamespace:
debug( 'moving', intf, 'into namespace for', self.name, '\n' )
moveIntf( intf.name, self )
def defaultIntf( self ): def defaultIntf( self ):
"Return interface for lowest port" "Return interface for lowest port"
@@ -400,6 +409,7 @@ class Node( object ):
for intf in self.intfs.values(): for intf in self.intfs.values():
intf.delete() intf.delete()
info( '.' ) info( '.' )
stdout.flush()
# Routing support # Routing support
@@ -835,11 +845,12 @@ class UserSwitch( Switch ):
' --fail=closed ' + self.opts + ' --fail=closed ' + self.opts +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' ) ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
def stop( self ): def stop( self, deleteIntfs=True ):
"Stop OpenFlow reference user datapath." "Stop OpenFlow reference user datapath."
self.cmd( 'kill %ofdatapath' ) self.cmd( 'kill %ofdatapath' )
self.cmd( 'kill %ofprotocol' ) self.cmd( 'kill %ofprotocol' )
self.deleteIntfs() if deleteIntfs:
self.deleteIntfs()
class OVSLegacyKernelSwitch( Switch ): class OVSLegacyKernelSwitch( Switch ):
@@ -886,11 +897,12 @@ class OVSLegacyKernelSwitch( Switch ):
' 1>' + ofplog + ' 2>' + ofplog + '&' ) ' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False self.execed = False
def stop( self ): def stop( self, deleteIntfs=True ):
"Terminate kernel datapath." "Terminate kernel datapath."
quietRun( 'ovs-dpctl del-dp ' + self.dp ) quietRun( 'ovs-dpctl del-dp ' + self.dp )
self.cmd( 'kill %ovs-openflowd' ) self.cmd( 'kill %ovs-openflowd' )
self.deleteIntfs() if deleteIntfs:
self.deleteIntfs()
class OVSSwitch( Switch ): class OVSSwitch( Switch ):
@@ -969,10 +981,11 @@ class OVSSwitch( Switch ):
clist += ' ptcp:%s' % self.listenPort clist += ' ptcp:%s' % self.listenPort
self.cmd( 'ovs-vsctl set-controller', self, clist ) self.cmd( 'ovs-vsctl set-controller', self, clist )
def stop( self ): def stop( self, deleteIntfs=True ):
"Terminate OVS switch." "Stop OVS switch."
self.cmd( 'ovs-vsctl del-br', self ) self.cmd( 'ovs-vsctl del-br', self )
self.deleteIntfs() if deleteIntfs:
self.deleteIntfs()
OVSKernelSwitch = OVSSwitch OVSKernelSwitch = OVSSwitch
+4 -14
View File
@@ -9,18 +9,6 @@ else
host=$1 host=$1
fi fi
pid=`ps ax | grep mininet:$host | grep bash | awk '{print $1};'`
if echo $pid | grep -q ' '; then
echo "Error: found multiple mininet:$host processes"
exit 2
fi
if [ "$pid" == "" ]; then
echo "Could not find Mininet host $host"
exit 3
fi
if [ -z $2 ]; then if [ -z $2 ]; then
cmd='bash' cmd='bash'
else else
@@ -28,9 +16,11 @@ else
cmd=$* cmd=$*
fi fi
# We could do this in this script, and we may want to eventually,
# but for now we'll use mnexec to attach to the host's cgroup
cgroup=/sys/fs/cgroup/cpu/$host cgroup=/sys/fs/cgroup/cpu/$host
if [ -d "$cgroup" ]; then if [ -d "$cgroup" ]; then
cg="-g $host" cg="mnexec -g $host"
fi fi
exec sudo mnexec -a $pid $cg $cmd exec sudo ip netns exec $host $cg $cmd