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