17423c01aSMichael Walsh#!/usr/bin/env python
27423c01aSMichael Walsh
37423c01aSMichael Walshimport sys
47423c01aSMichael Walshimport __builtin__
57423c01aSMichael Walshimport subprocess
67423c01aSMichael Walshimport os
77423c01aSMichael Walshimport argparse
87423c01aSMichael Walsh
97423c01aSMichael Walsh# python puts the program's directory path in sys.path[0].  In other words,
107423c01aSMichael Walsh# the user ordinarily has no way to override python's choice of a module from
117423c01aSMichael Walsh# its own dir.  We want to have that ability in our environment.  However, we
127423c01aSMichael Walsh# don't want to break any established python modules that depend on this
137423c01aSMichael Walsh# behavior.  So, we'll save the value from sys.path[0], delete it, import our
147423c01aSMichael Walsh# modules and then restore sys.path to its original value.
157423c01aSMichael Walsh
167423c01aSMichael Walshsave_path_0 = sys.path[0]
177423c01aSMichael Walshdel sys.path[0]
187423c01aSMichael Walsh
197423c01aSMichael Walshfrom gen_print import *
207423c01aSMichael Walshfrom gen_valid import *
217423c01aSMichael Walshfrom gen_arg import *
227423c01aSMichael Walshfrom gen_plug_in import *
2397d5b363SMichael Walshfrom gen_cmd import *
24*8c5a8a8dSMichael Walshfrom gen_misc import *
257423c01aSMichael Walsh
267423c01aSMichael Walsh# Restore sys.path[0].
277423c01aSMichael Walshsys.path.insert(0, save_path_0)
287423c01aSMichael Walsh# I use this variable in calls to print_var.
297423c01aSMichael Walshhex = 1
307423c01aSMichael Walsh
317423c01aSMichael Walsh# Create parser object to process command line parameters and args.
327423c01aSMichael Walsh
337423c01aSMichael Walsh# Create parser object.
347423c01aSMichael Walshparser = argparse.ArgumentParser(
357423c01aSMichael Walsh    usage='%(prog)s [OPTIONS]',
36004ad3c9SJoy Onyerikwu    description="%(prog)s will process the plug-in packages passed to it."
37004ad3c9SJoy Onyerikwu                + "  A plug-in package is essentially a directory containing"
38004ad3c9SJoy Onyerikwu                + " one or more call point programs.  Each of these call point"
39004ad3c9SJoy Onyerikwu                + " programs must have a prefix of \"cp_\".  When calling"
40004ad3c9SJoy Onyerikwu                + " %(prog)s, a user must provide a call_point parameter"
41004ad3c9SJoy Onyerikwu                + " (described below).  For each plug-in package passed,"
42004ad3c9SJoy Onyerikwu                + " %(prog)s will check for the presence of the specified call"
43004ad3c9SJoy Onyerikwu                + " point program in the plug-in directory.  If it is found,"
44004ad3c9SJoy Onyerikwu                + " %(prog)s will run it.  It is the responsibility of the"
45004ad3c9SJoy Onyerikwu                + " caller to set any environment variables needed by the call"
46004ad3c9SJoy Onyerikwu                + " point programs.\n\nAfter each call point program"
47004ad3c9SJoy Onyerikwu                + " has been run, %(prog)s will print the following values in"
48004ad3c9SJoy Onyerikwu                + " the following formats for use by the calling program:\n"
49004ad3c9SJoy Onyerikwu                + "  failed_plug_in_name:               <failed plug-in value,"
50004ad3c9SJoy Onyerikwu                + " if any>\n  shell_rc:                          "
51004ad3c9SJoy Onyerikwu                + "<shell return code value of last call point program - this"
52004ad3c9SJoy Onyerikwu                + " will be printed in hexadecimal format.  Also, be aware"
53004ad3c9SJoy Onyerikwu                + " that if a call point program returns a value it will be"
54004ad3c9SJoy Onyerikwu                + " shifted left 2 bytes (e.g. rc of 2 will be printed as"
55004ad3c9SJoy Onyerikwu                + " 0x00000200).  That is because the rightmost byte is"
56004ad3c9SJoy Onyerikwu                + " reserved for errors in calling the call point program"
57004ad3c9SJoy Onyerikwu                + " rather than errors generated by the call point program.>",
58d0741f8aSMichael Walsh    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
59c33ef37aSMichael Walsh    prefix_chars='-+')
607423c01aSMichael Walsh
617423c01aSMichael Walsh# Create arguments.
627423c01aSMichael Walshparser.add_argument(
637423c01aSMichael Walsh    'plug_in_dir_paths',
647423c01aSMichael Walsh    nargs='?',
657423c01aSMichael Walsh    default="",
66c33ef37aSMichael Walsh    help=plug_in_dir_paths_help_text + default_string)
677423c01aSMichael Walsh
687423c01aSMichael Walshparser.add_argument(
697423c01aSMichael Walsh    '--call_point',
707423c01aSMichael Walsh    default="setup",
717423c01aSMichael Walsh    required=True,
72004ad3c9SJoy Onyerikwu    help='The call point program name.  This value must not include the'
73004ad3c9SJoy Onyerikwu         + ' "cp_" prefix.  For each plug-in package passed to this program,'
74004ad3c9SJoy Onyerikwu         + ' the specified call_point program will be called if it exists in'
75004ad3c9SJoy Onyerikwu         + ' the plug-in directory.' + default_string)
767423c01aSMichael Walsh
777423c01aSMichael Walshparser.add_argument(
78ed18ec7aSMichael Walsh    '--allow_shell_rc',
797423c01aSMichael Walsh    default="0x00000000",
80004ad3c9SJoy Onyerikwu    help='The user may supply a value other than zero to indicate an'
81004ad3c9SJoy Onyerikwu         + ' acceptable non-zero return code.  For example, if this value'
82004ad3c9SJoy Onyerikwu         + ' equals 0x00000200, it means that for each plug-in call point that'
83004ad3c9SJoy Onyerikwu         + ' runs, a 0x00000200 will not be counted as a failure.  See note'
84004ad3c9SJoy Onyerikwu         + ' above regarding left-shifting of return codes.' + default_string)
857423c01aSMichael Walsh
867423c01aSMichael Walshparser.add_argument(
877423c01aSMichael Walsh    '--stop_on_plug_in_failure',
887423c01aSMichael Walsh    default=1,
897423c01aSMichael Walsh    type=int,
907423c01aSMichael Walsh    choices=[1, 0],
91004ad3c9SJoy Onyerikwu    help='If this parameter is set to 1, this program will stop and return '
92004ad3c9SJoy Onyerikwu         + 'non-zero if the call point program from any plug-in directory '
93004ad3c9SJoy Onyerikwu         + 'fails.  Conversely, if it is set to false, this program will run '
94004ad3c9SJoy Onyerikwu         + 'the call point program from each and every plug-in directory '
95004ad3c9SJoy Onyerikwu         + 'regardless of their return values.  Typical example cases where '
96004ad3c9SJoy Onyerikwu         + 'you\'d want to run all plug-in call points regardless of success '
97004ad3c9SJoy Onyerikwu         + 'or failure would be "cleanup" or "ffdc" call points.')
987423c01aSMichael Walsh
997423c01aSMichael Walshparser.add_argument(
1007423c01aSMichael Walsh    '--stop_on_non_zero_rc',
1017423c01aSMichael Walsh    default=0,
1027423c01aSMichael Walsh    type=int,
1037423c01aSMichael Walsh    choices=[1, 0],
104004ad3c9SJoy Onyerikwu    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),'
106004ad3c9SJoy Onyerikwu         + ' this program will stop processing and return 0 (success).  Since'
107004ad3c9SJoy Onyerikwu         + ' this constitutes a successful exit, this would normally be used'
108004ad3c9SJoy Onyerikwu         + ' where the caller wishes to stop processing if one of the plug-in'
109004ad3c9SJoy Onyerikwu         + ' directory call point programs returns a special value indicating'
110004ad3c9SJoy Onyerikwu         + ' 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'
112004ad3c9SJoy Onyerikwu         + ' 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'
114004ad3c9SJoy Onyerikwu         + ' therefore to be ignored.  That being the case, no other'
115004ad3c9SJoy Onyerikwu         + ' "check_errl" call point program would need to be called.'
116004ad3c9SJoy Onyerikwu         + default_string)
1177423c01aSMichael Walsh
1187423c01aSMichael Walshparser.add_argument(
1197423c01aSMichael Walsh    '--mch_class',
1207423c01aSMichael Walsh    default="obmc",
121c33ef37aSMichael Walsh    help=mch_class_help_text + default_string)
1227423c01aSMichael Walsh
1237423c01aSMichael Walsh# The stock_list will be passed to gen_get_options.  We populate it with the
1247423c01aSMichael Walsh# names of stock parm options we want.  These stock parms are pre-defined by
1257423c01aSMichael Walsh# gen_get_options.
1267423c01aSMichael Walshstock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)]
1277423c01aSMichael Walsh
1287423c01aSMichael Walsh
1297423c01aSMichael Walshdef exit_function(signal_number=0,
1307423c01aSMichael Walsh                  frame=None):
1317423c01aSMichael Walsh    r"""
1327423c01aSMichael Walsh    Execute whenever the program ends normally or with the signals that we
1337423c01aSMichael Walsh    catch (i.e. TERM, INT).
1347423c01aSMichael Walsh    """
1357423c01aSMichael Walsh
1367423c01aSMichael Walsh    dprint_executing()
1377423c01aSMichael Walsh    dprint_var(signal_number)
1387423c01aSMichael Walsh
1397423c01aSMichael Walsh    qprint_pgm_footer()
1407423c01aSMichael Walsh
1417423c01aSMichael Walsh
1427423c01aSMichael Walshdef signal_handler(signal_number, frame):
1437423c01aSMichael Walsh    r"""
1447423c01aSMichael Walsh    Handle signals.  Without a function to catch a SIGTERM or SIGINT, our
1457423c01aSMichael Walsh    program would terminate immediately with return code 143 and without
1467423c01aSMichael Walsh    calling our exit_function.
1477423c01aSMichael Walsh    """
1487423c01aSMichael Walsh
1497423c01aSMichael Walsh    # Our convention is to set up exit_function with atexit.registr() so
1507423c01aSMichael Walsh    # there is no need to explicitly call exit_function from here.
1517423c01aSMichael Walsh
1527423c01aSMichael Walsh    dprint_executing()
1537423c01aSMichael Walsh
1547423c01aSMichael Walsh    # Calling exit prevents us from returning to the code that was running
1557423c01aSMichael Walsh    # when we received the signal.
1567423c01aSMichael Walsh    exit(0)
1577423c01aSMichael Walsh
1587423c01aSMichael Walsh
1597423c01aSMichael Walshdef validate_parms():
1607423c01aSMichael Walsh    r"""
1617423c01aSMichael Walsh    Validate program parameters, etc.  Return True or False accordingly.
1627423c01aSMichael Walsh    """
1637423c01aSMichael Walsh
1647423c01aSMichael Walsh    if not valid_value(call_point):
1657423c01aSMichael Walsh        return False
1667423c01aSMichael Walsh
167ed18ec7aSMichael Walsh    global allow_shell_rc
168ed18ec7aSMichael Walsh    if not valid_integer(allow_shell_rc):
169c33ef37aSMichael Walsh        return False
170c33ef37aSMichael Walsh
171c33ef37aSMichael Walsh    # Convert to hex string for consistency in printout.
172ed18ec7aSMichael Walsh    allow_shell_rc = "0x%08x" % int(allow_shell_rc, 0)
173ed18ec7aSMichael Walsh    set_pgm_arg(allow_shell_rc)
174c33ef37aSMichael Walsh
1757423c01aSMichael Walsh    gen_post_validation(exit_function, signal_handler)
1767423c01aSMichael Walsh
1777423c01aSMichael Walsh    return True
1787423c01aSMichael Walsh
1797423c01aSMichael Walsh
1807423c01aSMichael Walshdef run_pgm(plug_in_dir_path,
1817423c01aSMichael Walsh            call_point,
182ed18ec7aSMichael Walsh            allow_shell_rc):
1837423c01aSMichael Walsh    r"""
1847423c01aSMichael Walsh    Run the call point program in the given plug_in_dir_path.  Return the
1857423c01aSMichael Walsh    following:
1867423c01aSMichael Walsh    rc                              The return code - 0 = PASS, 1 = FAIL.
1877423c01aSMichael Walsh    shell_rc                        The shell return code returned by
1887423c01aSMichael Walsh                                    process_plug_in_packages.py.
1897423c01aSMichael Walsh    failed_plug_in_name             The failed plug in name (if any).
1907423c01aSMichael Walsh
1917423c01aSMichael Walsh    Description of arguments:
1927423c01aSMichael Walsh    plug_in_dir_path                The directory path where the call_point
1937423c01aSMichael Walsh                                    program may be located.
1947423c01aSMichael Walsh    call_point                      The call point (e.g. "setup").  This
1957423c01aSMichael Walsh                                    program will look for a program named
1967423c01aSMichael Walsh                                    "cp_" + call_point in the
1977423c01aSMichael Walsh                                    plug_in_dir_path.  If no such call point
1987423c01aSMichael Walsh                                    program is found, this function returns an
1997423c01aSMichael Walsh                                    rc of 0 (i.e. success).
200ed18ec7aSMichael Walsh    allow_shell_rc                  The user may supply a value other than
2017423c01aSMichael Walsh                                    zero to indicate an acceptable non-zero
2027423c01aSMichael Walsh                                    return code.  For example, if this value
2037423c01aSMichael Walsh                                    equals 0x00000200, it means that for each
2047423c01aSMichael Walsh                                    plug-in call point that runs, a 0x00000200
2057423c01aSMichael Walsh                                    will not be counted as a failure.  See
2067423c01aSMichael Walsh                                    note above regarding left-shifting of
2077423c01aSMichael Walsh                                    return codes.
2087423c01aSMichael Walsh    """
2097423c01aSMichael Walsh
21097d5b363SMichael Walsh    global autoscript
21197d5b363SMichael Walsh
2127423c01aSMichael Walsh    rc = 0
2137423c01aSMichael Walsh    failed_plug_in_name = ""
2147423c01aSMichael Walsh    shell_rc = 0x00000000
2157423c01aSMichael Walsh
21697d5b363SMichael Walsh    plug_in_name = os.path.basename(os.path.normpath(plug_in_dir_path))
2177423c01aSMichael Walsh    cp_prefix = "cp_"
2187423c01aSMichael Walsh    plug_in_pgm_path = plug_in_dir_path + cp_prefix + call_point
2197423c01aSMichael Walsh    if not os.path.exists(plug_in_pgm_path):
2207423c01aSMichael Walsh        # No such call point in this plug in dir path.  This is legal so we
2217423c01aSMichael Walsh        # return 0, etc.
2227423c01aSMichael Walsh        return rc, shell_rc, failed_plug_in_name
2237423c01aSMichael Walsh
224004ad3c9SJoy Onyerikwu    print("------------------------------------------------- Starting plug-"
225004ad3c9SJoy Onyerikwu          + "in -----------------------------------------------")
226*8c5a8a8dSMichael Walsh
227*8c5a8a8dSMichael Walsh    print_timen("Running " + plug_in_name + "/" + cp_prefix + call_point + ".")
22897d5b363SMichael Walsh    if autoscript:
22997d5b363SMichael Walsh        stdout = 1 - quiet
23097d5b363SMichael Walsh        if AUTOBOOT_OPENBMC_NICKNAME != "":
23197d5b363SMichael Walsh            autoscript_prefix = AUTOBOOT_OPENBMC_NICKNAME + "."
23297d5b363SMichael Walsh        else:
23397d5b363SMichael Walsh            autoscript_prefix = ""
23497d5b363SMichael Walsh        autoscript_prefix += plug_in_name + ".cp_" + call_point
235*8c5a8a8dSMichael Walsh        status_dir_path =\
236*8c5a8a8dSMichael Walsh            add_trailing_slash(os.environ.get("STATUS_DIR_PATH",
237*8c5a8a8dSMichael Walsh                                              os.environ['HOME']
238*8c5a8a8dSMichael Walsh                                              + "/autoipl/status/"))
239*8c5a8a8dSMichael Walsh        status_file_name = autoscript_prefix + "." + file_date_time_stamp() \
240*8c5a8a8dSMichael Walsh            + ".status"
241*8c5a8a8dSMichael Walsh        autoscript_subcmd = "autoscript --status_dir_path=" + status_dir_path\
242*8c5a8a8dSMichael Walsh            + " --status_file_name=" + status_file_name\
243*8c5a8a8dSMichael Walsh            + " --quiet=1 --show_url=y --prefix=" +\
24497d5b363SMichael Walsh            autoscript_prefix + " --stdout=" + str(stdout) + " -- "
24597d5b363SMichael Walsh    else:
24697d5b363SMichael Walsh        autoscript_subcmd = ""
24797d5b363SMichael Walsh
2483ba8ecdcSMichael Walsh    cmd_buf = "PATH=" + plug_in_dir_path.rstrip("/") + ":${PATH} ; " +\
2493ba8ecdcSMichael Walsh        autoscript_subcmd + cp_prefix + call_point
250*8c5a8a8dSMichael Walsh    print_issuing(cmd_buf)
2517423c01aSMichael Walsh
252be6153b8SMichael Walsh    sub_proc = subprocess.Popen(cmd_buf, shell=True)
253be6153b8SMichael Walsh    sub_proc.communicate()
2547423c01aSMichael Walsh    shell_rc = sub_proc.returncode
255c33ef37aSMichael Walsh    # Shift to left.
256c33ef37aSMichael Walsh    shell_rc *= 0x100
257ed18ec7aSMichael Walsh    if shell_rc != 0 and shell_rc != allow_shell_rc:
2587423c01aSMichael Walsh        rc = 1
259*8c5a8a8dSMichael Walsh        failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point
2603ba8ecdcSMichael Walsh    if shell_rc != 0:
261*8c5a8a8dSMichael Walsh        failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point
262*8c5a8a8dSMichael Walsh    if failed_plug_in_name != "" and autoscript and not stdout:
263*8c5a8a8dSMichael Walsh        shell_cmd("cat " + status_dir_path + status_file_name, quiet=1,
264*8c5a8a8dSMichael Walsh                  print_output=1)
2657423c01aSMichael Walsh
266004ad3c9SJoy Onyerikwu    print("------------------------------------------------- Ending plug-in"
267004ad3c9SJoy Onyerikwu          + " -------------------------------------------------")
26897d5b363SMichael Walsh    if failed_plug_in_name != "":
26997d5b363SMichael Walsh        print_var(failed_plug_in_name)
27097d5b363SMichael Walsh    print_var(shell_rc, hex)
2717423c01aSMichael Walsh
2727423c01aSMichael Walsh    return rc, shell_rc, failed_plug_in_name
2737423c01aSMichael Walsh
2747423c01aSMichael Walsh
2757423c01aSMichael Walshdef main():
2767423c01aSMichael Walsh    r"""
2777423c01aSMichael Walsh    This is the "main" function.  The advantage of having this function vs
2787423c01aSMichael Walsh    just doing this in the true mainline is that you can:
2797423c01aSMichael Walsh    - Declare local variables
2807423c01aSMichael Walsh    - Use "return" instead of "exit".
2817423c01aSMichael Walsh    - Indent 4 chars like you would in any function.
2827423c01aSMichael Walsh    This makes coding more consistent, i.e. it's easy to move code from here
2837423c01aSMichael Walsh    into a function and vice versa.
2847423c01aSMichael Walsh    """
2857423c01aSMichael Walsh
2867423c01aSMichael Walsh    if not gen_get_options(parser, stock_list):
2877423c01aSMichael Walsh        return False
2887423c01aSMichael Walsh
2897423c01aSMichael Walsh    if not validate_parms():
2907423c01aSMichael Walsh        return False
2917423c01aSMichael Walsh
2927423c01aSMichael Walsh    qprint_pgm_header()
2937423c01aSMichael Walsh
2947423c01aSMichael Walsh    # Access program parameter globals.
2957423c01aSMichael Walsh    global plug_in_dir_paths
2967423c01aSMichael Walsh    global mch_class
297ed18ec7aSMichael Walsh    global allow_shell_rc
2987423c01aSMichael Walsh    global stop_on_plug_in_failure
2997423c01aSMichael Walsh    global stop_on_non_zero_rc
3007423c01aSMichael Walsh
3017423c01aSMichael Walsh    plug_in_packages_list = return_plug_in_packages_list(plug_in_dir_paths,
3027423c01aSMichael Walsh                                                         mch_class)
3037423c01aSMichael Walsh
3047423c01aSMichael Walsh    qpvar(plug_in_packages_list)
3057423c01aSMichael Walsh    qprint("\n")
3067423c01aSMichael Walsh
307ed18ec7aSMichael Walsh    allow_shell_rc = int(allow_shell_rc, 0)
308a6723f27SMichael Walsh    shell_rc = 0
3097423c01aSMichael Walsh    failed_plug_in_name = ""
3107423c01aSMichael Walsh
31197d5b363SMichael Walsh    # If the autoscript program is present, we will use it to direct call point
31297d5b363SMichael Walsh    # program output to a separate status file.  This keeps the output of the
31397d5b363SMichael Walsh    # main program (i.e. OBMC Boot Test) cleaner and yet preserves call point
31497d5b363SMichael Walsh    # output if it is needed for debug.
31597d5b363SMichael Walsh    global autoscript
31697d5b363SMichael Walsh    global AUTOBOOT_OPENBMC_NICKNAME
31797d5b363SMichael Walsh    autoscript = 0
31897d5b363SMichael Walsh    AUTOBOOT_OPENBMC_NICKNAME = ""
31997d5b363SMichael Walsh    rc, out_buf = cmd_fnc("which autoscript", quiet=1, print_output=0,
32097d5b363SMichael Walsh                          show_err=0)
32197d5b363SMichael Walsh    if rc == 0:
32297d5b363SMichael Walsh        autoscript = 1
32397d5b363SMichael Walsh        AUTOBOOT_OPENBMC_NICKNAME = os.environ.get("AUTOBOOT_OPENBMC_NICKNAME",
32497d5b363SMichael Walsh                                                   "")
3257423c01aSMichael Walsh    ret_code = 0
3267423c01aSMichael Walsh    for plug_in_dir_path in plug_in_packages_list:
3277423c01aSMichael Walsh        rc, shell_rc, failed_plug_in_name = \
328ed18ec7aSMichael Walsh            run_pgm(plug_in_dir_path, call_point, allow_shell_rc)
3297423c01aSMichael Walsh        if rc != 0:
3307423c01aSMichael Walsh            ret_code = 1
3317423c01aSMichael Walsh            if stop_on_plug_in_failure:
3327423c01aSMichael Walsh                break
3337423c01aSMichael Walsh        if shell_rc != 0 and stop_on_non_zero_rc:
334004ad3c9SJoy Onyerikwu            qprint_time("Stopping on non-zero shell return code as requested"
335004ad3c9SJoy Onyerikwu                        + " by caller.\n")
3367423c01aSMichael Walsh            break
3377423c01aSMichael Walsh
3387423c01aSMichael Walsh    if ret_code == 0:
3397423c01aSMichael Walsh        return True
3407423c01aSMichael Walsh    else:
34197d5b363SMichael Walsh        print_error("At least one plug-in failed.\n")
3427423c01aSMichael Walsh        return False
3437423c01aSMichael Walsh
3447423c01aSMichael Walsh
3457423c01aSMichael Walsh# Main
3467423c01aSMichael Walsh
3477423c01aSMichael Walshif not main():
3487423c01aSMichael Walsh    exit(1)
349