1#!/usr/bin/env python3
2
3r"""
4See help text for details.
5"""
6
7import sys
8import subprocess
9import os
10
11save_dir_path = sys.path.pop(0)
12
13modules = ['gen_arg', 'gen_print', 'gen_valid', 'gen_plug_in', 'gen_cmd', 'gen_misc']
14for module in modules:
15    exec("from " + module + " import *")
16
17sys.path.insert(0, save_dir_path)
18
19# Create parser object.
20parser = argparse.ArgumentParser(
21    usage='%(prog)s [OPTIONS]',
22    description="%(prog)s will process the plug-in packages passed to it."
23                + "  A plug-in package is essentially a directory containing"
24                + " one or more call point programs.  Each of these call point"
25                + " programs must have a prefix of \"cp_\".  When calling"
26                + " %(prog)s, a user must provide a call_point parameter"
27                + " (described below).  For each plug-in package passed,"
28                + " %(prog)s will check for the presence of the specified call"
29                + " point program in the plug-in directory.  If it is found,"
30                + " %(prog)s will run it.  It is the responsibility of the"
31                + " caller to set any environment variables needed by the call"
32                + " point programs.\n\nAfter each call point program"
33                + " has been run, %(prog)s will print the following values in"
34                + " the following formats for use by the calling program:\n"
35                + "  failed_plug_in_name:               <failed plug-in value,"
36                + " if any>\n  shell_rc:                          "
37                + "<shell return code value of last call point program - this"
38                + " will be printed in hexadecimal format.  Also, be aware"
39                + " that if a call point program returns a value it will be"
40                + " shifted left 2 bytes (e.g. rc of 2 will be printed as"
41                + " 0x00000200).  That is because the rightmost byte is"
42                + " reserved for errors in calling the call point program"
43                + " rather than errors generated by the call point program.>",
44    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
45    prefix_chars='-+')
46
47# Create arguments.
48parser.add_argument(
49    'plug_in_dir_paths',
50    nargs='?',
51    default="",
52    help=plug_in_dir_paths_help_text + default_string)
53
54parser.add_argument(
55    '--call_point',
56    default="setup",
57    required=True,
58    help='The call point program name.  This value must not include the'
59         + ' "cp_" prefix.  For each plug-in package passed to this program,'
60         + ' the specified call_point program will be called if it exists in'
61         + ' the plug-in directory.' + default_string)
62
63parser.add_argument(
64    '--allow_shell_rc',
65    default="0x00000000",
66    help='The user may supply a value other than zero to indicate an'
67         + ' acceptable non-zero return code.  For example, if this value'
68         + ' equals 0x00000200, it means that for each plug-in call point that'
69         + ' runs, a 0x00000200 will not be counted as a failure.  See note'
70         + ' above regarding left-shifting of return codes.' + default_string)
71
72parser.add_argument(
73    '--stop_on_plug_in_failure',
74    default=1,
75    type=int,
76    choices=[1, 0],
77    help='If this parameter is set to 1, this program will stop and return '
78         + 'non-zero if the call point program from any plug-in directory '
79         + 'fails.  Conversely, if it is set to false, this program will run '
80         + 'the call point program from each and every plug-in directory '
81         + 'regardless of their return values.  Typical example cases where '
82         + 'you\'d want to run all plug-in call points regardless of success '
83         + 'or failure would be "cleanup" or "ffdc" call points.')
84
85parser.add_argument(
86    '--stop_on_non_zero_rc',
87    default=0,
88    type=int,
89    choices=[1, 0],
90    help='If this parm is set to 1 and a plug-in call point program returns '
91         + 'a valid non-zero return code (see "allow_shell_rc" parm above),'
92         + ' this program will stop processing and return 0 (success).  Since'
93         + ' this constitutes a successful exit, this would normally be used'
94         + ' where the caller wishes to stop processing if one of the plug-in'
95         + ' directory call point programs returns a special value indicating'
96         + ' that some special case has been found.  An example might be in'
97         + ' calling some kind of "check_errl" call point program.  Such a'
98         + ' call point program might return a 2 (i.e. 0x00000200) to indicate'
99         + ' that a given error log entry was found in an "ignore" list and is'
100         + ' therefore to be ignored.  That being the case, no other'
101         + ' "check_errl" call point program would need to be called.'
102         + default_string)
103
104parser.add_argument(
105    '--mch_class',
106    default="obmc",
107    help=mch_class_help_text + default_string)
108
109# Populate stock_list with options we want.
110stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)]
111
112original_path = os.environ.get('PATH')
113
114
115def validate_parms():
116    r"""
117    Validate program parameters, etc.  Return True or False accordingly.
118    """
119
120    valid_value(call_point)
121
122    global allow_shell_rc
123    valid_integer(allow_shell_rc)
124
125    # Convert to hex string for consistency in printout.
126    allow_shell_rc = "0x%08x" % int(allow_shell_rc, 0)
127    set_pgm_arg(allow_shell_rc)
128
129
130def run_pgm(plug_in_dir_path,
131            call_point,
132            allow_shell_rc):
133    r"""
134    Run the call point program in the given plug_in_dir_path.  Return the following:
135    rc                              The return code - 0 = PASS, 1 = FAIL.
136    shell_rc                        The shell return code returned by process_plug_in_packages.py.
137    failed_plug_in_name             The failed plug in name (if any).
138
139    Description of arguments:
140    plug_in_dir_path                The directory path where the call_point program may be located.
141    call_point                      The call point (e.g. "setup").  This program will look for a program
142                                    named "cp_" + call_point in the plug_in_dir_path.  If no such call point
143                                    program is found, this function returns an rc of 0 (i.e. success).
144    allow_shell_rc                  The user may supply a value other than zero to indicate an acceptable
145                                    non-zero return code.  For example, if this value equals 0x00000200, it
146                                    means that for each plug-in call point that runs, a 0x00000200 will not
147                                    be counted as a failure.  See note above regarding left-shifting of
148                                    return codes.
149    """
150
151    rc = 0
152    failed_plug_in_name = ""
153    shell_rc = 0x00000000
154
155    plug_in_name = os.path.basename(os.path.normpath(plug_in_dir_path))
156    cp_prefix = "cp_"
157    plug_in_pgm_path = plug_in_dir_path + cp_prefix + call_point
158    if not os.path.exists(plug_in_pgm_path):
159        # No such call point in this plug in dir path.  This is legal so we return 0, etc.
160        return rc, shell_rc, failed_plug_in_name
161
162    print("------------------------------------------------- Starting plug-"
163          + "in -----------------------------------------------")
164
165    print_timen("Running " + plug_in_name + "/" + cp_prefix + call_point + ".")
166
167    stdout = 1 - quiet
168    if AUTOBOOT_OPENBMC_NICKNAME != "":
169        auto_status_file_prefix = AUTOBOOT_OPENBMC_NICKNAME + "."
170    else:
171        auto_status_file_prefix = ""
172    auto_status_file_prefix += plug_in_name + ".cp_" + call_point
173    status_dir_path =\
174        add_trailing_slash(os.environ.get("STATUS_DIR_PATH",
175                                          os.environ['HOME']
176                                          + "/status/"))
177    if not os.path.isdir(status_dir_path):
178        AUTOBOOT_EXECDIR = \
179            add_trailing_slash(os.environ.get("AUTOBOOT_EXECDIR", ""))
180        status_dir_path = AUTOBOOT_EXECDIR + "logs/"
181        if not os.path.exists(status_dir_path):
182            os.makedirs(status_dir_path)
183    status_file_name = auto_status_file_prefix + "." + file_date_time_stamp() \
184        + ".status"
185    auto_status_file_subcmd = "auto_status_file.py --status_dir_path=" \
186        + status_dir_path + " --status_file_name=" + status_file_name \
187        + " --quiet=1 --show_url=1 --prefix=" \
188        + auto_status_file_prefix + " --stdout=" + str(stdout) + " "
189
190    cmd_buf = "PATH=" + plug_in_dir_path.rstrip("/") + ":${PATH}"
191    print_issuing(cmd_buf)
192    os.environ['PATH'] = plug_in_dir_path.rstrip("/") + os.pathsep + original_path
193    cmd_buf = auto_status_file_subcmd + cp_prefix + call_point
194    print_issuing(cmd_buf)
195
196    sub_proc = subprocess.Popen(cmd_buf, shell=True)
197    sub_proc.communicate()
198    shell_rc = sub_proc.returncode
199    # Shift to left.
200    shell_rc *= 0x100
201    if shell_rc != 0 and shell_rc != allow_shell_rc:
202        rc = 1
203        failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point
204    if shell_rc != 0:
205        failed_plug_in_name = plug_in_name + "/" + cp_prefix + call_point
206    if failed_plug_in_name != "" and not stdout:
207        # Use tail to avoid double-printing of status_file_url.
208        shell_cmd("tail -n +2 " + status_dir_path + status_file_name, quiet=1,
209                  print_output=1)
210
211    print("------------------------------------------------- Ending plug-in"
212          + " -------------------------------------------------")
213    if failed_plug_in_name != "":
214        print_var(failed_plug_in_name)
215    print_var(shell_rc, hexa())
216
217    return rc, shell_rc, failed_plug_in_name
218
219
220def main():
221
222    gen_setup()
223
224    set_term_options(term_requests='children')
225
226    # Access program parameter globals.
227    global plug_in_dir_paths
228    global mch_class
229    global allow_shell_rc
230    global stop_on_plug_in_failure
231    global stop_on_non_zero_rc
232
233    plug_in_packages_list = return_plug_in_packages_list(plug_in_dir_paths,
234                                                         mch_class)
235
236    qprint_var(plug_in_packages_list)
237    qprint("\n")
238
239    allow_shell_rc = int(allow_shell_rc, 0)
240    shell_rc = 0
241    failed_plug_in_name = ""
242
243    global AUTOBOOT_OPENBMC_NICKNAME
244    AUTOBOOT_OPENBMC_NICKNAME = os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "")
245
246    ret_code = 0
247    for plug_in_dir_path in plug_in_packages_list:
248        rc, shell_rc, failed_plug_in_name = \
249            run_pgm(plug_in_dir_path, call_point, allow_shell_rc)
250        if rc != 0:
251            ret_code = 1
252            if stop_on_plug_in_failure:
253                break
254        if shell_rc != 0 and stop_on_non_zero_rc:
255            qprint_time("Stopping on non-zero shell return code as requested"
256                        + " by caller.\n")
257            break
258
259    if ret_code != 0:
260        print_error("At least one plug-in failed.\n")
261        exit(1)
262
263
264main()
265