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