blob: 57cd65bc62b897fafa549e21058add4aadbdbb09 [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
11robot_env = 1
12try:
Michael Walshc3b512e2017-02-20 15:59:01 -060013 from robot.libraries.BuiltIn import BuiltIn
14except ImportError:
15 robot_env = 0
16import gen_print as gp
17import gen_valid as gv
18import gen_misc as gm
Michael Walshafc53a22017-04-12 15:52:28 -050019if robot_env:
20 import gen_robot_print as grp
Michael Walshc3b512e2017-02-20 15:59:01 -060021
22
23###############################################################################
24def cmd_fnc(cmd_buf,
25 quiet=None,
26 test_mode=None,
Michael Walshafc53a22017-04-12 15:52:28 -050027 debug=0,
Michael Walshc3b512e2017-02-20 15:59:01 -060028 print_output=1,
Michael Walshcfe9fed2017-09-12 17:13:10 -050029 show_err=1,
30 return_stderr=0):
Michael Walshc3b512e2017-02-20 15:59:01 -060031
32 r"""
Michael Walshcfe9fed2017-09-12 17:13:10 -050033 Run the given command in a shell and return the shell return code and the
34 output.
Michael Walshc3b512e2017-02-20 15:59:01 -060035
36 Description of arguments:
37 cmd_buf The command string to be run in a shell.
38 quiet Indicates whether this function should run
Michael Walshcfe9fed2017-09-12 17:13:10 -050039 the print_issuing() function which prints
40 "Issuing: <cmd string>" to stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060041 test_mode If test_mode is set, this function will
Michael Walshcfe9fed2017-09-12 17:13:10 -050042 not actually run the command. If
43 print_output is set, it will print
44 "(test_mode) Issuing: <cmd string>" to
45 stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060046 debug If debug is set, this function will print
47 extra debug info.
48 print_output If this is set, this function will print
Michael Walshcfe9fed2017-09-12 17:13:10 -050049 the stdout/stderr generated by the shell
50 command.
Michael Walshc3b512e2017-02-20 15:59:01 -060051 show_err If show_err is set, this function will
Michael Walshcfe9fed2017-09-12 17:13:10 -050052 print a standardized error report if the
53 shell command returns non-zero.
54 return_stderr If return_stderr is set, this function
55 will process the stdout and stderr streams
56 from the shell command separately. It
57 will also return stderr in addition to the
58 return code and the stdout.
Michael Walshc3b512e2017-02-20 15:59:01 -060059 """
60
Michael Walshcfe9fed2017-09-12 17:13:10 -050061 # Determine default values.
Michael Walshc3b512e2017-02-20 15:59:01 -060062 quiet = int(gm.global_default(quiet, 0))
63 test_mode = int(gm.global_default(test_mode, 0))
Michael Walshc3b512e2017-02-20 15:59:01 -060064
65 if debug:
Michael Walshafc53a22017-04-12 15:52:28 -050066 gp.print_vars(cmd_buf, quiet, test_mode, debug)
Michael Walshc3b512e2017-02-20 15:59:01 -060067
68 err_msg = gv.svalid_value(cmd_buf)
69 if err_msg != "":
70 raise ValueError(err_msg)
71
72 if not quiet:
Michael Walshafc53a22017-04-12 15:52:28 -050073 gp.pissuing(cmd_buf, test_mode)
Michael Walshc3b512e2017-02-20 15:59:01 -060074
75 if test_mode:
Michael Walshcfe9fed2017-09-12 17:13:10 -050076 if return_stderr:
77 return 0, "", ""
78 else:
79 return 0, ""
80
81 if return_stderr:
82 err_buf = ""
83 stderr = subprocess.PIPE
84 else:
85 stderr = subprocess.STDOUT
Michael Walshc3b512e2017-02-20 15:59:01 -060086
87 sub_proc = subprocess.Popen(cmd_buf,
88 bufsize=1,
89 shell=True,
90 stdout=subprocess.PIPE,
Michael Walshcfe9fed2017-09-12 17:13:10 -050091 stderr=stderr)
Michael Walshc3b512e2017-02-20 15:59:01 -060092 out_buf = ""
Michael Walshcfe9fed2017-09-12 17:13:10 -050093 if return_stderr:
94 for line in sub_proc.stderr:
95 err_buf += line
96 if not print_output:
97 continue
98 if robot_env:
99 grp.rprint(line)
100 else:
101 sys.stdout.write(line)
Michael Walshc3b512e2017-02-20 15:59:01 -0600102 for line in sub_proc.stdout:
103 out_buf += line
104 if not print_output:
105 continue
106 if robot_env:
107 grp.rprint(line)
108 else:
109 sys.stdout.write(line)
110 if print_output and not robot_env:
111 sys.stdout.flush()
112 sub_proc.communicate()
113 shell_rc = sub_proc.returncode
114 if shell_rc != 0 and show_err:
Michael Walshcfe9fed2017-09-12 17:13:10 -0500115 err_msg = "The prior command failed.\n" + gp.sprint_var(shell_rc, 1)
116 if not print_output:
117 err_msg += "out_buf:\n" + out_buf
Michael Walshc3b512e2017-02-20 15:59:01 -0600118
Michael Walshcfe9fed2017-09-12 17:13:10 -0500119 if robot_env:
120 grp.rprint_error_report(err_msg)
121 else:
122 gp.print_error_report(err_msg)
123
124 if return_stderr:
125 return shell_rc, out_buf, err_buf
126 else:
127 return shell_rc, out_buf
Michael Walshc3b512e2017-02-20 15:59:01 -0600128
129###############################################################################
130
131
132###############################################################################
133def cmd_fnc_u(cmd_buf,
134 quiet=None,
135 debug=None,
136 print_output=1,
Michael Walshcfe9fed2017-09-12 17:13:10 -0500137 show_err=1,
138 return_stderr=0):
Michael Walshc3b512e2017-02-20 15:59:01 -0600139
140 r"""
141 Call cmd_fnc with test_mode=0. See cmd_fnc (above) for details.
142
143 Note the "u" in "cmd_fnc_u" stands for "unconditional".
144 """
145
146 return cmd_fnc(cmd_buf, test_mode=0, quiet=quiet, debug=debug,
Michael Walshcfe9fed2017-09-12 17:13:10 -0500147 print_output=print_output, show_err=show_err,
148 return_stderr=return_stderr)
Michael Walshc3b512e2017-02-20 15:59:01 -0600149
150###############################################################################
Michael Walshf41fac82017-08-02 15:05:24 -0500151
152
153###############################################################################
154def parse_command_string(command_string):
155
156 r"""
157 Parse a bash command-line command string and return the result as a
158 dictionary of parms.
159
160 This can be useful for answering questions like "What did the user specify
161 as the value for parm x in the command string?".
162
163 This function expects the command string to follow the following posix
164 conventions:
165 - Short parameters:
166 -<parm name><space><arg value>
167 - Long parameters:
168 --<parm name>=<arg value>
169
170 The first item in the string will be considered to be the command. All
171 values not conforming to the specifications above will be considered
172 positional parms. If there are multiple parms with the same name, they
173 will be put into a list (see illustration below where "-v" is specified
174 multiple times).
175
176 Description of argument(s):
177 command_string The complete command string including all
178 parameters and arguments.
179
180 Sample input:
181
182 robot_cmd_buf: robot -v
183 OPENBMC_HOST:dummy1 -v keyword_string:'Set Auto Reboot no' -v
184 lib_file_path:/home/user1/git/openbmc-test-automation/lib/utils.robot -v
185 quiet:0 -v test_mode:0 -v debug:0
186 --outputdir='/home/user1/status/children/'
187 --output=dummy1.Auto_reboot.170802.124544.output.xml
188 --log=dummy1.Auto_reboot.170802.124544.log.html
189 --report=dummy1.Auto_reboot.170802.124544.report.html
190 /home/user1/git/openbmc-test-automation/extended/run_keyword.robot
191
192 Sample output:
193
194 robot_cmd_buf_dict:
195 robot_cmd_buf_dict[command]: robot
196 robot_cmd_buf_dict[v]:
197 robot_cmd_buf_dict[v][0]: OPENBMC_HOST:dummy1
198 robot_cmd_buf_dict[v][1]: keyword_string:Set Auto
199 Reboot no
200 robot_cmd_buf_dict[v][2]:
201 lib_file_path:/home/user1/git/openbmc-test-automation/lib/utils.robot
202 robot_cmd_buf_dict[v][3]: quiet:0
203 robot_cmd_buf_dict[v][4]: test_mode:0
204 robot_cmd_buf_dict[v][5]: debug:0
205 robot_cmd_buf_dict[outputdir]:
206 /home/user1/status/children/
207 robot_cmd_buf_dict[output]:
208 dummy1.Auto_reboot.170802.124544.output.xml
209 robot_cmd_buf_dict[log]:
210 dummy1.Auto_reboot.170802.124544.log.html
211 robot_cmd_buf_dict[report]:
212 dummy1.Auto_reboot.170802.124544.report.html
213 robot_cmd_buf_dict[positional]:
214 /home/user1/git/openbmc-test-automation/extended/run_keyword.robot
215 """
216
217 # We want the parms in the string broken down the way bash would do it,
218 # so we'll call upon bash to do that by creating a simple inline bash
219 # function.
220 bash_func_def = "function parse { for parm in \"${@}\" ; do" +\
221 " echo $parm ; done ; }"
222
223 rc, outbuf = cmd_fnc_u(bash_func_def + " ; parse " + command_string,
224 quiet=1, print_output=0)
225 command_string_list = outbuf.rstrip("\n").split("\n")
226
227 command_string_dict = collections.OrderedDict()
228 ix = 1
229 command_string_dict['command'] = command_string_list[0]
230 while ix < len(command_string_list):
231 if command_string_list[ix].startswith("--"):
232 key, value = command_string_list[ix].split("=")
233 key = key.lstrip("-")
234 elif command_string_list[ix].startswith("-"):
235 key = command_string_list[ix].lstrip("-")
236 ix += 1
237 try:
238 value = command_string_list[ix]
239 except IndexError:
240 value = ""
241 else:
242 key = 'positional'
243 value = command_string_list[ix]
244 if key in command_string_dict:
245 if type(command_string_dict[key]) is str:
246 command_string_dict[key] = [command_string_dict[key]]
247 command_string_dict[key].append(value)
248 else:
249 command_string_dict[key] = value
250 ix += 1
251
252 return command_string_dict
253
254###############################################################################