pymtt
 All Classes Namespaces Files Functions Variables Groups
ExecuteCmd.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 from builtins import str
13 import sys
14 import select
15 import subprocess
16 import time
17 import datetime
18 from BaseMTTUtility import *
19 
20 ## @addtogroup Utilities
21 # @{
22 # @section ExecuteCmd
23 # Execute a command and capture its stdout and stderr
24 # @}
26  def __init__(self):
27  BaseMTTUtility.__init__(self)
28  self.options = {}
29  return
30 
31  def print_name(self):
32  return "ExecuteCmd"
33 
34  def print_options(self, testDef, prefix):
35  lines = testDef.printOptions(self.options)
36  for line in lines:
37  print(prefix + line)
38  return
39 
40  def _bool_option(self, options, name):
41  if options and name in options:
42  val = options[name]
43  if type(val) is bool:
44  return val
45  elif type(val) is str:
46  val = val.strip().lower()
47  return val in ['y', 'yes', 't', 'true', '1']
48  else:
49  return val > 0
50 
51  return False
52 
53  def _positive_int_option(self, options, name):
54  val = None
55  if options and name in options:
56  val = options[name]
57  if val is None or val < 0:
58  return 0
59  return int(val)
60 
61  def execute(self, options, cmdargs, testDef):
62  # if this is a dryrun, just declare success
63  if 'dryrun' in testDef.options and testDef.options['dryrun']:
64  return (0, [], [], 0)
65 
66  # check the options for a directive to merge
67  # stdout into stderr
68  merge = self._bool_option(options, 'merge_stdout_stderr')
69 
70  # check for line limits
71  stdoutlines = self._positive_int_option(options, 'stdout_save_lines')
72  stderrlines = self._positive_int_option(options, 'stderr_save_lines')
73 
74  # check for timing request
75  t1 = self._bool_option(options, 'cmdtime')
76  t2 = self._bool_option(options, 'time')
77  time_exec = t1 or t2
78 
79  elapsed_secs = -1
80  elapsed_datetime = None
81 
82  # setup the command arguments
83  mycmdargs = []
84  # if any cmd arg has quotes around it, remove
85  # them here
86  for arg in cmdargs:
87  mycmdargs.append(arg.replace('\"',''))
88  testDef.logger.verbose_print("ExecuteCmd start: " + ' '.join(mycmdargs), timestamp=datetime.datetime.now() if time_exec else None)
89 
90  if not mycmdargs:
91  testDef.logger.verbose_print("ExecuteCmd error: no cmdargs")
92  return (1, [], ["MTT ExecuteCmd error: no cmdargs"], 0)
93 
94  # it is possible that the command doesn't exist or
95  # isn't in our path, so protect us
96  p = None
97  try:
98  if time_exec:
99  starttime = datetime.datetime.now()
100 
101  # open a subprocess with stdout and stderr
102  # as distinct pipes so we can capture their
103  # output as the process runs
104  p = subprocess.Popen(mycmdargs,
105  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
106  # define storage to catch the output
107  stdout = []
108  stderr = []
109 
110  # loop until the pipes close
111  while True:
112  reads = [p.stdout.fileno(), p.stderr.fileno()]
113  ret = select.select(reads, [], [])
114 
115  stdout_done = True
116  stderr_done = True
117 
118  for fd in ret[0]:
119  # if the data
120  if fd == p.stdout.fileno():
121  read = p.stdout.readline()
122  if read:
123  read = read.decode('utf-8').rstrip()
124  testDef.logger.verbose_print('stdout: ' + read)
125  if merge:
126  stderr.append(read)
127  else:
128  stdout.append(read)
129  stdout_done = False
130  elif fd == p.stderr.fileno():
131  read = p.stderr.readline()
132  if read:
133  read = read.decode('utf-8').rstrip()
134  testDef.logger.verbose_print('stderr: ' + read)
135  stderr.append(read)
136  stderr_done = False
137 
138  if stdout_done and stderr_done:
139  break
140 
141  if time_exec:
142  endtime = datetime.datetime.now()
143  elapsed_datetime = endtime - starttime
144  elapsed_secs = elapsed_datetime.total_seconds()
145 
146  testDef.logger.verbose_print("ExecuteCmd done%s" % (": elapsed=%s"%elapsed_datetime if time_exec else ""), \
147  timestamp=endtime if time_exec else None)
148 
149  p.wait()
150 
151  except OSError as e:
152  if p:
153  p.wait()
154  return (1, [], [str(e)], elapsed_secs)
155 
156  return (p.returncode,
157  stdout[-1 * stdoutlines:],
158  stderr[-1 * stderrlines:],
159  elapsed_secs)