1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Files
CumulusNetworks-ifupdown2/ifupdownaddons/bridgeutils.py
Julien Fortin a193d8d1c0 performance fix: better handling fd to allow subprocess.close_fds=False and code re-organisation
Ticket: None
Reviewed By: CCR-4692
Testing Done: smoke + scale tests

If called with close_fds=True the subprocess module will try to close every fd
from 3 to MAXFD before executing the specified command. This is done in Python
not even with a C-implementation which truly affecting performances.

This patch aims to better handle the file descriptor used by ifupdown2. Either
by closing them after use or by setting the close-on-exec flag for the file
descriptor, which causes the file descriptor to be automatically
(and atomically) closed when any of the exec-family functions succeed.

With the actual patch all tests are passing, I can't think of any future issue
but if any a possible future modification might be to use the parameter
'preexec_fn', which allows us to set function which will be executed in the
child process before executing the command line. We can always manually close
any remaining open file descriptors with something like:

>>> os.listdir('/proc/self/fd/')
['0', '1', '2', ‘3’, etc..]
>>> for fd in os.listdir('/proc/self/fd/')
>>>    if int(fd) > 2:
>>>    	  os.close(fd)

This patch is also totally re-organising the use of subprocesses. By removing
all subprocess code redundancy.
2016-06-16 03:37:33 +01:00

506 lines
20 KiB
Python

#!/usr/bin/python
#
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
from ifupdown.iface import *
from utilsbase import *
import os
import re
import logging
from ifupdown.utils import utils
import ifupdown.ifupdownflags as ifupdownflags
from cache import *
class brctl(utilsBase):
""" This class contains helper functions to interact with the bridgeutils
commands """
_cache_fill_done = False
def __init__(self, *args, **kargs):
utilsBase.__init__(self, *args, **kargs)
if ifupdownflags.flags.CACHE and not brctl._cache_fill_done:
self._bridge_fill()
brctl._cache_fill_done = True
def _bridge_get_mcattrs_from_sysfs(self, bridgename):
mcattrs = {}
mcattrmap = {'mclmc': 'multicast_last_member_count',
'mcrouter': 'multicast_router',
'mcsnoop' : 'multicast_snooping',
'mcsqc' : 'multicast_startup_query_count',
'mcqifaddr' : 'multicast_query_use_ifaddr',
'mcquerier' : 'multicast_querier',
'hashel' : 'hash_elasticity',
'hashmax' : 'hash_max',
'mclmi' : 'multicast_last_member_interval',
'mcmi' : 'multicast_membership_interval',
'mcqpi' : 'multicast_querier_interval',
'mcqi' : 'multicast_query_interval',
'mcqri' : 'multicast_query_response_interval',
'mcsqi' : 'multicast_startup_query_interval'}
mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi']
for m, s in mcattrmap.items():
n = self.read_file_oneline('/sys/class/net/%s/bridge/%s'
%(bridgename, s))
if m in mcattrsdivby100:
try:
v = int(n) / 100
mcattrs[m] = str(v)
except Exception, e:
self.logger.warn('error getting mc attr %s (%s)'
%(m, str(e)))
pass
else:
mcattrs[m] = n
return mcattrs
def _bridge_attrs_fill(self, bridgename):
battrs = {}
bports = {}
brout = utils.exec_command('/sbin/brctl showstp %s' % bridgename)
chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE)
try:
# Get all bridge attributes
broutlines = chunks[0].splitlines()
#battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
battrs['maxage'] = broutlines[4].split(
'bridge max age')[1].strip().replace('.00', '')
battrs['hello'] = broutlines[5].split(
'bridge hello time')[1].strip().replace('.00',
'')
battrs['fd'] = broutlines[6].split(
'bridge forward delay')[1].strip(
).replace('.00', '')
battrs['ageing'] = broutlines[7].split(
'ageing time')[1].strip().replace('.00', '')
battrs['mcrouter'] = broutlines[12].split(
'mc router')[1].strip().split('\t\t\t')[0]
battrs['bridgeprio'] = self.read_file_oneline(
'/sys/class/net/%s/bridge/priority' %bridgename)
battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename))
# XXX: comment this out until mc attributes become available
# with brctl again
#battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip()
#battrs['hashmax'] = broutlines[10].split('hash max')[1].strip()
#battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip()
#battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
#battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip()
##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[1].strip()
#battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip()
except Exception, e:
self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
pass
linkCache.update_attrdict([bridgename, 'linkinfo'], battrs)
for cidx in range(1, len(chunks)):
bpout = chunks[cidx].lstrip('\n')
if not bpout or bpout[0] == ' ':
continue
bplines = bpout.splitlines()
pname = bplines[0].split()[0]
bportattrs = {}
try:
bportattrs['pathcost'] = bplines[2].split(
'path cost')[1].strip()
bportattrs['fdelay'] = bplines[4].split(
'forward delay timer')[1].strip()
bportattrs['portmcrouter'] = self.read_file_oneline(
'/sys/class/net/%s/brport/multicast_router' %pname)
bportattrs['portmcfl'] = self.read_file_oneline(
'/sys/class/net/%s/brport/multicast_fast_leave' %pname)
bportattrs['portprio'] = self.read_file_oneline(
'/sys/class/net/%s/brport/priority' %pname)
#bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip()
#bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip()
except Exception, e:
self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
bports[pname] = bportattrs
linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports)
def _bridge_fill(self, bridgename=None, refresh=False):
try:
# if cache is already filled, return
linkCache.get_attr([bridgename, 'linkinfo', 'fd'])
return
except:
pass
if not bridgename:
brctlout = utils.exec_command('/sbin/brctl show')
else:
brctlout = utils.exec_command('/sbin/brctl show %s' % bridgename)
if not brctlout:
return
for bline in brctlout.splitlines()[1:]:
bitems = bline.split()
if len(bitems) < 2:
continue
try:
linkCache.update_attrdict([bitems[0], 'linkinfo'],
{'stp' : bitems[2]})
except KeyError:
linkCache.update_attrdict([bitems[0]],
{'linkinfo' : {'stp' : bitems[2]}})
self._bridge_attrs_fill(bitems[0])
def _cache_get(self, attrlist, refresh=False):
try:
if ifupdownflags.flags.DRYRUN:
return None
if ifupdownflags.flags.CACHE:
if not self._cache_fill_done:
self._bridge_fill()
self._cache_fill_done = True
return linkCache.get_attr(attrlist)
if not refresh:
return linkCache.get_attr(attrlist)
self._bridge_fill(attrlist[0], refresh)
return linkCache.get_attr(attrlist)
except Exception, e:
self.logger.debug('_cache_get(%s) : [%s]'
%(str(attrlist), str(e)))
pass
return None
def _cache_check(self, attrlist, value, refresh=False):
try:
attrvalue = self._cache_get(attrlist, refresh)
if attrvalue and attrvalue == value:
return True
except Exception, e:
self.logger.debug('_cache_check(%s) : [%s]'
%(str(attrlist), str(e)))
pass
return False
def _cache_update(self, attrlist, value):
if ifupdownflags.flags.DRYRUN: return
try:
linkCache.add_attr(attrlist, value)
except:
pass
def _cache_delete(self, attrlist):
if ifupdownflags.flags.DRYRUN: return
try:
linkCache.del_attr(attrlist)
except:
pass
def _cache_invalidate(self):
if ifupdownflags.flags.DRYRUN: return
linkCache.invalidate()
def create_bridge(self, bridgename):
if self.bridge_exists(bridgename):
return
utils.exec_command('/sbin/brctl addbr %s' % bridgename)
self._cache_update([bridgename], {})
def delete_bridge(self, bridgename):
if not self.bridge_exists(bridgename):
return
utils.exec_command('/sbin/brctl delbr %s' % bridgename)
self._cache_invalidate()
def add_bridge_port(self, bridgename, bridgeportname):
""" Add port to bridge """
ports = self._cache_get([bridgename, 'linkinfo', 'ports'])
if ports and ports.get(bridgeportname):
return
utils.exec_command('/sbin/brctl addif %s %s' %
(bridgename, bridgeportname))
self._cache_update([bridgename, 'linkinfo', 'ports',
bridgeportname], {})
def delete_bridge_port(self, bridgename, bridgeportname):
""" Delete port from bridge """
ports = self._cache_get([bridgename, 'linkinfo', 'ports'])
if not ports or not ports.get(bridgeportname):
return
utils.exec_command('/sbin/brctl delif %s %s' %
(bridgename, bridgeportname))
self._cache_delete([bridgename, 'linkinfo', 'ports',
'bridgeportname'])
def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
portattrs = self._cache_get([bridgename, 'linkinfo',
'ports', bridgeportname])
if portattrs == None: portattrs = {}
for k, v in attrdict.iteritems():
if ifupdownflags.flags.CACHE:
curval = portattrs.get(k)
if curval and curval == v:
continue
utils.exec_command('/sbin/brctl set%s %s %s %s' %
(k, bridgename, bridgeportname, v))
def set_bridgeport_attr(self, bridgename, bridgeportname,
attrname, attrval):
if self._cache_check([bridgename, 'linkinfo', 'ports',
bridgeportname, attrname], attrval):
return
utils.exec_command('/sbin/brctl set%s %s %s %s' %
(attrname,
bridgename,
bridgeportname,
attrval))
def set_bridge_attrs(self, bridgename, attrdict):
for k, v in attrdict.iteritems():
if not v:
continue
if self._cache_check([bridgename, 'linkinfo', k], v):
continue
try:
cmd = '/sbin/brctl set%s %s %s' % (k, bridgename, v)
utils.exec_command(cmd)
except Exception, e:
self.logger.warn('%s: %s' %(bridgename, str(e)))
pass
def set_bridge_attr(self, bridgename, attrname, attrval):
if self._cache_check([bridgename, 'linkinfo', attrname], attrval):
return
utils.exec_command('/sbin/brctl set%s %s %s' %
(attrname, bridgename, attrval))
def get_bridge_attrs(self, bridgename):
return self._cache_get([bridgename, 'linkinfo'])
def get_bridgeport_attrs(self, bridgename, bridgeportname):
return self._cache_get([bridgename, 'linkinfo', 'ports',
bridgeportname])
def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
return self._cache_get([bridgename, 'linkinfo', 'ports',
bridgeportname, attrname])
def set_stp(self, bridge, stp_state):
utils.exec_command('/sbin/brctl stp %s %s' % (bridge, stp_state))
def get_stp(self, bridge):
sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' %bridge
if not os.path.exists(sysfs_stpstate):
return 'error'
stpstate = self.read_file_oneline(sysfs_stpstate)
if not stpstate:
return 'error'
try:
if int(stpstate) > 0:
return 'yes'
elif int(stpstate) == 0:
return 'no'
except:
return 'unknown'
def conv_value_to_user(self, str):
try:
ret = int(str) / 100
except:
return None
finally:
return '%d' %ret
def read_value_from_sysfs(self, filename, preprocess_func):
value = self.read_file_oneline(filename)
if not value:
return None
return preprocess_func(value)
def set_ageing(self, bridge, ageing):
utils.exec_command('/sbin/brctl setageing %s %s' % (bridge, ageing))
def get_ageing(self, bridge):
return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
%bridge, self.conv_value_to_user)
def set_bridgeprio(self, bridge, prio):
utils.exec_command('/sbin/brctl setbridgeprio %s %s' % (bridge, prio))
def get_bridgeprio(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/priority' %bridge)
def set_fd(self, bridge, fd):
utils.exec_command('/sbin/brctl setfd %s %s' % (bridge, fd))
def get_fd(self, bridge):
return self.read_value_from_sysfs(
'/sys/class/net/%s/bridge/forward_delay'
%bridge, self.conv_value_to_user)
def set_gcint(self, bridge, gcint):
#cmd = '/sbin/brctl setgcint ' + bridge + ' ' + gcint
raise Exception('set_gcint not implemented')
def set_hello(self, bridge, hello):
utils.exec_command('/sbin/brctl sethello %s %s' % (bridge, hello))
def get_hello(self, bridge):
return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
%bridge, self.conv_value_to_user)
def set_maxage(self, bridge, maxage):
utils.exec_command('/sbin/brctl setmaxage %s %s' % (bridge, maxage))
def get_maxage(self, bridge):
return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
%bridge, self.conv_value_to_user)
def set_pathcost(self, bridge, port, pathcost):
utils.exec_command('/sbin/brctl setpathcost %s %s %s' %
(bridge, port, pathcost))
def get_pathcost(self, bridge, port):
return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
%port)
def set_portprio(self, bridge, port, prio):
utils.exec_command('/sbin/brctl setportprio %s %s %s' %
(bridge, port, prio))
def get_portprio(self, bridge, port):
return self.read_file_oneline('/sys/class/net/%s/brport/priority'
%port)
def set_hashmax(self, bridge, hashmax):
utils.exec_command('/sbin/brctl sethashmax %s %s' % (bridge, hashmax))
def get_hashmax(self, bridge):
return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
%bridge)
def set_hashel(self, bridge, hashel):
utils.exec_command('/sbin/brctl sethashel %s %s' % (bridge, hashel))
def get_hashel(self, bridge):
return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
%bridge)
def set_mclmc(self, bridge, mclmc):
utils.exec_command('/sbin/brctl setmclmc %s %s' % (bridge, mclmc))
def get_mclmc(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_last_member_count'
%bridge)
def set_mcrouter(self, bridge, mcrouter):
utils.exec_command('/sbin/brctl setmcrouter %s %s' % (bridge, mcrouter))
def get_mcrouter(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_router' %bridge)
def set_mcsnoop(self, bridge, mcsnoop):
utils.exec_command('/sbin/brctl setmcsnoop %s %s' % (bridge, mcsnoop))
def get_mcsnoop(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_snooping' %bridge)
def set_mcsqc(self, bridge, mcsqc):
utils.exec_command('/sbin/brctl setmcsqc %s %s' % (bridge, mcsqc))
def get_mcsqc(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_startup_query_count'
%bridge)
def set_mcqifaddr(self, bridge, mcqifaddr):
utils.exec_command('/sbin/brctl setmcqifaddr %s %s' %
(bridge, mcqifaddr))
def get_mcqifaddr(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
%bridge)
def set_mcquerier(self, bridge, mcquerier):
utils.exec_command('/sbin/brctl setmcquerier %s %s' %
(bridge, mcquerier))
def get_mcquerier(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_querier' %bridge)
def set_mcqv4src(self, bridge, vlan, mcquerier):
if vlan == 0 or vlan > 4095:
self.logger.warn('mcqv4src vlan \'%d\' invalid range' %vlan)
return
ip = mcquerier.split('.')
if len(ip) != 4:
self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier)
return
for k in ip:
if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier)
return
utils.exec_command('/sbin/brctl setmcqv4src %s %d %s' %
(bridge, vlan, mcquerier))
def del_mcqv4src(self, bridge, vlan):
utils.exec_command('/sbin/brctl delmcqv4src %s %d' % (bridge, vlan))
def get_mcqv4src(self, bridge, vlan=None):
mcqv4src = {}
mcqout = utils.exec_command('/sbin/brctl showmcqv4src %s' % bridge)
if not mcqout: return None
mcqlines = mcqout.splitlines()
for l in mcqlines[1:]:
l=l.strip()
k, d, v = l.split('\t')
if not k or not v:
continue
mcqv4src[k] = v
if vlan:
return mcqv4src.get(vlan)
return mcqv4src
def set_mclmi(self, bridge, mclmi):
utils.exec_command('/sbin/brctl setmclmi %s %s' % (bridge, mclmi))
def get_mclmi(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_last_member_interval'
%bridge)
def set_mcmi(self, bridge, mcmi):
utils.exec_command('/sbin/brctl setmcmi %s %s' % (bridge, mcmi))
def get_mcmi(self, bridge):
return self.read_file_oneline(
'/sys/class/net/%s/bridge/multicast_membership_interval'
%bridge)
def bridge_exists(self, bridge):
return os.path.exists('/sys/class/net/%s/bridge' %bridge)
def is_bridge_port(self, ifacename):
return os.path.exists('/sys/class/net/%s/brport' %ifacename)
def bridge_port_exists(self, bridge, bridgeportname):
try:
return os.path.exists('/sys/class/net/%s/brif/%s'
%(bridge, bridgeportname))
except Exception:
return False
def get_bridge_ports(self, bridgename):
try:
return os.listdir('/sys/class/net/%s/brif/' %bridgename)
except:
return []