pymtt
 All Classes Namespaces Files Functions Variables Groups
ALPS.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 import os
13 from LauncherMTTTool import *
14 import shlex
15 
16 ## @addtogroup Tools
17 # @{
18 # @addtogroup Launcher
19 # @section ALPS
20 # Plugin for using ALPS to launch tests
21 # @param hostfile The hostfile for OpenMPI to use
22 # @param command Command for executing the application
23 # @param np Number of processes to run
24 # @param options Comma-delimited sets of command line options that shall be used on each test
25 # @param skipped Exit status of a test that declares it was skipped
26 # @param merge_stdout_stderr Merge stdout and stderr into one output stream
27 # @param stdout_save_lines Number of lines of stdout to save
28 # @param stderr_save_lines Number of lines of stderr to save
29 # @param test_dir Names of directories to be scanned for tests
30 # @param fail_tests Names of tests that are expected to fail
31 # @param fail_returncodes Expected returncodes of tests expected to fail
32 # @param fail_timeout Maximum execution time for tests expected to fail
33 # @param skip_tests Names of tests to be skipped
34 # @param max_num_tests Maximum number of tests to run
35 # @param modules Modules to load
36 # @param modules_unload Modules to unload
37 # @param test_list List of tests to run, default is all
38 # @param allocate_cmd Command to use for allocating nodes from the resource manager
39 # @param deallocate_cmd Command to use for deallocating nodes from the resource manager
40 # @}
42 
43  def __init__(self):
44  # initialise parent class
45  LauncherMTTTool.__init__(self)
46  self.options = {}
47  self.options['hostfile'] = (None, "The hostfile for ALPS to use")
48  self.options['command'] = ("aprun", "Command for executing the application")
49  self.options['np'] = (None, "Number of processes to run")
50  self.options['options'] = (None, "Comma-delimited sets of command line options that shall be used on each test")
51  self.options['skipped'] = ("77", "Exit status of a test that declares it was skipped")
52  self.options['merge_stdout_stderr'] = (False, "Merge stdout and stderr into one output stream")
53  self.options['stdout_save_lines'] = (-1, "Number of lines of stdout to save")
54  self.options['stderr_save_lines'] = (-1, "Number of lines of stderr to save")
55  self.options['test_dir'] = (None, "Names of directories to be scanned for tests")
56  self.options['fail_tests'] = (None, "Names of tests that are expected to fail")
57  self.options['fail_returncodes'] = (None, "Expected return code of tests expected to fail")
58  self.options['fail_timeout'] = (None, "Maximum execution time for tests expected to fail")
59  self.options['skip_tests'] = (None, "Names of tests to be skipped")
60  self.options['max_num_tests'] = (None, "Maximum number of tests to run")
61  self.options['modules'] = (None, "Modules to load")
62  self.options['modules_unload'] = (None, "Modules to unload")
63  self.options['test_list'] = (None, "List of tests to run, default is all")
64  self.options['allocate_cmd'] = (None, "Command to use for allocating nodes from the resource manager")
65  self.options['deallocate_cmd'] = (None, "Command to use for deallocating nodes from the resource manager")
66 
67  self.allocated = False
68  self.testDef = None
69  self.cmds = None
70  return
71 
72 
73  def activate(self):
74  # use the automatic procedure from IPlugin
75  IPlugin.activate(self)
76  return
77 
78 
79  def deactivate(self):
80  IPlugin.deactivate(self)
81  if self.allocated and self.testDef and self.cmds:
82  deallocate_cmdargs = shlex.split(self.cmds['deallocate_cmd'])
83  _status,_stdout,_stderr,_time = self.testDef.execmd.execute(self.cmds, deallocate_cmdargs, self.testDef)
84  self.allocated = False
85 
86 
87  def print_name(self):
88  return "ALPS"
89 
90  def print_options(self, testDef, prefix):
91  lines = testDef.printOptions(self.options)
92  for line in lines:
93  print(prefix + line)
94  return
95 
96  def execute(self, log, keyvals, testDef):
97 
98  self.testDef = testDef
99 
100  midpath = False
101 
102  testDef.logger.verbose_print("ALPS Launcher")
103  # check the log for the title so we can
104  # see if this is setting our default behavior
105  try:
106  if log['section'] is not None:
107  if "Default" in log['section']:
108  # this section contains default settings
109  # for this launcher
110  myopts = {}
111  testDef.parseOptions(log, self.options, keyvals, myopts)
112  # transfer the findings into our local storage
113  keys = list(self.options.keys())
114  optkeys = list(myopts.keys())
115  for optkey in optkeys:
116  for key in keys:
117  if key == optkey:
118  self.options[key] = (myopts[optkey],self.options[key][1])
119 
120  # we captured the default settings, so we can
121  # now return with success
122  log['status'] = 0
123  return
124  except KeyError:
125  # error - the section should have been there
126  log['status'] = 1
127  log['stderr'] = "Section not specified"
128  return
129  # must be executing a test of some kind - the install stage
130  # must be specified so we can find the tests to be run
131  try:
132  parent = keyvals['parent']
133  if parent is not None:
134  # get the log entry as it contains the location
135  # of the built tests
136  bldlog = testDef.logger.getLog(parent)
137  try:
138  location = bldlog['location']
139  except KeyError:
140  # if it wasn't recorded, then there is nothing
141  # we can do
142  log['status'] = 1
143  log['stderr'] = "Location of built tests was not provided"
144  return
145  try:
146  if bldlog['parameters'] is not None:
147  # check for modules unloaded during the build of these tests
148  for md in bldlog['parameters']:
149  if "modules_unload" == md[0]:
150  try:
151  if keyvals['modules_unload'] is not None:
152  # append these modules to those
153  mods = md[1].split(',')
154  newmods = keyvals['modules_unload'].split(',')
155  for mdx in newmods:
156  mods.append(mdx)
157  keyvals['modules_unload'] = ','.join(mods)
158  except KeyError:
159  keyvals['modules_unload'] = md[1]
160  break
161  # check for modules used during the build of these tests
162  for md in bldlog['parameters']:
163  if "modules" == md[0]:
164  try:
165  if keyvals['modules'] is not None:
166  # append these modules to those
167  mods = md[1].split(',')
168  newmods = keyvals['modules'].split(',')
169  for mdx in newmods:
170  mods.append(mdx)
171  keyvals['modules'] = ','.join(mods)
172  except KeyError:
173  keyvals['modules'] = md[1]
174  break
175  except KeyError:
176  pass
177  # get the log of any middleware so we can get its location
178  try:
179  midlog = testDef.logger.getLog(bldlog['middleware'])
180  if midlog is not None:
181  # get the location of the middleware
182  try:
183  if midlog['location'] is not None:
184  # prepend that location to our paths
185  try:
186  oldbinpath = os.environ['PATH']
187  pieces = oldbinpath.split(':')
188  except KeyError:
189  oldbinpath = ""
190  pieces = []
191  bindir = os.path.join(midlog['location'], "bin")
192  pieces.insert(0, bindir)
193  newpath = ":".join(pieces)
194  os.environ['PATH'] = newpath
195  # prepend the loadable lib path
196  try:
197  oldldlibpath = os.environ['LD_LIBRARY_PATH']
198  pieces = oldldlibpath.split(':')
199  except KeyError:
200  oldldlibpath = ""
201  pieces = []
202  bindir = os.path.join(midlog['location'], "lib")
203  pieces.insert(0, bindir)
204  newpath = ":".join(pieces)
205  os.environ['LD_LIBRARY_PATH'] = newpath
206 
207  # mark that this was done
208  midpath = True
209  except KeyError:
210  # if it was already installed, then no location would be provided
211  pass
212  try:
213  if midlog['parameters'] is not None:
214  # check for modules unloaded by the middleware
215  for md in midlog['parameters']:
216  if "modules_unload" == md[0]:
217  try:
218  if keyvals['modules_unload'] is not None:
219  # append these modules to those
220  mods = md[1].split(',')
221  newmods = keyvals['modules_unload'].split(',')
222  for mdx in newmods:
223  mods.append(mdx)
224  keyvals['modules_unload'] = ','.join(mods)
225  except KeyError:
226  keyvals['modules_unload'] = md[1]
227  break
228  # check for modules required by the middleware
229  for md in midlog['parameters']:
230  if "modules" == md[0]:
231  try:
232  if keyvals['modules'] is not None:
233  # append these modules to those
234  mods = md[1].split(',')
235  newmods = keyvals['modules'].split(',')
236  for mdx in newmods:
237  mods.append(mdx)
238  keyvals['modules'] = ','.join(mods)
239  except KeyError:
240  keyvals['modules'] = md[1]
241  break
242  except KeyError:
243  pass
244  except KeyError:
245  pass
246  except KeyError:
247  log['status'] = 1
248  log['stderr'] = "Parent test build stage was not provided"
249  return
250  # parse any provided options - these will override the defaults
251  cmds = {}
252  testDef.parseOptions(log, self.options, keyvals, cmds)
253  self.cmds = cmds
254 
255  # Check if command is correct
256  if cmds['command'] != "aprun":
257  log['status'] = 1
258  log['stderr'] = "Command for ALPS plugin must be aprun. It is currently '%s'" % (cmds['command'] if cmds['command'] is not None else "None")
259  return
260 
261  # now ready to execute the test - we are pointed at the middleware
262  # and have obtained the list of any modules associated with it. We need
263  # to change to the test location and begin executing, first saving
264  # our current location so we can return when done
265  cwd = os.getcwd()
266  os.chdir(location)
267  # did they give us a list of specific directories where the desired
268  # tests to be executed reside?
269  tests = []
270  if cmds['test_list'] is None:
271  try:
272  if cmds['test_dir'] is not None:
273  # pick up the executables from the specified directories
274  dirs = cmds['test_dir'].split()
275  for dr in dirs:
276  dr = dr.strip()
277  # remove any commas and quotes
278  dr = dr.replace('\"','')
279  dr = dr.replace(',','')
280  for dirName, subdirList, fileList in os.walk(dr):
281  for fname in fileList:
282  # see if this is an executable
283  filename = os.path.abspath(os.path.join(dirName,fname))
284  if os.path.isfile(filename) and os.access(filename, os.X_OK):
285  # add this file to our list of tests to execute
286  tests.append(filename)
287  else:
288  # get the list of executables from this directory and any
289  # subdirectories beneath it
290  for dirName, subdirList, fileList in os.walk("."):
291  for fname in fileList:
292  # see if this is an executable
293  filename = os.path.abspath(os.path.join(dirName,fname))
294  if os.path.isfile(filename) and os.access(filename, os.X_OK):
295  # add this file to our list of tests to execute
296  tests.append(filename)
297  except KeyError:
298  # get the list of executables from this directory and any
299  # subdirectories beneath it
300  for dirName, subdirList, fileList in os.walk("."):
301  for fname in fileList:
302  # see if this is an executable
303  filename = os.path.abspath(os.path.join(dirName,fname))
304  if os.path.isfile(filename) and os.access(filename, os.X_OK):
305  # add this file to our list of tests to execute
306  tests.append(filename)
307  # If list of tests is provided, use list rather than grabbing all tests
308  else:
309  if cmds['test_dir'] is not None:
310  dirs = cmds['test_dir'].split()
311  else:
312  dirs = ['.']
313  for dr in dirs:
314  dr = dr.strip()
315  dr = dr.replace('\"','')
316  dr = dr.replace(',','')
317  for dirName, subdirList, fileList in os.walk(dr):
318  for fname in cmds['test_list'].split(","):
319  fname = fname.strip()
320  if fname not in fileList:
321  continue
322  filename = os.path.abspath(os.path.join(dirName,fname))
323  if os.path.isfile(filename) and os.access(filename, os.X_OK):
324  tests.append(filename)
325 
326  # check that we found something
327  if not tests:
328  log['status'] = 1
329  log['stderr'] = "No tests found"
330  os.chdir(cwd)
331  return
332  # get the "skip" exit status
333  skipStatus = int(cmds['skipped'])
334  # assemble the command
335  cmdargs = [cmds['command']]
336  if cmds['options'] is not None:
337  for op in cmds['options'].split():
338  cmdargs.append(op)
339  cmdargs.append(cmds['options'])
340  if cmds['np'] is not None:
341  cmdargs.append("-n")
342  cmdargs.append(cmds['np'])
343  if cmds['hostfile'] is not None:
344  cmdargs.append("--node-list-file")
345  cmdargs.append(cmds['hostfile'])
346  # cycle thru the list of tests and execute each of them
347  log['testresults'] = []
348  finalStatus = 0
349  finalError = ""
350  numTests = 0
351  numPass = 0
352  numSkip = 0
353  numFail = 0
354  if cmds['max_num_tests'] is not None:
355  maxTests = int(cmds['max_num_tests'])
356  else:
357  maxTests = 10000000
358 
359  # unload modules that were removed during the middleware or test build
360  usedModuleUnload = False
361  try:
362  if cmds['modules_unload'] is not None:
363  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules_unload'], testDef)
364  if 0 != status:
365  log['status'] = status
366  log['stderr'] = stderr
367  os.chdir(cwd)
368  return
369  usedModuleUnload = True
370  except KeyError:
371  # not required to provide a module to unload
372  pass
373  # Load modules that were required during the middleware or test build
374  usedModule = False
375  try:
376  if cmds['modules'] is not None:
377  status,stdout,stderr = testDef.modcmd.loadModules(cmds['modules'], testDef)
378  if 0 != status:
379  log['status'] = status
380  log['stderr'] = stderr
381  os.chdir(cwd)
382  return
383  usedModule = True
384  except KeyError:
385  # not required to provide a module
386  pass
387 
388  fail_tests = cmds['fail_tests']
389  if fail_tests is not None:
390  fail_tests = [t.strip() for t in fail_tests.split(",")]
391  else:
392  fail_tests = []
393  for i,t in enumerate(fail_tests):
394  for t2 in tests:
395  if t2.split("/")[-1] == t:
396  fail_tests[i] = t2
397  fail_returncodes = cmds['fail_returncodes']
398  if fail_returncodes is not None:
399  fail_returncodes = [int(t.strip()) for t in fail_returncodes.split(",")]
400 
401  if fail_tests is None:
402  expected_returncodes = {test:0 for test in tests}
403  else:
404  if fail_returncodes is None:
405  expected_returncodes = {test:(None if test in fail_tests else 0) for test in tests}
406  else:
407  fail_returncodes = {test:rtncode for test,rtncode in zip(fail_tests,fail_returncodes)}
408  expected_returncodes = {test:(fail_returncodes[test] if test in fail_returncodes else 0) for test in tests}
409 
410  # Allocate cluster
411  self.allocated = False
412  if cmds['allocate_cmd'] is not None and cmds['deallocate_cmd'] is not None:
413  self.allocated = True
414  allocate_cmdargs = shlex.split(cmds['allocate_cmd'])
415  _status,_stdout,_stderr,_time = testDef.execmd.execute(cmds, allocate_cmdargs, testDef)
416  if 0 != _status:
417  log['status'] = _status
418  log['stderr'] = _stderr
419  os.chdir(cwd)
420  return
421 
422  # For test in tests
423  for test in tests:
424  # Skip tests that are in "skip_tests" ini input
425  if cmds['skip_tests'] is not None and test.split('/')[-1] in [st.strip() for st in cmds['skip_tests'].split()]:
426  numTests += 1
427  numSkip += 1
428  if numTests == maxTests:
429  break
430  continue
431  testLog = {'test':test}
432  cmdargs.append(test)
433  testLog['cmd'] = " ".join(cmdargs)
434 
435  harass_exec_ids = testDef.harasser.start(testDef)
436 
437  harass_check = testDef.harasser.check(harass_exec_ids, testDef)
438  if harass_check is not None:
439  testLog['stderr'] = 'Not all harasser scripts started. These failed to start: ' \
440  + ','.join([h_info[1]['start_script'] for h_info in harass_check[0]])
441  testLog['time'] = sum([r_info[3] for r_info in harass_check[1]])
442  testLog['status'] = 1
443  finalStatus = 1
444  finalError = testLog['stderr']
445  numFail = numFail + 1
446  testDef.harasser.stop(harass_exec_ids, testDef)
447  continue
448 
449  status,stdout,stderr,time = testDef.execmd.execute(cmds, cmdargs, testDef)
450 
451  testDef.harasser.stop(harass_exec_ids, testDef)
452 
453  if 0 != status and skipStatus != status and 0 == finalStatus:
454  if expected_returncodes[test] == 0:
455  finalStatus = status
456  else:
457  finalStatus = 1
458  finalError = stderr
459  if ((expected_returncodes[test] is None and 0 == status) or (expected_returncodes[test] is not None and expected_returncodes[test] != status)) and skipStatus != status and 0 == finalStatus:
460  numPass = numPass + 1
461  elif skipStatus == status:
462  numSkip = numSkip + 1
463  else:
464  numFail = numFail + 1
465  if expected_returncodes[test] == 0:
466  testLog['status'] = status
467  else:
468  if status == expected_returncodes[test]:
469  testLog['status'] = 0
470  else:
471  testLog['status'] = 1
472  testLog['stdout'] = stdout
473  testLog['stderr'] = stderr
474  testLog['time'] = time
475  log['testresults'].append(testLog)
476  cmdargs = cmdargs[:-1]
477  numTests = numTests + 1
478  if numTests == maxTests:
479  break
480 
481  # Deallocate cluster
482  if cmds['allocate_cmd'] is not None and cmds['deallocate_cmd'] is not None and self.allocated:
483  deallocate_cmdargs = shlex.split(cmds['deallocate_cmd'])
484  _status,_stdout,_stderr,_time = testDef.execmd.execute(cmds, deallocate_cmdargs, testDef)
485  if 0 != _status:
486  log['status'] = _status
487  log['stderr'] = _stderr
488  os.chdir(cwd)
489  return
490  self.allocated = False
491 
492  log['status'] = finalStatus
493  log['stderr'] = finalError
494  log['numTests'] = numTests
495  log['numPass'] = numPass
496  log['numSkip'] = numSkip
497  log['numFail'] = numFail
498 
499  # handle case where aprun is used instead of mpirun for number of processes (np)
500  if 'np' not in cmds or cmds['np'] is None:
501  if '-n ' in cmds['options']:
502  log['np'] = str(cmds['options'].split('-n ')[1].split(' ')[0])
503  elif '--ntasks=' in cmds['options']:
504  log['np'] = str(cmds['options'].split('--ntasks=')[1].split(' ')[0])
505  elif '-N ' in cmds['options']:
506  log['np'] = str(cmds['options'].split('-N ')[1].split(' ')[0])
507  elif '--nodes=' in cmds['options']:
508  log['np'] = str(cmds['options'].split('--nodes=')[1].split(' ')[0])
509  else:
510  log['np'] = None
511  else:
512  log['np'] = cmds['np']
513 
514  if usedModule:
515  # unload the modules before returning
516  status,stdout,stderr = testDef.modcmd.unloadModules(cmds['modules'], testDef)
517  if 0 != status:
518  log['status'] = status
519  log['stderr'] = stderr
520  os.chdir(cwd)
521  return
522  if usedModuleUnload:
523  status,stdout,stderr = testDef.modcmd.loadModules(cmds['modules_unload'], testDef)
524  if 0 != status:
525  log['status'] = status
526  log['stderr'] = stderr
527  os.chdir(cwd)
528  return
529 
530  # if we added middleware to the paths, remove it
531  if midpath:
532  os.environ['PATH'] = oldbinpath
533  os.environ['LD_LIBRARY_PATH'] = oldldlibpath
534 
535  os.chdir(cwd)
536  return
def execute
Definition: ALPS.py:96
def print_name
Definition: ALPS.py:87
def activate
Definition: ALPS.py:73
def print_options
Definition: ALPS.py:90
options
Definition: ALPS.py:46
allocated
Definition: ALPS.py:67
def deactivate
Definition: ALPS.py:79
testDef
Definition: ALPS.py:68
def __init__
Definition: ALPS.py:43
cmds
Definition: ALPS.py:69