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 *
237423c01aSMichael Walsh
247423c01aSMichael Walsh# Restore sys.path[0].
257423c01aSMichael Walshsys.path.insert(0, save_path_0)
267423c01aSMichael Walsh# I use this variable in calls to print_var.
277423c01aSMichael Walshhex = 1
287423c01aSMichael Walsh
297423c01aSMichael Walsh###############################################################################
307423c01aSMichael Walsh# Create parser object to process command line parameters and args.
317423c01aSMichael Walsh
327423c01aSMichael Walsh# Create parser object.
337423c01aSMichael Walshparser = argparse.ArgumentParser(
347423c01aSMichael Walsh    usage='%(prog)s [OPTIONS]',
357423c01aSMichael Walsh    description="%(prog)s will process the plug-in packages passed to it." +
367423c01aSMichael Walsh                "  A plug-in package is essentially a directory containing" +
377423c01aSMichael Walsh                " one or more call point programs.  Each of these call point" +
387423c01aSMichael Walsh                " programs must have a prefix of \"cp_\".  When calling" +
397423c01aSMichael Walsh                " %(prog)s, a user must provide a call_point parameter" +
407423c01aSMichael Walsh                " (described below).  For each plug-in package passed," +
417423c01aSMichael Walsh                " %(prog)s will check for the presence of the specified call" +
427423c01aSMichael Walsh                " point program in the plug-in directory.  If it is found," +
437423c01aSMichael Walsh                " %(prog)s will run it.  It is the responsibility of the" +
447423c01aSMichael Walsh                " caller to set any environment variables needed by the call" +
457423c01aSMichael Walsh                " point programs.\n\nAfter each call point program" +
467423c01aSMichael Walsh                " has been run, %(prog)s will print the following values in" +
477423c01aSMichael Walsh                " the following formats for use by the calling program:\n" +
487423c01aSMichael Walsh                "  failed_plug_in_name:               <failed plug-in value," +
497423c01aSMichael Walsh                " if any>\n  shell_rc:                          " +
507423c01aSMichael Walsh                "<shell return code value of last call point program - this" +
517423c01aSMichael Walsh                " will be printed in hexadecimal format.  Also, be aware" +
527423c01aSMichael Walsh                " that if a call point program returns a value it will be" +
537423c01aSMichael Walsh                " shifted left 2 bytes (e.g. rc of 2 will be printed as" +
547423c01aSMichael Walsh                " 0x00000200).  That is because the rightmost byte is" +
557423c01aSMichael Walsh                " reserverd for errors in calling the call point program" +
567423c01aSMichael Walsh                " rather than errors generated by the call point program.>",
577423c01aSMichael Walsh    formatter_class=argparse.RawTextHelpFormatter,
587423c01aSMichael Walsh    prefix_chars='-+'
597423c01aSMichael Walsh    )
607423c01aSMichael Walsh
617423c01aSMichael Walsh# Create arguments.
627423c01aSMichael Walshparser.add_argument(
637423c01aSMichael Walsh    'plug_in_dir_paths',
647423c01aSMichael Walsh    nargs='?',
657423c01aSMichael Walsh    default="",
667423c01aSMichael Walsh    help=plug_in_dir_paths_help_text + default_string
677423c01aSMichael Walsh    )
687423c01aSMichael Walsh
697423c01aSMichael Walshparser.add_argument(
707423c01aSMichael Walsh    '--call_point',
717423c01aSMichael Walsh    default="setup",
727423c01aSMichael Walsh    required=True,
737423c01aSMichael Walsh    help='The call point program name.  This value must not include the' +
747423c01aSMichael Walsh         ' "cp_" prefix.  For each plug-in package passed to this program,' +
757423c01aSMichael Walsh         ' the specified call_point program will be called if it exists in' +
767423c01aSMichael Walsh         ' the plug-in directory.' + default_string
777423c01aSMichael Walsh    )
787423c01aSMichael Walsh
797423c01aSMichael Walshparser.add_argument(
807423c01aSMichael Walsh    '--shell_rc',
817423c01aSMichael Walsh    default="0x00000000",
827423c01aSMichael Walsh    help='The user may supply a value other than zero to indicate an' +
837423c01aSMichael Walsh         ' acceptable non-zero return code.  For example, if this value' +
847423c01aSMichael Walsh         ' equals 0x00000200, it means that for each plug-in call point that' +
857423c01aSMichael Walsh         ' runs, a 0x00000200 will not be counted as a failure.  See note' +
867423c01aSMichael Walsh         ' above regarding left-shifting of return codes.' + default_string
877423c01aSMichael Walsh    )
887423c01aSMichael Walsh
897423c01aSMichael Walshparser.add_argument(
907423c01aSMichael Walsh    '--stop_on_plug_in_failure',
917423c01aSMichael Walsh    default=1,
927423c01aSMichael Walsh    type=int,
937423c01aSMichael Walsh    choices=[1, 0],
947423c01aSMichael Walsh    help='If this parameter is set to 1, this program will stop and return ' +
957423c01aSMichael Walsh         'non-zero if the call point program from any plug-in directory ' +
967423c01aSMichael Walsh         'fails.  Conversely, if it is set to false, this program will run ' +
977423c01aSMichael Walsh         'the call point program from each and every plug-in directory ' +
987423c01aSMichael Walsh         'regardless of their return values.  Typical example cases where ' +
997423c01aSMichael Walsh         'you\'d want to run all plug-in call points regardless of success ' +
1007423c01aSMichael Walsh         'or failure would be "cleanup" or "ffdc" call points.'
1017423c01aSMichael Walsh    )
1027423c01aSMichael Walsh
1037423c01aSMichael Walshparser.add_argument(
1047423c01aSMichael Walsh    '--stop_on_non_zero_rc',
1057423c01aSMichael Walsh    default=0,
1067423c01aSMichael Walsh    type=int,
1077423c01aSMichael Walsh    choices=[1, 0],
1087423c01aSMichael Walsh    help='If this parm is set to 1 and a plug-in call point program returns ' +
1097423c01aSMichael Walsh         'a valid non-zero return code (see "shell_rc" parm above), this' +
1107423c01aSMichael Walsh         ' program will stop processing and return 0 (success).  Since this' +
1117423c01aSMichael Walsh         ' constitutes a successful exit, this would normally be used where' +
1127423c01aSMichael Walsh         ' the caller wishes to stop processing if one of the plug-in' +
1137423c01aSMichael Walsh         ' directory call point programs returns a special value indicating' +
1147423c01aSMichael Walsh         ' that some special case has been found.  An example might be in' +
1157423c01aSMichael Walsh         ' calling some kind of "check_errl" call point program.  Such a' +
1167423c01aSMichael Walsh         ' call point program might return a 2 (i.e. 0x00000200) to indicate' +
1177423c01aSMichael Walsh         ' that a given error log entry was found in an "ignore" list and is' +
1187423c01aSMichael Walsh         ' therefore to be ignored.  That being the case, no other' +
1197423c01aSMichael Walsh         ' "check_errl" call point program would need to be called.' +
1207423c01aSMichael Walsh         default_string
1217423c01aSMichael Walsh    )
1227423c01aSMichael Walsh
1237423c01aSMichael Walshparser.add_argument(
1247423c01aSMichael Walsh    '--mch_class',
1257423c01aSMichael Walsh    default="obmc",
1267423c01aSMichael Walsh    help=mch_class_help_text + default_string
1277423c01aSMichael Walsh    )
1287423c01aSMichael Walsh
1297423c01aSMichael Walsh# The stock_list will be passed to gen_get_options.  We populate it with the
1307423c01aSMichael Walsh# names of stock parm options we want.  These stock parms are pre-defined by
1317423c01aSMichael Walsh# gen_get_options.
1327423c01aSMichael Walshstock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)]
1337423c01aSMichael Walsh###############################################################################
1347423c01aSMichael Walsh
1357423c01aSMichael Walsh
1367423c01aSMichael Walsh###############################################################################
1377423c01aSMichael Walshdef exit_function(signal_number=0,
1387423c01aSMichael Walsh                  frame=None):
1397423c01aSMichael Walsh
1407423c01aSMichael Walsh    r"""
1417423c01aSMichael Walsh    Execute whenever the program ends normally or with the signals that we
1427423c01aSMichael Walsh    catch (i.e. TERM, INT).
1437423c01aSMichael Walsh    """
1447423c01aSMichael Walsh
1457423c01aSMichael Walsh    dprint_executing()
1467423c01aSMichael Walsh    dprint_var(signal_number)
1477423c01aSMichael Walsh
1487423c01aSMichael Walsh    qprint_pgm_footer()
1497423c01aSMichael Walsh
1507423c01aSMichael Walsh###############################################################################
1517423c01aSMichael Walsh
1527423c01aSMichael Walsh
1537423c01aSMichael Walsh###############################################################################
1547423c01aSMichael Walshdef signal_handler(signal_number, frame):
1557423c01aSMichael Walsh
1567423c01aSMichael Walsh    r"""
1577423c01aSMichael Walsh    Handle signals.  Without a function to catch a SIGTERM or SIGINT, our
1587423c01aSMichael Walsh    program would terminate immediately with return code 143 and without
1597423c01aSMichael Walsh    calling our exit_function.
1607423c01aSMichael Walsh    """
1617423c01aSMichael Walsh
1627423c01aSMichael Walsh    # Our convention is to set up exit_function with atexit.registr() so
1637423c01aSMichael Walsh    # there is no need to explicitly call exit_function from here.
1647423c01aSMichael Walsh
1657423c01aSMichael Walsh    dprint_executing()
1667423c01aSMichael Walsh
1677423c01aSMichael Walsh    # Calling exit prevents us from returning to the code that was running
1687423c01aSMichael Walsh    # when we received the signal.
1697423c01aSMichael Walsh    exit(0)
1707423c01aSMichael Walsh
1717423c01aSMichael Walsh###############################################################################
1727423c01aSMichael Walsh
1737423c01aSMichael Walsh
1747423c01aSMichael Walsh###############################################################################
1757423c01aSMichael Walshdef validate_parms():
1767423c01aSMichael Walsh
1777423c01aSMichael Walsh    r"""
1787423c01aSMichael Walsh    Validate program parameters, etc.  Return True or False accordingly.
1797423c01aSMichael Walsh    """
1807423c01aSMichael Walsh
1817423c01aSMichael Walsh    if not valid_value(call_point):
1827423c01aSMichael Walsh        return False
1837423c01aSMichael Walsh
1847423c01aSMichael Walsh    gen_post_validation(exit_function, signal_handler)
1857423c01aSMichael Walsh
1867423c01aSMichael Walsh    return True
1877423c01aSMichael Walsh
1887423c01aSMichael Walsh###############################################################################
1897423c01aSMichael Walsh
1907423c01aSMichael Walsh
1917423c01aSMichael Walsh###############################################################################
1927423c01aSMichael Walshdef run_pgm(plug_in_dir_path,
1937423c01aSMichael Walsh            call_point,
1947423c01aSMichael Walsh            caller_shell_rc):
1957423c01aSMichael Walsh
1967423c01aSMichael Walsh    r"""
1977423c01aSMichael Walsh    Run the call point program in the given plug_in_dir_path.  Return the
1987423c01aSMichael Walsh    following:
1997423c01aSMichael Walsh    rc                              The return code - 0 = PASS, 1 = FAIL.
2007423c01aSMichael Walsh    shell_rc                        The shell return code returned by
2017423c01aSMichael Walsh                                    process_plug_in_packages.py.
2027423c01aSMichael Walsh    failed_plug_in_name             The failed plug in name (if any).
2037423c01aSMichael Walsh
2047423c01aSMichael Walsh    Description of arguments:
2057423c01aSMichael Walsh    plug_in_dir_path                The directory path where the call_point
2067423c01aSMichael Walsh                                    program may be located.
2077423c01aSMichael Walsh    call_point                      The call point (e.g. "setup").  This
2087423c01aSMichael Walsh                                    program will look for a program named
2097423c01aSMichael Walsh                                    "cp_" + call_point in the
2107423c01aSMichael Walsh                                    plug_in_dir_path.  If no such call point
2117423c01aSMichael Walsh                                    program is found, this function returns an
2127423c01aSMichael Walsh                                    rc of 0 (i.e. success).
2137423c01aSMichael Walsh    caller_shell_rc                 The user may supply a value other than
2147423c01aSMichael Walsh                                    zero to indicate an acceptable non-zero
2157423c01aSMichael Walsh                                    return code.  For example, if this value
2167423c01aSMichael Walsh                                    equals 0x00000200, it means that for each
2177423c01aSMichael Walsh                                    plug-in call point that runs, a 0x00000200
2187423c01aSMichael Walsh                                    will not be counted as a failure.  See
2197423c01aSMichael Walsh                                    note above regarding left-shifting of
2207423c01aSMichael Walsh                                    return codes.
2217423c01aSMichael Walsh    """
2227423c01aSMichael Walsh
2237423c01aSMichael Walsh    rc = 0
2247423c01aSMichael Walsh    failed_plug_in_name = ""
2257423c01aSMichael Walsh    shell_rc = 0x00000000
2267423c01aSMichael Walsh
2277423c01aSMichael Walsh    cp_prefix = "cp_"
2287423c01aSMichael Walsh    plug_in_pgm_path = plug_in_dir_path + cp_prefix + call_point
2297423c01aSMichael Walsh    if not os.path.exists(plug_in_pgm_path):
2307423c01aSMichael Walsh        # No such call point in this plug in dir path.  This is legal so we
2317423c01aSMichael Walsh        # return 0, etc.
2327423c01aSMichael Walsh        return rc, shell_rc, failed_plug_in_name
2337423c01aSMichael Walsh
2347423c01aSMichael Walsh    # Get some stats on the file.
2357423c01aSMichael Walsh    cmd_buf = "stat -c '%n %s %z' " + plug_in_pgm_path
236*a6723f27SMichael Walsh    dpissuing(cmd_buf)
2377423c01aSMichael Walsh    sub_proc = subprocess.Popen(cmd_buf, shell=True, stdout=subprocess.PIPE,
2387423c01aSMichael Walsh                                stderr=subprocess.STDOUT)
2397423c01aSMichael Walsh    out_buf, err_buf = sub_proc.communicate()
2407423c01aSMichael Walsh    shell_rc = sub_proc.returncode
2417423c01aSMichael Walsh    if shell_rc != 0:
2427423c01aSMichael Walsh        rc = 1
2437423c01aSMichael Walsh        print_var(shell_rc, hex)
2447423c01aSMichael Walsh        failed_plug_in_name = \
2457423c01aSMichael Walsh            os.path.basename(os.path.normpath(plug_in_dir_path))
2467423c01aSMichael Walsh        print(out_buf)
2477423c01aSMichael Walsh        return rc, shell_rc, failed_plug_in_name
2487423c01aSMichael Walsh
249*a6723f27SMichael Walsh    print("------------------------------------------------- Starting plug-" +
250*a6723f27SMichael Walsh          "in -----------------------------------------------")
2517423c01aSMichael Walsh    print(out_buf)
2527423c01aSMichael Walsh    cmd_buf = "PATH=" + plug_in_dir_path + ":${PATH} ; " + cp_prefix +\
2537423c01aSMichael Walsh              call_point
254*a6723f27SMichael Walsh    pissuing(cmd_buf)
2557423c01aSMichael Walsh
2567423c01aSMichael Walsh    sub_proc = subprocess.Popen(cmd_buf, shell=True, stdout=subprocess.PIPE,
2577423c01aSMichael Walsh                                stderr=subprocess.STDOUT)
2587423c01aSMichael Walsh    out_buf, err_buf = sub_proc.communicate()
2597423c01aSMichael Walsh    shell_rc = sub_proc.returncode
2607423c01aSMichael Walsh    if shell_rc != 0 and shell_rc != int(caller_shell_rc, 16):
2617423c01aSMichael Walsh        rc = 1
2627423c01aSMichael Walsh        failed_plug_in_name = \
2637423c01aSMichael Walsh            os.path.basename(os.path.normpath(plug_in_dir_path))
2647423c01aSMichael Walsh
2657423c01aSMichael Walsh    print(out_buf)
2667423c01aSMichael Walsh    if rc == 1 and out_buf.find('**ERROR**') == -1:
2677423c01aSMichael Walsh        # Plug-in output contains no "**ERROR**" text so we'll generate it.
2687423c01aSMichael Walsh        print_error_report("Plug-in failed.\n")
2697423c01aSMichael Walsh    print("------------------------------------------------- Ending plug-in" +
2707423c01aSMichael Walsh          " -------------------------------------------------")
2717423c01aSMichael Walsh
2727423c01aSMichael Walsh    return rc, shell_rc, failed_plug_in_name
2737423c01aSMichael Walsh
2747423c01aSMichael Walsh###############################################################################
2757423c01aSMichael Walsh
2767423c01aSMichael Walsh
2777423c01aSMichael Walsh###############################################################################
2787423c01aSMichael Walshdef main():
2797423c01aSMichael Walsh
2807423c01aSMichael Walsh    r"""
2817423c01aSMichael Walsh    This is the "main" function.  The advantage of having this function vs
2827423c01aSMichael Walsh    just doing this in the true mainline is that you can:
2837423c01aSMichael Walsh    - Declare local variables
2847423c01aSMichael Walsh    - Use "return" instead of "exit".
2857423c01aSMichael Walsh    - Indent 4 chars like you would in any function.
2867423c01aSMichael Walsh    This makes coding more consistent, i.e. it's easy to move code from here
2877423c01aSMichael Walsh    into a function and vice versa.
2887423c01aSMichael Walsh    """
2897423c01aSMichael Walsh
2907423c01aSMichael Walsh    if not gen_get_options(parser, stock_list):
2917423c01aSMichael Walsh        return False
2927423c01aSMichael Walsh
2937423c01aSMichael Walsh    if not validate_parms():
2947423c01aSMichael Walsh        return False
2957423c01aSMichael Walsh
2967423c01aSMichael Walsh    qprint_pgm_header()
2977423c01aSMichael Walsh
2987423c01aSMichael Walsh    # Access program parameter globals.
2997423c01aSMichael Walsh    global plug_in_dir_paths
3007423c01aSMichael Walsh    global mch_class
3017423c01aSMichael Walsh    global shell_rc
3027423c01aSMichael Walsh    global stop_on_plug_in_failure
3037423c01aSMichael Walsh    global stop_on_non_zero_rc
3047423c01aSMichael Walsh
3057423c01aSMichael Walsh    plug_in_packages_list = return_plug_in_packages_list(plug_in_dir_paths,
3067423c01aSMichael Walsh                                                         mch_class)
3077423c01aSMichael Walsh
3087423c01aSMichael Walsh    qpvar(plug_in_packages_list)
3097423c01aSMichael Walsh    qprint("\n")
3107423c01aSMichael Walsh
3117423c01aSMichael Walsh    caller_shell_rc = shell_rc
312*a6723f27SMichael Walsh    shell_rc = 0
3137423c01aSMichael Walsh    failed_plug_in_name = ""
3147423c01aSMichael Walsh
3157423c01aSMichael Walsh    ret_code = 0
3167423c01aSMichael Walsh    for plug_in_dir_path in plug_in_packages_list:
3177423c01aSMichael Walsh        rc, shell_rc, failed_plug_in_name = \
3187423c01aSMichael Walsh            run_pgm(plug_in_dir_path, call_point, caller_shell_rc)
3197423c01aSMichael Walsh        print_var(failed_plug_in_name)
3207423c01aSMichael Walsh        print_var(shell_rc, hex)
3217423c01aSMichael Walsh        if rc != 0:
3227423c01aSMichael Walsh            ret_code = 1
3237423c01aSMichael Walsh            if stop_on_plug_in_failure:
3247423c01aSMichael Walsh                break
3257423c01aSMichael Walsh        if shell_rc != 0 and stop_on_non_zero_rc:
3267423c01aSMichael Walsh            qprint_time("Stopping on non-zero shell return code as requested" +
3277423c01aSMichael Walsh                        " by caller.\n")
3287423c01aSMichael Walsh            break
3297423c01aSMichael Walsh
3307423c01aSMichael Walsh    if ret_code == 0:
3317423c01aSMichael Walsh        return True
3327423c01aSMichael Walsh    else:
3337423c01aSMichael Walsh        if not stop_on_plug_in_failure:
3347423c01aSMichael Walsh            # We print a summary error message to make the failure more
3357423c01aSMichael Walsh            # obvious.
3367423c01aSMichael Walsh            print_error_report("At least one plug-in failed.\n")
3377423c01aSMichael Walsh        return False
3387423c01aSMichael Walsh
3397423c01aSMichael Walsh###############################################################################
3407423c01aSMichael Walsh
3417423c01aSMichael Walsh
3427423c01aSMichael Walsh###############################################################################
3437423c01aSMichael Walsh# Main
3447423c01aSMichael Walsh
3457423c01aSMichael Walshif not main():
3467423c01aSMichael Walsh    exit(1)
3477423c01aSMichael Walsh
3487423c01aSMichael Walsh###############################################################################
349