diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/plugins/common/ConeCommandPlugin/commandplugin/commandml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configurationengine/source/plugins/common/ConeCommandPlugin/commandplugin/commandml.py Thu Mar 11 17:04:37 2010 +0200 @@ -0,0 +1,539 @@ +# +# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# +## +# @author +''' +ConE plugin to run external applications/tools with given parameters in .commandml file. Notice that values can be also +fecthed from ConfML to maximize portability and minimize maintenance. +''' + +import re +import os +import sys +import logging +import types + +import subprocess +import __init__ + +from cone.public import exceptions,plugin,utils,api, settings + +class CommandImpl(plugin.ImplBase): + """ + Plugin implementation class. + """ + + IMPL_TYPE_ID = "commandml" + + + def __init__(self,ref,configuration, reader): + """ + Overloading the default constructor + """ + plugin.ImplBase.__init__(self,ref,configuration)###3 + self.desc = "" + self.logger = logging.getLogger('cone.commandml(%s)' % self.ref) + self.reader = reader + + + def generate(self, context=None): + """ + Generate the given implementation. + """ + self.create_output() + return + + def generate_layers(self,layers): + """ + Generate the given Configuration layers. + """ + self.logger.info('Generating layers %s' % layers) + self.create_output(layers) + return + + def create_output(self, layers=None): + """ + Function to generate output files. + """ + + tmpDict = self.__create_helper_variables() + + for element in self.reader.elements: + #Element can be either command or condition. + element.set_logger(self.logger) + element.execute(tmpDict) + return + + def __create_helper_variables(self): + """ + Internal function to create dictionary containing most often used ConE "environment" variables. + """ + tmp = {} + tmp["%CONE_OUT%"] = self.output + tmp["%CONE_OUT_ABSOLUTE%"] = os.path.abspath(self.output) + return tmp + + def has_ref(self, refs): + """ + @returns True if the implementation uses the given ref as input value. + Otherwise return False. + """ + + # return true for now so that content copying is not filtered + return None + +class CommandImplReader(plugin.ReaderBase): + """ + Parses a single commandml file + """ + NAMESPACE = 'http://www.s60.com/xml/commandml/1' + FILE_EXTENSIONS = ['commandml'] + + def __init__(self): + """ + Constructor + """ + self.output_dir = None + self.input_dir = None + self.namespaces = [self.NAMESPACE] + self.dview = None + self.elements = [] + self.tags = None + + @classmethod + def read_impl(cls, resource_ref, configuration, etree): + reader = CommandImplReader() + reader.set_default_view(configuration.get_default_view()) + reader.from_etree(etree) + impl = CommandImpl(resource_ref, configuration, reader) + if reader.tags: + impl.set_tags(reader.tags) + return impl + + def set_default_view(self, dview): + """ + Function to set default view that is needed when solving out ConfML reference information + """ + self.dview = dview + + def from_etree(self, etree): + """ + Parser function for commandml element. + """ + self.parse_tree(etree) + + def parse_tree(self, etree): + """ + General parse function for condition and command elements. + """ + elements = list(etree) + for element in elements: + if element.tag == "{%s}condition" % self.namespaces[0]: + self.elements.append(self.parse_condition(element)) + elif element.tag == "{%s}command" % self.namespaces[0]: + self.elements.append(self.parse_command(element)) + else: + pass + self.tags = self.parse_tags(etree) + + def parse_condition(self, etree): + """ + Parse function for condition element. + """ + condition = Condition() + condition.set_condition(etree.get("value")) + condition.set_commands(self.parse_commands(etree)) + condition.set_default_view(self.dview) + return condition + + def parse_commands(self,etree): + """ + Parser function for commands. + """ + commands = [] + for com_elem in etree.findall("{%s}command" % self.namespaces[0]): + commands.append(self.parse_command(com_elem)) + return commands + + def parse_command(self,etree): + """ + Parser function for single command. + """ + cmd = Command() + cmd.set_executable(etree.get("executable")) + cmd.set_shell(etree.get("shell")) + cmd.set_bufsize(etree.get("bufsize")) + cmd.set_cwd(etree.get("cwd")) + cmd.set_all_envs(etree.get("env")) + cmd.set_all_arguments(self.parse_arguments(etree)) + cmd.set_all_pipes(self.parse_pipes(etree)) + cmd.set_filters(self.parse_filters(etree)) + cmd.set_default_view(self.dview) + return cmd + + def parse_arguments(self,etree): + """ + Parser function for command's arguments. + """ + arguments = [] + for argument in etree.findall("{%s}argument" % self.namespaces[0]): + value = argument.get("value") + if value: + arguments.append(value) + return arguments + + def parse_pipes(self,etree): + """ + Parser function for command's pipes. + """ + pipes = {} + for argument in etree.findall("{%s}pipe" % self.namespaces[0]): + name = argument.get("name") + value = argument.get("value") + if name: + pipes[name] = value + return pipes + + def parse_filters(self,etree): + """ + Parser function for command's filters. + """ + filters = [] + for argument in etree.findall("{%s}filter" % self.namespaces[0]): + f = Filter() + f.set_severity(argument.get("severity")) + f.set_condition(argument.get("condition")) + f.set_input(argument.get("input")) + f.set_formatter(argument.get("formatter")) + filters.append(f) + return filters + + def parse_tags(self,etree): + tags = {} + for tag in etree.getiterator("{%s}tag" % self.namespaces[0]): + tagname = tag.get('name','') + tagvalue = tag.get('value') + values = tags.get(tagname,[]) + values.append(tagvalue) + tags[tagname] = values + return tags + + +class Condition(object): + """ + Condition class is a simple wrapper class for commands so that commands are executed + only if condition is True. Otherwise class does nothing. Class has similar interface + than Command class so that they can be used similar way from plugin perspective. + """ + + def __init__(self): + self.condition = None + self.commands = [] + self.logger = None + self.dview = None + + def set_condition(self, condition): + self.condition = condition + + def set_default_view(self, dview): + self.dview = dview + + def set_commands(self, commands): + self.commands = commands + + def set_logger(self, logger): + self.logger = logger + for cmd in self.commands: + cmd.set_logger(logger) + + def add_command(self, command): + self.command.append(command) + + def execute(self, replaceDict=None): + if self.__solve_condition(self.condition): + #Condition is true -> running command + for command in self.commands: + command.execute(replaceDict) + else: + self.logger.info("Ignoring %s because it is evaluated as False." % self.condition) + + def __solve_condition(self, condition_str): + """ + Internal function to handle condition + """ + if condition_str != "": + #Expanding ConfML information + modstr = utils.expand_delimited_tokens( + condition_str, + lambda ref, index: repr(self.dview.get_feature(ref).get_value())) + return eval(modstr) + else: + #Empty condition is true always. + return True + +class Command(object): + """ + Command is a class that executes actual commands. It provides ways to handle input, output and error + streams and to control execution parameters. + """ + + def __init__(self): + """ + Constructor + """ + self.executable = None + self.shell = False + self.bufsize = 0 + self.cwd = None + self.envs = None + self.arguments = [] + self.pipes = {} + self.streams = {} + self.filters = [] + self.logger = None + self.dview = None + + def set_executable(self, executable): + self.executable = executable + + def set_shell(self, shell): + if shell and shell.lower() in ('true', 'yes', '1', 1, True): + self.shell = True + else: + self.shell = False + + def set_bufsize(self, bufsize): + if bufsize: + self.bufsize = int(bufsize) + + def set_cwd(self, cwd): + self.cwd = cwd + + def set_all_envs(self, envs): + if envs: + self.envs = eval(envs) + def set_default_view(self, dview): + self.dview = dview + + def set_env(self, name, value): + self.envs[name] = value + + def set_all_arguments(self, args): + self.arguments = args + + def get_arguments_string(self): + """ + Function to return arguments as a string + """ + arg_string = "" + for value in self.arguments: + arg_string += value + arg_string += " " + return arg_string + + def get_pipe(self, name, mode='w'): + """ + Function to return pipe based on the pipe name in requested mode. + """ + if self.pipes.has_key(name) and isinstance(self.pipes[name], types.IntType): + #Subprocess pipe + return self.pipes[name] + elif self.pipes.has_key(name) and isinstance(self.pipes[name], types.StringType): + return file(self.pipes[name], mode) + else: + return None + + def set_streams(self, stdin, stdout, stderr): + self.streams["stdin"] = stdin + self.streams["stdout"] = stdout + self.streams["stderr"] = stderr + + def get_streams(self, name, mode="r"): + if self.streams.has_key(name) and self.streams[name]: + #OK for streams set with subprocess.PIPE + return self.streams[name] + else: + #For file objects + return self.get_pipe(name, mode) + + def set_filters(self, filters): + self.filters = filters + for f in self.filters: + f.set_command(self) + + def get_filters(self): + return self.filters + + def set_argument(self, value): + self.arguments.append(value) + + def set_all_pipes(self, pipes): + for pipe in pipes.keys(): + self.set_pipe(pipe, pipes[pipe]) + + def set_pipe(self, name, value): + if value == "PIPE": + #Creating new stream for this. + self.pipes[name] = subprocess.PIPE + elif value == "STDOUT": + self.pipes[name] = subprocess.STDOUT + else: + #Setting filename + self.pipes[name] = value + #self.pipes[name] = file(value, 'w') + + def handle_filters(self): + """ + """ + for filter in self.filters: + filter.report(self.logger) + + def execute(self, replaceDict=None): + self.solve_refs() + + exit_code = 0 + try: + try: + if self.cwd is not None: + cwd = self.__replace_helper_variables(self.cwd, replaceDict) + else: + cwd = self.cwd + command_str = self.executable + " " + self.__replace_helper_variables(self.get_arguments_string(), replaceDict) + self.logger.info("Running command: \"%s\"" % command_str) + self.logger.info("with args: shell=%s envs=%s cwd=%s bufsize=%s stdin=%s stdout=%s stderr=%s" \ + % (self.shell, self.envs, cwd, self.bufsize, \ + self.get_pipe("stdin", 'r'),self.get_pipe("stdout"), self.get_pipe("stderr"))) + pid = subprocess.Popen(command_str, shell=self.shell, env=self.envs, cwd=cwd,\ + bufsize=self.bufsize, stdin = self.get_pipe("stdin", 'r'),\ + stdout = self.get_pipe("stdout"), stderr = self.get_pipe("stderr")) + #Waiting for process to complete + retcode = pid.wait() + #Storing stream information for possible further processing. + self.set_streams(pid.stdin, pid.stdout, pid.stderr) + + if retcode < 0: + self.logger.error("Child was terminated by signal %s" % (-retcode)) + else: + self.logger.info("Child returned: %s" % retcode) + except OSError, e: + self.logger.error("Execution failed: %s", repr(e)) + self.handle_filters() + except Exception,e: + utils.log_exception(self.logger, "Failed to execute command: %s" % e) + + def set_logger(self, logger): + self.logger = logger + + def __replace_helper_variables(self, inputstr, dictionary): + retstr = inputstr + for key in dictionary.keys(): + retstr = retstr.replace(key, dictionary[key]) + return retstr + + def solve_refs(self): + """ + Function to solve references just before generation. + """ + + self.executable = self.__solve_ref(self.executable) + self.shell = self.__solve_ref(self.shell) + self.bufsize = self.__solve_ref(self.bufsize) + self.cwd = self.__solve_ref(self.cwd) + for argument in self.arguments: + self.arguments[self.arguments.index(argument)] = self.__solve_ref(argument) + for pipe in self.pipes.keys(): + self.pipes[pipe] = self.__solve_ref(self.pipes[pipe]) + + def __solve_ref(self, inputstr): + """ + Internal function to solve whether input is ref or just normal input string. + For refs actual ConfML value is resolved and returned. Non-refs are returned + as such. + """ + if inputstr and isinstance(inputstr, types.StringType): + return utils.expand_refs_by_default_view(inputstr, self.dview) + else: + return inputstr + + + +class Filter(object): + """ + Filter class handles printing information to ConE log using filtering information. + Filtering severity, condition and the format of output can be configured in command ml. + """ + + def __init__(self): + self.severity = None + self.condition = None + self.input = None + self.command = None + self.formatter = None + + def set_severity(self, severity): + self.severity = severity + + def set_condition(self, condition): + self.condition = condition + + def set_input(self, input): + self.input = input + + def set_command(self, command): + self.command = command + + def set_formatter(self, formatter): + self.formatter = formatter + + def report(self, logger): + input_pipe = self.command.get_streams(self.input) + if isinstance(input_pipe, types.FileType): + #Subprocess.PIPE and file descriptors supported only. + data = input_pipe.read() + pattern = re.compile(self.condition) + for line in data.splitlines(): + mo = pattern.match(line) + if mo: + lf = self.__get_logger_function(logger) + if self.formatter: + lf(self.formatter % mo.groupdict()) + else: + lf(line) + + def __get_logger_function(self, logger): + if self.severity == "info": + return logger.info + elif self.severity == "warning": + return logger.warning + elif self.severity == "debug": + return logger.debug + elif self.severity == "exception": + return logger.exception + elif self.severity == "error": + return logger.error + elif self.severity == "critical": + return logger.critical + else: + #Default + return logger.info + + + + + + + +