Merge pull request #342 from cdburkard/devel/startup
improve startup performance, largely by removing unnecessary ifconfigs
This commit is contained in:
+38
-12
@@ -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
@@ -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 ):
|
||||||
|
|||||||
@@ -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 ):
|
||||||
|
|||||||
@@ -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
@@ -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' )
|
||||||
|
|||||||
Reference in New Issue
Block a user