Merge pull request #342 from cdburkard/devel/startup

improve startup performance, largely by removing unnecessary ifconfigs
This commit is contained in:
lantz
2014-08-26 19:59:12 -07:00
5 changed files with 67 additions and 32 deletions
+38 -12
View File
@@ -32,7 +32,7 @@ class Intf( object ):
"Basic interface object that can configure itself." "Basic interface object that can configure itself."
def __init__( self, name, node=None, port=None, link=None, **params ): def __init__( self, name, node=None, port=None, link=None, mac=None, **params ):
"""name: interface name (e.g. h1-eth0) """name: interface name (e.g. h1-eth0)
node: owning node (where this intf most likely lives) node: owning node (where this intf most likely lives)
link: parent link if we're part of a link link: parent link if we're part of a link
@@ -40,7 +40,13 @@ class Intf( object ):
self.node = node self.node = node
self.name = name self.name = name
self.link = link self.link = link
self.mac, self.ip, self.prefixLen = None, None, None self.mac = mac
self.ip, self.prefixLen = None, None
# if interface is lo, we know the ip is 127.0.0.1.
# This saves an ifconfig command per node
if self.name == 'lo':
self.ip = '127.0.0.1'
# Add to node (and move ourselves if necessary ) # Add to node (and move ourselves if necessary )
node.addIntf( self, port=port ) node.addIntf( self, port=port )
# Save params for future reference # Save params for future reference
@@ -91,6 +97,19 @@ class Intf( object ):
self.mac = macs[ 0 ] if macs else None self.mac = macs[ 0 ] if macs else None
return self.mac return self.mac
# Instead of updating ip and mac separately,
# use one ifconfig call to do it simultaneously.
# This saves an ifconfig command, which improves performance.
def updateAddr( self ):
"Return IP address and MAC address based on ifconfig."
ifconfig = self.ifconfig()
ips = self._ipMatchRegex.findall( ifconfig )
macs = self._macMatchRegex.findall( ifconfig )
self.ip = ips[ 0 ] if ips else None
self.mac = macs[ 0 ] if macs else None
return self.ip, self.mac
def IP( self ): def IP( self ):
"Return IP address" "Return IP address"
return self.ip return self.ip
@@ -102,7 +121,14 @@ class Intf( object ):
def isUp( self, setUp=False ): def isUp( self, setUp=False ):
"Return whether interface is up" "Return whether interface is up"
if setUp: if setUp:
self.ifconfig( 'up' ) cmdOutput = self.ifconfig( 'up' )
# no output indicates success
if cmdOutput:
error( "Error setting %s up: %s " % ( self.name, cmdOutput ) )
return False
else:
return True
else:
return "UP" in self.ifconfig() return "UP" in self.ifconfig()
def rename( self, newname ): def rename( self, newname ):
@@ -154,8 +180,6 @@ class Intf( object ):
self.setParam( r, 'setIP', ip=ip ) self.setParam( r, 'setIP', ip=ip )
self.setParam( r, 'isUp', up=up ) self.setParam( r, 'isUp', up=up )
self.setParam( r, 'ifconfig', ifconfig=ifconfig ) self.setParam( r, 'ifconfig', ifconfig=ifconfig )
self.updateIP()
self.updateMAC()
return r return r
def delete( self ): def delete( self ):
@@ -328,7 +352,7 @@ class Link( object ):
Other types of links could be tunnels, link emulators, etc..""" Other types of links could be tunnels, link emulators, etc.."""
def __init__( self, node1, node2, port1=None, port2=None, def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, intfName1=None, intfName2=None, addr1=None, addr2=None,
intf=Intf, cls1=None, cls2=None, params1=None, intf=Intf, cls1=None, cls2=None, params1=None,
params2=None ): params2=None ):
"""Create veth link to another node, making two new interfaces. """Create veth link to another node, making two new interfaces.
@@ -354,7 +378,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, addr1, addr2 )
if not cls1: if not cls1:
cls1 = intf cls1 = intf
@@ -366,9 +390,9 @@ class Link( object ):
params2 = {} params2 = {}
intf1 = cls1( name=intfName1, node=node1, port=port1, intf1 = cls1( name=intfName1, node=node1, port=port1,
link=self, **params1 ) link=self, mac=addr1, **params1 )
intf2 = cls2( name=intfName2, node=node2, port=port2, intf2 = cls2( name=intfName2, node=node2, port=port2,
link=self, **params2 ) link=self, mac=addr2, **params2 )
# All we are is dust in the wind, and our two interfaces # All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2 self.intf1, self.intf2 = intf1, intf2
@@ -379,13 +403,13 @@ 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, addr1=None, addr2=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 ) makeIntfPair( intf1, intf2, addr1, addr2 )
def delete( self ): def delete( self ):
"Delete this link" "Delete this link"
@@ -398,10 +422,12 @@ class Link( object ):
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,
intfName1=None, intfName2=None, **params ): intfName1=None, intfName2=None,
addr1=None, addr2=None, **params ):
Link.__init__( self, node1, node2, port1=port1, port2=port2, Link.__init__( self, node1, node2, port1=port1, port2=port2,
intfName1=intfName1, intfName2=intfName2, intfName1=intfName1, intfName2=intfName2,
cls1=TCIntf, cls1=TCIntf,
cls2=TCIntf, cls2=TCIntf,
addr1=addr1, addr2=addr2,
params1=params, params1=params,
params2=params) params2=params)
+5 -1
View File
@@ -90,6 +90,7 @@ import os
import re import re
import select import select
import signal import signal
import random
import copy import copy
from time import sleep from time import sleep
from itertools import chain, groupby from itertools import chain, groupby
@@ -326,8 +327,12 @@ class Mininet( object ):
port1: source port port1: source port
port2: dest port port2: dest port
returns: link object""" returns: link object"""
mac1 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 )
mac2 = macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff | 0x020000000000 )
defaults = { 'port1': port1, defaults = { 'port1': port1,
'port2': port2, 'port2': port2,
'addr1': mac1,
'addr2': mac2,
'intf': self.intf } 'intf': self.intf }
defaults.update( params ) defaults.update( params )
if not cls: if not cls:
@@ -350,7 +355,6 @@ class Mininet( object ):
# quietRun( 'renice +18 -p ' + repr( host.pid ) ) # quietRun( 'renice +18 -p ' + repr( host.pid ) )
# This may not be the right place to do this, but # This may not be the right place to do this, but
# it needs to be done somewhere. # it needs to be done somewhere.
host.cmd( 'ifconfig lo up' )
info( '\n' ) info( '\n' )
def buildFromTopo( self, topo=None ): def buildFromTopo( self, topo=None ):
-7
View File
@@ -924,7 +924,6 @@ class UserSwitch( Switch ):
for c in controllers ] ) for c in controllers ] )
ofdlog = '/tmp/' + self.name + '-ofd.log' ofdlog = '/tmp/' + self.name + '-ofd.log'
ofplog = '/tmp/' + self.name + '-ofp.log' ofplog = '/tmp/' + self.name + '-ofp.log'
self.cmd( 'ifconfig lo up' )
intfs = [ str( i ) for i in self.intfList() if not i.IP() ] intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) + self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
' punix:/tmp/' + self.name + ' -d %s ' % self.dpid + ' punix:/tmp/' + self.name + ' -d %s ' % self.dpid +
@@ -975,7 +974,6 @@ class OVSLegacyKernelSwitch( Switch ):
def start( self, controllers ): def start( self, controllers ):
"Start up kernel datapath." "Start up kernel datapath."
ofplog = '/tmp/' + self.name + '-ofp.log' ofplog = '/tmp/' + self.name + '-ofp.log'
quietRun( 'ifconfig lo up' )
# Delete local datapath if it exists; # Delete local datapath if it exists;
# then create a new one monitoring the given interfaces # then create a new one monitoring the given interfaces
self.cmd( 'ovs-dpctl del-dp ' + self.dp ) self.cmd( 'ovs-dpctl del-dp ' + self.dp )
@@ -1093,9 +1091,6 @@ class OVSSwitch( Switch ):
if self.inNamespace: if self.inNamespace:
raise Exception( raise Exception(
'OVS kernel switch does not work in a namespace' ) 'OVS kernel switch does not work in a namespace' )
# We should probably call config instead, but this
# requires some rethinking...
self.cmd( 'ifconfig lo up' )
# Annoyingly, --if-exists option seems not to work # Annoyingly, --if-exists option seems not to work
self.cmd( 'ovs-vsctl del-br', self ) self.cmd( 'ovs-vsctl del-br', self )
int( self.dpid, 16 ) # DPID must be a hex string int( self.dpid, 16 ) # DPID must be a hex string
@@ -1200,7 +1195,6 @@ class IVSSwitch(Switch):
logfile = '/tmp/ivs.%s.log' % self.name logfile = '/tmp/ivs.%s.log' % self.name
self.cmd( 'ifconfig lo up' )
self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' ) self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
def stop( self ): def stop( self ):
@@ -1240,7 +1234,6 @@ class Controller( Node ):
self.protocol = protocol self.protocol = protocol
Node.__init__( self, name, inNamespace=inNamespace, Node.__init__( self, name, inNamespace=inNamespace,
ip=ip, **params ) ip=ip, **params )
self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary
self.checkListening() self.checkListening()
def checkListening( self ): def checkListening( self ):
+12 -6
View File
@@ -45,7 +45,7 @@ class testOptionsTopoCommon( object ):
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts, mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
lopts=lopts ), lopts=lopts ),
host=CPULimitedHost, link=TCLink, host=CPULimitedHost, link=TCLink,
switch=self.switchClass ) switch=self.switchClass, waitConnected=True )
dropped = mn.run( mn.ping ) dropped = mn.run( mn.ping )
self.assertEqual( dropped, 0 ) self.assertEqual( dropped, 0 )
@@ -67,7 +67,8 @@ class testOptionsTopoCommon( object ):
#self.runOptionsTopoTest( N, hopts=hopts ) #self.runOptionsTopoTest( N, hopts=hopts )
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ), mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
host=CPULimitedHost, switch=self.switchClass ) host=CPULimitedHost, switch=self.switchClass,
waitConnected=True )
mn.start() mn.start()
results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
mn.stop() mn.stop()
@@ -77,13 +78,16 @@ class testOptionsTopoCommon( object ):
def testLinkBandwidth( self ): def testLinkBandwidth( self ):
"Verify that link bandwidths are accurate within a bound." "Verify that link bandwidths are accurate within a bound."
BW = .5 # Mbps if self.switchClass is UserSwitch:
self.skipTest ( 'UserSwitch has very poor performance, so skip for now' )
BW = 5 # Mbps
BW_TOLERANCE = 0.8 # BW fraction below which test should fail BW_TOLERANCE = 0.8 # BW fraction below which test should fail
# Verify ability to create limited-link topo first; # Verify ability to create limited-link topo first;
lopts = { 'bw': BW, 'use_htb': True } lopts = { 'bw': BW, 'use_htb': True }
# Also verify correctness of limit limitng within a bound. # Also verify correctness of limit limitng within a bound.
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass ) link=TCLink, switch=self.switchClass,
waitConnected=True )
bw_strs = mn.run( mn.iperf, format='m' ) bw_strs = mn.run( mn.iperf, format='m' )
for bw_str in bw_strs: for bw_str in bw_strs:
bw = float( bw_str.split(' ')[0] ) bw = float( bw_str.split(' ')[0] )
@@ -95,7 +99,8 @@ class testOptionsTopoCommon( object ):
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True } lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
link=TCLink, switch=self.switchClass, autoStaticArp=True ) link=TCLink, switch=self.switchClass, autoStaticArp=True,
waitConnected=True )
ping_delays = mn.run( mn.pingFull ) ping_delays = mn.run( mn.pingFull )
test_outputs = ping_delays[0] test_outputs = ping_delays[0]
# Ignore unused variables below # Ignore unused variables below
@@ -117,7 +122,8 @@ class testOptionsTopoCommon( object ):
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True } lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ), mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
host=CPULimitedHost, link=TCLink, host=CPULimitedHost, link=TCLink,
switch=self.switchClass ) switch=self.switchClass,
waitConnected=True )
# Drops are probabilistic, but the chance of no dropped packets is # Drops are probabilistic, but the chance of no dropped packets is
# 1 in 100 million with 4 hops for a link w/99% loss. # 1 in 100 million with 4 hops for a link w/99% loss.
dropped_total = 0 dropped_total = 0
+10 -4
View File
@@ -145,7 +145,7 @@ isShellBuiltin.builtIns = None
# live in the root namespace and thus do not have to be # live in the root namespace and thus do not have to be
# explicitly moved. # explicitly moved.
def makeIntfPair( intf1, intf2 ): def makeIntfPair( intf1, intf2, addr1=None, addr2=None ):
"""Make a veth pair connecting intf1 and intf2. """Make a veth pair connecting intf1 and intf2.
intf1: string, interface intf1: string, interface
intf2: string, interface intf2: string, interface
@@ -154,7 +154,11 @@ def makeIntfPair( intf1, intf2 ):
quietRun( 'ip link del ' + intf1 ) quietRun( 'ip link del ' + intf1 )
quietRun( 'ip link del ' + intf2 ) quietRun( 'ip link del ' + intf2 )
# Create new pair # Create new pair
if addr1 is None and addr2 is None:
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
else:
cmd = ( 'ip link add name ' + intf1 + ' address ' + addr1 +
' type veth peer name ' + intf2 + ' address ' + addr2 )
cmdOutput = quietRun( cmd ) cmdOutput = quietRun( cmd )
if cmdOutput == '': if cmdOutput == '':
return True return True
@@ -185,10 +189,12 @@ def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
intf = str( intf ) intf = str( intf )
cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid ) cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
if srcNode: if srcNode:
srcNode.cmd( cmd ) cmdOutput = srcNode.cmd( cmd )
else: else:
quietRun( cmd ) cmdOutput = quietRun( cmd )
if ( ' %s:' % intf ) not in dstNode.cmd( 'ip link show', intf ): # If ip link set does not produce any output, then we can assume
# that the link has been moved successfully.
if cmdOutput:
if printError: if printError:
error( '*** Error: moveIntf: ' + intf + error( '*** Error: moveIntf: ' + intf +
' not successfully moved to ' + dstNode.name + '\n' ) ' not successfully moved to ' + dstNode.name + '\n' )