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 11robot_env = 1 12try: 13 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 19if robot_env: 20 import gen_robot_print as grp 21 22 23############################################################################### 24def cmd_fnc(cmd_buf, 25 quiet=None, 26 test_mode=None, 27 debug=0, 28 print_output=1, 29 show_err=1, 30 return_stderr=0): 31 32 r""" 33 Run the given command in a shell and return the shell return code and the 34 output. 35 36 Description of arguments: 37 cmd_buf The command string to be run in a shell. 38 quiet Indicates whether this function should run 39 the print_issuing() function which prints 40 "Issuing: <cmd string>" to stdout. 41 test_mode If test_mode is set, this function will 42 not actually run the command. If 43 print_output is set, it will print 44 "(test_mode) Issuing: <cmd string>" to 45 stdout. 46 debug If debug is set, this function will print 47 extra debug info. 48 print_output If this is set, this function will print 49 the stdout/stderr generated by the shell 50 command. 51 show_err If show_err is set, this function will 52 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. 59 """ 60 61 # Determine default values. 62 quiet = int(gm.global_default(quiet, 0)) 63 test_mode = int(gm.global_default(test_mode, 0)) 64 65 if debug: 66 gp.print_vars(cmd_buf, quiet, test_mode, debug) 67 68 err_msg = gv.svalid_value(cmd_buf) 69 if err_msg != "": 70 raise ValueError(err_msg) 71 72 if not quiet: 73 gp.pissuing(cmd_buf, test_mode) 74 75 if test_mode: 76 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 86 87 sub_proc = subprocess.Popen(cmd_buf, 88 bufsize=1, 89 shell=True, 90 stdout=subprocess.PIPE, 91 stderr=stderr) 92 out_buf = "" 93 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) 102 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: 115 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 118 119 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 128 129############################################################################### 130 131 132############################################################################### 133def cmd_fnc_u(cmd_buf, 134 quiet=None, 135 debug=None, 136 print_output=1, 137 show_err=1, 138 return_stderr=0): 139 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, 147 print_output=print_output, show_err=show_err, 148 return_stderr=return_stderr) 149 150############################################################################### 151 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############################################################################### 255