1#!/usr/bin/env python
2
3r"""
4This module provides functions which are useful for running plug-ins from a
5robot program.
6"""
7
8import sys
9import subprocess
10from robot.libraries.BuiltIn import BuiltIn
11import commands
12import os
13import tempfile
14
15import gen_print as gp
16import gen_robot_print as grp
17import gen_misc as gm
18
19
20###############################################################################
21def rvalidate_plug_ins(plug_in_dir_paths,
22                       quiet=1):
23
24    r"""
25    Call the external validate_plug_ins.py program which validates the plug-in
26    dir paths given to it.  Return a list containing a normalized path for
27    each plug-in selected.
28
29    Description of arguments:
30    plug_in_dir_paths               A colon-separated list of plug-in
31                                    directory paths.
32    quiet                           If quiet is set to 1, this function will
33                                    NOT write status messages to stdout.
34    """
35
36    cmd_buf = "validate_plug_ins.py \"" + plug_in_dir_paths + "\""
37    if int(quiet) != 1:
38        grp.rpissuing(cmd_buf)
39    rc, out_buf = commands.getstatusoutput(cmd_buf)
40    if rc != 0:
41        message = gp.sprint_varx("rc", rc, 1) + out_buf
42        grp.rprintn(out_buf, 'STDERR')
43        BuiltIn().fail(gp.sprint_error("Validate plug ins call failed.  See" +
44                                       " stderr text for details.\n"))
45
46    plug_in_packages_list = out_buf.split("\n")
47    if len(plug_in_packages_list) == 1 and plug_in_packages_list[0] == "":
48        return []
49
50    return plug_in_packages_list
51
52###############################################################################
53
54
55###############################################################################
56def rprocess_plug_in_packages(plug_in_packages_list=None,
57                              call_point="setup",
58                              shell_rc="0x00000000",
59                              stop_on_plug_in_failure=1,
60                              stop_on_non_zero_rc=0,
61                              release_type="obmc",
62                              quiet=None,
63                              debug=None):
64
65    r"""
66    Call the external process_plug_in_packages.py to process the plug-in
67    packages.  Return the following:
68    rc                              The return code - 0 = PASS, 1 = FAIL.
69    shell_rc                        The shell return code returned by
70                                    process_plug_in_packages.py.
71    failed_plug_in_name             The failed plug in name (if any).
72
73    Description of arguments:
74    plug_in_packages_list           A python list of plug-in directory paths.
75    call_point                      The call point program to be called for
76                                    each plug-in package (e.g. post_boot).
77                                    This name should not include the "cp_"
78                                    prefix.
79    shell_rc                        The user may supply a value other than
80                                    zero to indicate an acceptable non-zero
81                                    return code.  For example, if this value
82                                    equals 0x00000200, it means that for each
83                                    plug-in call point that runs, a 0x00000200
84                                    will not be counted as a failure.
85    stop_on_plug_in_failure         If this parameter is set to 1, this
86                                    program will stop and return non-zero if
87                                    the call point program from any plug-in
88                                    directory fails.  Conversely, if it is set
89                                    to false, this program will run the call
90                                    point program from each and every plug-in
91                                    directory regardless of their return
92                                    values.  Typical example cases where you'd
93                                    want to run all plug-in call points
94                                    regardless of success or failure would be
95                                    "cleanup" or "ffdc" call points.
96    stop_on_non_zero_rc             If this parm is set to 1 and a plug-in
97                                    call point program returns a valid
98                                    non-zero return code (see "shell_rc" parm
99                                    above), this program will stop processing
100                                    and return 0 (success).  Since this
101                                    constitutes a successful exit, this would
102                                    normally be used where the caller wishes
103                                    to stop processing if one of the plug-in
104                                    directory call point programs returns a
105                                    special value indicating that some special
106                                    case has been found.  An example might be
107                                    in calling some kind of "check_errl" call
108                                    point program.  Such a call point program
109                                    might return a 2 (i.e. 0x00000200) to
110                                    indicate that a given error log entry was
111                                    found in an "ignore" list and is therefore
112                                    to be ignored.  That being the case, no
113                                    other "check_errl" call point program
114                                    would need to be called.
115    release_type                    The type of release being tested (e.g.
116                                    "obmc", "op", "fips").  This influences
117                                    which integrated plug-ins are selected.
118    quiet                           If quiet is set to 1, this function will
119                                    NOT write status messages to stdout.  This
120                                    will default to the global quiet program
121                                    parm or to 0.
122    debug                           If this parameter is set to 1, this
123                                    function will print additional debug
124                                    information.  This is mainly to be used by
125                                    the developer of this function.  This will
126                                    default to the global quiet program parm
127                                    or to 0.
128    """
129
130    rc = 0
131
132    if plug_in_packages_list is None:
133        plug_in_packages_list = BuiltIn().get_variable_value(
134            "${plug_in_packages_list}")
135
136    # If there are no plug-in packages to process, return successfully.
137    if len(plug_in_packages_list) == 0:
138        return 0, 0, ""
139
140    if quiet is None:
141        try:
142            quiet = int(BuiltIn().get_variable_value("${quiet}"))
143        except TypeError:
144            quiet = 0
145
146    if debug is None:
147        try:
148            debug = int(BuiltIn().get_variable_value("${debug}"))
149        except TypeError:
150            debug = 0
151
152    # Create string from list.
153    plug_in_dir_paths = ':'.join(plug_in_packages_list)
154
155    temp = tempfile.NamedTemporaryFile()
156    temp_file_path = temp.name
157    temp2 = tempfile.NamedTemporaryFile()
158    temp_properties_file_path = temp2.name
159
160    if int(debug) == 1:
161        os.environ["PERF_TRACE"] = "1"
162        debug_string = " --quiet=0"
163    else:
164        debug_string = ""
165
166    loc_shell_rc = 0
167
168    sub_cmd_buf = "process_plug_in_packages.py" + debug_string +\
169                  " --call_point=" + call_point + " --shell_rc=" +\
170                  str(shell_rc) + " --stop_on_plug_in_failure=" +\
171                  str(stop_on_plug_in_failure) + " --stop_on_non_zero_rc=" +\
172                  str(stop_on_non_zero_rc) + " " + plug_in_dir_paths
173    if int(quiet) == 1:
174        cmd_buf = sub_cmd_buf + " > " + temp_file_path + " 2>&1"
175    else:
176        cmd_buf = "set -o pipefail ; " + sub_cmd_buf + " 2>&1 | tee " +\
177                  temp_file_path
178
179        if int(debug) == 1:
180            grp.rpissuing(cmd_buf)
181        else:
182            grp.rprint_timen("Processing " + call_point +
183                             " call point programs.")
184
185    proc_plug_pkg_rc = subprocess.call(cmd_buf, shell=True)
186
187    # As process_plug_in_packages.py help text states, it will print the
188    # values of failed_plug_in_name and shell_rc in the following format:
189    # failed_plug_in_name:               <failed plug-in value, if any>
190    # shell_rc:                          <shell return code value of last
191    # call point program>
192
193    # We want to obtain those values from the output.  To make the task
194    # simpler, we'll start by grepping the output for lines that might fit
195    # such a format:
196    # - Zero or more spaces
197    # - One or more non-colon characters
198    # - A colon
199    # - Zero or more spaces
200    # I added code to exclude any line that starts with spaces and a
201    # left-square bracket (e.g. '[ ERROR ]').  These would be interpreted by
202    # my_parm_file as a new section.
203    cmd_buf = "egrep '^[ ]*[^:]+:[ ]*' " + temp_file_path +\
204              " | egrep -v '^[ ]*\[' > " + temp_properties_file_path
205    if int(debug) == 1:
206        grp.rpissuing(cmd_buf)
207    gen_rc = os.system(cmd_buf)
208
209    # Next we call my_parm_file to create a properties dictionary.
210    properties = gm.my_parm_file(temp_properties_file_path)
211
212    # Finally, we access the 2 values that we need.
213    try:
214        shell_rc = int(properties['shell_rc'], 16)
215    except KeyError:
216        shell_rc = 0
217    try:
218        failed_plug_in_name = properties['failed_plug_in_name']
219    except KeyError:
220        failed_plug_in_name = ""
221
222    if gen_rc != 0 or proc_plug_pkg_rc != 0:
223        hex = 1
224        grp.rprint_error("Call to process_plug_in_packages failed.\n")
225        grp.rprint_varx("gen_rc", gen_rc, hex)
226        grp.rprint_varx("proc_plug_pkg_rc", proc_plug_pkg_rc, hex)
227        grp.rprint_varx("failed_plug_in_name", failed_plug_in_name)
228        grp.rprint_varx("shell_rc", shell_rc, hex)
229        rc = 1
230
231    return rc, shell_rc, failed_plug_in_name
232
233###############################################################################
234