1e7e9171eSGeorge Keishing#!/usr/bin/env python3
27423c01aSMichael Walsh
360d08f3dSMichael Walshr"""
460d08f3dSMichael WalshSee help text for details.
560d08f3dSMichael Walsh"""
660d08f3dSMichael Walsh
7e635ddc0SGeorge Keishingimport os
8*20f38712SPatrick Williamsimport subprocess
9*20f38712SPatrick Williamsimport sys
107423c01aSMichael Walsh
1160d08f3dSMichael Walshsave_dir_path = sys.path.pop(0)
127423c01aSMichael Walsh
13*20f38712SPatrick Williamsmodules = [
14*20f38712SPatrick Williams    "gen_arg",
15*20f38712SPatrick Williams    "gen_print",
16*20f38712SPatrick Williams    "gen_valid",
17*20f38712SPatrick Williams    "gen_plug_in",
18*20f38712SPatrick Williams    "gen_cmd",
19*20f38712SPatrick Williams    "gen_misc",
20*20f38712SPatrick Williams]
2160d08f3dSMichael Walshfor module in modules:
2260d08f3dSMichael Walsh    exec("from " + module + " import *")
237423c01aSMichael Walsh
2460d08f3dSMichael Walshsys.path.insert(0, save_dir_path)
257423c01aSMichael Walsh
267423c01aSMichael Walsh# Create parser object.
277423c01aSMichael Walshparser = argparse.ArgumentParser(
28*20f38712SPatrick Williams    usage="%(prog)s [OPTIONS]",
29004ad3c9SJoy Onyerikwu    description="%(prog)s will process the plug-in packages passed to it."
30004ad3c9SJoy Onyerikwu    + "  A plug-in package is essentially a directory containing"
31004ad3c9SJoy Onyerikwu    + " one or more call point programs.  Each of these call point"
32*20f38712SPatrick Williams    + ' programs must have a prefix of "cp_".  When calling'
33004ad3c9SJoy Onyerikwu    + " %(prog)s, a user must provide a call_point parameter"
34004ad3c9SJoy Onyerikwu    + " (described below).  For each plug-in package passed,"
35004ad3c9SJoy Onyerikwu    + " %(prog)s will check for the presence of the specified call"
36004ad3c9SJoy Onyerikwu    + " point program in the plug-in directory.  If it is found,"
37004ad3c9SJoy Onyerikwu    + " %(prog)s will run it.  It is the responsibility of the"
38004ad3c9SJoy Onyerikwu    + " caller to set any environment variables needed by the call"
39004ad3c9SJoy Onyerikwu    + " point programs.\n\nAfter each call point program"
40004ad3c9SJoy Onyerikwu    + " has been run, %(prog)s will print the following values in"
41004ad3c9SJoy Onyerikwu    + " the following formats for use by the calling program:\n"
42004ad3c9SJoy Onyerikwu    + "  failed_plug_in_name:               <failed plug-in value,"
43004ad3c9SJoy Onyerikwu    + " if any>\n  shell_rc:                          "
44004ad3c9SJoy Onyerikwu    + "<shell return code value of last call point program - this"
45004ad3c9SJoy Onyerikwu    + " will be printed in hexadecimal format.  Also, be aware"
46004ad3c9SJoy Onyerikwu    + " that if a call point program returns a value it will be"
47004ad3c9SJoy Onyerikwu    + " shifted left 2 bytes (e.g. rc of 2 will be printed as"
48004ad3c9SJoy Onyerikwu    + " 0x00000200).  That is because the rightmost byte is"
49004ad3c9SJoy Onyerikwu    + " reserved for errors in calling the call point program"
50004ad3c9SJoy Onyerikwu    + " rather than errors generated by the call point program.>",
51d0741f8aSMichael Walsh    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
52*20f38712SPatrick Williams    prefix_chars="-+",
53*20f38712SPatrick Williams)
547423c01aSMichael Walsh
557423c01aSMichael Walsh# Create arguments.
567423c01aSMichael Walshparser.add_argument(
57*20f38712SPatrick Williams    "plug_in_dir_paths",
58*20f38712SPatrick Williams    nargs="?",
597423c01aSMichael Walsh    default="",
60*20f38712SPatrick Williams    help=plug_in_dir_paths_help_text + default_string,
61*20f38712SPatrick Williams)
627423c01aSMichael Walsh
637423c01aSMichael Walshparser.add_argument(
64*20f38712SPatrick Williams    "--call_point",
657423c01aSMichael Walsh    default="setup",
667423c01aSMichael Walsh    required=True,
67*20f38712SPatrick Williams    help="The call point program name.  This value must not include the"
68004ad3c9SJoy Onyerikwu    + ' "cp_" prefix.  For each plug-in package passed to this program,'
69*20f38712SPatrick Williams    + " the specified call_point program will be called if it exists in"
70*20f38712SPatrick Williams    + " the plug-in directory."
71*20f38712SPatrick Williams    + default_string,
72*20f38712SPatrick Williams)
737423c01aSMichael Walsh
747423c01aSMichael Walshparser.add_argument(
75*20f38712SPatrick Williams    "--allow_shell_rc",
767423c01aSMichael Walsh    default="0x00000000",
77*20f38712SPatrick Williams    help="The user may supply a value other than zero to indicate an"
78*20f38712SPatrick Williams    + " acceptable non-zero return code.  For example, if this value"
79*20f38712SPatrick Williams    + " equals 0x00000200, it means that for each plug-in call point that"
80*20f38712SPatrick Williams    + " runs, a 0x00000200 will not be counted as a failure.  See note"
81*20f38712SPatrick Williams    + " above regarding left-shifting of return codes."
82*20f38712SPatrick Williams    + default_string,
83*20f38712SPatrick Williams)
847423c01aSMichael Walsh
857423c01aSMichael Walshparser.add_argument(
86*20f38712SPatrick Williams    "--stop_on_plug_in_failure",
877423c01aSMichael Walsh    default=1,
887423c01aSMichael Walsh    type=int,
897423c01aSMichael Walsh    choices=[1, 0],
90*20f38712SPatrick Williams    help="If this parameter is set to 1, this program will stop and return "
91*20f38712SPatrick Williams    + "non-zero if the call point program from any plug-in directory "
92*20f38712SPatrick Williams    + "fails.  Conversely, if it is set to false, this program will run "
93*20f38712SPatrick Williams    + "the call point program from each and every plug-in directory "
94*20f38712SPatrick Williams    + "regardless of their return values.  Typical example cases where "
95*20f38712SPatrick Williams    + "you'd want to run all plug-in call points regardless of success "
96*20f38712SPatrick Williams    + 'or failure would be "cleanup" or "ffdc" call points.',
97*20f38712SPatrick Williams)
987423c01aSMichael Walsh
997423c01aSMichael Walshparser.add_argument(
100*20f38712SPatrick Williams    "--stop_on_non_zero_rc",
1017423c01aSMichael Walsh    default=0,
1027423c01aSMichael Walsh    type=int,
1037423c01aSMichael Walsh    choices=[1, 0],
104*20f38712SPatrick Williams    help="If this parm is set to 1 and a plug-in call point program returns "
105004ad3c9SJoy Onyerikwu    + 'a valid non-zero return code (see "allow_shell_rc" parm above),'
106*20f38712SPatrick Williams    + " this program will stop processing and return 0 (success).  Since"
107*20f38712SPatrick Williams    + " this constitutes a successful exit, this would normally be used"
108*20f38712SPatrick Williams    + " where the caller wishes to stop processing if one of the plug-in"
109*20f38712SPatrick Williams    + " directory call point programs returns a special value indicating"
110*20f38712SPatrick Williams    + " that some special case has been found.  An example might be in"
111004ad3c9SJoy Onyerikwu    + ' calling some kind of "check_errl" call point program.  Such a'
112*20f38712SPatrick Williams    + " call point program might return a 2 (i.e. 0x00000200) to indicate"
113004ad3c9SJoy Onyerikwu    + ' that a given error log entry was found in an "ignore" list and is'
114*20f38712SPatrick Williams    + " therefore to be ignored.  That being the case, no other"
115004ad3c9SJoy Onyerikwu    + ' "check_errl" call point program would need to be called.'
116*20f38712SPatrick Williams    + default_string,
117*20f38712SPatrick Williams)
1187423c01aSMichael Walsh
1197423c01aSMichael Walshparser.add_argument(
120*20f38712SPatrick Williams    "--mch_class", default="obmc", help=mch_class_help_text + default_string
121*20f38712SPatrick Williams)
1227423c01aSMichael Walsh
12360d08f3dSMichael Walsh# Populate stock_list with options we want.
1247423c01aSMichael Walshstock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)]
1257423c01aSMichael Walsh
126*20f38712SPatrick Williamsoriginal_path = os.environ.get("PATH")
127de093d20SMichael Walsh
1287423c01aSMichael Walsh
1297423c01aSMichael Walshdef validate_parms():
1307423c01aSMichael Walsh    r"""
1317423c01aSMichael Walsh    Validate program parameters, etc.  Return True or False accordingly.
1327423c01aSMichael Walsh    """
1337423c01aSMichael Walsh
13460d08f3dSMichael Walsh    valid_value(call_point)
1357423c01aSMichael Walsh
136ed18ec7aSMichael Walsh    global allow_shell_rc
13760d08f3dSMichael Walsh    valid_integer(allow_shell_rc)
138c33ef37aSMichael Walsh
139c33ef37aSMichael Walsh    # Convert to hex string for consistency in printout.
140ed18ec7aSMichael Walsh    allow_shell_rc = "0x%08x" % int(allow_shell_rc, 0)
141ed18ec7aSMichael Walsh    set_pgm_arg(allow_shell_rc)
142c33ef37aSMichael Walsh
1437423c01aSMichael Walsh
144*20f38712SPatrick Williamsdef run_pgm(plug_in_dir_path, call_point, allow_shell_rc):
1457423c01aSMichael Walsh    r"""
146410b1787SMichael Walsh    Run the call point program in the given plug_in_dir_path.  Return the following:
1477423c01aSMichael Walsh    rc                              The return code - 0 = PASS, 1 = FAIL.
148410b1787SMichael Walsh    shell_rc                        The shell return code returned by process_plug_in_packages.py.
1497423c01aSMichael Walsh    failed_plug_in_name             The failed plug in name (if any).
1507423c01aSMichael Walsh
1517423c01aSMichael Walsh    Description of arguments:
152410b1787SMichael Walsh    plug_in_dir_path                The directory path where the call_point program may be located.
153410b1787SMichael Walsh    call_point                      The call point (e.g. "setup").  This program will look for a program
154410b1787SMichael Walsh                                    named "cp_" + call_point in the plug_in_dir_path.  If no such call point
155410b1787SMichael Walsh                                    program is found, this function returns an rc of 0 (i.e. success).
156410b1787SMichael Walsh    allow_shell_rc                  The user may supply a value other than zero to indicate an acceptable
157410b1787SMichael Walsh                                    non-zero return code.  For example, if this value equals 0x00000200, it
158410b1787SMichael Walsh                                    means that for each plug-in call point that runs, a 0x00000200 will not
159410b1787SMichael Walsh                                    be counted as a failure.  See note above regarding left-shifting of
1607423c01aSMichael Walsh                                    return codes.
1617423c01aSMichael Walsh    """
1627423c01aSMichael Walsh
1637423c01aSMichael Walsh    rc = 0
1647423c01aSMichael Walsh    failed_plug_in_name = ""
1657423c01aSMichael Walsh    shell_rc = 0x00000000
1667423c01aSMichael Walsh
16797d5b363SMichael Walsh    plug_in_name = os.path.basename(os.path.normpath(plug_in_dir_path))
1687423c01aSMichael Walsh    cp_prefix = "cp_"
1697423c01aSMichael Walsh    plug_in_pgm_path = plug_in_dir_path + cp_prefix + call_point
1707423c01aSMichael Walsh    if not os.path.exists(plug_in_pgm_path):
171410b1787SMichael Walsh        # No such call point in this plug in dir path.  This is legal so we return 0, etc.
1727423c01aSMichael Walsh        return rc, shell_rc, failed_plug_in_name
1737423c01aSMichael Walsh
174*20f38712SPatrick Williams    print(
175*20f38712SPatrick Williams        "------------------------------------------------- Starting plug-"
176*20f38712SPatrick Williams        + "in -----------------------------------------------"
177*20f38712SPatrick Williams    )
1788c5a8a8dSMichael Walsh
1798c5a8a8dSMichael Walsh    print_timen("Running " + plug_in_name + "/" + cp_prefix + call_point + ".")
180b3beaa84SMichael Walsh
18197d5b363SMichael Walsh    stdout = 1 - quiet
18297d5b363SMichael Walsh    if AUTOBOOT_OPENBMC_NICKNAME != "":
183b3beaa84SMichael Walsh        auto_status_file_prefix = AUTOBOOT_OPENBMC_NICKNAME + "."
18497d5b363SMichael Walsh    else:
185b3beaa84SMichael Walsh        auto_status_file_prefix = ""
186b3beaa84SMichael Walsh    auto_status_file_prefix += plug_in_name + ".cp_" + call_point
187*20f38712SPatrick Williams    status_dir_path = add_trailing_slash(
188*20f38712SPatrick Williams        os.environ.get("STATUS_DIR_PATH", os.environ["HOME"] + "/status/")
189*20f38712SPatrick Williams    )
1900a3bdb4cSMichael Walsh    if not os.path.isdir(status_dir_path):
191*20f38712SPatrick Williams        AUTOBOOT_EXECDIR = add_trailing_slash(
192*20f38712SPatrick Williams            os.environ.get("AUTOBOOT_EXECDIR", "")
193*20f38712SPatrick Williams        )
1940a3bdb4cSMichael Walsh        status_dir_path = AUTOBOOT_EXECDIR + "logs/"
1950a3bdb4cSMichael Walsh        if not os.path.exists(status_dir_path):
1960a3bdb4cSMichael Walsh            os.makedirs(status_dir_path)
197*20f38712SPatrick Williams    status_file_name = (
198*20f38712SPatrick Williams        auto_status_file_prefix + "." + file_date_time_stamp() + ".status"
199*20f38712SPatrick Williams    )
200*20f38712SPatrick Williams    auto_status_file_subcmd = (
201*20f38712SPatrick Williams        "auto_status_file.py --status_dir_path="
202*20f38712SPatrick Williams        + status_dir_path
203*20f38712SPatrick Williams        + " --status_file_name="
204*20f38712SPatrick Williams        + status_file_name
205*20f38712SPatrick Williams        + " --quiet=1 --show_url=1 --prefix="
206*20f38712SPatrick Williams        + auto_status_file_prefix
207*20f38712SPatrick Williams        + " --stdout="
208*20f38712SPatrick Williams        + str(stdout)
209*20f38712SPatrick Williams        + " "
210*20f38712SPatrick Williams    )
21197d5b363SMichael Walsh
212de093d20SMichael Walsh    cmd_buf = "PATH=" + plug_in_dir_path.rstrip("/") + ":${PATH}"
213de093d20SMichael Walsh    print_issuing(cmd_buf)
214*20f38712SPatrick Williams    os.environ["PATH"] = (
215*20f38712SPatrick Williams        plug_in_dir_path.rstrip("/") + os.pathsep + original_path
216*20f38712SPatrick Williams    )
217de093d20SMichael Walsh    cmd_buf = auto_status_file_subcmd + cp_prefix + call_point
2188c5a8a8dSMichael Walsh    print_issuing(cmd_buf)
2197423c01aSMichael Walsh
220be6153b8SMichael Walsh    sub_proc = subprocess.Popen(cmd_buf, shell=True)
221be6153b8SMichael Walsh    sub_proc.communicate()
2227423c01aSMichael Walsh    shell_rc = sub_proc.returncode
223c33ef37aSMichael Walsh    # Shift to left.
224c33ef37aSMichael Walsh    shell_rc *= 0x100
225ed18ec7aSMichael Walsh    if shell_rc != 0 and shell_rc != allow_shell_rc:
2267423c01aSMichael Walsh        rc = 1
2278c5a8a8dSMichael Walsh        failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point
2283ba8ecdcSMichael Walsh    if shell_rc != 0:
2298c5a8a8dSMichael Walsh        failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point
230b3beaa84SMichael Walsh    if failed_plug_in_name != "" and not stdout:
231b3beaa84SMichael Walsh        # Use tail to avoid double-printing of status_file_url.
232*20f38712SPatrick Williams        shell_cmd(
233*20f38712SPatrick Williams            "tail -n +2 " + status_dir_path + status_file_name,
234*20f38712SPatrick Williams            quiet=1,
235*20f38712SPatrick Williams            print_output=1,
236*20f38712SPatrick Williams        )
2377423c01aSMichael Walsh
238*20f38712SPatrick Williams    print(
239*20f38712SPatrick Williams        "------------------------------------------------- Ending plug-in"
240*20f38712SPatrick Williams        + " -------------------------------------------------"
241*20f38712SPatrick Williams    )
24297d5b363SMichael Walsh    if failed_plug_in_name != "":
24397d5b363SMichael Walsh        print_var(failed_plug_in_name)
2440d5f96a4SMichael Walsh    print_var(shell_rc, hexa())
2457423c01aSMichael Walsh
2467423c01aSMichael Walsh    return rc, shell_rc, failed_plug_in_name
2477423c01aSMichael Walsh
2487423c01aSMichael Walsh
2497423c01aSMichael Walshdef main():
25060d08f3dSMichael Walsh    gen_setup()
2517423c01aSMichael Walsh
252*20f38712SPatrick Williams    set_term_options(term_requests="children")
253de093d20SMichael Walsh
2547423c01aSMichael Walsh    # Access program parameter globals.
2557423c01aSMichael Walsh    global plug_in_dir_paths
2567423c01aSMichael Walsh    global mch_class
257ed18ec7aSMichael Walsh    global allow_shell_rc
2587423c01aSMichael Walsh    global stop_on_plug_in_failure
2597423c01aSMichael Walsh    global stop_on_non_zero_rc
2607423c01aSMichael Walsh
261*20f38712SPatrick Williams    plug_in_packages_list = return_plug_in_packages_list(
262*20f38712SPatrick Williams        plug_in_dir_paths, mch_class
263*20f38712SPatrick Williams    )
2647423c01aSMichael Walsh
2650d5f96a4SMichael Walsh    qprint_var(plug_in_packages_list)
2667423c01aSMichael Walsh    qprint("\n")
2677423c01aSMichael Walsh
268ed18ec7aSMichael Walsh    allow_shell_rc = int(allow_shell_rc, 0)
269a6723f27SMichael Walsh    shell_rc = 0
2707423c01aSMichael Walsh    failed_plug_in_name = ""
2717423c01aSMichael Walsh
27297d5b363SMichael Walsh    global AUTOBOOT_OPENBMC_NICKNAME
273b3beaa84SMichael Walsh    AUTOBOOT_OPENBMC_NICKNAME = os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "")
274b3beaa84SMichael Walsh
2757423c01aSMichael Walsh    ret_code = 0
2767423c01aSMichael Walsh    for plug_in_dir_path in plug_in_packages_list:
277*20f38712SPatrick Williams        rc, shell_rc, failed_plug_in_name = run_pgm(
278*20f38712SPatrick Williams            plug_in_dir_path, call_point, allow_shell_rc
279*20f38712SPatrick Williams        )
2807423c01aSMichael Walsh        if rc != 0:
2817423c01aSMichael Walsh            ret_code = 1
2827423c01aSMichael Walsh            if stop_on_plug_in_failure:
2837423c01aSMichael Walsh                break
2847423c01aSMichael Walsh        if shell_rc != 0 and stop_on_non_zero_rc:
285*20f38712SPatrick Williams            qprint_time(
286*20f38712SPatrick Williams                "Stopping on non-zero shell return code as requested"
287*20f38712SPatrick Williams                + " by caller.\n"
288*20f38712SPatrick Williams            )
2897423c01aSMichael Walsh            break
2907423c01aSMichael Walsh
29160d08f3dSMichael Walsh    if ret_code != 0:
29297d5b363SMichael Walsh        print_error("At least one plug-in failed.\n")
2937423c01aSMichael Walsh        exit(1)
29460d08f3dSMichael Walsh
29560d08f3dSMichael Walsh
29660d08f3dSMichael Walshmain()
297