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