1#!/usr/bin/env python
2
3r"""
4This module provides functions which are useful to plug-in call point programs.
5"""
6
7import sys
8import os
9import re
10import collections
11
12import gen_print as gp
13import gen_misc as gm
14import gen_cmd as gc
15
16PLUG_VAR_PREFIX = os.environ.get("PLUG_VAR_PREFIX", "AUTOBOOT")
17
18
19def get_plug_in_package_name(case=None):
20    r"""
21    Return the plug-in package name (e.g. "OS_Console", "DB_Logging").
22
23    Description of argument(s):
24    case                            Indicates whether the value returned
25                                    should be converted to upper or lower
26                                    case.  Valid values are "upper", "lower"
27                                    or None.
28    """
29
30    plug_in_package_name = os.path.basename(gp.pgm_dir_path[:-1])
31    if case == "upper":
32        return plug_in_package_name.upper()
33    elif case == "lower":
34        return plug_in_package_name.lower()
35    else:
36        return plug_in_package_name
37
38
39def return_plug_vars():
40    r"""
41    Return an OrderedDict which is sorted by key and which contains all of the
42    plug-in environment variables.
43
44    Example excerpt of resulting dictionary:
45
46    plug_var_dict:
47      [AUTOBOOT_BASE_TOOL_DIR_PATH]:  /fspmount/
48      [AUTOBOOT_BB_LEVEL]:            <blank>
49      [AUTOBOOT_BOOT_FAIL]:           0
50      ...
51
52    This function also does the following:
53    - Set a default value for environment variable
54      AUTOBOOT_OPENBMC_NICKNAME/AUTOIPL_FSP1_NICKNAME if it is not already set.
55    - Register PASSWORD variables to prevent their values from being printed.
56
57    Note: The programmer may set a default for any given environment variable
58    by declaring a global variable of the same name and setting its value.
59    For example, let's say the calling program has this global declaration:
60
61    PERF_EXERCISERS_TOTAL_TIMEOUT = '180'
62
63    If environment variable PERF_EXERCISERS_TOTAL_TIMEOUT is blank or not set,
64    this function will set it to 180.
65    """
66
67    plug_in_package_name = get_plug_in_package_name(case="upper")
68    regex = "^(" + PLUG_VAR_PREFIX + "|AUTOGUI|" + plug_in_package_name + ")_"
69
70    # Set a default for nickname.
71    if os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "") == "":
72        os.environ['AUTOBOOT_OPENBMC_NICKNAME'] = \
73            os.environ.get("AUTOBOOT_OPENBMC_HOST", "")
74
75    if os.environ.get("AUTOIPL_FSP1_NICKNAME", "") == "":
76        os.environ['AUTOIPL_FSP1_NICKNAME'] = \
77            os.environ.get("AUTOIPL_FSP1_NAME", "").split(".")[0]
78
79    # For all variables specified in the parm_def file, we want them to
80    # default to "" rather than being unset.
81    # Process the parm_def file if it exists.
82    parm_def_file_path = gp.pgm_dir_path + "parm_def"
83    if os.path.exists(parm_def_file_path):
84        parm_defs = gm.my_parm_file(parm_def_file_path)
85    else:
86        parm_defs = collections.OrderedDict()
87    # Example parm_defs:
88    # parm_defs:
89    #   parm_defs[rest_fail]:           boolean
90    #   parm_defs[command]:             string
91    #   parm_defs[esel_stop_file_path]: string
92
93    # Create a list of plug-in environment variables by pre-pending <all caps
94    # plug-in package name>_<all caps var name>
95    plug_in_parm_names = [plug_in_package_name + "_" + x for x in
96                          map(str.upper, parm_defs.keys())]
97    # Example plug_in_parm_names:
98    # plug_in_parm_names:
99    #  plug_in_parm_names[0]: STOP_REST_FAIL
100    #  plug_in_parm_names[1]: STOP_COMMAND
101    #  plug_in_parm_names[2]: STOP_ESEL_STOP_FILE_PATH
102
103    # Initialize unset plug-in vars.
104    for var_name in plug_in_parm_names:
105        # If there is a global variable with the same name as the environment
106        # variable, use its value as a default.
107        default_value = gm.get_mod_global(var_name, "")
108        os.environ[var_name] = os.environ.get(var_name, default_value)
109        if os.environ[var_name] == "":
110            os.environ[var_name] = default_value
111
112    plug_var_dict = \
113        collections.OrderedDict(sorted({k: v for (k, v) in
114                                        os.environ.items()
115                                        if re.match(regex, k)}.items()))
116
117    # Register password values to prevent printing them out.  Any plug var
118    # whose name ends in PASSWORD will be registered.
119    password_vals = {k: v for (k, v) in plug_var_dict.items()
120                     if re.match(r".*_PASSWORD$", k)}.values()
121    map(gp.register_passwords, password_vals)
122
123    return plug_var_dict
124
125
126def sprint_plug_vars(headers=1):
127    r"""
128    Sprint the plug-in environment variables (i.e. those that begin with the
129    global PLUG_VAR_PREFIX value or those that begin with <plug-in
130    package_name>_ in upper case letters.).
131
132    Example excerpt of output:
133    AUTOBOOT_BASE_TOOL_DIR_PATH=/fspmount/
134    AUTOBOOT_BB_LEVEL=
135    AUTOBOOT_BOOT_FAIL=0
136    AUTOBOOT_BOOT_FAIL_THRESHOLD=1000000
137
138    Description of argument(s):
139    headers                         Print a header and a footer.
140    """
141
142    plug_var_dict = return_plug_vars()
143    buffer = ""
144    if headers:
145        buffer += "\n" + gp.sprint_dashes()
146    for key, value in plug_var_dict.items():
147        buffer += key + "=" + value + "\n"
148    if headers:
149        buffer += gp.sprint_dashes() + "\n"
150
151    return buffer
152
153
154def get_plug_vars():
155    r"""
156    Get all plug-in variables and put them in corresponding global variables.
157
158    This would include all environment variables beginning with either the
159    global PLUG_VAR_PREFIX value or with the upper case version of the plug-in
160    package name + underscore (e.g. OP_SAMPLE_VAR1 for plug-in OP_Sample).
161
162    The global variables to be set will be both with and without the global
163    PLUG_VAR_PREFIX value prefix.  For example, if the environment variable in
164    question is AUTOBOOT_OPENBMC_HOST, this function will set global variable
165    AUTOBOOT_OPENBMC_HOST and global variable OPENBMC_HOST.
166    """
167
168    module = sys.modules['__main__']
169    plug_var_dict = return_plug_vars()
170
171    # Get all PLUG_VAR_PREFIX environment variables and put them into globals.
172    for key, value in plug_var_dict.items():
173        setattr(module, key, value)
174        setattr(module, re.sub("^" + PLUG_VAR_PREFIX + "_", "", key), value)
175
176
177def get_plug_default(var_name,
178                     default=None):
179    r"""
180    Derive and return a default value for the given parm variable.
181
182    Dependencies:
183    Global variable PLUG_VAR_PREFIX must be set.
184
185    This function will assign a default by checking the following environment
186    variables in the order shown.  The first one that has a value will be used.
187    - <upper case package_name>_<var_name>
188    - <PLUG_VAR_PREFIX>_OVERRIDE_<var_name>
189    - <PLUG_VAR_PREFIX>_<var_name>
190
191    If none of these are found, this function will return the value passed by
192    the caller in the "default" parm.
193
194    Example:
195
196    Let's say your plug-in is named "OS_Console" and you call this function as
197    follows:
198
199    get_plug_default("quiet", 0)
200
201    The first of these environment variables that is found to be set will be
202    used to provide the default value.
203    - OS_CONSOLE_QUIET
204    - AUTOBOOT_OVERRIDE_QUIET
205    - AUTOBOOT_QUIET
206
207    If none of those has a value, 0 (as specified by the caller in this
208    example) is returned.
209
210    Let's say the master driver program is named obmc_boot.  obmc_boot program
211    is responsible for calling plug-ins.  Let's further suppose that the user
212    wishes to run the master program with --debug=0 but wishes to have all
213    plug-ins run with --debug=1.  This could be accomplished with the
214    following call:
215    export AUTOBOOT_OVERRIDE_DEBUG=1 ; obmc_boot --debug=0
216    --plug_in_dir_paths=<list of plug ins>
217
218    As another example, let's suppose that the user wishes to have just the
219    OS_Console plug-in run with debug and everything else to default to
220    debug=0.  This could be accomplished as follows:
221    export OS_CONSOLE_DEBUG=1 ; obmc_boot --debug=0 --plug_in_dir_paths=<list
222    of plug ins>
223
224    And as one more example, let's say the user wishes to have obmc_boot and
225    OS_Console run without debug but have all other plug-ins run with debug:
226    export AUTOBOOT_OVERRIDE_DEBUG=1 ; export OS_CONSOLE_DEBUG=0 ; obmc_boot
227    --debug=0 --plug_in_dir_paths=<list of plug ins>
228
229    Description of argument(s):
230    var_name                        The name of the variable for which a
231                                    default value is to be calculated.
232    default                         The default value if one cannot be
233                                    determined.
234    """
235
236    var_name = var_name.upper()
237    plug_in_package_name = get_plug_in_package_name(case="upper")
238
239    package_var_name = plug_in_package_name + "_" + var_name
240    default_value = os.environ.get(package_var_name, None)
241    if default_value is not None:
242        # A package-name version of the variable was found so return its value.
243        return(default_value)
244
245    plug_var_name = PLUG_VAR_PREFIX + "_OVERRIDE_" + var_name
246    default_value = os.environ.get(plug_var_name, None)
247    if default_value is not None:
248        # A PLUG_VAR_PREFIX version of the variable was found so return its
249        # value.
250        return default_value
251
252    plug_var_name = PLUG_VAR_PREFIX + "_" + var_name
253    default_value = os.environ.get(plug_var_name, None)
254    if default_value is not None:
255        # A PLUG_VAR_PREFIX version of the variable was found so return its
256        # value.
257        return default_value
258
259    return default
260
261
262def srequired_plug_in(req_plug_in_names,
263                      plug_in_dir_paths=None):
264    r"""
265    Return an empty string if the required plug-ins are found in
266    plug_in_dir_paths.  Otherwise, return an error string.
267
268    Example call:
269    error_message = srequired_plug_in(req_plug_in_names, plug_in_dir_paths)
270
271    Description of argument(s):
272    req_plug_in_names               A list of plug_in names that the caller
273                                    requires (e.g. ['OS_Console']).
274    plug_in_dir_paths               A string which is a colon-delimited list
275                                    of plug-ins specified by the user (e.g.
276                                    DB_Logging:FFDC:OS_Console:Perf).  Path
277                                    values (e.g. "/home/robot/dir1") will be
278                                    stripped from this list to do the
279                                    analysis.  Default value is the
280                                    <PLUG_VAR_PREFIX>_PLUG_IN_DIR_PATHS
281                                    environment variable.
282    """
283
284    # Calculate default value for plug_in_dir_paths.
285    if plug_in_dir_paths is None:
286        plug_in_dir_paths = os.environ.get(PLUG_VAR_PREFIX
287                                           + "_PLUG_IN_DIR_PATHS", "")
288
289    error_message = ""
290
291    # Convert plug_in_dir_paths to a list of base names.
292    plug_in_dir_paths = \
293        list(filter(None, map(os.path.basename, plug_in_dir_paths.split(":"))))
294
295    # Check for each of the user's required plug-ins.
296    for plug_in_name in req_plug_in_names:
297        if plug_in_name not in plug_in_dir_paths:
298            error_message = "The \"" + get_plug_in_package_name() +\
299                "\" plug-in cannot run unless the user also selects the \"" +\
300                plug_in_name + "\" plug in:\n" +\
301                gp.sprint_var(plug_in_dir_paths)
302
303    return error_message
304
305
306def required_plug_in(req_plug_in_names,
307                     plug_in_dir_paths=None):
308    r"""
309    Return True if each of the plug-ins in req_plug_in_names can be found in
310    plug_in_dir_paths  Otherwise, return False and print an error message to
311    stderr.
312
313    Example call:
314    if not required_plug_in(['OS_Console'], AUTOBOOT_PLUG_IN_DIR_PATHS):
315        return False
316
317    Description of argument(s):
318    (See Description of arguments for srequired_plug_in (above)).
319    """
320
321    error_message = srequired_plug_in(req_plug_in_names, plug_in_dir_paths)
322    if not error_message == "":
323        gp.print_error_report(error_message)
324        return False
325
326    return True
327
328
329def compose_plug_in_save_dir_path(plug_in_package_name=None):
330    r"""
331    Create and return a directory path name that is suitable for saving
332    plug-in data.
333
334    The name will be comprised of things such as plug_in package name, pid,
335    etc. in order to guarantee that it is unique for a given test run.
336
337    Description of argument(s):
338    plug_in_package_name            The plug-in package name.  This defaults
339                                    to the name of the caller's plug-in
340                                    package.  However, the caller can specify
341                                    another value in order to retrieve data
342                                    saved by another plug-in package.
343    """
344
345    plug_in_package_name = gm.dft(plug_in_package_name,
346                                  get_plug_in_package_name())
347
348    BASE_TOOL_DIR_PATH = \
349        gm.add_trailing_slash(os.environ.get(PLUG_VAR_PREFIX
350                                             + "BASE_TOOL_DIR_PATH",
351                                             "/fspmount/"))
352    NICKNAME = os.environ.get("AUTOBOOT_OPENBMC_NICKNAME", "")
353    if NICKNAME == "":
354        NICKNAME = os.environ["AUTOIPL_FSP1_NICKNAME"]
355    MASTER_PID = os.environ[PLUG_VAR_PREFIX + "_MASTER_PID"]
356    return BASE_TOOL_DIR_PATH + os.environ["USER"] + "/" + NICKNAME + "/" +\
357        plug_in_package_name + "/" + MASTER_PID + "/"
358
359
360def create_plug_in_save_dir(plug_in_package_name=None):
361    r"""
362    Create a directory suitable for saving plug-in processing data.  See
363    compose_plug_in_save_dir_path for details.
364
365    Description of argument(s):
366    plug_in_package_name            See compose_plug_in_save_dir_path for
367                                    details.
368    """
369
370    plug_in_save_dir_path = compose_plug_in_save_dir_path(plug_in_package_name)
371    if os.path.isdir(plug_in_save_dir_path):
372        return plug_in_save_dir_path
373    gc.shell_cmd("mkdir -p " + plug_in_save_dir_path)
374    return plug_in_save_dir_path
375
376
377def delete_plug_in_save_dir(plug_in_package_name=None):
378    r"""
379    Delete the plug_in save directory.  See compose_plug_in_save_dir_path for
380    details.
381
382    Description of argument(s):
383    plug_in_package_name            See compose_plug_in_save_dir_path for
384                                    details.
385    """
386
387    gc.shell_cmd("rm -rf "
388                 + compose_plug_in_save_dir_path(plug_in_package_name))
389
390
391def save_plug_in_value(value, plug_in_package_name=None):
392    r"""
393    Save a value in a plug-in save file.  The value may be retrieved later via
394    a call to the restore_plug_in_value function.
395
396    This function will figure out the variable name of the value passed and
397    use that name in creating the plug-in save file.
398
399    Example call:
400
401    my_var1 = 5
402    save_plug_in_value(my_var1)
403
404    In this example, the value "5" would be saved to the "my_var1" file in the
405    plug-in save directory.
406
407    Description of argument(s):
408    value                           The value to be saved.
409    plug_in_package_name            See compose_plug_in_save_dir_path for
410                                    details.
411    """
412
413    # Get the name of the variable used as argument one to this function.
414    var_name = gp.get_arg_name(0, 1, stack_frame_ix=2)
415    plug_in_save_dir_path = create_plug_in_save_dir(plug_in_package_name)
416    save_file_path = plug_in_save_dir_path + var_name
417    gp.qprint_timen("Saving \"" + var_name + "\" value.")
418    gc.shell_cmd("echo '" + str(value) + "' > " + save_file_path)
419
420
421def restore_plug_in_value(default="", plug_in_package_name=None):
422    r"""
423    Return a value from a plug-in save file.
424
425    The name of the value to be restored will be determined by this function
426    based on the lvalue being assigned.  Consider the following example:
427
428    my_var1 = restore_plug_in_value(2)
429
430    In this example, this function would look for the "my_var1" file in the
431    plug-in save directory, read its value and return it.  If no such file
432    exists, the default value of 2 would be returned.
433
434    Description of argument(s):
435    default                         The default value to be returned if there
436                                    is no plug-in save file for the value in
437                                    question.
438    plug_in_package_name            See compose_plug_in_save_dir_path for
439                                    details.
440    """
441
442    # Get the lvalue from the caller's invocation of this function.
443    lvalue = gp.get_arg_name(0, -1, stack_frame_ix=2)
444    plug_in_save_dir_path = create_plug_in_save_dir(plug_in_package_name)
445    save_file_path = plug_in_save_dir_path + lvalue
446    if os.path.isfile(save_file_path):
447        gp.qprint_timen("Restoring " + lvalue + " value from "
448                        + save_file_path + ".")
449        value = gm.file_to_list(save_file_path, newlines=0, comments=0,
450                                trim=1)[0]
451        if type(default) is bool:
452            # Convert from string to bool.
453            value = (value == 'True')
454        if type(default) is int:
455            # Convert from string to int.
456            value = int(value)
457        gp.qprint_varx(lvalue, value)
458        return value
459    else:
460        gp.qprint_timen("Save file " + save_file_path
461                        + " does not exist so returning default value.")
462        gp.qprint_var(default)
463        return default
464
465
466# Create print wrapper functions for all sprint functions defined above.
467# func_names contains a list of all print functions which should be created
468# from their sprint counterparts.
469func_names = ['print_plug_vars']
470
471# stderr_func_names is a list of functions whose output should go to stderr
472# rather than stdout.
473stderr_func_names = []
474
475replace_dict = dict(gp.replace_dict)
476replace_dict['mod_qualifier'] = 'gp.'
477func_defs = gp.create_print_wrapper_funcs(func_names, stderr_func_names,
478                                          replace_dict)
479gp.gp_debug_print(func_defs)
480exec(func_defs)
481