1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
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

214 lines
8.3 KiB
Python

#!/usr/bin/python
#
# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
#
#
'''
The PolicyManager should be subclassed by addon modules
to read a JSON policy config file that is later used to
set defaults:
Initialize: This module defines a list of config file location based
on module. There are defined in the __init__(): All the
addon modules need to do is import the policymanager module.
import ifupdown.policymanager as policymanager
Provides: an API to retrieve link attributes based on addon module name,
interface name, and attribute.
The ifupdown.policymanager module provides a global object policymanager_api
that can be called like so:
speed_default = policymanager.policymanager_api.get_default(
module_name='ethtool',
ifname=ifaceobj.name,
attr='link-speed'
)
'''
import json
import logging
import glob
class policymanager():
def __init__(self):
# we should check for these files in order
# so that customers can override the /var/lib file settings
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
# we grab the json files from a known location and make sure that
# the defaults_policy is checked first
user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
# grab the default module files
default_files = glob.glob('/var/lib/ifupdown2/policy.d/*.json')
# keep an array of defaults indexed by module name
self.system_policy_array = {}
for filename in default_files:
system_array = {}
try:
with open(filename, 'r') as fd:
system_array = json.load(fd)
self.logger.debug('reading %s system policy defaults config' \
% filename)
except Exception, e:
self.logger.info('could not read %s system policy defaults config' \
% filename)
self.logger.info(' exception is %s' % str(e))
for module in system_array.keys():
if self.system_policy_array.has_key(module):
self.logger.debug('warning: overwriting system module %s from file %s' \
% (module,filename))
self.system_policy_array[module] = system_array[module]
# take care of user defined policy defaults
self.user_policy_array = {}
for filename in user_files:
user_array = {}
try:
with open(filename, 'r') as fd:
user_array = json.load(fd)
self.logger.debug('reading %s policy user defaults config' \
% filename)
except Exception, e:
self.logger.debug('could not read %s user policy defaults config' \
% filename)
self.logger.debug(' exception is %s' % str(e))
# customer added module attributes
for module in user_array.keys():
if self.system_policy_array.has_key(module):
# warn user that we are overriding the system module setting
self.logger.debug('warning: overwriting system with user module %s from file %s' \
% (module,filename))
self.user_policy_array[module] = user_array[module]
return
def get_iface_default(self,module_name=None,ifname=None,attr=None):
'''
get_iface_default: Addon modules must use one of two types of access methods to
the default configs. In this method, we expect the default to be
either in
[module]['iface_defaults'][ifname][attr] or
[module]['defaults'][attr]
We first check the user_policy_array and return that value. But if
the user did not specify an override, we use the system_policy_array.
'''
# make sure we have an index
if (not ifname or not attr or not module_name):
return None
val = None
# users can specify defaults to override the systemwide settings
# look for user specific interface attribute iface_defaults first
try:
# looks for user specified value
val = self.user_policy_array[module_name]['iface_defaults'][ifname][attr]
return val
except (TypeError, KeyError, IndexError):
pass
try:
# failing that, there may be a user default for all intefaces
val = self.user_policy_array[module_name]['defaults'][attr]
return val
except (TypeError, KeyError, IndexError):
pass
try:
# failing that, look for system setting for the interface
val = self.system_policy_array[module_name]['iface_defaults'][ifname][attr]
return val
except (TypeError, KeyError, IndexError):
pass
try:
# failing that, look for system setting for all interfaces
val = self.system_policy_array[module_name]['defaults'][attr]
return val
except (TypeError, KeyError, IndexError):
pass
# could not find any system or user default so return Non
return val
def get_attr_default(self,module_name=None,attr=None):
'''
get_attr_default: Addon modules must use one of two types of access methods to
the default configs. In this method, we expect the default to be in
[module]['defaults'][attr]
We first check the user_policy_array and return that value. But if
the user did not specify an override, we use the system_policy_array.
'''
if (not attr or not module_name):
return None
# users can specify defaults to override the systemwide settings
# look for user specific attribute defaults first
val = None
try:
# looks for user specified value
val = self.user_policy_array[module_name]['defaults'][attr]
return val
except (TypeError, KeyError, IndexError):
pass
try:
# failing that, look for system setting
val = self.system_policy_array[module_name]['defaults'][attr]
return val
except (TypeError, KeyError, IndexError):
pass
return val
def get_module_globals(self,module_name=None,attr=None):
'''
get_module_globals: Addon modules must use one of two types of access methods to
the default configs. In this method, we expect the default to be in
[module]['module_globals'][attr]
We first check the user_policy_array and return that value. But if
the user did not specify an override, we use the system_policy_array.
'''
if (not attr or not module_name):
return None
# users can specify defaults to override the systemwide settings
# look for user specific attribute defaults first
val = None
try:
# looks for user specified value
val = self.user_policy_array[module_name]['module_globals'][attr]
return val
except (TypeError, KeyError, IndexError):
pass
try:
# failing that, look for system setting
val = self.system_policy_array[module_name]['module_globals'][attr]
return val
except (TypeError, KeyError, IndexError):
pass
return val
def get_module_default(self,module_name=None):
'''
get_module_default: Addon modules can also access the entire config
This method returns indexed by "system" and "user": these are the
system-wide and user-defined policy arrays for a specific module.
'''
if not module_name:
return None
if self.system_policy_array.get(module_name) and \
self.user_policy_array.get(module_name):
mod_array = {"system":self.system_policy_array[module_name],
"user":self.user_policy_array[module_name]}
else:
# the module must not have these defined, return None
mod_array = None
return mod_array
policymanager_api = policymanager()