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