#!/usr/bin/env python import sys try: import __builtin__ except ImportError: import builtins as __builtin__ import subprocess import os import argparse # python puts the program's directory path in sys.path[0]. In other words, # the user ordinarily has no way to override python's choice of a module from # its own dir. We want to have that ability in our environment. However, we # don't want to break any established python modules that depend on this # behavior. So, we'll save the value from sys.path[0], delete it, import our # modules and then restore sys.path to its original value. save_path_0 = sys.path[0] del sys.path[0] from gen_print import * from gen_valid import * from gen_arg import * from gen_plug_in import * from gen_cmd import * from gen_misc import * # Restore sys.path[0]. sys.path.insert(0, save_path_0) # I use this variable in calls to print_var. hex = 1 # Create parser object to process command line parameters and args. # Create parser object. parser = argparse.ArgumentParser( usage='%(prog)s [OPTIONS]', description="%(prog)s will process the plug-in packages passed to it." + " A plug-in package is essentially a directory containing" + " one or more call point programs. Each of these call point" + " programs must have a prefix of \"cp_\". When calling" + " %(prog)s, a user must provide a call_point parameter" + " (described below). For each plug-in package passed," + " %(prog)s will check for the presence of the specified call" + " point program in the plug-in directory. If it is found," + " %(prog)s will run it. It is the responsibility of the" + " caller to set any environment variables needed by the call" + " point programs.\n\nAfter each call point program" + " has been run, %(prog)s will print the following values in" + " the following formats for use by the calling program:\n" + " failed_plug_in_name: \n shell_rc: " + "", formatter_class=argparse.ArgumentDefaultsHelpFormatter, prefix_chars='-+') # Create arguments. parser.add_argument( 'plug_in_dir_paths', nargs='?', default="", help=plug_in_dir_paths_help_text + default_string) parser.add_argument( '--call_point', default="setup", required=True, help='The call point program name. This value must not include the' + ' "cp_" prefix. For each plug-in package passed to this program,' + ' the specified call_point program will be called if it exists in' + ' the plug-in directory.' + default_string) parser.add_argument( '--allow_shell_rc', default="0x00000000", help='The user may supply a value other than zero to indicate an' + ' acceptable non-zero return code. For example, if this value' + ' equals 0x00000200, it means that for each plug-in call point that' + ' runs, a 0x00000200 will not be counted as a failure. See note' + ' above regarding left-shifting of return codes.' + default_string) parser.add_argument( '--stop_on_plug_in_failure', default=1, type=int, choices=[1, 0], help='If this parameter is set to 1, this program will stop and return ' + 'non-zero if the call point program from any plug-in directory ' + 'fails. Conversely, if it is set to false, this program will run ' + 'the call point program from each and every plug-in directory ' + 'regardless of their return values. Typical example cases where ' + 'you\'d want to run all plug-in call points regardless of success ' + 'or failure would be "cleanup" or "ffdc" call points.') parser.add_argument( '--stop_on_non_zero_rc', default=0, type=int, choices=[1, 0], help='If this parm is set to 1 and a plug-in call point program returns ' + 'a valid non-zero return code (see "allow_shell_rc" parm above),' + ' this program will stop processing and return 0 (success). Since' + ' this constitutes a successful exit, this would normally be used' + ' where the caller wishes to stop processing if one of the plug-in' + ' directory call point programs returns a special value indicating' + ' that some special case has been found. An example might be in' + ' calling some kind of "check_errl" call point program. Such a' + ' call point program might return a 2 (i.e. 0x00000200) to indicate' + ' that a given error log entry was found in an "ignore" list and is' + ' therefore to be ignored. That being the case, no other' + ' "check_errl" call point program would need to be called.' + default_string) parser.add_argument( '--mch_class', default="obmc", help=mch_class_help_text + default_string) # The stock_list will be passed to gen_get_options. We populate it with the # names of stock parm options we want. These stock parms are pre-defined by # gen_get_options. stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)] def exit_function(signal_number=0, frame=None): r""" Execute whenever the program ends normally or with the signals that we catch (i.e. TERM, INT). """ dprint_executing() dprint_var(signal_number) qprint_pgm_footer() def signal_handler(signal_number, frame): r""" Handle signals. Without a function to catch a SIGTERM or SIGINT, our program would terminate immediately with return code 143 and without calling our exit_function. """ # Our convention is to set up exit_function with atexit.registr() so # there is no need to explicitly call exit_function from here. dprint_executing() # Calling exit prevents us from returning to the code that was running # when we received the signal. exit(0) def validate_parms(): r""" Validate program parameters, etc. Return True or False accordingly. """ if not valid_value(call_point): return False global allow_shell_rc if not valid_integer(allow_shell_rc): return False # Convert to hex string for consistency in printout. allow_shell_rc = "0x%08x" % int(allow_shell_rc, 0) set_pgm_arg(allow_shell_rc) gen_post_validation(exit_function, signal_handler) return True def run_pgm(plug_in_dir_path, call_point, allow_shell_rc): r""" Run the call point program in the given plug_in_dir_path. Return the following: rc The return code - 0 = PASS, 1 = FAIL. shell_rc The shell return code returned by process_plug_in_packages.py. failed_plug_in_name The failed plug in name (if any). Description of arguments: plug_in_dir_path The directory path where the call_point program may be located. call_point The call point (e.g. "setup"). This program will look for a program named "cp_" + call_point in the plug_in_dir_path. If no such call point program is found, this function returns an rc of 0 (i.e. success). allow_shell_rc The user may supply a value other than zero to indicate an acceptable non-zero return code. For example, if this value equals 0x00000200, it means that for each plug-in call point that runs, a 0x00000200 will not be counted as a failure. See note above regarding left-shifting of return codes. """ global autoscript rc = 0 failed_plug_in_name = "" shell_rc = 0x00000000 plug_in_name = os.path.basename(os.path.normpath(plug_in_dir_path)) cp_prefix = "cp_" plug_in_pgm_path = plug_in_dir_path + cp_prefix + call_point if not os.path.exists(plug_in_pgm_path): # No such call point in this plug in dir path. This is legal so we # return 0, etc. return rc, shell_rc, failed_plug_in_name print("------------------------------------------------- Starting plug-" + "in -----------------------------------------------") print_timen("Running " + plug_in_name + "/" + cp_prefix + call_point + ".") if autoscript: stdout = 1 - quiet if AUTOBOOT_OPENBMC_NICKNAME != "": autoscript_prefix = AUTOBOOT_OPENBMC_NICKNAME + "." else: autoscript_prefix = "" autoscript_prefix += plug_in_name + ".cp_" + call_point status_dir_path =\ add_trailing_slash(os.environ.get("STATUS_DIR_PATH", os.environ['HOME'] + "/autoipl/status/")) status_file_name = autoscript_prefix + "." + file_date_time_stamp() \ + ".status" autoscript_subcmd = "autoscript --status_dir_path=" + status_dir_path\ + " --status_file_name=" + status_file_name\ + " --quiet=1 --show_url=y --prefix=" +\ autoscript_prefix + " --stdout=" + str(stdout) + " -- " else: autoscript_subcmd = "" cmd_buf = "PATH=" + plug_in_dir_path.rstrip("/") + ":${PATH} ; " +\ autoscript_subcmd + cp_prefix + call_point print_issuing(cmd_buf) sub_proc = subprocess.Popen(cmd_buf, shell=True) sub_proc.communicate() shell_rc = sub_proc.returncode # Shift to left. shell_rc *= 0x100 if shell_rc != 0 and shell_rc != allow_shell_rc: rc = 1 failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point if shell_rc != 0: failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point if failed_plug_in_name != "" and autoscript and not stdout: shell_cmd("cat " + status_dir_path + status_file_name, quiet=1, print_output=1) print("------------------------------------------------- Ending plug-in" + " -------------------------------------------------") if failed_plug_in_name != "": print_var(failed_plug_in_name) print_var(shell_rc, hex) return rc, shell_rc, failed_plug_in_name def main(): r""" This is the "main" function. The advantage of having this function vs just doing this in the true mainline is that you can: - Declare local variables - Use "return" instead of "exit". - Indent 4 chars like you would in any function. This makes coding more consistent, i.e. it's easy to move code from here into a function and vice versa. """ if not gen_get_options(parser, stock_list): return False if not validate_parms(): return False qprint_pgm_header() # Access program parameter globals. global plug_in_dir_paths global mch_class global allow_shell_rc global stop_on_plug_in_failure global stop_on_non_zero_rc plug_in_packages_list = return_plug_in_packages_list(plug_in_dir_paths, mch_class) qpvar(plug_in_packages_list) qprint("\n") allow_shell_rc = int(allow_shell_rc, 0) shell_rc = 0 failed_plug_in_name = "" # If the autoscript program is present, we will use it to direct call point # program output to a separate status file. This keeps the output of the # main program (i.e. OBMC Boot Test) cleaner and yet preserves call point # output if it is needed for debug. global autoscript global AUTOBOOT_OPENBMC_NICKNAME autoscript = 0 AUTOBOOT_OPENBMC_NICKNAME = "" rc, out_buf = cmd_fnc("which autoscript", quiet=1, print_output=0, show_err=0) if rc == 0: autoscript = 1 AUTOBOOT_OPENBMC_NICKNAME = os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "") ret_code = 0 for plug_in_dir_path in plug_in_packages_list: rc, shell_rc, failed_plug_in_name = \ run_pgm(plug_in_dir_path, call_point, allow_shell_rc) if rc != 0: ret_code = 1 if stop_on_plug_in_failure: break if shell_rc != 0 and stop_on_non_zero_rc: qprint_time("Stopping on non-zero shell return code as requested" + " by caller.\n") break if ret_code == 0: return True else: print_error("At least one plug-in failed.\n") return False # Main if not main(): exit(1)