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."
|
||||
|
||||
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)
|
||||
node: owning node (where this intf most likely lives)
|
||||
link: parent link if we're part of a link
|
||||
@@ -40,7 +40,13 @@ class Intf( object ):
|
||||
self.node = node
|
||||
self.name = name
|
||||
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 )
|
||||
node.addIntf( self, port=port )
|
||||
# Save params for future reference
|
||||
@@ -91,6 +97,19 @@ class Intf( object ):
|
||||
self.mac = macs[ 0 ] if macs else None
|
||||
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 ):
|
||||
"Return IP address"
|
||||
return self.ip
|
||||
@@ -102,7 +121,14 @@ class Intf( object ):
|
||||
def isUp( self, setUp=False ):
|
||||
"Return whether interface is up"
|
||||
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()
|
||||
|
||||
def rename( self, newname ):
|
||||
@@ -154,8 +180,6 @@ class Intf( object ):
|
||||
self.setParam( r, 'setIP', ip=ip )
|
||||
self.setParam( r, 'isUp', up=up )
|
||||
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
|
||||
self.updateIP()
|
||||
self.updateMAC()
|
||||
return r
|
||||
|
||||
def delete( self ):
|
||||
@@ -328,7 +352,7 @@ class Link( object ):
|
||||
Other types of links could be tunnels, link emulators, etc.."""
|
||||
|
||||
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,
|
||||
params2=None ):
|
||||
"""Create veth link to another node, making two new interfaces.
|
||||
@@ -354,7 +378,7 @@ class Link( object ):
|
||||
if not intfName2:
|
||||
intfName2 = self.intfName( node2, port2 )
|
||||
|
||||
self.makeIntfPair( intfName1, intfName2 )
|
||||
self.makeIntfPair( intfName1, intfName2, addr1, addr2 )
|
||||
|
||||
if not cls1:
|
||||
cls1 = intf
|
||||
@@ -366,9 +390,9 @@ class Link( object ):
|
||||
params2 = {}
|
||||
|
||||
intf1 = cls1( name=intfName1, node=node1, port=port1,
|
||||
link=self, **params1 )
|
||||
link=self, mac=addr1, **params1 )
|
||||
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
|
||||
self.intf1, self.intf2 = intf1, intf2
|
||||
@@ -379,13 +403,13 @@ class Link( object ):
|
||||
return node.name + '-eth' + repr( n )
|
||||
|
||||
@classmethod
|
||||
def makeIntfPair( cls, intf1, intf2 ):
|
||||
def makeIntfPair( cls, intf1, intf2, addr1=None, addr2=None ):
|
||||
"""Create pair of interfaces
|
||||
intf1: name of interface 1
|
||||
intf2: name of interface 2
|
||||
(override this class method [and possibly delete()]
|
||||
to change link type)"""
|
||||
makeIntfPair( intf1, intf2 )
|
||||
makeIntfPair( intf1, intf2, addr1, addr2 )
|
||||
|
||||
def delete( self ):
|
||||
"Delete this link"
|
||||
@@ -398,10 +422,12 @@ class Link( object ):
|
||||
class TCLink( Link ):
|
||||
"Link with symmetric TC interfaces configured via opts"
|
||||
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,
|
||||
intfName1=intfName1, intfName2=intfName2,
|
||||
cls1=TCIntf,
|
||||
cls2=TCIntf,
|
||||
addr1=addr1, addr2=addr2,
|
||||
params1=params,
|
||||
params2=params)
|
||||
|
||||
+5
-1
@@ -90,6 +90,7 @@ import os
|
||||
import re
|
||||
import select
|
||||
import signal
|
||||
import random
|
||||
import copy
|
||||
from time import sleep
|
||||
from itertools import chain, groupby
|
||||
@@ -326,8 +327,12 @@ class Mininet( object ):
|
||||
port1: source port
|
||||
port2: dest port
|
||||
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,
|
||||
'port2': port2,
|
||||
'addr1': mac1,
|
||||
'addr2': mac2,
|
||||
'intf': self.intf }
|
||||
defaults.update( params )
|
||||
if not cls:
|
||||
@@ -350,7 +355,6 @@ class Mininet( object ):
|
||||
# quietRun( 'renice +18 -p ' + repr( host.pid ) )
|
||||
# This may not be the right place to do this, but
|
||||
# it needs to be done somewhere.
|
||||
host.cmd( 'ifconfig lo up' )
|
||||
info( '\n' )
|
||||
|
||||
def buildFromTopo( self, topo=None ):
|
||||
|
||||
@@ -924,7 +924,6 @@ class UserSwitch( Switch ):
|
||||
for c in controllers ] )
|
||||
ofdlog = '/tmp/' + self.name + '-ofd.log'
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
|
||||
self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
|
||||
' punix:/tmp/' + self.name + ' -d %s ' % self.dpid +
|
||||
@@ -975,7 +974,6 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
def start( self, controllers ):
|
||||
"Start up kernel datapath."
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
quietRun( 'ifconfig lo up' )
|
||||
# Delete local datapath if it exists;
|
||||
# then create a new one monitoring the given interfaces
|
||||
self.cmd( 'ovs-dpctl del-dp ' + self.dp )
|
||||
@@ -1093,9 +1091,6 @@ class OVSSwitch( Switch ):
|
||||
if self.inNamespace:
|
||||
raise Exception(
|
||||
'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
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
int( self.dpid, 16 ) # DPID must be a hex string
|
||||
@@ -1200,7 +1195,6 @@ class IVSSwitch(Switch):
|
||||
|
||||
logfile = '/tmp/ivs.%s.log' % self.name
|
||||
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
|
||||
|
||||
def stop( self ):
|
||||
@@ -1240,7 +1234,6 @@ class Controller( Node ):
|
||||
self.protocol = protocol
|
||||
Node.__init__( self, name, inNamespace=inNamespace,
|
||||
ip=ip, **params )
|
||||
self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary
|
||||
self.checkListening()
|
||||
|
||||
def checkListening( self ):
|
||||
|
||||
@@ -45,7 +45,7 @@ class testOptionsTopoCommon( object ):
|
||||
mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
|
||||
lopts=lopts ),
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
switch=self.switchClass )
|
||||
switch=self.switchClass, waitConnected=True )
|
||||
dropped = mn.run( mn.ping )
|
||||
self.assertEqual( dropped, 0 )
|
||||
|
||||
@@ -67,7 +67,8 @@ class testOptionsTopoCommon( object ):
|
||||
#self.runOptionsTopoTest( N, hopts=hopts )
|
||||
|
||||
mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
|
||||
host=CPULimitedHost, switch=self.switchClass )
|
||||
host=CPULimitedHost, switch=self.switchClass,
|
||||
waitConnected=True )
|
||||
mn.start()
|
||||
results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
|
||||
mn.stop()
|
||||
@@ -77,13 +78,16 @@ class testOptionsTopoCommon( object ):
|
||||
|
||||
def testLinkBandwidth( self ):
|
||||
"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
|
||||
# Verify ability to create limited-link topo first;
|
||||
lopts = { 'bw': BW, 'use_htb': True }
|
||||
# Also verify correctness of limit limitng within a bound.
|
||||
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' )
|
||||
for bw_str in bw_strs:
|
||||
bw = float( bw_str.split(' ')[0] )
|
||||
@@ -95,7 +99,8 @@ class testOptionsTopoCommon( object ):
|
||||
DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
|
||||
lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
|
||||
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 )
|
||||
test_outputs = ping_delays[0]
|
||||
# Ignore unused variables below
|
||||
@@ -117,7 +122,8 @@ class testOptionsTopoCommon( object ):
|
||||
lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
|
||||
mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
|
||||
host=CPULimitedHost, link=TCLink,
|
||||
switch=self.switchClass )
|
||||
switch=self.switchClass,
|
||||
waitConnected=True )
|
||||
# Drops are probabilistic, but the chance of no dropped packets is
|
||||
# 1 in 100 million with 4 hops for a link w/99% loss.
|
||||
dropped_total = 0
|
||||
|
||||
+10
-4
@@ -145,7 +145,7 @@ isShellBuiltin.builtIns = None
|
||||
# live in the root namespace and thus do not have to be
|
||||
# explicitly moved.
|
||||
|
||||
def makeIntfPair( intf1, intf2 ):
|
||||
def makeIntfPair( intf1, intf2, addr1=None, addr2=None ):
|
||||
"""Make a veth pair connecting intf1 and intf2.
|
||||
intf1: string, interface
|
||||
intf2: string, interface
|
||||
@@ -154,7 +154,11 @@ def makeIntfPair( intf1, intf2 ):
|
||||
quietRun( 'ip link del ' + intf1 )
|
||||
quietRun( 'ip link del ' + intf2 )
|
||||
# Create new pair
|
||||
if addr1 is None and addr2 is None:
|
||||
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 )
|
||||
if cmdOutput == '':
|
||||
return True
|
||||
@@ -185,10 +189,12 @@ def moveIntfNoRetry( intf, dstNode, srcNode=None, printError=False ):
|
||||
intf = str( intf )
|
||||
cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
|
||||
if srcNode:
|
||||
srcNode.cmd( cmd )
|
||||
cmdOutput = srcNode.cmd( cmd )
|
||||
else:
|
||||
quietRun( cmd )
|
||||
if ( ' %s:' % intf ) not in dstNode.cmd( 'ip link show', intf ):
|
||||
cmdOutput = quietRun( cmd )
|
||||
# If ip link set does not produce any output, then we can assume
|
||||
# that the link has been moved successfully.
|
||||
if cmdOutput:
|
||||
if printError:
|
||||
error( '*** Error: moveIntf: ' + intf +
|
||||
' not successfully moved to ' + dstNode.name + '\n' )
|
||||
|
||||
Reference in New Issue
Block a user