Commit 8e107684 authored by Ad Schellevis's avatar Ad Schellevis

style fixes configd

parent cba10171
......@@ -29,3 +29,4 @@
--------------------------------------------------------------------------------------
package : configd
"""
......@@ -30,8 +30,9 @@
package : configd
"""
class Helpers(object):
def __init__(self,template_in_data):
def __init__(self, template_in_data):
""" initialize template helpers
:param template_in_data:
......@@ -39,22 +40,22 @@ class Helpers(object):
"""
self._template_in_data = template_in_data
def getNodeByTag(self,tag):
def getNodeByTag(self, tag):
""" get tree node by tag
:param tag: tag in dot notation (section.item)
:return: dict or None if not found
"""
node = self._template_in_data
for item in tag.split('.'):
if node.has_key(item):
node=node[item]
if item in node:
node = node[item]
else:
# not found
return None
# path found, return
return node
def exists(self,tag):
def exists(self, tag):
"""
check if node exists in dictionary structure
:param tag: tag in dot notation (section.item)
......@@ -64,3 +65,4 @@ class Helpers(object):
return True
else:
return False
......@@ -43,7 +43,7 @@ import xml.etree.cElementTree as ElementTree
class Config(object):
def __init__(self,filename):
def __init__(self, filename):
self._filename = filename
self._config_data = {}
self._file_mod = 0
......@@ -62,25 +62,25 @@ class Config(object):
self._config_data = self._traverse(root)
self._file_mod = mod_time
def _traverse(self,xmlNode):
def _traverse(self, xmlNode):
""" traverse xml node and return ordered dictionary structure
:param xmlNode: ElementTree node
:return: collections.OrderedDict
"""
this_item = collections.OrderedDict()
if len(list(xmlNode)) > 0 :
if len(list(xmlNode)) > 0:
for item in list(xmlNode):
item_content = self._traverse(item)
if this_item.has_key(item.tag):
if item.tag in this_item:
if type(this_item[item.tag]) != list:
tmp_item = copy.deepcopy(this_item[item.tag])
this_item[item.tag] = []
this_item[item.tag].append(tmp_item)
if item_content != None:
if item_content is not None:
# skip empty fields
this_item[item.tag].append(item_content)
elif item_content != None:
elif item_content is not None:
# create a new named item
this_item[item.tag] = self._traverse(item)
else:
......@@ -89,8 +89,7 @@ class Config(object):
return this_item
def indent(self,elem, level=0):
def indent(self, elem, level=0):
""" indent cElementTree (prettyprint fix)
used from : http://infix.se/2007/02/06/gentlemen-indent-your-xml
@param elem: cElementTree
......@@ -110,7 +109,6 @@ class Config(object):
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def get(self):
""" get active config data, load from disc if file in memory is different
......
......@@ -37,7 +37,7 @@ __author__ = 'Ad Schellevis'
import syslog
def execute(action,parameters):
def execute(action, parameters):
""" wrapper for inline functions
:param action: action object ( processhandler.Action type )
......@@ -54,7 +54,7 @@ def execute(action,parameters):
# send generated filenames to syslog
for filename in filenames:
syslog.syslog(syslog.LOG_DEBUG,' %s generated %s' % ( parameters, filename ) )
syslog.syslog(syslog.LOG_DEBUG, ' %s generated %s' % (parameters, filename))
del conf
del tmpl
......
......@@ -47,6 +47,7 @@ import uuid
import shlex
import ph_inline_actions
class Handler(object):
""" Main handler class, opens unix domain socket and starts listening
- New connections are handed over to a HandlerClient type object in a new thread
......@@ -58,12 +59,12 @@ class Handler(object):
-> execute ActionHandler command using Action objects
<- send back result string
"""
def __init__(self,socket_filename,config_path,simulation_mode=False):
def __init__(self, socket_filename, config_path, simulation_mode=False):
""" Constructor
:param socket_filename: filename of unix domain socket to use
:param config_path: location of configuration files
:param emulate: emulation mode, do not start actual (script) commands
:param simulation_mode: emulation mode, do not start actual (script) commands
:return: object
"""
self.socket_filename = socket_filename
......@@ -90,7 +91,7 @@ class Handler(object):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(self.socket_filename)
os.chmod(self.socket_filename,0o666)
os.chmod(self.socket_filename, 0o666)
sock.listen(30)
while True:
# wait for a connection to arrive
......@@ -100,7 +101,7 @@ class Handler(object):
client_address=client_address,
action_handler=actHandler,
simulation_mode=self.simulation_mode)
if self.single_threaded :
if self.single_threaded:
# run single threaded
cmd_thread.run()
else:
......@@ -116,20 +117,20 @@ class Handler(object):
except:
# something went wrong... send traceback to syslog, restart listener (wait for a short time)
print (traceback.format_exc())
syslog.syslog(syslog.LOG_ERR, 'Handler died on %s'%traceback.format_exc())
syslog.syslog(syslog.LOG_ERR, 'Handler died on %s' % traceback.format_exc())
time.sleep(1)
class HandlerClient(threading.Thread):
""" Handle commands via specified socket connection
"""
def __init__ (self,connection,client_address,action_handler,simulation_mode=False):
def __init__(self, connection, client_address, action_handler, simulation_mode=False):
"""
:param connection: socket connection object
:param client_address: client address ( from socket accept )
:param action_handler: action handler object
:param emulate: Emulation mode, do not start actual (script) commands
:param simulation_mode: Emulation mode, do not start actual (script) commands
:return: None
"""
threading.Thread.__init__(self)
......@@ -180,10 +181,10 @@ class HandlerClient(threading.Thread):
# execute requested action
if self.simulation_mode:
self.action_handler.showAction(exec_command,exec_action,exec_params)
self.action_handler.showAction(exec_command, exec_action, exec_params)
result = 'OK'
else:
result = self.action_handler.execute(exec_command,exec_action,exec_params)
result = self.action_handler.execute(exec_command, exec_action, exec_params)
if not exec_in_background:
# send response back to client( including trailing enter )
......@@ -197,7 +198,7 @@ class HandlerClient(threading.Thread):
# send end of stream characters
if not exec_in_background:
self.connection.sendall("%c%c%c"%(chr(0),chr(0),chr(0)))
self.connection.sendall("%c%c%c" % (chr(0), chr(0), chr(0)))
except SystemExit:
# ignore system exit related errors
pass
......@@ -214,10 +215,11 @@ class HandlerClient(threading.Thread):
if not exec_in_background:
self.connection.close()
class ActionHandler(object):
""" Start/stop services and functions using configuration data defined in conf/actions_<topic>.conf
"""
def __init__(self,config_path):
def __init__(self, config_path):
""" Initialize action handler to start system functions
:param config_path: full path of configuration data
......@@ -234,11 +236,12 @@ class ActionHandler(object):
"""
self.action_map = {}
for config_filename in glob.glob('%s/actions_*.conf'%(self.config_path)) + glob.glob('%s/actions.d/actions_*.conf'%(self.config_path)):
for config_filename in glob.glob('%s/actions_*.conf' % self.config_path) \
+ glob.glob('%s/actions.d/actions_*.conf' % self.config_path):
# this topic's name (service, filter, template, etc)
# make sure there's an action map index for this topic
topic_name = config_filename.split('actions_')[-1].split('.')[0]
if self.action_map.has_key(topic_name) == False:
if topic_name not in self.action_map:
self.action_map[topic_name] = {}
# traverse config directory and open all filenames starting with actions_
......@@ -248,12 +251,12 @@ class ActionHandler(object):
# map configuration data on object
action_obj = Action()
for act_prop in cnf.items(section):
setattr(action_obj,act_prop[0],act_prop[1])
setattr(action_obj, act_prop[0], act_prop[1])
if section.find('.') > -1:
# at this moment we only support 2 levels of actions ( 3 if you count topic as well )
for alias in section.split('.')[0].split('|'):
if self.action_map[topic_name].has_key(alias) == False:
if alias not in self.action_map[topic_name]:
self.action_map[topic_name][alias] = {}
self.action_map[topic_name][alias][section.split('.')[1]] = action_obj
else:
......@@ -269,20 +272,20 @@ class ActionHandler(object):
:return: action object or None if not found
"""
action_obj = None
if self.action_map.has_key(command):
if self.action_map[command].has_key(action):
if command in self.action_map:
if action in self.action_map[command]:
if type(self.action_map[command][action]) == dict:
if len(parameters) > 0 and self.action_map[command][action].has_key(parameters[0]) == True:
if len(parameters) > 0 and parameters[0] in self.action_map[command][action]:
# 3 level action ( "interface linkup start" for example )
if isinstance(self.action_map[command][action][parameters[0]],Action):
if isinstance(self.action_map[command][action][parameters[0]], Action):
action_obj = self.action_map[command][action][parameters[0]]
action_obj.setParameterStartPos(1)
elif isinstance(self.action_map[command][action],Action):
elif isinstance(self.action_map[command][action], Action):
action_obj = self.action_map[command][action]
return action_obj
def execute(self,command,action,parameters):
def execute(self, command, action, parameters):
""" execute configuration defined action
:param command: command/topic for example interface
......@@ -291,27 +294,27 @@ class ActionHandler(object):
:return: OK on success, else error code
"""
action_params = []
action_obj = self.findAction(command,action,parameters)
action_obj = self.findAction(command, action, parameters)
if action_obj is not None:
if parameters is not None and len(parameters) > action_obj.getParameterStartPos():
action_params = parameters[action_obj.getParameterStartPos():]
return '%s\n'%action_obj.execute(action_params)
return '%s\n' % action_obj.execute(action_params)
return 'Action not found\n'
def showAction(self,command,action,parameters):
def showAction(self, command, action, parameters):
""" debug/simulation mode: show action information
:return:
"""
action_obj = self.findAction(command,action,parameters)
action_obj = self.findAction(command, action, parameters)
print ('---------------------------------------------------------------------')
print ('execute %s.%s with parameters : %s '%(command,action,parameters) )
print ('action object %s (%s)' % (action_obj,action_obj.command) )
print ('execute %s.%s with parameters : %s ' % (command, action, parameters))
print ('action object %s (%s)' % (action_obj, action_obj.command))
print ('---------------------------------------------------------------------')
class Action(object):
""" Action class, handles actual (system) calls.
set command, parameters (template) type and log message
......@@ -327,7 +330,7 @@ class Action(object):
self.message = None
self._parameter_start_pos = 0
def setParameterStartPos(self,pos):
def setParameterStartPos(self, pos):
"""
:param pos: start position of parameter list
......@@ -341,7 +344,7 @@ class Action(object):
"""
return self._parameter_start_pos
def execute(self,parameters):
def execute(self, parameters):
""" execute an action
:param parameters: list of parameters
......@@ -350,15 +353,15 @@ class Action(object):
# send-out syslog message
if self.message is not None:
if self.message.count('%s') > 0 and parameters is not None and len(parameters) > 0:
syslog.syslog(syslog.LOG_NOTICE,self.message % tuple(parameters[0:self.message.count('%s')]) )
syslog.syslog(syslog.LOG_NOTICE, self.message % tuple(parameters[0:self.message.count('%s')]))
else:
syslog.syslog(syslog.LOG_NOTICE,self.message)
syslog.syslog(syslog.LOG_NOTICE, self.message)
# validate input
if self.type is None:
# no action type, nothing to do here
return 'No action type'
elif self.type.lower() in ('script','script_output'):
elif self.type.lower() in ('script', 'script_output'):
# script type commands, basic script type only uses exit statuses, script_output sends back stdout data.
if self.command is None:
# no command supplied, exit
......@@ -367,11 +370,11 @@ class Action(object):
# build script command to execute, shared for both types
script_command = self.command
if self.parameters is not None and type(self.parameters) == str:
script_command = '%s %s'%(script_command,self.parameters)
script_command = '%s %s' % (script_command, self.parameters)
if script_command.find('%s') > -1 and len(parameters) > 0:
# use command execution parameters in action parameter template
# use quotes on parameters to prevent code injection
script_command = script_command % tuple(map(lambda x:'"'+x.replace('"','\\"')+'"',
script_command = script_command % tuple(map(lambda x: '"'+x.replace('"', '\\"')+'"',
parameters[0:script_command.count('%s')]))
if self.type.lower() == 'script':
......@@ -382,16 +385,16 @@ class Action(object):
if exit_status == 0:
return 'OK'
else:
return 'Error (%d)'%exit_status
return 'Error (%d)' % exit_status
except:
syslog.syslog(syslog.LOG_ERR, 'Script action failed at %s'%traceback.format_exc())
syslog.syslog(syslog.LOG_ERR, 'Script action failed at %s' % traceback.format_exc())
return 'Execute error'
elif self.type.lower() == 'script_output':
try:
script_output = subprocess.check_output(script_command, shell=True)
return script_output
except:
syslog.syslog(syslog.LOG_ERR, 'Script action failed at %s'%traceback.format_exc())
syslog.syslog(syslog.LOG_ERR, 'Script action failed at %s' % traceback.format_exc())
return 'Execute error'
# fallback should never get here
......@@ -405,13 +408,10 @@ class Action(object):
else:
inline_act_parameters = ''
return ph_inline_actions.execute(self,inline_act_parameters)
return ph_inline_actions.execute(self, inline_act_parameters)
except:
syslog.syslog(syslog.LOG_ERR, 'Inline action failed at %s'%traceback.format_exc())
syslog.syslog(syslog.LOG_ERR, 'Inline action failed at %s' % traceback.format_exc())
return 'Execute error'
return 'Unknown action type'
......@@ -41,9 +41,10 @@ import copy
import jinja2
import addons.template_helpers
class Template(object):
def __init__(self,target_root_directory="/"):
def __init__(self, target_root_directory="/"):
""" constructor
:return:
"""
......@@ -55,82 +56,80 @@ class Template(object):
# setup jinja2 environment
self._template_dir = os.path.dirname(os.path.abspath(__file__))+'/../templates/'
self._j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(self._template_dir),trim_blocks=True)
self._j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(self._template_dir), trim_blocks=True)
def _readManifest(self,filename):
def _readManifest(self, filename):
"""
:param filename: manifest filename (path/+MANIFEST)
:return: dictionary containing manifest items
"""
result = {}
for line in open(filename,'r').read().split('\n'):
for line in open(filename, 'r').read().split('\n'):
parts = line.split(':')
if len(parts) > 1:
result[parts[0]] = ':'.join(parts[1:])
return result
def _readTargets(self,filename):
def _readTargets(self, filename):
""" read raw target filename masks
:param filename: targets filename (path/+TARGETS)
:return: dictionary containing +TARGETS filename sets
"""
result = {}
for line in open(filename,'r').read().split('\n'):
for line in open(filename, 'r').read().split('\n'):
parts = line.split(':')
if len(parts) > 1 and parts[0].strip()[0] != '#':
result[parts[0]] = ':'.join(parts[1:]).strip()
return result
def list_module(self,module_name,read_manifest=False):
def list_module(self, module_name, read_manifest=False):
""" list single module content
:param module_name: module name in dot notation ( company.module )
:param read_manifest: boolean, read manifest file if it exists
:return: dictionary with module data
"""
result = {}
file_path = '%s/%s'%(self._template_dir,module_name.replace('.','/'))
if os.path.exists('%s/+MANIFEST'%file_path ) and read_manifest:
result['+MANIFEST'] = self._readManifest('%s/+MANIFEST'%file_path)
if os.path.exists('%s/+TARGETS'%file_path ) :
result['+TARGETS'] = self._readTargets('%s/+TARGETS'%file_path)
file_path = '%s/%s' % (self._template_dir, module_name.replace('.', '/'))
if os.path.exists('%s/+MANIFEST' % file_path) and read_manifest:
result['+MANIFEST'] = self._readManifest('%s/+MANIFEST' % file_path)
if os.path.exists('%s/+TARGETS' % file_path):
result['+TARGETS'] = self._readTargets('%s/+TARGETS' % file_path)
else:
result['+TARGETS'] = {}
return result
def list_modules(self,read_manifest=False):
def list_modules(self):
""" traverse template directory and list all modules
the template directory is structured like Manufacturer/Module/config_files
:param read_manifest: boolean, read manifest file if it exists
:return: list (dict) of registered modules
"""
result = {}
for root, dirs, files in os.walk(self._template_dir):
if len(root) > len(self._template_dir):
module_name = '.'.join(root.replace(self._template_dir,'').split('/')[:2])
if result.has_key(module_name) == False:
module_name = '.'.join(root.replace(self._template_dir, '').split('/')[:2])
if module_name not in result:
result[module_name] = self.list_module(module_name)
return result
def setConfig(self,config_data):
def setConfig(self, config_data):
""" set config data
:param config_data: config data as dictionary/list structure
:return: None
"""
if type(config_data) in( dict, collections.OrderedDict):
if type(config_data) in(dict, collections.OrderedDict):
self._config = config_data
else:
# no data given, reset
self._config = {}
def __findStringTags(self,instr):
def __findStringTags(self, instr):
"""
:param instr: string with optional tags [field.$$]
:return:
......@@ -142,7 +141,7 @@ class Template(object):
return retval
def __findFilters(self,tags):
def __findFilters(self, tags):
""" match tags to config and construct a dictionary which we can use to construct the output filenames
:param tags: list of tags [xmlnode.xmlnode.%.xmlnode,xmlnode]
:return: dictionary containing key (tagname) value {existing node key, value}
......@@ -155,25 +154,25 @@ class Template(object):
config_ptr = self._config
target_keys = []
for xmlNodeName in tag.split('.'):
if config_ptr.has_key(xmlNodeName):
if xmlNodeName in config_ptr:
config_ptr = config_ptr[xmlNodeName]
elif xmlNodeName == '%':
target_keys = config_ptr.keys()
else:
break
if len(target_keys) == 0 :
if len(target_keys) == 0:
# single node, only used for string replacement in output name.
result[tag] = {tag:config_ptr}
result[tag] = {tag: config_ptr}
else:
# multiple node's, find all nodes
for target_node in target_keys:
config_ptr = self._config
str_wildcard_loc = len(tag.split('%')[0].split('.'))
filter_target= []
for xmlNodeName in tag.replace('%',target_node).split('.'):
if config_ptr.has_key(xmlNodeName):
if type (config_ptr[xmlNodeName]) in (collections.OrderedDict,dict):
filter_target = []
for xmlNodeName in tag.replace('%', target_node).split('.'):
if xmlNodeName in config_ptr:
if type(config_ptr[xmlNodeName]) in (collections.OrderedDict, dict):
if str_wildcard_loc >= len(filter_target):
filter_target.append(xmlNodeName)
if str_wildcard_loc == len(filter_target):
......@@ -186,47 +185,44 @@ class Template(object):
return result
def _create_directory(self,filename):
def _create_directory(self, filename):
""" create directory
:param filename: create path for filename ( if not existing )
:return: None
"""
fparts=[]
fparts = []
for fpart in filename.strip().split('/')[:-1]:
fparts.append(fpart)
if len(fpart) >1:
if os.path.exists('/'.join(fparts)) == False:
if len(fpart) > 1:
if not os.path.exists('/'.join(fparts)):
os.mkdir('/'.join(fparts))
def generate(self,module_name,create_directory=True):
def generate(self, module_name, create_directory=True):
""" generate configuration files using bound config and template data
:param module_name: module name in dot notation ( company.module )
:param create_directory: automatically create directories to place template output in ( if not existing )
:return: list of generated output files
"""
result=[]
result = []
module_data = self.list_module(module_name)
for src_template in module_data['+TARGETS'].keys():
target = module_data['+TARGETS'][src_template]
target_filename_tags = self.__findStringTags(target)
target_filters = self.__findFilters(target_filename_tags)
result_filenames = {target:{}}
result_filenames = {target: {}}
for target_filter in target_filters.keys():
for key in target_filters[target_filter].keys():
for filename in result_filenames.keys():
if filename.find('[%s]'%target_filter) > -1:
new_filename = filename.replace('[%s]'%target_filter,target_filters[target_filter][key])
if filename.find('[%s]' % target_filter) > -1:
new_filename = filename.replace('[%s]' % target_filter, target_filters[target_filter][key])
result_filenames[new_filename] = copy.deepcopy(result_filenames[filename])
result_filenames[new_filename][key] = target_filters[target_filter][key]
j2_page = self._j2_env.get_template('%s/%s'%( module_name.replace('.','/'), src_template))
j2_page = self._j2_env.get_template('%s/%s' % (module_name.replace('.', '/'), src_template))
for filename in result_filenames.keys():
if not ( filename.find('[') != -1 and filename.find(']') != -1 ) :
if not (filename.find('[') != -1 and filename.find(']') != -1):
# copy config data
cnf_data = copy.deepcopy(self._config)
cnf_data['TARGET_FILTERS'] = result_filenames[filename]
......@@ -240,16 +236,15 @@ class Template(object):
content = j2_page.render(cnf_data)
# prefix filename with defined root directory
filename = ('%s/%s'%(self._target_root_directory, filename)).replace('//','/')
filename = ('%s/%s' % (self._target_root_directory, filename)).replace('//', '/')
if create_directory:
# make sure the target directory exists
self._create_directory(filename)
f_out = open(filename,'wb')
f_out = open(filename, 'wb')
f_out.write(content)
f_out.close()
result.append(filename)
return result
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment