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 OBMC_TOOLS_BASE_DIR_PATH = \ 138 os.path.dirname(ROBOT_TEST_BASE_DIR_PATH.rstrip("/")) \ 139 + "/openbmc-tools/" 140 OPENBMCTOOL_DIR_PATH = OBMC_TOOLS_BASE_DIR_PATH + "thalerj/" 141 MSBARTH_TOOLS_DIR_PATH = OBMC_TOOLS_BASE_DIR_PATH + "msbarth/" 142 143 gv.valid_value(ROBOT_TEST_BASE_DIR_PATH) 144 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB, ROBOT_TEST_BASE_DIR_PATH, OBMC_TOOLS_BASE_DIR_PATH, 145 OPENBMCTOOL_DIR_PATH, MSBARTH_TOOLS_DIR_PATH) 146 gv.valid_dir_path(ROBOT_TEST_BASE_DIR_PATH) 147 148 ROBOT_TEST_BASE_DIR_PATH = gm.add_trailing_slash(ROBOT_TEST_BASE_DIR_PATH) 149 gm.set_mod_global(ROBOT_TEST_BASE_DIR_PATH) 150 os.environ['ROBOT_TEST_BASE_DIR_PATH'] = ROBOT_TEST_BASE_DIR_PATH 151 152 gm.set_mod_global(ROBOT_TEST_RUNNING_FROM_SB) 153 os.environ['ROBOT_TEST_RUNNING_FROM_SB'] = str(ROBOT_TEST_RUNNING_FROM_SB) 154 155 gm.set_mod_global(OBMC_TOOLS_BASE_DIR_PATH) 156 os.environ['OBMC_TOOLS_BASE_DIR_PATH'] = str(OBMC_TOOLS_BASE_DIR_PATH) 157 158 gm.set_mod_global(OPENBMCTOOL_DIR_PATH) 159 os.environ['OPENBMCTOOL_DIR_PATH'] = str(OPENBMCTOOL_DIR_PATH) 160 161 gm.set_mod_global(MSBARTH_TOOLS_DIR_PATH) 162 os.environ['MSBARTH_TOOLS_DIR_PATH'] = str(MSBARTH_TOOLS_DIR_PATH) 163 164 165raw_robot_file_search_path = "${ROBOT_TEST_BASE_DIR_PATH}:" +\ 166 "${ROBOT_TEST_BASE_DIR_PATH}tests:${ROBOT_TEST_BASE_DIR_PATH}extended:" +\ 167 "${ROBOT_TEST_BASE_DIR_PATH}scratch:${PATH}" 168 169 170def init_robot_file_path(robot_file_path): 171 r""" 172 Determine full path name for the file path passed in robot_file_path and return it. 173 174 If robot_file_path contains a fully qualified path name, this function will verify that the file exists. 175 If robot_file_path contains a relative path, this function will search for the file and set 176 robot_file_path so that it contains the absolute path to the robot file. This function will search for 177 the robot file using the raw_robot_file_search_path (defined above). Note that if 178 ROBOT_TEST_BASE_DIR_PATH is not set, this function will call init_robot_test_base_dir_path to set it. 179 180 Description of arguments: 181 robot_file_path The absolute or relative path to a robot file. 182 """ 183 184 gv.valid_value(robot_file_path) 185 186 try: 187 if ROBOT_TEST_BASE_DIR_PATH is NONE: 188 init_robot_test_base_dir_path() 189 except NameError: 190 init_robot_test_base_dir_path() 191 192 if not re.match(r".*\.(robot|py)$", robot_file_path): 193 # No suffix so we'll assign one of "\.robot". 194 robot_file_path = robot_file_path + ".robot" 195 196 abs_path = 0 197 if robot_file_path[0:1] == "/": 198 abs_path = 1 199 200 gp.dprint_vars(abs_path, robot_file_path) 201 202 if not abs_path: 203 cmd_buf = "echo -n \"" + raw_robot_file_search_path + "\"" 204 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=(not debug), 205 print_output=0) 206 robot_file_search_paths = out_buf 207 gp.dprint_var(robot_file_search_paths) 208 robot_file_search_paths_list = robot_file_search_paths.split(':') 209 for search_path in robot_file_search_paths_list: 210 search_path = gm.add_trailing_slash(search_path) 211 candidate_file_path = search_path + robot_file_path 212 gp.dprint_var(candidate_file_path) 213 if os.path.isfile(candidate_file_path): 214 gp.dprint_timen("Found full path to " + robot_file_path + ".") 215 robot_file_path = candidate_file_path 216 break 217 218 gp.dprint_var(robot_file_path) 219 gv.valid_file_path(robot_file_path) 220 221 return robot_file_path 222 223 224def get_robot_parm_names(): 225 r""" 226 Return a list containing all of the long parm names (e.g. --outputdir) supported by the robot program. 227 Double dashes are not included in the names returned. 228 """ 229 230 cmd_buf = "robot -h | egrep " +\ 231 "'^([ ]\\-[a-zA-Z0-9])?[ ]+--[a-zA-Z0-9]+[ ]+' | sed -re" +\ 232 " s'/.*\\-\\-//g' -e s'/ .*//g' | sort -u" 233 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=1, print_output=0) 234 235 return out_buf.split("\n") 236 237 238def create_robot_cmd_string(robot_file_path, *parms): 239 r""" 240 Create a robot command string and return it. On failure, return an empty string. 241 242 Description of arguments: 243 robot_file_path The path to the robot file to be run. 244 parms The list of parms to be included in the command string. The name of each 245 variable in this list must be the same as the name of the corresponding 246 parm. This function figures out that name. This function is also able 247 to distinguish robot parms (e.g. --outputdir) from robot program parms 248 (all other parms which will be passed as "-v 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", OPENBMC_HOST, quiet, test_mode, debug, 254 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/status --output=beye6.OS_Console.output.xml 259 --log=beye6.OS_Console.log.html --report=beye6.OS_Console.report.html tools/start_sol_console.robot 260 """ 261 262 robot_file_path = init_robot_file_path(robot_file_path) 263 264 robot_parm_names = get_robot_parm_names() 265 266 robot_parm_list = [] 267 268 stack_frame = 2 269 ix = 2 270 for arg in parms: 271 parm = arg 272 parm = gm.quote_bash_parm(gm.escape_bash_quotes(str(parm))) 273 var_name = gp.get_arg_name(None, ix, stack_frame) 274 if var_name in robot_parm_names: 275 p_string = "--" + var_name + "=" + str(parm) 276 robot_parm_list.append(p_string) 277 else: 278 p_string = "-v " + var_name + ":" + str(parm) 279 robot_parm_list.append(p_string) 280 ix += 1 281 282 robot_cmd_buf = "robot " + ' '.join(robot_parm_list) + " " +\ 283 robot_file_path 284 285 return robot_cmd_buf 286 287 288# Global variables to aid in cleanup after running robot_cmd_fnc. 289gcr_last_robot_cmd_buf = "" 290gcr_last_robot_rc = 0 291 292 293def process_robot_output_files(robot_cmd_buf=None, 294 robot_rc=None, 295 gzip=None): 296 r""" 297 Process robot output files which can involve several operations: 298 - If the files are in a temporary location, using SAVE_STATUS_POLICY to decide whether to move them to a 299 permanent location or to delete them. 300 - Gzipping them. 301 302 Description of argument(s): 303 robot_cmd_buf The complete command string used to invoke robot. 304 robot_rc The return code from running the robot command string. 305 gzip Indicates whether robot-generated output should be gzipped. 306 """ 307 308 robot_cmd_buf = gm.dft(robot_cmd_buf, gcr_last_robot_cmd_buf) 309 robot_rc = gm.dft(robot_rc, gcr_last_robot_rc) 310 gzip = gm.dft(gzip, int(os.environ.get("GZIP_ROBOT", "1"))) 311 312 if robot_cmd_buf == "": 313 # This can legitimately occur if this function is called from an exit_function without the program 314 # having ever run robot_cmd_fnc. 315 return 316 317 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS") 318 gp.qprint_vars(SAVE_STATUS_POLICY) 319 320 # When SAVE_STATUS_POLICY is "NEVER" robot output files don't even get generated. 321 if SAVE_STATUS_POLICY == "NEVER": 322 return 323 324 # Compose file_list based on robot command buffer passed in. 325 robot_cmd_buf_dict = gc.parse_command_string(robot_cmd_buf) 326 outputdir = robot_cmd_buf_dict['outputdir'] 327 outputdir = gm.add_trailing_slash(outputdir) 328 file_list = outputdir + robot_cmd_buf_dict['output'] + " " + outputdir\ 329 + robot_cmd_buf_dict['log'] + " " + outputdir\ 330 + robot_cmd_buf_dict['report'] 331 332 # Double checking that files are present. 333 shell_rc, out_buf = gc.shell_cmd("ls -1 " + file_list + " 2>/dev/null", 334 show_err=0) 335 file_list = re.sub("\n", " ", out_buf.rstrip("\n")) 336 337 if file_list == "": 338 gp.qprint_timen("No robot output files were found in " + outputdir 339 + ".") 340 return 341 gp.qprint_var(robot_rc, gp.hexa()) 342 if SAVE_STATUS_POLICY == "FAIL" and robot_rc == 0: 343 gp.qprint_timen("The call to robot produced no failures." 344 + " Deleting robot output files.") 345 gc.shell_cmd("rm -rf " + file_list) 346 return 347 348 if gzip: 349 gc.shell_cmd("gzip -f " + file_list) 350 # Update the values in file_list. 351 file_list = re.sub(" ", ".gz ", file_list) + ".gz" 352 353 # It TMP_ROBOT_DIR_PATH is set, it means the caller wanted the robot output initially directed to 354 # TMP_ROBOT_DIR_PATH but later moved to FFDC_DIR_PATH. Otherwise, we're done. 355 356 if os.environ.get("TMP_ROBOT_DIR_PATH", "") is "": 357 return 358 359 # We're directing these to the FFDC dir path so that they'll be subjected to FFDC cleanup. 360 target_dir_path = os.environ.get("FFDC_DIR_PATH", 361 os.environ.get("HOME", ".") 362 + "/ffdc") 363 target_dir_path = gm.add_trailing_slash(target_dir_path) 364 365 targ_file_list = [re.sub(".*/", target_dir_path, x) 366 for x in file_list.split(" ")] 367 368 gc.shell_cmd("mv " + file_list + " " + target_dir_path + " >/dev/null", 369 time_out=600) 370 371 gp.qprint_timen("New robot log file locations:") 372 gp.qprintn('\n'.join(targ_file_list)) 373 374 375def robot_cmd_fnc(robot_cmd_buf, 376 robot_jail=os.environ.get('ROBOT_JAIL', ''), test_mode=0): 377 r""" 378 Run the robot command string. 379 380 This function will set the various PATH variables correctly so that you are running the proper version of 381 all imported files, etc. 382 383 Description of argument(s): 384 robot_cmd_buf The complete robot command string. 385 robot_jail Indicates that this is to run in "robot jail" meaning without visibility 386 to any apolloxxx import files, programs, etc. 387 test_mode If test_mode is set, this function will not actually run the command. 388 """ 389 390 gv.valid_value(robot_cmd_buf) 391 392 # Set global variables to aid in cleanup with process_robot_output_files. 393 global gcr_last_robot_cmd_buf 394 global gcr_last_robot_rc 395 gcr_last_robot_cmd_buf = robot_cmd_buf 396 397 # Get globals set by init_robot_test_base_dir_path(). 398 module = sys.modules["__main__"] 399 try: 400 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH") 401 except NameError: 402 init_robot_test_base_dir_path() 403 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH") 404 405 ROBOT_TEST_RUNNING_FROM_SB = gm.get_mod_global("ROBOT_TEST_RUNNING_FROM_SB") 406 OPENBMCTOOL_DIR_PATH = gm.get_mod_global("OPENBMCTOOL_DIR_PATH") 407 408 if robot_jail == "": 409 if ROBOT_TEST_RUNNING_FROM_SB: 410 robot_jail = 0 411 else: 412 robot_jail = 1 413 414 robot_jail = int(robot_jail) 415 ROBOT_JAIL = os.environ.get('ROBOT_JAIL', '') 416 gp.dprint_vars(ROBOT_TEST_BASE_DIR_PATH, ROBOT_TEST_RUNNING_FROM_SB, 417 ROBOT_JAIL, robot_jail) 418 419 # Save PATH and PYTHONPATH to be restored later. 420 os.environ["SAVED_PYTHONPATH"] = os.environ.get("PYTHONPATH", "") 421 os.environ["SAVED_PATH"] = os.environ.get("PATH", "") 422 423 if robot_jail: 424 # Make sure required programs like python and robot can be found in the new restricted PATH. 425 required_programs = "python robot" 426 # It is expected that there will be a "python" program in the tool base bin path which is really a 427 # link to select_version. Ditto for "robot". Call each with the --print_only option to get the 428 # paths to the "real" programs. 429 cmd_buf = "for program in " + required_programs \ 430 + " ; do dirname $(${program} --print_only) ; done 2>/dev/null" 431 rc, out_buf = gc.shell_cmd(cmd_buf, quiet=1, print_output=0) 432 PYTHONPATH = ROBOT_TEST_BASE_DIR_PATH + "lib" 433 NEW_PATH_LIST = [ROBOT_TEST_BASE_DIR_PATH + "bin"] 434 NEW_PATH_LIST.extend(list(set(out_buf.rstrip("\n").split("\n")))) 435 NEW_PATH_LIST.extend(["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", 436 "/usr/bin", "/sbin", "/bin", 437 OPENBMCTOOL_DIR_PATH.rstrip('/')]) 438 PATH = ":".join(NEW_PATH_LIST) 439 else: 440 PYTHONPATH = os.environ.get('PYTHONPATH', '') + ":" +\ 441 ROBOT_TEST_BASE_DIR_PATH + "lib" 442 PATH = os.environ.get('PATH', '') + ":" + ROBOT_TEST_BASE_DIR_PATH +\ 443 "bin" + ":" + OPENBMCTOOL_DIR_PATH.rstrip('/') 444 445 os.environ['PYTHONPATH'] = PYTHONPATH 446 os.environ['PATH'] = PATH 447 gp.dprint_vars(PATH, PYTHONPATH) 448 449 os.environ['FFDC_DIR_PATH_STYLE'] = os.environ.get('FFDC_DIR_PATH_STYLE', 450 '1') 451 gp.qpissuing(robot_cmd_buf, test_mode) 452 if test_mode: 453 os.environ["PATH"] = os.environ.get("SAVED_PATH", "") 454 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "") 455 return True 456 457 if quiet: 458 DEVNULL = open(os.devnull, 'wb') 459 stdout = DEVNULL 460 else: 461 stdout = None 462 sub_proc = subprocess.Popen(robot_cmd_buf, stdout=stdout, shell=True) 463 sub_proc.communicate() 464 shell_rc = sub_proc.returncode 465 os.environ["PATH"] = os.environ.get("SAVED_PATH", "") 466 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "") 467 gcr_last_robot_rc = shell_rc 468 process_robot_output_files() 469 if shell_rc != 0: 470 gp.print_var(shell_rc, gp.hexa()) 471 return False 472 473 return True 474