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