1#!/usr/bin/env python 2 3r""" 4This module provides functions which are useful to plug-ins call-point 5programs that wish to make external robot program calls. 6""" 7 8import sys 9import os 10import subprocess 11import re 12import time 13import imp 14 15import gen_print as gp 16import gen_valid as gv 17import gen_misc as gm 18import gen_cmd as gc 19 20base_path = \ 21 os.path.dirname(os.path.dirname(imp.find_module("gen_robot_print")[1])) +\ 22 os.sep 23 24 25def init_robot_out_parms(extra_prefix=""): 26 r""" 27 Initialize robot output parms such as outputdir, output, etc. 28 29 This function will set global values for the following robot output parms. 30 31 outputdir, output, log, report, loglevel 32 33 This function would typically be called prior to calling 34 create_robot_cmd_string. 35 """ 36 37 gp.dprint_executing() 38 AUTOBOOT_OPENBMC_NICKNAME = gm.get_mod_global("AUTOBOOT_OPENBMC_NICKNAME") 39 40 # Set values for call to create_robot_cmd_string. 41 # Environment variable TMP_ROBOT_DIR_PATH can be set by the user to 42 # indicate that robot-generated output should initially be written to the 43 # specified temporary directory and then moved to the normal output 44 # location after completion. 45 outputdir =\ 46 os.environ.get("TMP_ROBOT_DIR_PATH", 47 os.environ.get("STATUS_DIR_PATH", 48 os.environ.get("HOME", ".") 49 + "/autoipl/status")) 50 outputdir = gm.add_trailing_slash(outputdir) 51 seconds = time.time() 52 loc_time = time.localtime(seconds) 53 time_string = time.strftime("%y%m%d.%H%M%S", loc_time) 54 file_prefix = AUTOBOOT_OPENBMC_NICKNAME + "." + extra_prefix +\ 55 time_string + "." 56 # Environment variable SAVE_STATUS_POLICY governs when robot-generated 57 # output files (e.g. the log.html) will be moved from TMP_ROBOT_DIR_PATH 58 # to FFDC_DIR_PATH. Valid values are "ALWAYS", "NEVER" and "FAIL". 59 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS") 60 if SAVE_STATUS_POLICY == "NEVER": 61 output = "NONE" 62 log = "NONE" 63 report = "NONE" 64 else: 65 output = file_prefix + "output.xml" 66 log = file_prefix + "log.html" 67 report = file_prefix + "report.html" 68 loglevel = "TRACE" 69 70 # Make create_robot_cmd_string values global. 71 gm.set_mod_global(outputdir) 72 gm.set_mod_global(output) 73 gm.set_mod_global(log) 74 gm.set_mod_global(report) 75 gm.set_mod_global(loglevel) 76 77 78def init_robot_test_base_dir_path(): 79 r""" 80 Initialize and validate the environment variable, ROBOT_TEST_BASE_DIR_PATH 81 and set corresponding global variable ROBOT_TEST_RUNNING_FROM_SB. 82 83 If ROBOT_TEST_BASE_DIR_PATH is already set, this function will merely 84 validate it. This function will also set environment variable 85 ROBOT_TEST_RUNNING_FROM_SB when ROBOT_TEST_BASE_DIR_PATH is not pre-set. 86 """ 87 88 # ROBOT_TEST_BASE_DIR_PATH will be set as follows: 89 # This function will determine whether we are running in a user sandbox 90 # or from a standard apolloxxx environment. 91 # - User sandbox: 92 # If there is a <developer's home dir>/git/openbmc-test-automation/, 93 # ROBOT_TEST_BASE_DIR_PATH will be set to that path. Otherwise, we set it 94 # to <program dir path>/git/openbmc-test-automation/ 95 # - Not in user sandbox: 96 # ROBOT_TEST_BASE_DIR_PATH will be set to <program dir 97 # path>/git/openbmc-test-automation/ 98 99 ROBOT_TEST_BASE_DIR_PATH = os.environ.get('ROBOT_TEST_BASE_DIR_PATH', "") 100 ROBOT_TEST_RUNNING_FROM_SB = \ 101 int(os.environ.get('ROBOT_TEST_RUNNING_FROM_SB', "0")) 102 if ROBOT_TEST_BASE_DIR_PATH == "": 103 # ROBOT_TEST_BASE_DIR_PATH was not set by user/caller. 104 AUTOIPL_VERSION = os.environ.get('AUTOIPL_VERSION', '') 105 if AUTOIPL_VERSION == "": 106 ROBOT_TEST_BASE_DIR_PATH = base_path 107 else: 108 suffix = "git/openbmc-test-automation/" 109 110 # Determine whether we're running out of a developer sandbox or 111 # simply out of an apolloxxx/bin path. 112 shell_rc, out_buf = gc.shell_cmd('dirname $(which gen_print.py)', 113 quiet=(not debug), print_output=0) 114 executable_base_dir_path = os.path.realpath(out_buf.rstrip()) + "/" 115 apollo_dir_path = os.environ['AUTO_BASE_PATH'] + AUTOIPL_VERSION +\ 116 "/bin/" 117 developer_home_dir_path = re.sub('/sandbox.*', '', 118 executable_base_dir_path) 119 developer_home_dir_path = \ 120 gm.add_trailing_slash(developer_home_dir_path) 121 gp.dprint_vars(executable_base_dir_path, developer_home_dir_path, 122 apollo_dir_path) 123 124 ROBOT_TEST_RUNNING_FROM_SB = 0 125 if executable_base_dir_path != apollo_dir_path: 126 ROBOT_TEST_RUNNING_FROM_SB = 1 127 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB) 128 ROBOT_TEST_BASE_DIR_PATH = developer_home_dir_path + suffix 129 if not os.path.isdir(ROBOT_TEST_BASE_DIR_PATH): 130 gp.dprint_timen("NOTE: Sandbox directory " 131 + ROBOT_TEST_BASE_DIR_PATH + " does not" 132 + " exist.") 133 # Fall back to the apollo dir path. 134 ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix 135 else: 136 # Use to the apollo dir path. 137 ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix 138 139 if not gv.valid_value(ROBOT_TEST_BASE_DIR_PATH): 140 return False 141 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB, ROBOT_TEST_BASE_DIR_PATH) 142 if not gv.valid_dir_path(ROBOT_TEST_BASE_DIR_PATH): 143 return False 144 145 ROBOT_TEST_BASE_DIR_PATH = gm.add_trailing_slash(ROBOT_TEST_BASE_DIR_PATH) 146 gm.set_mod_global(ROBOT_TEST_BASE_DIR_PATH) 147 os.environ['ROBOT_TEST_BASE_DIR_PATH'] = ROBOT_TEST_BASE_DIR_PATH 148 149 gm.set_mod_global(ROBOT_TEST_RUNNING_FROM_SB) 150 os.environ['ROBOT_TEST_RUNNING_FROM_SB'] = str(ROBOT_TEST_RUNNING_FROM_SB) 151 152 153raw_robot_file_search_path = "${ROBOT_TEST_BASE_DIR_PATH}:" +\ 154 "${ROBOT_TEST_BASE_DIR_PATH}tests:${ROBOT_TEST_BASE_DIR_PATH}extended:" +\ 155 "${ROBOT_TEST_BASE_DIR_PATH}scratch:${PATH}" 156 157 158def init_robot_file_path(robot_file_path): 159 r""" 160 Determine full path name for the file path passed in robot_file_path and 161 return it. 162 163 If robot_file_path contains a fully qualified path name, this function 164 will verify that the file exists. If robot_file_path contains a relative 165 path, this function will search for the file and set robot_file_path so 166 that it contains the absolute path to the robot file. This function will 167 search for the robot file using the raw_robot_file_search_path (defined 168 above). Note that if ROBOT_TEST_BASE_DIR_PATH is not set, this function 169 will call init_robot_test_base_dir_path to set it. 170 171 Description of arguments: 172 robot_file_path The absolute or relative path to a robot 173 file. 174 """ 175 176 if not gv.valid_value(robot_file_path): 177 raise ValueError('Programmer error.') 178 179 try: 180 if ROBOT_TEST_BASE_DIR_PATH is NONE: 181 init_robot_test_base_dir_path() 182 except NameError: 183 init_robot_test_base_dir_path() 184 185 if not re.match(r".*\.(robot|py)$", robot_file_path): 186 # No suffix so we'll assign one of "\.robot". 187 robot_file_path = robot_file_path + ".robot" 188 189 abs_path = 0 190 if robot_file_path[0:1] == "/": 191 abs_path = 1 192 193 gp.dprint_vars(abs_path, robot_file_path) 194 195 if not abs_path: 196 cmd_buf = "echo -n \"" + raw_robot_file_search_path + "\"" 197 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=(not debug), 198 print_output=0) 199 robot_file_search_paths = out_buf 200 gp.dprint_var(robot_file_search_paths) 201 robot_file_search_paths_list = robot_file_search_paths.split(':') 202 for search_path in robot_file_search_paths_list: 203 search_path = gm.add_trailing_slash(search_path) 204 candidate_file_path = search_path + robot_file_path 205 gp.dprint_var(candidate_file_path) 206 if os.path.isfile(candidate_file_path): 207 gp.dprint_timen("Found full path to " + robot_file_path + ".") 208 robot_file_path = candidate_file_path 209 break 210 211 gp.dprint_var(robot_file_path) 212 if not gv.valid_file_path(robot_file_path): 213 raise ValueError('Programmer error.') 214 215 return robot_file_path 216 217 218def get_robot_parm_names(): 219 r""" 220 Return a list containing all of the long parm names (e.g. --outputdir) 221 supported by the robot program. Double dashes are not included in the 222 names returned. 223 """ 224 225 cmd_buf = "robot -h | egrep " +\ 226 "'^([ ]\\-[a-zA-Z0-9])?[ ]+--[a-zA-Z0-9]+[ ]+' | sed -re" +\ 227 " s'/.*\\-\\-//g' -e s'/ .*//g' | sort -u" 228 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=1, print_output=0) 229 230 return out_buf.split("\n") 231 232 233def create_robot_cmd_string(robot_file_path, *parms): 234 r""" 235 Create a robot command string and return it. On failure, return an empty 236 string. 237 238 Description of arguments: 239 robot_file_path The path to the robot file to be run. 240 parms The list of parms to be included in the 241 command string. The name of each variable 242 in this list must be the same as the name 243 of the corresponding parm. This function 244 figures out that name. This function is 245 also able to distinguish robot parms (e.g. 246 --outputdir) from robot program parms (all 247 other parms which will be passed as "-v 248 PARM_NAME:parm_value").. 249 250 Example: 251 252 The following call to this function... 253 cmd_buf = create_robot_cmd_string("tools/start_sol_console.robot", 254 OPENBMC_HOST, quiet, test_mode, debug, outputdir, output, log, report) 255 256 Would return a string something like this. 257 robot -v OPENBMC_HOST:beye6 -v quiet:0 -v test_mode:1 -v debug:1 258 --outputdir=/gsa/ausgsa/projects/a/autoipl/status 259 --output=beye6.OS_Console.output.xml --log=beye6.OS_Console.log.html 260 --report=beye6.OS_Console.report.html tools/start_sol_console.robot 261 """ 262 263 robot_file_path = init_robot_file_path(robot_file_path) 264 265 robot_parm_names = get_robot_parm_names() 266 267 robot_parm_list = [] 268 269 stack_frame = 2 270 ix = 2 271 for arg in parms: 272 parm = arg 273 parm = gm.quote_bash_parm(gm.escape_bash_quotes(str(parm))) 274 var_name = gp.get_arg_name(None, ix, stack_frame) 275 if var_name in robot_parm_names: 276 p_string = "--" + var_name + "=" + str(parm) 277 robot_parm_list.append(p_string) 278 else: 279 p_string = "-v " + var_name + ":" + str(parm) 280 robot_parm_list.append(p_string) 281 ix += 1 282 283 robot_cmd_buf = "robot " + ' '.join(robot_parm_list) + " " +\ 284 robot_file_path 285 286 return robot_cmd_buf 287 288 289# Global variables to aid in cleanup after running robot_cmd_fnc. 290gcr_last_robot_cmd_buf = "" 291gcr_last_robot_rc = 0 292 293 294def process_robot_output_files(robot_cmd_buf=None, 295 robot_rc=None, 296 gzip=1): 297 r""" 298 Process robot output files which can involve several operations: 299 - If the files are in a temporary location, using SAVE_STATUS_POLICY to 300 decide whether to move them to a permanent location or to delete them. 301 - Gzipping them. 302 303 Description of argument(s): 304 robot_cmd_buf The complete command string used to invoke 305 robot. 306 robot_rc The return code from running the robot 307 command string. 308 gzip Indicates whether robot-generated output 309 should be gzipped. 310 """ 311 312 robot_cmd_buf = gm.dft(robot_cmd_buf, gcr_last_robot_cmd_buf) 313 robot_rc = gm.dft(robot_rc, gcr_last_robot_rc) 314 315 if robot_cmd_buf == "": 316 # This can legitimately occur if this function is called from an 317 # exit_function without the program having ever run robot_cmd_fnc. 318 return 319 320 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS") 321 gp.qprint_vars(SAVE_STATUS_POLICY) 322 323 # When SAVE_STATUS_POLICY is "NEVER" robot output files don't even get 324 # generated. 325 if SAVE_STATUS_POLICY == "NEVER": 326 return 327 328 # Compose file_list based on robot command buffer passed in. 329 robot_cmd_buf_dict = gc.parse_command_string(robot_cmd_buf) 330 outputdir = robot_cmd_buf_dict['outputdir'] 331 outputdir = gm.add_trailing_slash(outputdir) 332 file_list = outputdir + robot_cmd_buf_dict['output'] + " " + outputdir\ 333 + robot_cmd_buf_dict['log'] + " " + outputdir\ 334 + robot_cmd_buf_dict['report'] 335 336 # Double checking that files are present. 337 shell_rc, out_buf = gc.shell_cmd("ls -1 " + file_list + " 2>/dev/null", 338 show_err=0) 339 file_list = re.sub("\n", " ", out_buf.rstrip("\n")) 340 341 if file_list == "": 342 gp.qprint_timen("No robot output files were found in " + outputdir 343 + ".") 344 return 345 gp.qprint_var(robot_rc, 1) 346 if SAVE_STATUS_POLICY == "FAIL" and robot_rc == 0: 347 gp.qprint_timen("The call to robot produced no failures." 348 + " Deleting robot output files.") 349 gc.shell_cmd("rm -rf " + file_list) 350 return 351 352 if gzip: 353 gc.shell_cmd("gzip -f " + file_list) 354 # Update the values in file_list. 355 file_list = re.sub(" ", ".gz ", file_list) + ".gz" 356 357 # It TMP_ROBOT_DIR_PATH is set, it means the caller wanted the robot 358 # output initially directed to TMP_ROBOT_DIR_PATH but later moved to 359 # FFDC_DIR_PATH. Otherwise, we're done. 360 361 if os.environ.get("TMP_ROBOT_DIR_PATH", "") is "": 362 return 363 364 # We're directing these to the FFDC dir path so that they'll be subjected 365 # to FFDC cleanup. 366 target_dir_path = os.environ.get("FFDC_DIR_PATH", 367 os.environ.get("HOME", ".") 368 + "/autoipl/ffdc") 369 target_dir_path = gm.add_trailing_slash(target_dir_path) 370 371 targ_file_list = [re.sub(".*/", target_dir_path, x) 372 for x in file_list.split(" ")] 373 374 gc.shell_cmd("mv " + file_list + " " + target_dir_path + " >/dev/null", 375 time_out=600) 376 377 gp.qprint_timen("New robot log file locations:") 378 gp.qprintn('\n'.join(targ_file_list)) 379 380 381def robot_cmd_fnc(robot_cmd_buf, 382 robot_jail=os.environ.get('ROBOT_JAIL', '')): 383 r""" 384 Run the robot command string. 385 386 This function will set the various PATH variables correctly so that you 387 are running the proper version of all imported files, etc. 388 389 Description of argument(s): 390 robot_cmd_buf The complete robot command string. 391 robot_jail Indicates that this is to run in "robot 392 jail" meaning without visibility to any 393 apolloxxx import files, programs, etc. 394 """ 395 396 if not gv.valid_value(robot_cmd_buf): 397 return False 398 399 # Set global variables to aid in cleanup with process_robot_output_files. 400 global gcr_last_robot_cmd_buf 401 global gcr_last_robot_rc 402 gcr_last_robot_cmd_buf = robot_cmd_buf 403 404 # Get globals set by init_robot_test_base_dir_path(). 405 module = sys.modules["__main__"] 406 try: 407 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH") 408 except NameError: 409 init_robot_test_base_dir_path() 410 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH") 411 412 ROBOT_TEST_RUNNING_FROM_SB = \ 413 gm.get_mod_global("ROBOT_TEST_RUNNING_FROM_SB") 414 415 if robot_jail == "": 416 if ROBOT_TEST_RUNNING_FROM_SB: 417 robot_jail = 0 418 else: 419 robot_jail = 1 420 421 robot_jail = int(robot_jail) 422 ROBOT_JAIL = os.environ.get('ROBOT_JAIL', '') 423 gp.dprint_vars(ROBOT_TEST_BASE_DIR_PATH, ROBOT_TEST_RUNNING_FROM_SB, 424 ROBOT_JAIL, robot_jail) 425 426 # Save PATH and PYTHONPATH to be restored later. 427 os.environ["SAVED_PYTHONPATH"] = os.environ.get("PYTHONPATH", "") 428 os.environ["SAVED_PATH"] = os.environ.get("PATH", "") 429 430 if robot_jail: 431 PYTHONPATH = ROBOT_TEST_BASE_DIR_PATH + "lib" 432 NEW_PATH_LIST = [ROBOT_TEST_BASE_DIR_PATH + "bin"] 433 # Coding special case to preserve python27_path. 434 python27_path = "/opt/rh/python27/root/usr/bin" 435 PATH_LIST = os.environ.get("PATH", "").split(":") 436 if python27_path in PATH_LIST: 437 NEW_PATH_LIST.append(python27_path) 438 NEW_PATH_LIST.extend(["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", 439 "/usr/bin", "/sbin", "/bin"]) 440 PATH = ":".join(NEW_PATH_LIST) 441 else: 442 PYTHONPATH = os.environ.get('PYTHONPATH', '') + ":" +\ 443 ROBOT_TEST_BASE_DIR_PATH + "lib/" 444 PATH = os.environ.get('PATH', '') + ":" + ROBOT_TEST_BASE_DIR_PATH +\ 445 "bin/" 446 447 os.environ['PYTHONPATH'] = PYTHONPATH 448 os.environ['PATH'] = PATH 449 gp.dprint_vars(PATH, PYTHONPATH) 450 451 os.environ['FFDC_DIR_PATH_STYLE'] = os.environ.get('FFDC_DIR_PATH_STYLE', 452 '1') 453 test_mode = getattr(module, "test_mode") 454 455 gp.qpissuing(robot_cmd_buf, test_mode) 456 if test_mode: 457 os.environ["PATH"] = os.environ.get("SAVED_PATH", "") 458 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "") 459 return True 460 461 if quiet: 462 DEVNULL = open(os.devnull, 'wb') 463 stdout = DEVNULL 464 else: 465 stdout = None 466 sub_proc = subprocess.Popen(robot_cmd_buf, stdout=stdout, shell=True) 467 sub_proc.communicate() 468 shell_rc = sub_proc.returncode 469 os.environ["PATH"] = os.environ.get("SAVED_PATH", "") 470 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "") 471 gcr_last_robot_rc = shell_rc 472 process_robot_output_files() 473 if shell_rc != 0: 474 hex = 1 475 gp.print_var(shell_rc, hex) 476 return False 477 478 return True 479