1#!/usr/bin/env python3 2 3r""" 4This module provides functions which are useful for running plug-ins from a robot program. 5""" 6 7import sys 8import subprocess 9from robot.libraries.BuiltIn import BuiltIn 10import os 11import tempfile 12 13import gen_print as gp 14import gen_misc as gm 15import gen_cmd as gc 16 17 18def rvalidate_plug_ins(plug_in_dir_paths, 19 quiet=1): 20 r""" 21 Call the external validate_plug_ins.py program which validates the plug-in dir paths given to it. Return 22 a list containing a normalized path for each plug-in selected. 23 24 Description of arguments: 25 plug_in_dir_paths A colon-separated list of plug-in directory paths. 26 quiet If quiet is set to 1, this function will NOT write status messages to 27 stdout. 28 """ 29 30 cmd_buf = "validate_plug_ins.py \"" + plug_in_dir_paths + "\"" 31 rc, out_buf = gc.shell_cmd(cmd_buf, print_output=0) 32 if rc != 0: 33 BuiltIn().fail(gp.sprint_error("Validate plug ins call failed. See" 34 + " stderr text for details.\n")) 35 36 # plug_in_packages_list = out_buf.split("\n") 37 plug_in_packages_list = list(filter(None, out_buf.split("\n"))) 38 if len(plug_in_packages_list) == 1 and plug_in_packages_list[0] == "": 39 return [] 40 41 return plug_in_packages_list 42 43 44def rprocess_plug_in_packages(plug_in_packages_list=None, 45 call_point="setup", 46 shell_rc="0x00000000", 47 stop_on_plug_in_failure=1, 48 stop_on_non_zero_rc=0, 49 release_type="obmc", 50 quiet=None, 51 debug=None, 52 return_history=False): 53 r""" 54 Call the external process_plug_in_packages.py to process the plug-in packages. Return the following: 55 rc The return code - 0 = PASS, 1 = FAIL. 56 shell_rc The shell return code returned by process_plug_in_packages.py. 57 failed_plug_in_name The failed plug in name (if any). 58 59 Description of arguments: 60 plug_in_packages_list A python list of plug-in directory paths. 61 call_point The call point program to be called for each plug-in package (e.g. 62 post_boot). This name should not include the "cp_" prefix. 63 shell_rc The user may supply a value other than zero to indicate an acceptable 64 non-zero return code. For example, if this value equals 0x00000200, it 65 means that for each plug-in call point that runs, a 0x00000200 will not 66 be counted as a failure. 67 stop_on_plug_in_failure If this parameter is set to 1, this program will stop and return non-zero 68 if the call point program from any plug-in directory fails. Conversely, 69 if it is set to false, this program will run the call point program from 70 each and every plug-in directory regardless of their return values. 71 Typical example cases where you'd want to run all plug-in call points 72 regardless of success or failure would be "cleanup" or "ffdc" call points. 73 stop_on_non_zero_rc If this parm is set to 1 and a plug-in call point program returns a valid 74 non-zero return code (see "shell_rc" parm above), this program will stop 75 processing and return 0 (success). Since this constitutes a successful 76 exit, this would normally be used where the caller wishes to stop 77 processing if one of the plug-in directory call point programs returns a 78 special value indicating that some special case has been found. An 79 example might be in calling some kind of "check_errl" call point program. 80 Such a call point program might return a 2 (i.e. 0x00000200) to indicate 81 that a given error log entry was found in an "ignore" list and is 82 therefore to be ignored. That being the case, no other "check_errl" call 83 point program would need to be called. 84 release_type The type of release being tested (e.g. "obmc", "op", "fips"). This 85 influences which integrated plug-ins are selected. 86 quiet If quiet is set to 1, this function will NOT write status messages to 87 stdout. This will default to the global quiet program parm or to 0. 88 debug If this parameter is set to 1, this function will print additional debug 89 information. This is mainly to be used by the developer of this 90 function. This will default to the global quiet program parm or to 0. 91 return_history In addition to rc, shell_rc and failed_plug_in_name, return a list 92 containing historical output that looks like the following: 93 94 history: 95 history[0]: #(CDT) 2018/10/30 12:25:49 - Running OBMC_Sample/cp_post_stack 96 """ 97 98 rc = 0 99 100 plug_in_packages_list = gp.get_var_value(plug_in_packages_list, []) 101 102 # If there are no plug-in packages to process, return successfully. 103 if len(plug_in_packages_list) == 0: 104 if return_history: 105 return 0, 0, "", [] 106 else: 107 return 0, 0, "" 108 109 quiet = int(gp.get_var_value(quiet, 0)) 110 debug = int(gp.get_var_value(debug, 0)) 111 112 # Create string from list. 113 plug_in_dir_paths = ':'.join(plug_in_packages_list) 114 115 temp = tempfile.NamedTemporaryFile() 116 temp_file_path = temp.name 117 temp2 = tempfile.NamedTemporaryFile() 118 temp_properties_file_path = temp2.name 119 120 if debug: 121 os.environ["PERF_TRACE"] = "1" 122 debug_string = " --quiet=0" 123 else: 124 debug_string = "" 125 126 loc_shell_rc = 0 127 128 sub_cmd_buf = "process_plug_in_packages.py" + debug_string +\ 129 " --call_point=" + call_point + " --allow_shell_rc=" +\ 130 str(shell_rc) + " --stop_on_plug_in_failure=" +\ 131 str(stop_on_plug_in_failure) + " --stop_on_non_zero_rc=" +\ 132 str(stop_on_non_zero_rc) + " " + plug_in_dir_paths 133 if quiet: 134 cmd_buf = sub_cmd_buf + " > " + temp_file_path + " 2>&1" 135 else: 136 cmd_buf = "set -o pipefail ; " + sub_cmd_buf + " 2>&1 | tee " +\ 137 temp_file_path 138 if debug: 139 gp.print_issuing(cmd_buf) 140 else: 141 gp.print_timen("Processing " + call_point 142 + " call point programs.") 143 144 sub_proc = subprocess.Popen(cmd_buf, shell=True, executable='/bin/bash') 145 sub_proc.communicate() 146 proc_plug_pkg_rc = sub_proc.returncode 147 148 if return_history: 149 # Get the "Running" statements from the output. 150 regex = " Running [^/]+/cp_" 151 cmd_buf = "egrep '" + regex + "' " + temp_file_path 152 _, history = gc.shell_cmd(cmd_buf, quiet=(not debug), print_output=0, 153 show_err=0, ignore_err=1) 154 history = [x + "\n" for x in filter(None, history.split("\n"))] 155 else: 156 history = [] 157 158 # As process_plug_in_packages.py help text states, it will print the values of failed_plug_in_name and 159 # shell_rc in the following format: 160 # failed_plug_in_name: <failed plug-in value, if any> 161 # shell_rc: <shell return code value of last call point program> 162 163 # We want to obtain those values from the output. To make the task simpler, we'll start by grepping the 164 # output for lines that might fit such a format: 165 # A valid bash variable against the left margin followed by... 166 # - A colon followed by... 167 # - Zero or more spaces 168 bash_var_regex = "[_[:alpha:]][_[:alnum:]]*" 169 regex = "^" + bash_var_regex + ":[ ]*" 170 cmd_buf = "egrep '" + regex + "' " + temp_file_path + " > " +\ 171 temp_properties_file_path 172 gp.dprint_issuing(cmd_buf) 173 grep_rc = os.system(cmd_buf) 174 175 # Next we call my_parm_file to create a properties dictionary. 176 properties = gm.my_parm_file(temp_properties_file_path) 177 178 # Finally, we access the 2 values that we need. 179 shell_rc = int(properties.get('shell_rc', '0x0000000000000000'), 16) 180 failed_plug_in_name = properties.get('failed_plug_in_name', '') 181 182 if proc_plug_pkg_rc != 0: 183 if quiet: 184 os.system("cat " + temp_file_path + " >&2") 185 if grep_rc != 0: 186 gp.print_var(grep_rc, gp.hexa()) 187 gp.print_var(proc_plug_pkg_rc, gp.hexa()) 188 gp.print_timen("Re-cap of plug-in failures:") 189 gc.cmd_fnc_u("egrep -A 1 '^failed_plug_in_name:[ ]+' " 190 + temp_properties_file_path + " | egrep -v '^\\--'", 191 quiet=1, show_err=0) 192 rc = 1 193 194 if return_history: 195 return rc, shell_rc, failed_plug_in_name, history 196 else: 197 return rc, shell_rc, failed_plug_in_name 198