blob: deb2fd75fba7a4eb923272329e9316b1a80c6c75 [file] [log] [blame]
Michael Walshc3b512e2017-02-20 15:59:01 -06001#!/usr/bin/env python
2
3r"""
4This module provides command execution functions such as cmd_fnc and cmd_fnc_u.
5"""
6
7import sys
8import subprocess
Michael Walshf41fac82017-08-02 15:05:24 -05009import collections
Michael Walshc3b512e2017-02-20 15:59:01 -060010
Michael Walshc3b512e2017-02-20 15:59:01 -060011import gen_print as gp
12import gen_valid as gv
13import gen_misc as gm
Michael Walshd6901502017-11-14 12:58:37 -060014
15robot_env = gp.robot_env
16
Michael Walshafc53a22017-04-12 15:52:28 -050017if robot_env:
18 import gen_robot_print as grp
Michael Walsha3e2f532018-01-10 13:43:42 -060019 from robot.libraries.BuiltIn import BuiltIn
Michael Walshc3b512e2017-02-20 15:59:01 -060020
21
Michael Walshc3b512e2017-02-20 15:59:01 -060022def cmd_fnc(cmd_buf,
23 quiet=None,
24 test_mode=None,
Michael Walshafc53a22017-04-12 15:52:28 -050025 debug=0,
Michael Walshc3b512e2017-02-20 15:59:01 -060026 print_output=1,
Michael Walshcfe9fed2017-09-12 17:13:10 -050027 show_err=1,
Michael Walsha3e2f532018-01-10 13:43:42 -060028 return_stderr=0,
29 ignore_err=1):
Michael Walshc3b512e2017-02-20 15:59:01 -060030
31 r"""
Michael Walshcfe9fed2017-09-12 17:13:10 -050032 Run the given command in a shell and return the shell return code and the
33 output.
Michael Walshc3b512e2017-02-20 15:59:01 -060034
35 Description of arguments:
36 cmd_buf The command string to be run in a shell.
37 quiet Indicates whether this function should run
Michael Walshcfe9fed2017-09-12 17:13:10 -050038 the print_issuing() function which prints
39 "Issuing: <cmd string>" to stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060040 test_mode If test_mode is set, this function will
Michael Walshcfe9fed2017-09-12 17:13:10 -050041 not actually run the command. If
42 print_output is set, it will print
43 "(test_mode) Issuing: <cmd string>" to
44 stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060045 debug If debug is set, this function will print
46 extra debug info.
47 print_output If this is set, this function will print
Michael Walshcfe9fed2017-09-12 17:13:10 -050048 the stdout/stderr generated by the shell
49 command.
Michael Walshc3b512e2017-02-20 15:59:01 -060050 show_err If show_err is set, this function will
Michael Walshcfe9fed2017-09-12 17:13:10 -050051 print a standardized error report if the
52 shell command returns non-zero.
53 return_stderr If return_stderr is set, this function
54 will process the stdout and stderr streams
55 from the shell command separately. It
56 will also return stderr in addition to the
57 return code and the stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060058 """
59
Michael Walshcfe9fed2017-09-12 17:13:10 -050060 # Determine default values.
Michael Walshc3b512e2017-02-20 15:59:01 -060061 quiet = int(gm.global_default(quiet, 0))
62 test_mode = int(gm.global_default(test_mode, 0))
Michael Walshc3b512e2017-02-20 15:59:01 -060063
64 if debug:
Michael Walshafc53a22017-04-12 15:52:28 -050065 gp.print_vars(cmd_buf, quiet, test_mode, debug)
Michael Walshc3b512e2017-02-20 15:59:01 -060066
67 err_msg = gv.svalid_value(cmd_buf)
68 if err_msg != "":
69 raise ValueError(err_msg)
70
71 if not quiet:
Michael Walshafc53a22017-04-12 15:52:28 -050072 gp.pissuing(cmd_buf, test_mode)
Michael Walshc3b512e2017-02-20 15:59:01 -060073
74 if test_mode:
Michael Walshcfe9fed2017-09-12 17:13:10 -050075 if return_stderr:
76 return 0, "", ""
77 else:
78 return 0, ""
79
80 if return_stderr:
81 err_buf = ""
82 stderr = subprocess.PIPE
83 else:
84 stderr = subprocess.STDOUT
Michael Walshc3b512e2017-02-20 15:59:01 -060085
86 sub_proc = subprocess.Popen(cmd_buf,
87 bufsize=1,
88 shell=True,
89 stdout=subprocess.PIPE,
Michael Walshcfe9fed2017-09-12 17:13:10 -050090 stderr=stderr)
Michael Walshc3b512e2017-02-20 15:59:01 -060091 out_buf = ""
Michael Walshcfe9fed2017-09-12 17:13:10 -050092 if return_stderr:
93 for line in sub_proc.stderr:
94 err_buf += line
95 if not print_output:
96 continue
97 if robot_env:
98 grp.rprint(line)
99 else:
100 sys.stdout.write(line)
Michael Walshc3b512e2017-02-20 15:59:01 -0600101 for line in sub_proc.stdout:
102 out_buf += line
103 if not print_output:
104 continue
105 if robot_env:
106 grp.rprint(line)
107 else:
108 sys.stdout.write(line)
109 if print_output and not robot_env:
110 sys.stdout.flush()
111 sub_proc.communicate()
112 shell_rc = sub_proc.returncode
Michael Walsha3e2f532018-01-10 13:43:42 -0600113 if shell_rc != 0:
114 err_msg = "The prior shell command failed.\n"
115 err_msg += gp.sprint_var(shell_rc, 1)
Michael Walshcfe9fed2017-09-12 17:13:10 -0500116 if not print_output:
117 err_msg += "out_buf:\n" + out_buf
Michael Walshc3b512e2017-02-20 15:59:01 -0600118
Michael Walsha3e2f532018-01-10 13:43:42 -0600119 if show_err:
120 if robot_env:
121 grp.rprint_error_report(err_msg)
122 else:
123 gp.print_error_report(err_msg)
124 if not ignore_err:
125 if robot_env:
126 BuiltIn().fail(err_msg)
127 else:
128 raise ValueError(err_msg)
Michael Walshcfe9fed2017-09-12 17:13:10 -0500129
130 if return_stderr:
131 return shell_rc, out_buf, err_buf
132 else:
133 return shell_rc, out_buf
Michael Walshc3b512e2017-02-20 15:59:01 -0600134
Michael Walshc3b512e2017-02-20 15:59:01 -0600135
Michael Walshc3b512e2017-02-20 15:59:01 -0600136def cmd_fnc_u(cmd_buf,
137 quiet=None,
138 debug=None,
139 print_output=1,
Michael Walshcfe9fed2017-09-12 17:13:10 -0500140 show_err=1,
Michael Walsha3e2f532018-01-10 13:43:42 -0600141 return_stderr=0,
142 ignore_err=1):
Michael Walshc3b512e2017-02-20 15:59:01 -0600143
144 r"""
145 Call cmd_fnc with test_mode=0. See cmd_fnc (above) for details.
146
147 Note the "u" in "cmd_fnc_u" stands for "unconditional".
148 """
149
150 return cmd_fnc(cmd_buf, test_mode=0, quiet=quiet, debug=debug,
Michael Walshcfe9fed2017-09-12 17:13:10 -0500151 print_output=print_output, show_err=show_err,
Michael Walsha3e2f532018-01-10 13:43:42 -0600152 return_stderr=return_stderr, ignore_err=ignore_err)
Michael Walshc3b512e2017-02-20 15:59:01 -0600153
Michael Walshf41fac82017-08-02 15:05:24 -0500154
Michael Walshf41fac82017-08-02 15:05:24 -0500155def parse_command_string(command_string):
156
157 r"""
158 Parse a bash command-line command string and return the result as a
159 dictionary of parms.
160
161 This can be useful for answering questions like "What did the user specify
162 as the value for parm x in the command string?".
163
164 This function expects the command string to follow the following posix
165 conventions:
166 - Short parameters:
167 -<parm name><space><arg value>
168 - Long parameters:
169 --<parm name>=<arg value>
170
171 The first item in the string will be considered to be the command. All
172 values not conforming to the specifications above will be considered
173 positional parms. If there are multiple parms with the same name, they
174 will be put into a list (see illustration below where "-v" is specified
175 multiple times).
176
177 Description of argument(s):
178 command_string The complete command string including all
179 parameters and arguments.
180
181 Sample input:
182
183 robot_cmd_buf: robot -v
184 OPENBMC_HOST:dummy1 -v keyword_string:'Set Auto Reboot no' -v
185 lib_file_path:/home/user1/git/openbmc-test-automation/lib/utils.robot -v
186 quiet:0 -v test_mode:0 -v debug:0
187 --outputdir='/home/user1/status/children/'
188 --output=dummy1.Auto_reboot.170802.124544.output.xml
189 --log=dummy1.Auto_reboot.170802.124544.log.html
190 --report=dummy1.Auto_reboot.170802.124544.report.html
191 /home/user1/git/openbmc-test-automation/extended/run_keyword.robot
192
193 Sample output:
194
195 robot_cmd_buf_dict:
196 robot_cmd_buf_dict[command]: robot
197 robot_cmd_buf_dict[v]:
198 robot_cmd_buf_dict[v][0]: OPENBMC_HOST:dummy1
199 robot_cmd_buf_dict[v][1]: keyword_string:Set Auto
200 Reboot no
201 robot_cmd_buf_dict[v][2]:
202 lib_file_path:/home/user1/git/openbmc-test-automation/lib/utils.robot
203 robot_cmd_buf_dict[v][3]: quiet:0
204 robot_cmd_buf_dict[v][4]: test_mode:0
205 robot_cmd_buf_dict[v][5]: debug:0
206 robot_cmd_buf_dict[outputdir]:
207 /home/user1/status/children/
208 robot_cmd_buf_dict[output]:
209 dummy1.Auto_reboot.170802.124544.output.xml
210 robot_cmd_buf_dict[log]:
211 dummy1.Auto_reboot.170802.124544.log.html
212 robot_cmd_buf_dict[report]:
213 dummy1.Auto_reboot.170802.124544.report.html
214 robot_cmd_buf_dict[positional]:
215 /home/user1/git/openbmc-test-automation/extended/run_keyword.robot
216 """
217
218 # We want the parms in the string broken down the way bash would do it,
219 # so we'll call upon bash to do that by creating a simple inline bash
220 # function.
221 bash_func_def = "function parse { for parm in \"${@}\" ; do" +\
222 " echo $parm ; done ; }"
223
224 rc, outbuf = cmd_fnc_u(bash_func_def + " ; parse " + command_string,
225 quiet=1, print_output=0)
226 command_string_list = outbuf.rstrip("\n").split("\n")
227
228 command_string_dict = collections.OrderedDict()
229 ix = 1
230 command_string_dict['command'] = command_string_list[0]
231 while ix < len(command_string_list):
232 if command_string_list[ix].startswith("--"):
233 key, value = command_string_list[ix].split("=")
234 key = key.lstrip("-")
235 elif command_string_list[ix].startswith("-"):
236 key = command_string_list[ix].lstrip("-")
237 ix += 1
238 try:
239 value = command_string_list[ix]
240 except IndexError:
241 value = ""
242 else:
243 key = 'positional'
244 value = command_string_list[ix]
245 if key in command_string_dict:
246 if type(command_string_dict[key]) is str:
247 command_string_dict[key] = [command_string_dict[key]]
248 command_string_dict[key].append(value)
249 else:
250 command_string_dict[key] = value
251 ix += 1
252
253 return command_string_dict