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