blob: 8f0c249a365619c1f47e427e72edf9031eae7ac0 [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 r"""
Michael Walshcfe9fed2017-09-12 17:13:10 -050031 Run the given command in a shell and return the shell return code and the
32 output.
Michael Walshc3b512e2017-02-20 15:59:01 -060033
34 Description of arguments:
35 cmd_buf The command string to be run in a shell.
36 quiet Indicates whether this function should run
Michael Walshcfe9fed2017-09-12 17:13:10 -050037 the print_issuing() function which prints
38 "Issuing: <cmd string>" to stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060039 test_mode If test_mode is set, this function will
Michael Walshcfe9fed2017-09-12 17:13:10 -050040 not actually run the command. If
41 print_output is set, it will print
42 "(test_mode) Issuing: <cmd string>" to
43 stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060044 debug If debug is set, this function will print
45 extra debug info.
46 print_output If this is set, this function will print
Michael Walshcfe9fed2017-09-12 17:13:10 -050047 the stdout/stderr generated by the shell
48 command.
Michael Walshc3b512e2017-02-20 15:59:01 -060049 show_err If show_err is set, this function will
Michael Walshcfe9fed2017-09-12 17:13:10 -050050 print a standardized error report if the
51 shell command returns non-zero.
52 return_stderr If return_stderr is set, this function
53 will process the stdout and stderr streams
54 from the shell command separately. It
55 will also return stderr in addition to the
56 return code and the stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060057 """
58
Michael Walshcfe9fed2017-09-12 17:13:10 -050059 # Determine default values.
Michael Walshc3b512e2017-02-20 15:59:01 -060060 quiet = int(gm.global_default(quiet, 0))
61 test_mode = int(gm.global_default(test_mode, 0))
Michael Walshc3b512e2017-02-20 15:59:01 -060062
63 if debug:
Michael Walshafc53a22017-04-12 15:52:28 -050064 gp.print_vars(cmd_buf, quiet, test_mode, debug)
Michael Walshc3b512e2017-02-20 15:59:01 -060065
66 err_msg = gv.svalid_value(cmd_buf)
67 if err_msg != "":
68 raise ValueError(err_msg)
69
70 if not quiet:
Michael Walshafc53a22017-04-12 15:52:28 -050071 gp.pissuing(cmd_buf, test_mode)
Michael Walshc3b512e2017-02-20 15:59:01 -060072
73 if test_mode:
Michael Walshcfe9fed2017-09-12 17:13:10 -050074 if return_stderr:
75 return 0, "", ""
76 else:
77 return 0, ""
78
79 if return_stderr:
80 err_buf = ""
81 stderr = subprocess.PIPE
82 else:
83 stderr = subprocess.STDOUT
Michael Walshc3b512e2017-02-20 15:59:01 -060084
85 sub_proc = subprocess.Popen(cmd_buf,
86 bufsize=1,
87 shell=True,
88 stdout=subprocess.PIPE,
Michael Walshcfe9fed2017-09-12 17:13:10 -050089 stderr=stderr)
Michael Walshc3b512e2017-02-20 15:59:01 -060090 out_buf = ""
Michael Walshcfe9fed2017-09-12 17:13:10 -050091 if return_stderr:
92 for line in sub_proc.stderr:
93 err_buf += line
94 if not print_output:
95 continue
96 if robot_env:
97 grp.rprint(line)
98 else:
99 sys.stdout.write(line)
Michael Walshc3b512e2017-02-20 15:59:01 -0600100 for line in sub_proc.stdout:
101 out_buf += line
102 if not print_output:
103 continue
104 if robot_env:
105 grp.rprint(line)
106 else:
107 sys.stdout.write(line)
108 if print_output and not robot_env:
109 sys.stdout.flush()
110 sub_proc.communicate()
111 shell_rc = sub_proc.returncode
Michael Walsha3e2f532018-01-10 13:43:42 -0600112 if shell_rc != 0:
113 err_msg = "The prior shell command failed.\n"
114 err_msg += gp.sprint_var(shell_rc, 1)
Michael Walshcfe9fed2017-09-12 17:13:10 -0500115 if not print_output:
116 err_msg += "out_buf:\n" + out_buf
Michael Walshc3b512e2017-02-20 15:59:01 -0600117
Michael Walsha3e2f532018-01-10 13:43:42 -0600118 if show_err:
119 if robot_env:
120 grp.rprint_error_report(err_msg)
121 else:
122 gp.print_error_report(err_msg)
123 if not ignore_err:
124 if robot_env:
125 BuiltIn().fail(err_msg)
126 else:
127 raise ValueError(err_msg)
Michael Walshcfe9fed2017-09-12 17:13:10 -0500128
129 if return_stderr:
130 return shell_rc, out_buf, err_buf
131 else:
132 return shell_rc, out_buf
Michael Walshc3b512e2017-02-20 15:59:01 -0600133
Michael Walshc3b512e2017-02-20 15:59:01 -0600134
Michael Walshc3b512e2017-02-20 15:59:01 -0600135def cmd_fnc_u(cmd_buf,
136 quiet=None,
137 debug=None,
138 print_output=1,
Michael Walshcfe9fed2017-09-12 17:13:10 -0500139 show_err=1,
Michael Walsha3e2f532018-01-10 13:43:42 -0600140 return_stderr=0,
141 ignore_err=1):
Michael Walshc3b512e2017-02-20 15:59:01 -0600142 r"""
143 Call cmd_fnc with test_mode=0. See cmd_fnc (above) for details.
144
145 Note the "u" in "cmd_fnc_u" stands for "unconditional".
146 """
147
148 return cmd_fnc(cmd_buf, test_mode=0, quiet=quiet, debug=debug,
Michael Walshcfe9fed2017-09-12 17:13:10 -0500149 print_output=print_output, show_err=show_err,
Michael Walsha3e2f532018-01-10 13:43:42 -0600150 return_stderr=return_stderr, ignore_err=ignore_err)
Michael Walshc3b512e2017-02-20 15:59:01 -0600151
Michael Walshf41fac82017-08-02 15:05:24 -0500152
Michael Walshf41fac82017-08-02 15:05:24 -0500153def parse_command_string(command_string):
Michael Walshf41fac82017-08-02 15:05:24 -0500154 r"""
155 Parse a bash command-line command string and return the result as a
156 dictionary of parms.
157
158 This can be useful for answering questions like "What did the user specify
159 as the value for parm x in the command string?".
160
161 This function expects the command string to follow the following posix
162 conventions:
163 - Short parameters:
164 -<parm name><space><arg value>
165 - Long parameters:
166 --<parm name>=<arg value>
167
168 The first item in the string will be considered to be the command. All
169 values not conforming to the specifications above will be considered
170 positional parms. If there are multiple parms with the same name, they
171 will be put into a list (see illustration below where "-v" is specified
172 multiple times).
173
174 Description of argument(s):
175 command_string The complete command string including all
176 parameters and arguments.
177
178 Sample input:
179
180 robot_cmd_buf: robot -v
181 OPENBMC_HOST:dummy1 -v keyword_string:'Set Auto Reboot no' -v
182 lib_file_path:/home/user1/git/openbmc-test-automation/lib/utils.robot -v
183 quiet:0 -v test_mode:0 -v debug:0
184 --outputdir='/home/user1/status/children/'
185 --output=dummy1.Auto_reboot.170802.124544.output.xml
186 --log=dummy1.Auto_reboot.170802.124544.log.html
187 --report=dummy1.Auto_reboot.170802.124544.report.html
188 /home/user1/git/openbmc-test-automation/extended/run_keyword.robot
189
190 Sample output:
191
192 robot_cmd_buf_dict:
193 robot_cmd_buf_dict[command]: robot
194 robot_cmd_buf_dict[v]:
195 robot_cmd_buf_dict[v][0]: OPENBMC_HOST:dummy1
196 robot_cmd_buf_dict[v][1]: keyword_string:Set Auto
197 Reboot no
198 robot_cmd_buf_dict[v][2]:
199 lib_file_path:/home/user1/git/openbmc-test-automation/lib/utils.robot
200 robot_cmd_buf_dict[v][3]: quiet:0
201 robot_cmd_buf_dict[v][4]: test_mode:0
202 robot_cmd_buf_dict[v][5]: debug:0
203 robot_cmd_buf_dict[outputdir]:
204 /home/user1/status/children/
205 robot_cmd_buf_dict[output]:
206 dummy1.Auto_reboot.170802.124544.output.xml
207 robot_cmd_buf_dict[log]:
208 dummy1.Auto_reboot.170802.124544.log.html
209 robot_cmd_buf_dict[report]:
210 dummy1.Auto_reboot.170802.124544.report.html
211 robot_cmd_buf_dict[positional]:
212 /home/user1/git/openbmc-test-automation/extended/run_keyword.robot
213 """
214
215 # We want the parms in the string broken down the way bash would do it,
216 # so we'll call upon bash to do that by creating a simple inline bash
217 # function.
218 bash_func_def = "function parse { for parm in \"${@}\" ; do" +\
219 " echo $parm ; done ; }"
220
221 rc, outbuf = cmd_fnc_u(bash_func_def + " ; parse " + command_string,
222 quiet=1, print_output=0)
223 command_string_list = outbuf.rstrip("\n").split("\n")
224
225 command_string_dict = collections.OrderedDict()
226 ix = 1
227 command_string_dict['command'] = command_string_list[0]
228 while ix < len(command_string_list):
229 if command_string_list[ix].startswith("--"):
230 key, value = command_string_list[ix].split("=")
231 key = key.lstrip("-")
232 elif command_string_list[ix].startswith("-"):
233 key = command_string_list[ix].lstrip("-")
234 ix += 1
235 try:
236 value = command_string_list[ix]
237 except IndexError:
238 value = ""
239 else:
240 key = 'positional'
241 value = command_string_list[ix]
242 if key in command_string_dict:
243 if type(command_string_dict[key]) is str:
244 command_string_dict[key] = [command_string_dict[key]]
245 command_string_dict[key].append(value)
246 else:
247 command_string_dict[key] = value
248 ix += 1
249
250 return command_string_dict