blob: 77a1f35b96282bac92408843b73f1deffb0ab6ac [file] [log] [blame]
#!/usr/bin/env python3
r"""
This module provides functions which are useful for running plug-ins from a robot program.
"""
import os
import subprocess
import sys
import tempfile
import gen_cmd as gc
import gen_misc as gm
import gen_print as gp
from robot.libraries.BuiltIn import BuiltIn
def rvalidate_plug_ins(plug_in_dir_paths, quiet=1):
r"""
Call the external validate_plug_ins.py program which validates the plug-in dir paths given to it. Return
a list containing a normalized path for each plug-in selected.
Description of arguments:
plug_in_dir_paths A colon-separated list of plug-in directory paths.
quiet If quiet is set to 1, this function will NOT write status messages to
stdout.
"""
cmd_buf = 'validate_plug_ins.py "' + plug_in_dir_paths + '"'
rc, out_buf = gc.shell_cmd(cmd_buf, print_output=0)
if rc != 0:
BuiltIn().fail(
gp.sprint_error(
"Validate plug ins call failed. See"
+ " stderr text for details.\n"
)
)
# plug_in_packages_list = out_buf.split("\n")
plug_in_packages_list = list(filter(None, out_buf.split("\n")))
if len(plug_in_packages_list) == 1 and plug_in_packages_list[0] == "":
return []
return plug_in_packages_list
def rprocess_plug_in_packages(
plug_in_packages_list=None,
call_point="setup",
shell_rc="0x00000000",
stop_on_plug_in_failure=1,
stop_on_non_zero_rc=0,
release_type="obmc",
quiet=None,
debug=None,
return_history=False,
):
r"""
Call the external process_plug_in_packages.py to process the plug-in packages. 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_packages_list A python list of plug-in directory paths.
call_point The call point program to be called for each plug-in package (e.g.
post_boot). This name should not include the "cp_" prefix.
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.
stop_on_plug_in_failure 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.
stop_on_non_zero_rc If this parm is set to 1 and a plug-in call point program returns a valid
non-zero return code (see "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.
release_type The type of release being tested (e.g. "obmc", "op", "fips"). This
influences which integrated plug-ins are selected.
quiet If quiet is set to 1, this function will NOT write status messages to
stdout. This will default to the global quiet program parm or to 0.
debug If this parameter is set to 1, this function will print additional debug
information. This is mainly to be used by the developer of this
function. This will default to the global quiet program parm or to 0.
return_history In addition to rc, shell_rc and failed_plug_in_name, return a list
containing historical output that looks like the following:
history:
history[0]: #(CDT) 2018/10/30 12:25:49 - Running OBMC_Sample/cp_post_stack
"""
rc = 0
plug_in_packages_list = gp.get_var_value(plug_in_packages_list, [])
# If there are no plug-in packages to process, return successfully.
if len(plug_in_packages_list) == 0:
if return_history:
return 0, 0, "", []
else:
return 0, 0, ""
quiet = int(gp.get_var_value(quiet, 0))
debug = int(gp.get_var_value(debug, 0))
# Create string from list.
plug_in_dir_paths = ":".join(plug_in_packages_list)
temp = tempfile.NamedTemporaryFile()
temp_file_path = temp.name
temp2 = tempfile.NamedTemporaryFile()
temp_properties_file_path = temp2.name
if debug:
os.environ["PERF_TRACE"] = "1"
debug_string = " --quiet=0"
else:
debug_string = ""
loc_shell_rc = 0
sub_cmd_buf = (
"process_plug_in_packages.py"
+ debug_string
+ " --call_point="
+ call_point
+ " --allow_shell_rc="
+ str(shell_rc)
+ " --stop_on_plug_in_failure="
+ str(stop_on_plug_in_failure)
+ " --stop_on_non_zero_rc="
+ str(stop_on_non_zero_rc)
+ " "
+ plug_in_dir_paths
)
if quiet:
cmd_buf = sub_cmd_buf + " > " + temp_file_path + " 2>&1"
else:
cmd_buf = (
"set -o pipefail ; "
+ sub_cmd_buf
+ " 2>&1 | tee "
+ temp_file_path
)
if debug:
gp.print_issuing(cmd_buf)
else:
gp.print_timen(
"Processing " + call_point + " call point programs."
)
sub_proc = subprocess.Popen(cmd_buf, shell=True, executable="/bin/bash")
sub_proc.communicate()
proc_plug_pkg_rc = sub_proc.returncode
if return_history:
# Get the "Running" statements from the output.
regex = " Running [^/]+/cp_"
cmd_buf = "egrep '" + regex + "' " + temp_file_path
_, history = gc.shell_cmd(
cmd_buf,
quiet=(not debug),
print_output=0,
show_err=0,
ignore_err=1,
)
history = [x + "\n" for x in filter(None, history.split("\n"))]
else:
history = []
# As process_plug_in_packages.py help text states, it will print the values of failed_plug_in_name and
# shell_rc in the following format:
# failed_plug_in_name: <failed plug-in value, if any>
# shell_rc: <shell return code value of last call point program>
# We want to obtain those values from the output. To make the task simpler, we'll start by grepping the
# output for lines that might fit such a format:
# A valid bash variable against the left margin followed by...
# - A colon followed by...
# - Zero or more spaces
bash_var_regex = "[_[:alpha:]][_[:alnum:]]*"
regex = "^" + bash_var_regex + ":[ ]*"
cmd_buf = (
"egrep '"
+ regex
+ "' "
+ temp_file_path
+ " > "
+ temp_properties_file_path
)
gp.dprint_issuing(cmd_buf)
grep_rc = os.system(cmd_buf)
# Next we call my_parm_file to create a properties dictionary.
properties = gm.my_parm_file(temp_properties_file_path)
# Finally, we access the 2 values that we need.
shell_rc = int(properties.get("shell_rc", "0x0000000000000000"), 16)
failed_plug_in_name = properties.get("failed_plug_in_name", "")
if proc_plug_pkg_rc != 0:
if quiet:
os.system("cat " + temp_file_path + " >&2")
if grep_rc != 0:
gp.print_var(grep_rc, gp.hexa())
gp.print_var(proc_plug_pkg_rc, gp.hexa())
gp.print_timen("Re-cap of plug-in failures:")
gc.cmd_fnc_u(
"egrep -A 1 '^failed_plug_in_name:[ ]+' "
+ temp_properties_file_path
+ " | egrep -v '^\\--'",
quiet=1,
show_err=0,
)
rc = 1
if return_history:
return rc, shell_rc, failed_plug_in_name, history
else:
return rc, shell_rc, failed_plug_in_name