1#!/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 9import collections 10 11import gen_print as gp 12import gen_valid as gv 13import gen_misc as gm 14 15robot_env = gp.robot_env 16 17if robot_env: 18 import gen_robot_print as grp 19 from robot.libraries.BuiltIn import BuiltIn 20 21 22def cmd_fnc(cmd_buf, 23 quiet=None, 24 test_mode=None, 25 debug=0, 26 print_output=1, 27 show_err=1, 28 return_stderr=0, 29 ignore_err=1): 30 r""" 31 Run the given command in a shell and return the shell return code and the 32 output. 33 34 Description of arguments: 35 cmd_buf The command string to be run in a shell. 36 quiet Indicates whether this function should run 37 the print_issuing() function which prints 38 "Issuing: <cmd string>" to stdout. 39 test_mode If test_mode is set, this function will 40 not actually run the command. If 41 print_output is set, it will print 42 "(test_mode) Issuing: <cmd string>" to 43 stdout. 44 debug If debug is set, this function will print 45 extra debug info. 46 print_output If this is set, this function will print 47 the stdout/stderr generated by the shell 48 command. 49 show_err If show_err is set, this function will 50 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. 57 """ 58 59 # Determine default values. 60 quiet = int(gm.global_default(quiet, 0)) 61 test_mode = int(gm.global_default(test_mode, 0)) 62 63 if debug: 64 gp.print_vars(cmd_buf, quiet, test_mode, debug) 65 66 err_msg = gv.svalid_value(cmd_buf) 67 if err_msg != "": 68 raise ValueError(err_msg) 69 70 if not quiet: 71 gp.pissuing(cmd_buf, test_mode) 72 73 if test_mode: 74 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 84 85 sub_proc = subprocess.Popen(cmd_buf, 86 bufsize=1, 87 shell=True, 88 stdout=subprocess.PIPE, 89 stderr=stderr) 90 out_buf = "" 91 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) 100 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 112 if shell_rc != 0: 113 err_msg = "The prior shell command failed.\n" 114 err_msg += gp.sprint_var(shell_rc, 1) 115 if not print_output: 116 err_msg += "out_buf:\n" + out_buf 117 118 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) 128 129 if return_stderr: 130 return shell_rc, out_buf, err_buf 131 else: 132 return shell_rc, out_buf 133 134 135def cmd_fnc_u(cmd_buf, 136 quiet=None, 137 debug=None, 138 print_output=1, 139 show_err=1, 140 return_stderr=0, 141 ignore_err=1): 142 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, 149 print_output=print_output, show_err=show_err, 150 return_stderr=return_stderr, ignore_err=ignore_err) 151 152 153def parse_command_string(command_string): 154 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 251