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