1e7e9171eSGeorge Keishing#!/usr/bin/env python3
2ffee58a1SMichael Walsh
3ffee58a1SMichael Walshr"""
4410b1787SMichael WalshThis module provides functions which are useful for running plug-ins from a robot program.
5ffee58a1SMichael Walsh"""
6ffee58a1SMichael Walsh
7e635ddc0SGeorge Keishingimport os
8*20f38712SPatrick Williamsimport subprocess
9*20f38712SPatrick Williamsimport sys
10ffee58a1SMichael Walshimport tempfile
11ffee58a1SMichael Walsh
12e635ddc0SGeorge Keishingimport gen_cmd as gc
13*20f38712SPatrick Williamsimport gen_misc as gm
14*20f38712SPatrick Williamsimport gen_print as gp
15*20f38712SPatrick Williamsfrom robot.libraries.BuiltIn import BuiltIn
16ffee58a1SMichael Walsh
17ffee58a1SMichael Walsh
18*20f38712SPatrick Williamsdef rvalidate_plug_ins(plug_in_dir_paths, quiet=1):
19ffee58a1SMichael Walsh    r"""
20410b1787SMichael Walsh    Call the external validate_plug_ins.py program which validates the plug-in dir paths given to it.  Return
21410b1787SMichael Walsh    a list containing a normalized path for each plug-in selected.
22ffee58a1SMichael Walsh
23ffee58a1SMichael Walsh    Description of arguments:
24410b1787SMichael Walsh    plug_in_dir_paths               A colon-separated list of plug-in directory paths.
25410b1787SMichael Walsh    quiet                           If quiet is set to 1, this function will NOT write status messages to
26410b1787SMichael Walsh                                    stdout.
27ffee58a1SMichael Walsh    """
28ffee58a1SMichael Walsh
29*20f38712SPatrick Williams    cmd_buf = 'validate_plug_ins.py "' + plug_in_dir_paths + '"'
3036efbc04SGeorge Keishing    rc, out_buf = gc.shell_cmd(cmd_buf, print_output=0)
31ffee58a1SMichael Walsh    if rc != 0:
32*20f38712SPatrick Williams        BuiltIn().fail(
33*20f38712SPatrick Williams            gp.sprint_error(
34*20f38712SPatrick Williams                "Validate plug ins call failed.  See"
35*20f38712SPatrick Williams                + " stderr text for details.\n"
36*20f38712SPatrick Williams            )
37*20f38712SPatrick Williams        )
38ffee58a1SMichael Walsh
3936efbc04SGeorge Keishing    # plug_in_packages_list = out_buf.split("\n")
4036efbc04SGeorge Keishing    plug_in_packages_list = list(filter(None, out_buf.split("\n")))
41ffee58a1SMichael Walsh    if len(plug_in_packages_list) == 1 and plug_in_packages_list[0] == "":
42ffee58a1SMichael Walsh        return []
43ffee58a1SMichael Walsh
44ffee58a1SMichael Walsh    return plug_in_packages_list
45ffee58a1SMichael Walsh
46ffee58a1SMichael Walsh
47*20f38712SPatrick Williamsdef rprocess_plug_in_packages(
48*20f38712SPatrick Williams    plug_in_packages_list=None,
49ffee58a1SMichael Walsh    call_point="setup",
50ffee58a1SMichael Walsh    shell_rc="0x00000000",
51ffee58a1SMichael Walsh    stop_on_plug_in_failure=1,
52ffee58a1SMichael Walsh    stop_on_non_zero_rc=0,
53ffee58a1SMichael Walsh    release_type="obmc",
54ffee58a1SMichael Walsh    quiet=None,
55fa74bbbaSMichael Walsh    debug=None,
56*20f38712SPatrick Williams    return_history=False,
57*20f38712SPatrick Williams):
58ffee58a1SMichael Walsh    r"""
59410b1787SMichael Walsh    Call the external process_plug_in_packages.py to process the plug-in packages.  Return the following:
60ffee58a1SMichael Walsh    rc                              The return code - 0 = PASS, 1 = FAIL.
61410b1787SMichael Walsh    shell_rc                        The shell return code returned by process_plug_in_packages.py.
62ffee58a1SMichael Walsh    failed_plug_in_name             The failed plug in name (if any).
63ffee58a1SMichael Walsh
64ffee58a1SMichael Walsh    Description of arguments:
65ffee58a1SMichael Walsh    plug_in_packages_list           A python list of plug-in directory paths.
66410b1787SMichael Walsh    call_point                      The call point program to be called for each plug-in package (e.g.
67410b1787SMichael Walsh                                    post_boot).  This name should not include the "cp_" prefix.
68410b1787SMichael Walsh    shell_rc                        The user may supply a value other than zero to indicate an acceptable
69410b1787SMichael Walsh                                    non-zero return code.  For example, if this value equals 0x00000200, it
70410b1787SMichael Walsh                                    means that for each plug-in call point that runs, a 0x00000200 will not
71410b1787SMichael Walsh                                    be counted as a failure.
72410b1787SMichael Walsh    stop_on_plug_in_failure         If this parameter is set to 1, this program will stop and return non-zero
73410b1787SMichael Walsh                                    if the call point program from any plug-in directory fails.  Conversely,
74410b1787SMichael Walsh                                    if it is set to false, this program will run the call point program from
75410b1787SMichael Walsh                                    each and every plug-in directory regardless of their return values.
76410b1787SMichael Walsh                                    Typical example cases where you'd want to run all plug-in call points
77410b1787SMichael Walsh                                    regardless of success or failure would be "cleanup" or "ffdc" call points.
78410b1787SMichael Walsh    stop_on_non_zero_rc             If this parm is set to 1 and a plug-in call point program returns a valid
79410b1787SMichael Walsh                                    non-zero return code (see "shell_rc" parm above), this program will stop
80410b1787SMichael Walsh                                    processing and return 0 (success).  Since this constitutes a successful
81410b1787SMichael Walsh                                    exit, this would normally be used where the caller wishes to stop
82410b1787SMichael Walsh                                    processing if one of the plug-in directory call point programs returns a
83410b1787SMichael Walsh                                    special value indicating that some special case has been found.  An
84410b1787SMichael Walsh                                    example might be in calling some kind of "check_errl" call point program.
85410b1787SMichael Walsh                                    Such a call point program might return a 2 (i.e. 0x00000200) to indicate
86410b1787SMichael Walsh                                    that a given error log entry was found in an "ignore" list and is
87410b1787SMichael Walsh                                    therefore to be ignored.  That being the case, no other "check_errl" call
88410b1787SMichael Walsh                                    point program would need to be called.
89410b1787SMichael Walsh    release_type                    The type of release being tested (e.g. "obmc", "op", "fips").  This
90410b1787SMichael Walsh                                    influences which integrated plug-ins are selected.
91410b1787SMichael Walsh    quiet                           If quiet is set to 1, this function will NOT write status messages to
92410b1787SMichael Walsh                                    stdout.  This will default to the global quiet program parm or to 0.
93410b1787SMichael Walsh    debug                           If this parameter is set to 1, this function will print additional debug
94410b1787SMichael Walsh                                    information.  This is mainly to be used by the developer of this
95410b1787SMichael Walsh                                    function.  This will default to the global quiet program parm or to 0.
96410b1787SMichael Walsh    return_history                  In addition to rc, shell_rc and failed_plug_in_name, return a list
97410b1787SMichael Walsh                                    containing historical output that looks like the following:
98fa74bbbaSMichael Walsh
99fa74bbbaSMichael Walsh    history:
100410b1787SMichael Walsh      history[0]:                   #(CDT) 2018/10/30 12:25:49 - Running OBMC_Sample/cp_post_stack
101ffee58a1SMichael Walsh    """
102ffee58a1SMichael Walsh
103ffee58a1SMichael Walsh    rc = 0
104ffee58a1SMichael Walsh
105fa74bbbaSMichael Walsh    plug_in_packages_list = gp.get_var_value(plug_in_packages_list, [])
106ffee58a1SMichael Walsh
107ffee58a1SMichael Walsh    # If there are no plug-in packages to process, return successfully.
108ffee58a1SMichael Walsh    if len(plug_in_packages_list) == 0:
109fa74bbbaSMichael Walsh        if return_history:
110fa74bbbaSMichael Walsh            return 0, 0, "", []
111fa74bbbaSMichael Walsh        else:
112ffee58a1SMichael Walsh            return 0, 0, ""
113ffee58a1SMichael Walsh
114fa74bbbaSMichael Walsh    quiet = int(gp.get_var_value(quiet, 0))
115fa74bbbaSMichael Walsh    debug = int(gp.get_var_value(debug, 0))
116ffee58a1SMichael Walsh
117ffee58a1SMichael Walsh    # Create string from list.
118*20f38712SPatrick Williams    plug_in_dir_paths = ":".join(plug_in_packages_list)
119ffee58a1SMichael Walsh
120ffee58a1SMichael Walsh    temp = tempfile.NamedTemporaryFile()
121ffee58a1SMichael Walsh    temp_file_path = temp.name
122ffee58a1SMichael Walsh    temp2 = tempfile.NamedTemporaryFile()
123ffee58a1SMichael Walsh    temp_properties_file_path = temp2.name
124ffee58a1SMichael Walsh
125fa74bbbaSMichael Walsh    if debug:
126ffee58a1SMichael Walsh        os.environ["PERF_TRACE"] = "1"
127ffee58a1SMichael Walsh        debug_string = " --quiet=0"
128ffee58a1SMichael Walsh    else:
129ffee58a1SMichael Walsh        debug_string = ""
130ffee58a1SMichael Walsh
131ffee58a1SMichael Walsh    loc_shell_rc = 0
132ffee58a1SMichael Walsh
133*20f38712SPatrick Williams    sub_cmd_buf = (
134*20f38712SPatrick Williams        "process_plug_in_packages.py"
135*20f38712SPatrick Williams        + debug_string
136*20f38712SPatrick Williams        + " --call_point="
137*20f38712SPatrick Williams        + call_point
138*20f38712SPatrick Williams        + " --allow_shell_rc="
139*20f38712SPatrick Williams        + str(shell_rc)
140*20f38712SPatrick Williams        + " --stop_on_plug_in_failure="
141*20f38712SPatrick Williams        + str(stop_on_plug_in_failure)
142*20f38712SPatrick Williams        + " --stop_on_non_zero_rc="
143*20f38712SPatrick Williams        + str(stop_on_non_zero_rc)
144*20f38712SPatrick Williams        + " "
145*20f38712SPatrick Williams        + plug_in_dir_paths
146*20f38712SPatrick Williams    )
147fa74bbbaSMichael Walsh    if quiet:
148ffee58a1SMichael Walsh        cmd_buf = sub_cmd_buf + " > " + temp_file_path + " 2>&1"
149ffee58a1SMichael Walsh    else:
150*20f38712SPatrick Williams        cmd_buf = (
151*20f38712SPatrick Williams            "set -o pipefail ; "
152*20f38712SPatrick Williams            + sub_cmd_buf
153*20f38712SPatrick Williams            + " 2>&1 | tee "
154*20f38712SPatrick Williams            + temp_file_path
155*20f38712SPatrick Williams        )
156fa74bbbaSMichael Walsh        if debug:
157fa74bbbaSMichael Walsh            gp.print_issuing(cmd_buf)
15809305180SMichael Walsh        else:
159*20f38712SPatrick Williams            gp.print_timen(
160*20f38712SPatrick Williams                "Processing " + call_point + " call point programs."
161*20f38712SPatrick Williams            )
162ffee58a1SMichael Walsh
163*20f38712SPatrick Williams    sub_proc = subprocess.Popen(cmd_buf, shell=True, executable="/bin/bash")
16435c4c62fSMichael Walsh    sub_proc.communicate()
16535c4c62fSMichael Walsh    proc_plug_pkg_rc = sub_proc.returncode
166ffee58a1SMichael Walsh
167fa74bbbaSMichael Walsh    if return_history:
168fa74bbbaSMichael Walsh        # Get the "Running" statements from the output.
169fa74bbbaSMichael Walsh        regex = " Running [^/]+/cp_"
170fa74bbbaSMichael Walsh        cmd_buf = "egrep '" + regex + "' " + temp_file_path
171*20f38712SPatrick Williams        _, history = gc.shell_cmd(
172*20f38712SPatrick Williams            cmd_buf,
173*20f38712SPatrick Williams            quiet=(not debug),
174*20f38712SPatrick Williams            print_output=0,
175*20f38712SPatrick Williams            show_err=0,
176*20f38712SPatrick Williams            ignore_err=1,
177*20f38712SPatrick Williams        )
178fa74bbbaSMichael Walsh        history = [x + "\n" for x in filter(None, history.split("\n"))]
179fa74bbbaSMichael Walsh    else:
180fa74bbbaSMichael Walsh        history = []
181fa74bbbaSMichael Walsh
182410b1787SMichael Walsh    # As process_plug_in_packages.py help text states, it will print the values of failed_plug_in_name and
183410b1787SMichael Walsh    # shell_rc in the following format:
184ffee58a1SMichael Walsh    # failed_plug_in_name:               <failed plug-in value, if any>
185410b1787SMichael Walsh    # shell_rc:                          <shell return code value of last call point program>
186ffee58a1SMichael Walsh
187410b1787SMichael Walsh    # We want to obtain those values from the output.  To make the task simpler, we'll start by grepping the
188410b1787SMichael Walsh    # output for lines that might fit such a format:
189fa74bbbaSMichael Walsh    # A valid bash variable against the left margin followed by...
190fa74bbbaSMichael Walsh    # - A colon followed by...
191ffee58a1SMichael Walsh    # - Zero or more spaces
192957a2b26SMichael Walsh    bash_var_regex = "[_[:alpha:]][_[:alnum:]]*"
193957a2b26SMichael Walsh    regex = "^" + bash_var_regex + ":[ ]*"
194*20f38712SPatrick Williams    cmd_buf = (
195*20f38712SPatrick Williams        "egrep '"
196*20f38712SPatrick Williams        + regex
197*20f38712SPatrick Williams        + "' "
198*20f38712SPatrick Williams        + temp_file_path
199*20f38712SPatrick Williams        + " > "
200*20f38712SPatrick Williams        + temp_properties_file_path
201*20f38712SPatrick Williams    )
202fa74bbbaSMichael Walsh    gp.dprint_issuing(cmd_buf)
203957a2b26SMichael Walsh    grep_rc = os.system(cmd_buf)
204ffee58a1SMichael Walsh
205ffee58a1SMichael Walsh    # Next we call my_parm_file to create a properties dictionary.
206ffee58a1SMichael Walsh    properties = gm.my_parm_file(temp_properties_file_path)
207ffee58a1SMichael Walsh
208ffee58a1SMichael Walsh    # Finally, we access the 2 values that we need.
209*20f38712SPatrick Williams    shell_rc = int(properties.get("shell_rc", "0x0000000000000000"), 16)
210*20f38712SPatrick Williams    failed_plug_in_name = properties.get("failed_plug_in_name", "")
211ffee58a1SMichael Walsh
212957a2b26SMichael Walsh    if proc_plug_pkg_rc != 0:
2130a3bdb4cSMichael Walsh        if quiet:
2140a3bdb4cSMichael Walsh            os.system("cat " + temp_file_path + " >&2")
215fa74bbbaSMichael Walsh        if grep_rc != 0:
2160d5f96a4SMichael Walsh            gp.print_var(grep_rc, gp.hexa())
2170d5f96a4SMichael Walsh        gp.print_var(proc_plug_pkg_rc, gp.hexa())
218fa74bbbaSMichael Walsh        gp.print_timen("Re-cap of plug-in failures:")
219*20f38712SPatrick Williams        gc.cmd_fnc_u(
220*20f38712SPatrick Williams            "egrep -A 1 '^failed_plug_in_name:[ ]+' "
221*20f38712SPatrick Williams            + temp_properties_file_path
222*20f38712SPatrick Williams            + " | egrep -v '^\\--'",
223*20f38712SPatrick Williams            quiet=1,
224*20f38712SPatrick Williams            show_err=0,
225*20f38712SPatrick Williams        )
22640822372SMichael Walsh        rc = 1
227ffee58a1SMichael Walsh
228fa74bbbaSMichael Walsh    if return_history:
229fa74bbbaSMichael Walsh        return rc, shell_rc, failed_plug_in_name, history
230fa74bbbaSMichael Walsh    else:
231ffee58a1SMichael Walsh        return rc, shell_rc, failed_plug_in_name
232