pymtt
 All Classes Namespaces Files Functions Variables Groups
Shell.py
Go to the documentation of this file.
1 #!/usr/bin/env python
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 import os
13 import re
14 import shlex
15 from BuildMTTTool import *
16 
17 ## @addtogroup Tools
18 # @{
19 # @addtogroup Build
20 # @section Shell
21 # Run shell commands to configure and build a software package
22 # @param middleware Middleware stage that these tests are to be built against
23 # @param command Command to execute
24 # @param parent Section that precedes this one in the dependency tree
25 # @param merge_stdout_stderr Merge stdout and stderr into one output stream
26 # @param stdout_save_lines Number of lines of stdout to save
27 # @param stderr_save_lines Number of lines of stderr to save
28 # @param modules Modules to load
29 # @param modules_unload Modules to unload
30 # @param fail_test Specifies whether this test is expected to fail (value=None means test is expected to succeed)
31 # @param fail_returncode Specifies the expected failure returncode of this test
32 # @param allocate_cmd Command to use for allocating nodes from the resource manager
33 # @param deallocate_cmd Command to use for deallocating nodes from the resource manager
34 # @param asis_target Specifies name of asis_target being built. This is used with \"ASIS\" keyword to determine whether to do anything.
35 # @}
37  def __init__(self):
38  BuildMTTTool.__init__(self)
39  self.activated = False
40  self.options = {}
41  self.options['middleware'] = (None, "Middleware stage that these tests are to be built against")
42  self.options['command'] = (None, "Command to execute")
43  self.options['parent'] = (None, "Section that precedes this one in the dependency tree")
44  self.options['merge_stdout_stderr'] = (False, "Merge stdout and stderr into one output stream")
45  self.options['stdout_save_lines'] = (-1, "Number of lines of stdout to save")
46  self.options['stderr_save_lines'] = (-1, "Number of lines of stderr to save")
47  self.options['modules'] = (None, "Modules to load")
48  self.options['modules_unload'] = (None, "Modules to unload")
49  self.options['fail_test'] = (None, "Specifies whether this test is expected to fail (value=None means test is expected to succeed)")
50  self.options['fail_returncode'] = (None, "Specifies the expected failure returncode of this test")
51  self.options['allocate_cmd'] = (None, "Command to use for allocating nodes from the resource manager")
52  self.options['deallocate_cmd'] = (None, "Command to use for deallocating nodes from the resource manager")
53  self.options['asis_target'] = (None, "Specifies name of asis_target being built. This is used with \"ASIS\" keyword to determine whether to do anything.")
54 
55  self.allocated = False
56  self.testDef = None
57  self.cmds = None
58  return
59 
60  def activate(self):
61  if not self.activated:
62  # use the automatic procedure from IPlugin
63  IPlugin.activate(self)
64  self.activated = True
65  return
66 
67  def deactivate(self):
68  if self.activated:
69  IPlugin.deactivate(self)
70  self.activated = False
71  if self.allocated and self.cmds and self.testDef:
72  self.deallocate({}, self.cmds, self.testDef)
73  return
74 
75  def print_name(self):
76  return "Shell"
77 
78  def print_options(self, testDef, prefix):
79  lines = testDef.printOptions(self.options)
80  for line in lines:
81  print(prefix + line)
82  return
83 
84  def allocate(self, log, cmds, testDef):
85  self.allocated = False
86  if cmds['allocate_cmd'] is not None and cmds['deallocate_cmd'] is not None:
87  self.allocated = True
88  allocate_cmdargs = shlex.split(cmds['allocate_cmd'])
89  status,stdout,stderr,time = testDef.execmd.execute(cmds, allocate_cmdargs, testDef)
90  if 0 != status:
91  self.allocated = False
92  log['status'] = status
93  if log['stderr']:
94  log['stderr'].extend(stderr)
95  else:
96  log['stderr'] = stderr
97  return False
98  return True
99 
100  def deallocate(self, log, cmds, testDef):
101  if cmds['allocate_cmd'] is not None and cmds['deallocate_cmd'] is not None and self.allocated == True:
102  deallocate_cmdargs = shlex.split(cmds['deallocate_cmd'])
103  status,stdout,stderr,time = testDef.execmd.execute(cmds, deallocate_cmdargs, testDef)
104  if 0 != status:
105  log['status'] = status
106  if log['stderr']:
107  log['stderr'].extend(stderr)
108  else:
109  log['stderr'] = stderr
110  return False
111  self.allocated = False
112  return True
113 
114  def execute(self, log, keyvals, testDef):
115 
116  self.testDef = testDef
117 
118  testDef.logger.verbose_print("Shell Execute")
119  # parse any provided options - these will override the defaults
120  cmds = {}
121  testDef.parseOptions(log, self.options, keyvals, cmds)
122  self.cmds = cmds
123  # if they didn't give us a shell command to execute, then error
124  try:
125  if cmds['command'] is None:
126  log['status'] = 1
127  log['stderr'] = "No command specified"
128  return
129  except KeyError:
130  log['status'] = 1
131  log['stderr'] = "No command specified"
132  return
133 
134  # get the location of the software we are to build
135  parentlog = None
136  try:
137  if cmds['parent'] is not None:
138  # we have to retrieve the log entry from
139  # the parent section so we can get the
140  # location of the package. The logger
141  # can provide it for us
142  parentlog = testDef.logger.getLog(cmds['parent'])
143  if parentlog is None:
144  log['status'] = 1
145  log['stderr'] = "Parent",cmds['parent'],"log not found"
146  return
147  except KeyError:
148  log['status'] = 1
149  log['stderr'] = "Parent not specified"
150  return
151  try:
152  parentloc = os.path.join(os.getcwd(),log['options']['scratch'])
153  location = parentloc
154  except KeyError:
155  log['status'] = 1
156  log['stderr'] = "No scratch directory in log"
157  return
158  if parentlog is not None:
159  try:
160  parentloc = parentlog['location']
161  location = parentloc
162  except KeyError:
163  log['status'] = 1
164  log['stderr'] = "Location of package to build was not specified in parent stage"
165  return
166  else:
167  try:
168  if log['section'].startswith("TestGet:") or log['section'].startswith("MiddlewareGet:"):
169  location = os.path.join(parentloc,log['section'].replace(":","_"))
170  except KeyError:
171  log['status'] = 1
172  log['stderr'] = "No section in log"
173  return
174  # check to see if this is a dryrun
175  if testDef.options['dryrun']:
176  # just log success and return
177  log['status'] = 0
178  return
179 
180  # Check to see if this needs to be ran if ASIS is specified
181  try:
182  if cmds['asis']:
183  if cmds['asis_target'] is not None:
184  if os.path.exists(os.path.join(parentloc,cmds['asis_target'])):
185  testDef.logger.verbose_print("asis_target " + os.path.join(parentloc,cmds['asis_target']) + " exists. Skipping...")
186  log['location'] = location
187  log['status'] = 0
188  return
189  else:
190  testDef.logger.verbose_print("asis_target " + os.path.join(parentloc,cmds['asis_target']) + " does not exist. Continuing...")
191  else: # no asis target, default to check for directory
192  if os.path.exists(location):
193  testDef.logger.verbose_print("directory " + location + " exists. Skipping...")
194  log['location'] = location
195  log['status'] = 0
196  return
197  else:
198  testDef.logger.verbose_print("directory " + location + " does not exist. Continuing...")
199  except KeyError:
200  pass
201 
202  # check if we need to point to middleware
203  midpath = False
204  try:
205  if cmds['middleware'] is not None:
206  # pass it down
207  log['middleware'] = cmds['middleware']
208  # get the log entry of its location
209  midlog = testDef.logger.getLog(cmds['middleware'])
210  if midlog is not None:
211  # get the location of the middleware
212  try:
213  if midlog['location'] is not None:
214  # prepend that location to our paths
215  try:
216  oldbinpath = os.environ['PATH']
217  pieces = oldbinpath.split(':')
218  except KeyError:
219  oldbinpath = ""
220  pieces = []
221  bindir = os.path.join(midlog['location'], "bin")
222  pieces.insert(0, bindir)
223  newpath = ":".join(pieces)
224  os.environ['PATH'] = newpath
225  # prepend the loadable lib path
226  try:
227  oldldlibpath = os.environ['LD_LIBRARY_PATH']
228  pieces = oldldlibpath.split(':')
229  except KeyError:
230  oldldlibpath = ""
231  pieces = []
232  bindir = os.path.join(midlog['location'], "lib")
233  pieces.insert(0, bindir)
234  newpath = ":".join(pieces)
235  os.environ['LD_LIBRARY_PATH'] = newpath
236  # prepend the include path
237  try:
238  oldcpath = os.environ['CPATH']
239  pieces = oldcpath.split(':')
240  except KeyError:
241  oldcpath = ""
242  pieces = []
243  bindir = os.path.join(midlog['location'], "include")
244  pieces.insert(0, bindir)
245  newpath = ":".join(pieces)
246  os.environ['CPATH'] = newpath
247  # prepend the lib path
248  try:
249  oldlibpath = os.environ['LIBRARY_PATH']
250  pieces = oldlibpath.split(':')
251  except KeyError:
252  oldlibpath = ""
253  pieces = []
254  bindir = os.path.join(midlog['location'], "lib")
255  pieces.insert(0, bindir)
256  newpath = ":".join(pieces)
257  os.environ['LIBRARY_PATH'] = newpath
258 
259  # mark that this was done
260  midpath = True
261  except KeyError:
262  pass
263  # check for modules required by the middleware
264  try:
265  if midlog['parameters'] is not None:
266  for md in midlog['parameters']:
267  if "modules" == md[0]:
268  try:
269  if cmds['modules'] is not None:
270  # append these modules to those
271  mods = md[1].split(',')
272  newmods = modules.split(',')
273  for md in newmods:
274  mods.append(md)
275  cmds['modules'] = ','.join(mods)
276  except KeyError:
277  cmds['modules'] = md[1]
278  break
279  except KeyError:
280  pass
281  except KeyError:
282  pass
283 
284  # check to see if they specified a module to use
285  # where the compiler can be found
286  usedModuleUnload = False
287  try:
288  if cmds['modules_unload'] is not None:
289  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules_unload'], testDef)
290  if 0 != status:
291  log['status'] = status
292  log['stderr'] = stderr
293  return
294  usedModuleUnload = True
295  except KeyError:
296  # not required to provide a module to unload
297  pass
298  usedModule = False
299  try:
300  if cmds['modules'] is not None:
301  status,stdout,stderr = testDef.modcmd.loadModules(cmds['modules'], testDef)
302  if 0 != status:
303  log['status'] = status
304  log['stderr'] = stderr
305  return
306  usedModule = True
307  except KeyError:
308  # not required to provide a module
309  pass
310 
311  # sense and record the compiler being used
312  plugin = None
313  availUtil = list(testDef.loader.utilities.keys())
314  for util in availUtil:
315  for pluginInfo in testDef.utilities.getPluginsOfCategory(util):
316  if "Compilers" == pluginInfo.plugin_object.print_name():
317  plugin = pluginInfo.plugin_object
318  break
319  if plugin is None:
320  log['compiler'] = {'status' : 1, 'family' : "unknown", 'version' : "unknown"}
321  else:
322  compilerLog = {}
323  plugin.execute(compilerLog, testDef)
324  log['compiler'] = compilerLog
325 
326  # Find MPI information for IUDatabase plugin
327  if log['section'].startswith("TestBuild:") or log['section'].startswith("MiddlewareBuild:"):
328  plugin = None
329  availUtil = list(testDef.loader.utilities.keys())
330  for util in availUtil:
331  for pluginInfo in testDef.utilities.getPluginsOfCategory(util):
332  if "MPIVersion" == pluginInfo.plugin_object.print_name():
333  plugin = pluginInfo.plugin_object
334  break
335  if plugin is None:
336  log['mpi_info'] = {'name' : 'unknown', 'version' : 'unknown'}
337  else:
338  mpi_info = {}
339  plugin.execute(mpi_info, testDef)
340  log['mpi_info'] = mpi_info
341 
342  # save the current directory so we can return to it
343  cwd = os.getcwd()
344  # now move to the package location
345  if not os.path.exists(location):
346  os.makedirs(location)
347  os.chdir(location)
348  # execute the specified command
349  # Use shlex.split() for correct tokenization for args
350 
351  # Allocate cluster
352  if False == self.allocate(log, cmds, testDef):
353  return
354 
355  cfgargs = shlex.split(cmds['command'])
356 
357  if 'TestRun' in log['section'].split(":")[0]:
358  harass_exec_ids = testDef.harasser.start(testDef)
359 
360  harass_check = testDef.harasser.check(harass_exec_ids, testDef)
361  if harass_check is not None:
362  log['stderr'] = 'Not all harasser scripts started. These failed to start: ' \
363  + ','.join([h_info[1]['start_script'] for h_info in harass_check[0]])
364  log['time'] = sum([r_info[3] for r_info in harass_check[1]])
365  log['status'] = 1
366  self.deallocate(log, cmds, testDef)
367  return
368 
369  status, stdout, stderr, time = testDef.execmd.execute(cmds, cfgargs, testDef)
370 
371  if 'TestRun' in log['section'].split(":")[0]:
372  testDef.harasser.stop(harass_exec_ids, testDef)
373 
374  # Deallocate cluster
375  if False == self.deallocate(log, cmds, testDef):
376  return
377 
378  if (cmds['fail_test'] is None and 0 != status) \
379  or (cmds['fail_test'] is not None and cmds['fail_returncode'] is None and 0 == status) \
380  or (cmds['fail_test'] is not None and cmds['fail_returncode'] is not None and int(cmds['fail_returncode']) != status):
381  # return to original location
382  os.chdir(cwd)
383  if log['status'] == 0:
384  log['status'] = 1
385  else:
386  log['status'] = status
387  log['stdout'] = stdout
388  log['stderr'] = stderr
389  log['time'] = time
390  return
391  log['status'] = 0
392  log['stdout'] = stdout
393  log['time'] = time
394  if cmds['fail_test'] == True:
395  log['stderr'] = stderr
396  # record this location for any follow-on steps
397  log['location'] = location
398  if usedModule:
399  # unload the modules before returning
400  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
401  if 0 != status:
402  log['status'] = status
403  log['stderr'] = stderr
404  os.chdir(cwd)
405  return
406  if usedModuleUnload:
407  status,stdout,stderr = testDef.modcmd.loadModules(cmds['modules_unload'], testDef)
408  if 0 != status:
409  log['status'] = status
410  log['stderr'] = stderr
411  os.chdir(cwd)
412  return
413  # if we added middleware to the paths, remove it
414  if midpath:
415  os.environ['PATH'] = oldbinpath
416  os.environ['LD_LIBRARY_PATH'] = oldldlibpath
417  os.environ['CPATH'] = oldcpath
418  os.environ['LIBRARY_PATH'] = oldlibpath
419 
420  # return to original location
421  os.chdir(cwd)
422  return
def activate
Definition: Shell.py:60
def __init__
Definition: Shell.py:37
def execute
Definition: Shell.py:114
def deallocate
Definition: Shell.py:100
def deactivate
Definition: Shell.py:67
def print_name
Definition: Shell.py:75
def print_options
Definition: Shell.py:78
def allocate
Definition: Shell.py:84