pymtt
 All Classes Namespaces Files Functions Variables Groups
sequential.py
Go to the documentation of this file.
1 # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: f; python-indent: 4 -*-
2 #
3 # Copyright (c) 2015-2018 Intel, Inc. All rights reserved.
4 # $COPYRIGHT$
5 #
6 # Additional copyrights may follow
7 #
8 # $HEADER$
9 #
10 
11 
12 import os
13 import sys
14 import traceback
15 import configparser
16 import importlib
17 import logging
18 import imp
19 import datetime
20 from yapsy.PluginManager import PluginManager
21 
22 from ExecutorMTTTool import *
23 
24 
25 # Theory of Operation
26 #
27 # The sequential executor executes a single, ordered pass thru the
28 # provided test description. By ordered we mean that execution starts
29 # with the first provided step and continues until it reaches the end.
30 # Thus, the user is responsible for ensuring that the order of execution
31 # is correct.
32 #
33 
34 ## @addtogroup Tools
35 # @{
36 # @addtogroup Executor
37 # @section SequentialEx
38 # Sequential execution executor
39 # @}
41 
42  def __init__(self):
43  # initialise parent class
44  ExecutorMTTTool.__init__(self)
45  self.options = {}
46 
47  def activate(self):
48  # use the automatic procedure from IPlugin
49  IPlugin.activate(self)
50  return
51 
52  def deactivate(self):
53  IPlugin.deactivate(self)
54  return
55 
56  def print_name(self):
57  return "Sequential executor"
58 
59  def print_options(self, testDef, prefix):
60  lines = testDef.printOptions(self.options)
61  for line in lines:
62  print(prefix + line)
63  return
64 
66  self.one_last_loop = True
67 
68  def execute_sections(self, testDef):
69  for step in testDef.loader.stageOrder:
70  for title in testDef.config.sections():
71  if self.only_reporter and step != "Reporter":
72  continue
73  elif self.looping and not self.only_reporter and step == "Reporter":
74  continue
75  elif self.looping and self.only_reporter:
76  self.looping = False
77  if step != "Reporter":
78  continue
79  try:
80  testDef.plugin_trans_sem.release()
81  if (":" in title and step not in title.split(":")[0]) or \
82  (":" not in title and step not in title):
83  testDef.plugin_trans_sem.acquire()
84  continue
85  # see if this is a step we are to execute
86  if title not in testDef.actives:
87  testDef.plugin_trans_sem.acquire()
88  continue
89 
90  # create display title for section in case loopforever is on
91  title_append = "-loop%d" % (self.loop_count) if self.loopforever else ""
92  disp_title = title + title_append
93 
94  testDef.logger.verbose_print(disp_title)
95  # if they provided the STOP section, that means we
96  # are to immediately stop processing the test definition
97  # file and return
98  if "STOP" in title:
99  return
100  # if they included the "SKIP" qualifier, then we skip
101  # this section
102  if "SKIP" in title:
103  testDef.plugin_trans_sem.acquire()
104  continue
105  # extract the stage and stage name from the title
106  if ":" in title:
107  stage,name = title.split(':')
108  stage = stage.strip()
109  else:
110  stage = title
111 
112  # Print that section is now starting
113  testDef.logger.stage_start_print(disp_title)
114 
115  # Refresh test options if not running combinatorial plugin
116  if testDef.options['executor'] != "combinatorial":
117  # Check for updated environment variables
118  testDef.fill_env_hidden_section()
119  # Check for updated log variables
120  testDef.fill_log_hidden_section()
121  # Print section options
122  testDef.logger.verbose_print("OPTIONS FOR SECTION: %s" % disp_title)
123  strs_to_print = testDef.logger.get_tuplelist_contents(testDef.config.items(title))
124  if strs_to_print:
125  for s in strs_to_print:
126  testDef.logger.verbose_print(" %s" % s)
127  else:
128  testDef.logger.verbose_print(" No options provided for section")
129 
130  # setup the log
131  stageLog = {'section':disp_title}
132  # get the key-value tuples output by the configuration parser
133  stageLog["parameters"] = testDef.config.items(title)
134  # convert the list of key-value tuples provided in this stage
135  # by the user to a dictionary for easier parsing.
136  # Yes, we could do this automatically, but we instead do it
137  # manually so we can strip all the keys and values for easier
138  # parsing later
139  keyvals = {'section':disp_title.strip()}
140  for kv in testDef.config.items(title):
141  keyvals[kv[0].strip()] = kv[1].strip()
142  if 'parent' in keyvals:
143  keyvals['parent'] = keyvals['parent'] + title_append
144  # if they included the "ASIS" qualifier, remove it
145  # from the stage name
146  if "ASIS" in stage:
147  # find the first non-space character
148  i = 4
149  while stage[i].isspace():
150  i = i + 1
151  stage = stage[i:]
152  stageLog['section'] = disp_title[i:].strip()
153  keyvals['section'] = disp_title[i:].strip()
154  keyvals['asis'] = True
155  # if this stage has a parent, get the log for that stage
156  # and check its status - if it didn't succeed, then we shall
157  # log this stage as also having failed and skip it
158  try:
159  parent = keyvals['parent']
160  if parent is not None:
161  # get the log entry as it contains the status
162  bldlog = testDef.logger.getLog(parent)
163  if bldlog is None:
164  # couldn't find the parent's log - cannot continue
165  stageLog['status'] = 1
166  stageLog['stderr'] = ["Prior dependent step did not record a log"]
167  testDef.logger.logResults(disp_title, stageLog, testDef)
168  testDef.plugin_trans_sem.acquire()
169  continue
170  try:
171  if bldlog['status'] != 0:
172  # the parent step failed, and so we
173  # cannot proceed here either
174  stageLog['status'] = bldlog['status']
175  stageLog['stderr'] = ["Prior dependent step failed - cannot proceed"]
176  testDef.logger.logResults(disp_title, stageLog, testDef)
177  testDef.plugin_trans_sem.acquire()
178  continue
179  except KeyError:
180  # if it didn't report a status, we shouldn't rely on it
181  stageLog['status'] = 1
182  stageLog['stderr'] = ["Prior dependent step failed to provide a status"]
183  testDef.logger.logResults(disp_title, stageLog, testDef)
184  testDef.plugin_trans_sem.acquire()
185  continue
186  except KeyError:
187  pass
188  # extract the name of the plugin to use
189  plugin = None
190  try:
191  module = keyvals['plugin']
192  # see if this plugin exists
193  try:
194  for pluginInfo in testDef.stages.getPluginsOfCategory(stage):
195  if module == pluginInfo.plugin_object.print_name():
196  plugin = pluginInfo.plugin_object
197  break
198  if plugin is None:
199  # this plugin doesn't exist, or it may not be a stage as
200  # sometimes a stage consists of executing a tool or utility.
201  # so let's check the tools too, noting that those
202  # are not stage-specific.
203  availTools = list(testDef.loader.tools.keys())
204  for tool in availTools:
205  for pluginInfo in testDef.tools.getPluginsOfCategory(tool):
206  if module == pluginInfo.plugin_object.print_name():
207  plugin = pluginInfo.plugin_object
208  break
209  if plugin is not None:
210  break;
211  if plugin is None:
212  # Check the utilities
213  availUtils = list(testDef.loader.utilities.keys())
214  for util in availUtils:
215  for pluginInfo in testDef.utilities.getPluginsOfCategory(util):
216  if module == pluginInfo.plugin_object.print_name():
217  plugin = pluginInfo.plugin_object
218  break
219  if plugin is not None:
220  break;
221  if plugin is None:
222  stageLog['status'] = 1
223  stageLog['stderr'] = "Specified plugin",module,"does not exist in stage",stage,"or in the available tools and utilities"
224  testDef.logger.logResults(disp_title, stageLog, testDef)
225  testDef.plugin_trans_sem.acquire()
226  continue
227  else:
228  # activate the specified plugin
229  testDef.tools.activatePluginByName(module, tool)
230  else:
231  # activate the specified plugin
232  testDef.stages.activatePluginByName(module, stage)
233  except KeyError:
234  # If this stage has no plugins then check the tools and the utilities
235  availTools = list(testDef.loader.tools.keys())
236  for tool in availTools:
237  for pluginInfo in testDef.tools.getPluginsOfCategory(tool):
238  if module == pluginInfo.plugin_object.print_name():
239  plugin = pluginInfo.plugin_object
240  break
241  if plugin is not None:
242  break;
243  if plugin is None:
244  # Check the utilities
245  availUtils = list(testDef.loader.utilities.keys())
246  for util in availUtils:
247  for pluginInfo in testDef.utilities.getPluginsOfCategory(util):
248  if module == pluginInfo.plugin_object.print_name():
249  plugin = pluginInfo.plugin_object
250  break
251  if plugin is not None:
252  break;
253  if plugin is None:
254  stageLog['status'] = 1
255  stageLog['stderr'] = "Specified plugin",module,"does not exist in stage",stage,"or in the available tools and utilities"
256  testDef.logger.logResults(disp_title, stageLog, testDef)
257  testDef.plugin_trans_sem.acquire()
258  continue
259  else:
260  # activate the specified plugin
261  testDef.tools.activatePluginByName(module, tool)
262  except KeyError:
263  # if they didn't specify a plugin, use the default if one
264  # is available and so designated
265  default = "Default{0}".format(stage)
266  for pluginInfo in testDef.stages.getPluginsOfCategory(stage):
267  if default == pluginInfo.plugin_object.print_name():
268  plugin = pluginInfo.plugin_object
269  break
270  if plugin is None:
271  # we really have no way of executing this
272  stageLog['status'] = 1
273  stageLog['stderr'] = "Plugin for stage",stage,"was not specified, and no default is available"
274  testDef.logger.logResults(disp_title, stageLog, testDef)
275  testDef.plugin_trans_sem.acquire()
276  continue
277 
278  # Make sure that the plugin was activated
279  if not plugin.is_activated:
280  plugin.activate()
281 
282  # execute the provided test description and capture the result
283  testDef.logger.verbose_print("Executing plugin %s" % plugin.print_name())
284  plugin.execute(stageLog, keyvals, testDef)
285 
286 
287  # Make sure stdout and stderr are properly formatted
288  if 'stdout' in stageLog and isinstance(stageLog['stdout'], str):
289  stageLog['stdout'] = stageLog['stdout'].split("\n")
290  if 'stderr' in stageLog and isinstance(stageLog['stderr'], str):
291  stageLog['stderr'] = stageLog['stderr'].split("\n")
292 
293  # Print end of section
294  testDef.logger.stage_end_print(disp_title, stageLog)
295 
296  # Log results for section
297  testDef.logger.logResults(disp_title, stageLog, testDef)
298 
299  if testDef.options['stop_on_fail'] is not False and stageLog['status'] != 0:
300  print("Section " + stageLog['section'] + ": Status " + str(stageLog['status']))
301  try:
302  print("Section " + stageLog['section'] + ": Stderr " + str(stageLog['stderr']))
303  except KeyError:
304  pass
305  sys.exit(1)
306 
307  # Set flag if any stage failed so that a return code can be passed back up
308  if stageLog['status'] != 0:
309  self.status = 1
310 
311  # sem for exclusive access while outside exception-catching-zone
312  testDef.plugin_trans_sem.acquire()
313 
314  except KeyboardInterrupt as e:
315  self.looping = False
316  for p in testDef.stages.getAllPlugins() \
317  + testDef.tools.getAllPlugins() \
318  + testDef.utilities.getAllPlugins():
319  if not p._getIsActivated():
320  continue
321  p.plugin_object.deactivate()
322  testDef.logger.logResults(disp_title, stageLog, testDef)
323  testDef.logger.verbose_print("=======================================")
324  testDef.logger.verbose_print("KeyboardInterrupt exception was raised: %s %s" \
325  % (type(e), str(e)))
326  testDef.logger.verbose_print("=======================================")
327  stageLog['status'] = 0
328  stageLog['stderr'] = ["Exception was raised: %s %s" % (type(e), str(e))]
329  self.status = 1
330  self.only_reporter = True
331  continue
332 
333  except BaseException as e:
334  self.looping = False
335  for p in testDef.stages.getAllPlugins() \
336  + testDef.tools.getAllPlugins() \
337  + testDef.utilities.getAllPlugins():
338  if not p._getIsActivated():
339  continue
340  p.plugin_object.deactivate()
341  testDef.logger.verbose_print("=======================================")
342  testDef.logger.verbose_print("Exception was raised: %s %s" \
343  % (type(e), str(e)))
344  testDef.logger.verbose_print("=======================================")
345  type_, value_, traceback_ = sys.exc_info()
346  ex = traceback.format_exception(type_, value_, traceback_)
347  testDef.logger.verbose_print("\n".join(ex))
348  testDef.logger.verbose_print("=======================================")
349  stageLog['status'] = 1
350  stageLog['stderr'] = ["Exception was raised: %s %s" % (type(e), str(e))]
351  testDef.logger.logResults(disp_title, stageLog, testDef)
352  self.status = 1
353  self.only_reporter = True
354  continue
355  self.only_reporter = False
356 
357  def execute(self, testDef):
358  testDef.logger.verbose_print("ExecuteSequential")
359  self.status = 0
360  self.only_reporter = False
361  self.looping = False
362  self.one_last_loop = False
363  self.loopforever = False
364  self.loop_count = 0
365 
366  testDef.watchdog.__init__(testDef=testDef)
367  testDef.watchdog.activate()
368 
369  # Holding a semaphore while in transition between plugins
370  # so async threads don't interrupt in the wrong context
371  testDef.plugin_trans_sem.acquire()
372 
373  # If --duration switch is used, activate watchdog timer
374  if testDef.options['duration']:
375  testDef.watchdog.start(timeout=testDef.options['duration'])
376 
377  # If --loopforever switch is used, loop forever
378  if testDef.options['loopforever']:
379  self.looping = True
380  self.loopforever = True
381 
382  # If --loop switch is used, loop until loop timeout is done
383  elif testDef.options['loop']:
384  testDef.watchdog.start(handler=self.durationTimeoutHandler,
385  timeout=testDef.options['loop'])
386  testDef.looping = True
387 
388 
389  # Start harasser
390  if testDef.options["harass_trigger_scripts"] is not None:
391  stageLog = {'section':"DefaultHarasser"}
392  testDef.harasser.execute(stageLog,{"trigger_scripts": testDef.options["harass_trigger_scripts"],
393  "stop_scripts": testDef.options["harass_stop_scripts"],
394  "join_timeout": testDef.options["harass_join_timeout"]}, testDef)
395  if stageLog['status'] != 0:
396  self.status = 1
397  self.only_reporter = True
398  testDef.logger.logResults("DefaultHarasser", stageLog, testDef)
399 
400  # Keep on looping as long as it's needed
401  while self.looping or self.loop_count == 0 or (self.one_last_loop and self.looping):
402  self.loop_count += 1
403  if self.one_last_loop:
404  self.only_reporter = True
405  self.looping = False
406  # Execute all sections in INI file
407  self.execute_sections(testDef)
408 
409  for p in testDef.stages.getAllPlugins() \
410  + testDef.tools.getAllPlugins() \
411  + testDef.utilities.getAllPlugins():
412  if p._getIsActivated():
413  p.plugin_object.deactivate()
414 
415  testDef.plugin_trans_sem.release()
416 
417  return self.status