configurationengine/source/plugins/common/ConeCommandPlugin/commandplugin/commandml.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
equal deleted inserted replaced
-1:000000000000 0:2e8eeb919028
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description:
       
    15 #
       
    16 ## 
       
    17 # @author <author>
       
    18 '''
       
    19 ConE plugin to run external applications/tools with given parameters in .commandml file. Notice that values can be also
       
    20 fecthed from ConfML to maximize portability and minimize maintenance.
       
    21 '''
       
    22 
       
    23 import re
       
    24 import os
       
    25 import sys
       
    26 import logging
       
    27 import types
       
    28 
       
    29 import subprocess
       
    30 import __init__
       
    31 
       
    32 from cone.public import exceptions,plugin,utils,api, settings
       
    33 
       
    34 class CommandImpl(plugin.ImplBase):
       
    35     """
       
    36     Plugin implementation class. 
       
    37     """
       
    38     
       
    39     IMPL_TYPE_ID = "commandml"
       
    40     
       
    41     
       
    42     def __init__(self,ref,configuration, reader):
       
    43         """
       
    44         Overloading the default constructor
       
    45         """
       
    46         plugin.ImplBase.__init__(self,ref,configuration)###3
       
    47         self.desc = ""
       
    48         self.logger = logging.getLogger('cone.commandml(%s)' % self.ref)
       
    49         self.reader = reader
       
    50         
       
    51 
       
    52     def generate(self, context=None):
       
    53         """
       
    54         Generate the given implementation.
       
    55         """
       
    56         self.create_output()        
       
    57         return 
       
    58     
       
    59     def generate_layers(self,layers):
       
    60         """
       
    61         Generate the given Configuration layers.
       
    62         """
       
    63         self.logger.info('Generating layers %s' % layers)
       
    64         self.create_output(layers)
       
    65         return 
       
    66     
       
    67     def create_output(self, layers=None):
       
    68         """
       
    69         Function to generate output files.
       
    70         """
       
    71         
       
    72         tmpDict = self.__create_helper_variables()
       
    73         
       
    74         for element in self.reader.elements:
       
    75             #Element can be either command or condition.
       
    76             element.set_logger(self.logger)
       
    77             element.execute(tmpDict)        
       
    78         return
       
    79 
       
    80     def __create_helper_variables(self):
       
    81         """
       
    82         Internal function to create dictionary containing most often used ConE "environment" variables.
       
    83         """        
       
    84         tmp = {}
       
    85         tmp["%CONE_OUT%"] = self.output
       
    86         tmp["%CONE_OUT_ABSOLUTE%"] = os.path.abspath(self.output)
       
    87         return tmp    
       
    88     
       
    89     def has_ref(self, refs):
       
    90         """
       
    91         @returns True if the implementation uses the given ref as input value.
       
    92         Otherwise return False.
       
    93         """
       
    94                 
       
    95         # return true for now so that content copying is not filtered 
       
    96         return None
       
    97     
       
    98 class CommandImplReader(plugin.ReaderBase):
       
    99     """
       
   100     Parses a single commandml file
       
   101     """ 
       
   102     NAMESPACE = 'http://www.s60.com/xml/commandml/1'
       
   103     FILE_EXTENSIONS = ['commandml']
       
   104     
       
   105     def __init__(self):
       
   106         """
       
   107         Constructor
       
   108         """
       
   109         self.output_dir = None
       
   110         self.input_dir = None
       
   111         self.namespaces = [self.NAMESPACE]
       
   112         self.dview = None
       
   113         self.elements = []
       
   114         self.tags = None
       
   115     
       
   116     @classmethod
       
   117     def read_impl(cls, resource_ref, configuration, etree):
       
   118         reader = CommandImplReader()
       
   119         reader.set_default_view(configuration.get_default_view())
       
   120         reader.from_etree(etree)
       
   121         impl = CommandImpl(resource_ref, configuration, reader)
       
   122         if reader.tags:
       
   123             impl.set_tags(reader.tags)
       
   124         return impl
       
   125             
       
   126     def set_default_view(self, dview):
       
   127         """
       
   128         Function to set default view that is needed when solving out ConfML reference information
       
   129         """        
       
   130         self.dview = dview
       
   131             
       
   132     def from_etree(self, etree):
       
   133         """
       
   134         Parser function for commandml element.
       
   135         """
       
   136         self.parse_tree(etree)
       
   137 
       
   138     def parse_tree(self, etree):
       
   139         """
       
   140         General parse function for condition and command elements.
       
   141         """        
       
   142         elements = list(etree)
       
   143         for element in elements:
       
   144             if element.tag == "{%s}condition" % self.namespaces[0]:
       
   145                 self.elements.append(self.parse_condition(element))
       
   146             elif element.tag == "{%s}command" % self.namespaces[0]:
       
   147                 self.elements.append(self.parse_command(element))
       
   148             else:
       
   149                 pass
       
   150         self.tags = self.parse_tags(etree)
       
   151 
       
   152     def parse_condition(self, etree):
       
   153         """
       
   154         Parse function for condition element.
       
   155         """        
       
   156         condition = Condition()
       
   157         condition.set_condition(etree.get("value"))
       
   158         condition.set_commands(self.parse_commands(etree))
       
   159         condition.set_default_view(self.dview)
       
   160         return condition                                   
       
   161         
       
   162     def parse_commands(self,etree):
       
   163         """
       
   164         Parser function for commands.
       
   165         """
       
   166         commands = []
       
   167         for com_elem in etree.findall("{%s}command" % self.namespaces[0]):
       
   168             commands.append(self.parse_command(com_elem))
       
   169         return commands
       
   170          
       
   171     def parse_command(self,etree):
       
   172         """
       
   173         Parser function for single command.
       
   174         """
       
   175         cmd = Command()
       
   176         cmd.set_executable(etree.get("executable"))
       
   177         cmd.set_shell(etree.get("shell"))
       
   178         cmd.set_bufsize(etree.get("bufsize"))
       
   179         cmd.set_cwd(etree.get("cwd")) 
       
   180         cmd.set_all_envs(etree.get("env"))
       
   181         cmd.set_all_arguments(self.parse_arguments(etree))
       
   182         cmd.set_all_pipes(self.parse_pipes(etree))
       
   183         cmd.set_filters(self.parse_filters(etree))
       
   184         cmd.set_default_view(self.dview)
       
   185         return cmd
       
   186     
       
   187     def parse_arguments(self,etree):
       
   188         """
       
   189         Parser function for command's arguments.
       
   190         """
       
   191         arguments = []
       
   192         for argument in etree.findall("{%s}argument" % self.namespaces[0]):
       
   193             value = argument.get("value")
       
   194             if value:
       
   195                 arguments.append(value)
       
   196         return arguments
       
   197     
       
   198     def parse_pipes(self,etree):
       
   199         """
       
   200         Parser function for command's pipes.
       
   201         """
       
   202         pipes = {}
       
   203         for argument in etree.findall("{%s}pipe" % self.namespaces[0]):
       
   204             name = argument.get("name")
       
   205             value = argument.get("value")
       
   206             if name:
       
   207                 pipes[name] = value        
       
   208         return pipes
       
   209 
       
   210     def parse_filters(self,etree):
       
   211         """
       
   212         Parser function for command's filters.
       
   213         """
       
   214         filters = []
       
   215         for argument in etree.findall("{%s}filter" % self.namespaces[0]):
       
   216             f = Filter()
       
   217             f.set_severity(argument.get("severity"))
       
   218             f.set_condition(argument.get("condition"))
       
   219             f.set_input(argument.get("input"))
       
   220             f.set_formatter(argument.get("formatter"))
       
   221             filters.append(f)
       
   222         return filters
       
   223     
       
   224     def parse_tags(self,etree):
       
   225         tags = {}
       
   226         for tag in etree.getiterator("{%s}tag" % self.namespaces[0]):
       
   227             tagname = tag.get('name','')
       
   228             tagvalue = tag.get('value')
       
   229             values = tags.get(tagname,[])
       
   230             values.append(tagvalue)
       
   231             tags[tagname] = values
       
   232         return tags
       
   233 
       
   234 
       
   235 class Condition(object):
       
   236     """
       
   237     Condition class is a simple wrapper class for commands so that commands are executed
       
   238     only if condition is True. Otherwise class does nothing. Class has similar interface 
       
   239     than Command class so that they can be used similar way from plugin perspective. 
       
   240     """
       
   241     
       
   242     def __init__(self):
       
   243         self.condition = None
       
   244         self.commands = []
       
   245         self.logger = None
       
   246         self.dview = None
       
   247 
       
   248     def set_condition(self, condition):
       
   249         self.condition = condition
       
   250 
       
   251     def set_default_view(self, dview):
       
   252         self.dview = dview
       
   253 
       
   254     def set_commands(self, commands):
       
   255         self.commands = commands
       
   256 
       
   257     def set_logger(self, logger):
       
   258         self.logger = logger
       
   259         for cmd in self.commands:
       
   260             cmd.set_logger(logger)        
       
   261 
       
   262     def add_command(self, command):
       
   263         self.command.append(command)
       
   264 
       
   265     def execute(self, replaceDict=None):
       
   266         if self.__solve_condition(self.condition):
       
   267             #Condition is true -> running command
       
   268             for command in self.commands:                
       
   269                 command.execute(replaceDict)
       
   270         else:
       
   271             self.logger.info("Ignoring %s because it is evaluated as False." % self.condition)
       
   272 
       
   273     def __solve_condition(self, condition_str):
       
   274         """
       
   275         Internal function to handle condition
       
   276         """
       
   277         if condition_str != "":
       
   278             #Expanding ConfML information
       
   279             modstr = utils.expand_delimited_tokens(
       
   280                 condition_str,
       
   281                 lambda ref, index: repr(self.dview.get_feature(ref).get_value()))
       
   282             return eval(modstr)
       
   283         else:
       
   284             #Empty condition is true always.
       
   285             return True
       
   286 
       
   287 class Command(object):
       
   288     """
       
   289     Command is a class that executes actual commands. It provides ways to handle input, output and error 
       
   290     streams and to control execution parameters.
       
   291     """
       
   292         
       
   293     def __init__(self):
       
   294         """
       
   295         Constructor
       
   296         """        
       
   297         self.executable = None
       
   298         self.shell = False
       
   299         self.bufsize = 0
       
   300         self.cwd = None
       
   301         self.envs = None
       
   302         self.arguments = []
       
   303         self.pipes = {}
       
   304         self.streams = {}
       
   305         self.filters = []
       
   306         self.logger = None
       
   307         self.dview = None
       
   308     
       
   309     def set_executable(self, executable):
       
   310         self.executable = executable
       
   311     
       
   312     def set_shell(self, shell):
       
   313         if shell and shell.lower() in ('true', 'yes', '1', 1, True):
       
   314             self.shell = True
       
   315         else:
       
   316             self.shell = False
       
   317         
       
   318     def set_bufsize(self, bufsize):
       
   319         if bufsize:
       
   320             self.bufsize = int(bufsize)
       
   321     
       
   322     def set_cwd(self, cwd):
       
   323         self.cwd = cwd
       
   324     
       
   325     def set_all_envs(self, envs):
       
   326         if envs:
       
   327             self.envs = eval(envs)
       
   328     def set_default_view(self, dview):
       
   329         self.dview = dview
       
   330         
       
   331     def set_env(self, name, value):
       
   332         self.envs[name] = value
       
   333     
       
   334     def set_all_arguments(self, args):
       
   335         self.arguments = args
       
   336     
       
   337     def get_arguments_string(self):
       
   338         """
       
   339         Function to return arguments as a string
       
   340         """
       
   341         arg_string = ""        
       
   342         for value in self.arguments:            
       
   343             arg_string += value
       
   344             arg_string += " "                
       
   345         return arg_string
       
   346     
       
   347     def get_pipe(self, name, mode='w'):
       
   348         """
       
   349         Function to return pipe based on the pipe name in requested mode.
       
   350         """
       
   351         if self.pipes.has_key(name) and isinstance(self.pipes[name], types.IntType):
       
   352         #Subprocess pipe
       
   353             return self.pipes[name]
       
   354         elif self.pipes.has_key(name) and isinstance(self.pipes[name], types.StringType):            
       
   355             return file(self.pipes[name], mode)
       
   356         else:
       
   357             return None
       
   358     
       
   359     def set_streams(self, stdin, stdout, stderr):
       
   360         self.streams["stdin"] = stdin
       
   361         self.streams["stdout"] = stdout
       
   362         self.streams["stderr"] = stderr
       
   363                         
       
   364     def get_streams(self, name, mode="r"):
       
   365         if self.streams.has_key(name) and self.streams[name]:
       
   366         #OK for streams set with subprocess.PIPE
       
   367             return self.streams[name]
       
   368         else:
       
   369         #For file objects
       
   370             return self.get_pipe(name, mode)
       
   371     
       
   372     def set_filters(self, filters):
       
   373         self.filters = filters
       
   374         for f in self.filters:
       
   375             f.set_command(self)
       
   376     
       
   377     def get_filters(self):
       
   378         return self.filters
       
   379         
       
   380     def set_argument(self, value):
       
   381         self.arguments.append(value)
       
   382     
       
   383     def set_all_pipes(self, pipes):
       
   384         for pipe in pipes.keys():
       
   385             self.set_pipe(pipe, pipes[pipe])
       
   386         
       
   387     def set_pipe(self, name, value):
       
   388         if value == "PIPE":
       
   389             #Creating new stream for this.
       
   390             self.pipes[name] = subprocess.PIPE
       
   391         elif value == "STDOUT":
       
   392             self.pipes[name] = subprocess.STDOUT
       
   393         else:
       
   394             #Setting filename
       
   395             self.pipes[name] = value
       
   396             #self.pipes[name] = file(value, 'w')
       
   397             
       
   398     def handle_filters(self):
       
   399         """
       
   400         """
       
   401         for filter in self.filters:
       
   402             filter.report(self.logger)
       
   403 
       
   404     def execute(self, replaceDict=None):
       
   405         self.solve_refs()
       
   406         
       
   407         exit_code = 0
       
   408         try:
       
   409             try:
       
   410                 if self.cwd is not None:
       
   411                     cwd = self.__replace_helper_variables(self.cwd, replaceDict)
       
   412                 else:
       
   413                     cwd = self.cwd
       
   414                 command_str = self.executable + " " + self.__replace_helper_variables(self.get_arguments_string(), replaceDict)
       
   415                 self.logger.info("Running command: \"%s\"" % command_str)
       
   416                 self.logger.info("with args: shell=%s envs=%s cwd=%s bufsize=%s stdin=%s stdout=%s stderr=%s" \
       
   417                                  % (self.shell, self.envs, cwd, self.bufsize, \
       
   418                                     self.get_pipe("stdin", 'r'),self.get_pipe("stdout"), self.get_pipe("stderr")))                    
       
   419                 pid = subprocess.Popen(command_str, shell=self.shell, env=self.envs, cwd=cwd,\
       
   420                                           bufsize=self.bufsize, stdin = self.get_pipe("stdin", 'r'),\
       
   421                                           stdout = self.get_pipe("stdout"), stderr = self.get_pipe("stderr"))
       
   422                 #Waiting for process to complete
       
   423                 retcode = pid.wait()
       
   424                 #Storing stream information for possible further processing.
       
   425                 self.set_streams(pid.stdin, pid.stdout, pid.stderr)
       
   426                 
       
   427                 if retcode < 0:
       
   428                     self.logger.error("Child was terminated by signal %s" % (-retcode))
       
   429                 else:
       
   430                     self.logger.info("Child returned: %s" % retcode)
       
   431             except OSError, e:
       
   432                 self.logger.error("Execution failed: %s", repr(e))            
       
   433             self.handle_filters()
       
   434         except Exception,e:
       
   435             utils.log_exception(self.logger, "Failed to execute command: %s" % e)
       
   436 
       
   437     def set_logger(self, logger):
       
   438         self.logger = logger        
       
   439 
       
   440     def __replace_helper_variables(self, inputstr, dictionary):
       
   441         retstr = inputstr
       
   442         for key in dictionary.keys():
       
   443             retstr = retstr.replace(key, dictionary[key])            
       
   444         return retstr
       
   445 
       
   446     def solve_refs(self):
       
   447         """
       
   448         Function to solve references just before generation.
       
   449         """
       
   450         
       
   451         self.executable = self.__solve_ref(self.executable)
       
   452         self.shell = self.__solve_ref(self.shell)
       
   453         self.bufsize = self.__solve_ref(self.bufsize)
       
   454         self.cwd = self.__solve_ref(self.cwd)
       
   455         for argument in self.arguments:
       
   456             self.arguments[self.arguments.index(argument)] = self.__solve_ref(argument) 
       
   457         for pipe in self.pipes.keys():
       
   458             self.pipes[pipe] = self.__solve_ref(self.pipes[pipe])
       
   459 
       
   460     def __solve_ref(self, inputstr):
       
   461         """
       
   462         Internal function to solve whether input is ref or just normal input string. 
       
   463         For refs actual ConfML value is resolved and returned. Non-refs are returned 
       
   464         as such.
       
   465         """        
       
   466         if inputstr and isinstance(inputstr, types.StringType):
       
   467             return utils.expand_refs_by_default_view(inputstr, self.dview)
       
   468         else:
       
   469             return inputstr
       
   470 
       
   471 
       
   472         
       
   473 class Filter(object):
       
   474     """
       
   475     Filter class handles printing information to ConE log using filtering information.
       
   476     Filtering severity, condition and the format of output can be configured in command ml. 
       
   477     """
       
   478 
       
   479     def __init__(self):
       
   480         self.severity = None
       
   481         self.condition = None
       
   482         self.input = None
       
   483         self.command = None
       
   484         self.formatter = None
       
   485         
       
   486     def set_severity(self, severity):
       
   487         self.severity = severity
       
   488 
       
   489     def set_condition(self, condition):
       
   490         self.condition = condition
       
   491 
       
   492     def set_input(self, input):
       
   493         self.input = input
       
   494 
       
   495     def set_command(self, command):
       
   496         self.command = command
       
   497 
       
   498     def set_formatter(self, formatter):
       
   499         self.formatter = formatter
       
   500         
       
   501     def report(self, logger):
       
   502         input_pipe = self.command.get_streams(self.input)
       
   503         if isinstance(input_pipe, types.FileType):
       
   504             #Subprocess.PIPE and file descriptors supported only.
       
   505             data = input_pipe.read()
       
   506             pattern = re.compile(self.condition)
       
   507             for line in data.splitlines():
       
   508                 mo = pattern.match(line)
       
   509                 if mo:
       
   510                     lf = self.__get_logger_function(logger)
       
   511                     if self.formatter:                        
       
   512                         lf(self.formatter % mo.groupdict())
       
   513                     else:
       
   514                         lf(line)
       
   515 
       
   516     def __get_logger_function(self, logger):
       
   517         if self.severity == "info":
       
   518             return logger.info
       
   519         elif self.severity == "warning":
       
   520             return logger.warning
       
   521         elif self.severity == "debug":
       
   522             return logger.debug
       
   523         elif self.severity == "exception":
       
   524             return logger.exception
       
   525         elif self.severity == "error":
       
   526             return logger.error
       
   527         elif self.severity == "critical":
       
   528             return logger.critical
       
   529         else:
       
   530             #Default
       
   531             return logger.info
       
   532 
       
   533         
       
   534 
       
   535 
       
   536 
       
   537 
       
   538 
       
   539