|
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 |