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