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
18import gen_cmd as gc
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
53def rprocess_plug_in_packages(plug_in_packages_list=None,
54                              call_point="setup",
55                              shell_rc="0x00000000",
56                              stop_on_plug_in_failure=1,
57                              stop_on_non_zero_rc=0,
58                              release_type="obmc",
59                              quiet=None,
60                              debug=None):
61
62    r"""
63    Call the external process_plug_in_packages.py to process the plug-in
64    packages.  Return the following:
65    rc                              The return code - 0 = PASS, 1 = FAIL.
66    shell_rc                        The shell return code returned by
67                                    process_plug_in_packages.py.
68    failed_plug_in_name             The failed plug in name (if any).
69
70    Description of arguments:
71    plug_in_packages_list           A python list of plug-in directory paths.
72    call_point                      The call point program to be called for
73                                    each plug-in package (e.g. post_boot).
74                                    This name should not include the "cp_"
75                                    prefix.
76    shell_rc                        The user may supply a value other than
77                                    zero to indicate an acceptable non-zero
78                                    return code.  For example, if this value
79                                    equals 0x00000200, it means that for each
80                                    plug-in call point that runs, a 0x00000200
81                                    will not be counted as a failure.
82    stop_on_plug_in_failure         If this parameter is set to 1, this
83                                    program will stop and return non-zero if
84                                    the call point program from any plug-in
85                                    directory fails.  Conversely, if it is set
86                                    to false, this program will run the call
87                                    point program from each and every plug-in
88                                    directory regardless of their return
89                                    values.  Typical example cases where you'd
90                                    want to run all plug-in call points
91                                    regardless of success or failure would be
92                                    "cleanup" or "ffdc" call points.
93    stop_on_non_zero_rc             If this parm is set to 1 and a plug-in
94                                    call point program returns a valid
95                                    non-zero return code (see "shell_rc" parm
96                                    above), this program will stop processing
97                                    and return 0 (success).  Since this
98                                    constitutes a successful exit, this would
99                                    normally be used where the caller wishes
100                                    to stop processing if one of the plug-in
101                                    directory call point programs returns a
102                                    special value indicating that some special
103                                    case has been found.  An example might be
104                                    in calling some kind of "check_errl" call
105                                    point program.  Such a call point program
106                                    might return a 2 (i.e. 0x00000200) to
107                                    indicate that a given error log entry was
108                                    found in an "ignore" list and is therefore
109                                    to be ignored.  That being the case, no
110                                    other "check_errl" call point program
111                                    would need to be called.
112    release_type                    The type of release being tested (e.g.
113                                    "obmc", "op", "fips").  This influences
114                                    which integrated plug-ins are selected.
115    quiet                           If quiet is set to 1, this function will
116                                    NOT write status messages to stdout.  This
117                                    will default to the global quiet program
118                                    parm or to 0.
119    debug                           If this parameter is set to 1, this
120                                    function will print additional debug
121                                    information.  This is mainly to be used by
122                                    the developer of this function.  This will
123                                    default to the global quiet program parm
124                                    or to 0.
125    """
126
127    rc = 0
128
129    if plug_in_packages_list is None:
130        plug_in_packages_list = BuiltIn().get_variable_value(
131            "${plug_in_packages_list}")
132
133    # If there are no plug-in packages to process, return successfully.
134    if len(plug_in_packages_list) == 0:
135        return 0, 0, ""
136
137    if quiet is None:
138        try:
139            quiet = int(BuiltIn().get_variable_value("${quiet}"))
140        except TypeError:
141            quiet = 0
142
143    if debug is None:
144        try:
145            debug = int(BuiltIn().get_variable_value("${debug}"))
146        except TypeError:
147            debug = 0
148
149    # Create string from list.
150    plug_in_dir_paths = ':'.join(plug_in_packages_list)
151
152    temp = tempfile.NamedTemporaryFile()
153    temp_file_path = temp.name
154    temp2 = tempfile.NamedTemporaryFile()
155    temp_properties_file_path = temp2.name
156
157    if int(debug) == 1:
158        os.environ["PERF_TRACE"] = "1"
159        debug_string = " --quiet=0"
160    else:
161        debug_string = ""
162
163    loc_shell_rc = 0
164
165    sub_cmd_buf = "process_plug_in_packages.py" + debug_string +\
166                  " --call_point=" + call_point + " --allow_shell_rc=" +\
167                  str(shell_rc) + " --stop_on_plug_in_failure=" +\
168                  str(stop_on_plug_in_failure) + " --stop_on_non_zero_rc=" +\
169                  str(stop_on_non_zero_rc) + " " + plug_in_dir_paths
170    if int(quiet) == 1:
171        cmd_buf = sub_cmd_buf + " > " + temp_file_path + " 2>&1"
172    else:
173        cmd_buf = "set -o pipefail ; " + sub_cmd_buf + " 2>&1 | tee " +\
174                  temp_file_path
175
176        if int(debug) == 1:
177            grp.rpissuing(cmd_buf)
178        else:
179            grp.rprint_timen("Processing " + call_point +
180                             " call point programs.")
181
182    proc_plug_pkg_rc = subprocess.call(cmd_buf, shell=True)
183
184    # As process_plug_in_packages.py help text states, it will print the
185    # values of failed_plug_in_name and shell_rc in the following format:
186    # failed_plug_in_name:               <failed plug-in value, if any>
187    # shell_rc:                          <shell return code value of last
188    # call point program>
189
190    # We want to obtain those values from the output.  To make the task
191    # simpler, we'll start by grepping the output for lines that might fit
192    # such a format:
193    # A valid bash variable against the left margin
194    # - A colon
195    # - Zero or more spaces
196    bash_var_regex = "[_[:alpha:]][_[:alnum:]]*"
197    regex = "^" + bash_var_regex + ":[ ]*"
198    cmd_buf = "egrep '" + regex + "' " + temp_file_path + " > " +\
199              temp_properties_file_path
200    if int(debug) == 1:
201        grp.rpissuing(cmd_buf)
202    grep_rc = os.system(cmd_buf)
203
204    # Next we call my_parm_file to create a properties dictionary.
205    properties = gm.my_parm_file(temp_properties_file_path)
206
207    # Finally, we access the 2 values that we need.
208    try:
209        shell_rc = int(properties['shell_rc'], 16)
210    except KeyError:
211        shell_rc = 0
212    try:
213        failed_plug_in_name = properties['failed_plug_in_name']
214    except KeyError:
215        failed_plug_in_name = ""
216
217    if proc_plug_pkg_rc != 0:
218        hex = 1
219        grp.rprint_error("Call to process_plug_in_packages failed.\n")
220        grp.rprint_varx("grep_rc", grep_rc, hex)
221        grp.rprint_varx("proc_plug_pkg_rc", proc_plug_pkg_rc, hex)
222        # Show all of the failed plug in names and shell_rcs.
223        gc.cmd_fnc_u("egrep -A 1 '^failed_plug_in_name:[ ]+' " +
224                     temp_properties_file_path, quiet=1, show_err=0)
225        rc = 1
226
227    return rc, shell_rc, failed_plug_in_name
228