1#!/usr/bin/env python3 2 3r""" 4This module is the python counterpart to obmc_boot_test. 5""" 6 7import os 8import imp 9import time 10import glob 11import random 12import re 13import signal 14try: 15 import cPickle as pickle 16except ImportError: 17 import pickle 18import socket 19 20from robot.utils import DotDict 21from robot.libraries.BuiltIn import BuiltIn 22 23from boot_data import * 24import gen_print as gp 25import gen_robot_plug_in as grpi 26import gen_arg as ga 27import gen_valid as gv 28import gen_misc as gm 29import gen_cmd as gc 30import gen_robot_keyword as grk 31import state as st 32import var_stack as vs 33import gen_plug_in_utils as gpu 34import pel_utils as pel 35import logging_utils as log 36 37base_path = os.path.dirname(os.path.dirname( 38 imp.find_module("gen_robot_print")[1])) +\ 39 os.sep 40sys.path.append(base_path + "extended/") 41import run_keyword as rk 42 43# Setting master_pid correctly influences the behavior of plug-ins like 44# DB_Logging 45program_pid = os.getpid() 46master_pid = os.environ.get('AUTOBOOT_MASTER_PID', program_pid) 47pgm_name = re.sub('\\.py$', '', os.path.basename(__file__)) 48 49# Set up boot data structures. 50os_host = BuiltIn().get_variable_value("${OS_HOST}", default="") 51 52boot_lists = read_boot_lists() 53 54# The maximum number of entries that can be in the boot_history global variable. 55max_boot_history = 10 56boot_history = [] 57 58state = st.return_state_constant('default_state') 59cp_setup_called = 0 60next_boot = "" 61base_tool_dir_path = os.path.normpath(os.environ.get( 62 'AUTOBOOT_BASE_TOOL_DIR_PATH', "/tmp")) + os.sep 63 64ffdc_dir_path = os.path.normpath(os.environ.get('FFDC_DIR_PATH', '')) + os.sep 65boot_success = 0 66status_dir_path = os.environ.get('STATUS_DIR_PATH', "") 67if status_dir_path != "": 68 status_dir_path = os.path.normpath(status_dir_path) + os.sep 69redfish_support_trans_state = int(os.environ.get('REDFISH_SUPPORT_TRANS_STATE', 0)) or \ 70 int(BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0)) 71redfish_supported = BuiltIn().get_variable_value("${REDFISH_SUPPORTED}", default=False) 72redfish_rest_supported = BuiltIn().get_variable_value("${REDFISH_REST_SUPPORTED}", default=False) 73redfish_delete_sessions = int(BuiltIn().get_variable_value("${REDFISH_DELETE_SESSIONS}", default=1)) 74if redfish_supported: 75 redfish = BuiltIn().get_library_instance('redfish') 76 default_power_on = "Redfish Power On" 77 default_power_off = "Redfish Power Off" 78 if not redfish_support_trans_state: 79 delete_errlogs_cmd = "Delete Error Logs ${quiet}=${1}" 80 delete_bmcdump_cmd = "Delete All BMC Dump" 81 default_set_power_policy = "Set BMC Power Policy ALWAYS_POWER_OFF" 82 else: 83 delete_errlogs_cmd = "Redfish Purge Event Log" 84 delete_bmcdump_cmd = "Redfish Delete All BMC Dumps" 85 delete_sysdump_cmd = "Redfish Delete All System Dumps" 86 default_set_power_policy = "Redfish Set Power Restore Policy AlwaysOff" 87else: 88 default_power_on = "REST Power On" 89 default_power_off = "REST Power Off" 90 delete_errlogs_cmd = "Delete Error Logs ${quiet}=${1}" 91 delete_bmcdump_cmd = "Delete All BMC Dump" 92 default_set_power_policy = "Set BMC Power Policy ALWAYS_POWER_OFF" 93boot_count = 0 94 95LOG_LEVEL = BuiltIn().get_variable_value("${LOG_LEVEL}") 96AUTOBOOT_FFDC_PREFIX = os.environ.get('AUTOBOOT_FFDC_PREFIX', '') 97ffdc_prefix = AUTOBOOT_FFDC_PREFIX 98boot_start_time = "" 99boot_end_time = "" 100save_stack = vs.var_stack('save_stack') 101main_func_parm_list = ['boot_stack', 'stack_mode', 'quiet'] 102 103 104def dump_ffdc_rc(): 105 r""" 106 Return the constant dump ffdc test return code value. 107 108 When a plug-in call point program returns this value, it indicates that 109 this program should collect FFDC. 110 """ 111 112 return 0x00000200 113 114 115def stop_test_rc(): 116 r""" 117 Return the constant stop test return code value. 118 119 When a plug-in call point program returns this value, it indicates that 120 this program should stop running. 121 """ 122 123 return 0x00000200 124 125 126def process_host(host, 127 host_var_name=""): 128 r""" 129 Process a host by getting the associated host name and IP address and 130 setting them in global variables. 131 132 If the caller does not pass the host_var_name, this function will try to 133 figure out the name of the variable used by the caller for the host parm. 134 Callers are advised to explicitly specify the host_var_name when calling 135 with an exec command. In such cases, the get_arg_name cannot figure out 136 the host variable name. 137 138 This function will then create similar global variable names by 139 removing "_host" and appending "_host_name" or "_ip" to the host variable 140 name. 141 142 Example: 143 144 If a call is made like this: 145 process_host(openbmc_host) 146 147 Global variables openbmc_host_name and openbmc_ip will be set. 148 149 Description of argument(s): 150 host A host name or IP. The name of the variable used should 151 have a suffix of "_host". 152 host_var_name The name of the variable being used as the host parm. 153 """ 154 155 if host_var_name == "": 156 host_var_name = gp.get_arg_name(0, 1, stack_frame_ix=2) 157 158 host_name_var_name = re.sub("host", "host_name", host_var_name) 159 ip_var_name = re.sub("host", "ip", host_var_name) 160 cmd_buf = "global " + host_name_var_name + ", " + ip_var_name + " ; " +\ 161 host_name_var_name + ", " + ip_var_name + " = gm.get_host_name_ip('" +\ 162 host + "')" 163 exec(cmd_buf) 164 165 166def process_pgm_parms(): 167 r""" 168 Process the program parameters by assigning them all to corresponding 169 globals. Also, set some global values that depend on program parameters. 170 """ 171 172 # Program parameter processing. 173 # Assign all program parms to python variables which are global to this 174 # module. 175 176 global parm_list 177 parm_list = BuiltIn().get_variable_value("${parm_list}") 178 # The following subset of parms should be processed as integers. 179 int_list = ['max_num_tests', 'boot_pass', 'boot_fail', 'ffdc_only', 180 'boot_fail_threshold', 'delete_errlogs', 181 'call_post_stack_plug', 'do_pre_boot_plug_in_setup', 'quiet', 182 'test_mode', 'debug'] 183 for parm in parm_list: 184 if parm in int_list: 185 sub_cmd = "int(BuiltIn().get_variable_value(\"${" + parm +\ 186 "}\", \"0\"))" 187 else: 188 sub_cmd = "BuiltIn().get_variable_value(\"${" + parm + "}\")" 189 cmd_buf = "global " + parm + " ; " + parm + " = " + sub_cmd 190 gp.dpissuing(cmd_buf) 191 exec(cmd_buf) 192 if re.match(r".*_host$", parm): 193 cmd_buf = "process_host(" + parm + ", '" + parm + "')" 194 exec(cmd_buf) 195 if re.match(r".*_password$", parm): 196 # Register the value of any parm whose name ends in _password. 197 # This will cause the print functions to replace passwords with 198 # asterisks in the output. 199 cmd_buf = "gp.register_passwords(" + parm + ")" 200 exec(cmd_buf) 201 202 global ffdc_dir_path_style 203 global boot_list 204 global boot_stack 205 global boot_results_file_path 206 global boot_results 207 global boot_history 208 global ffdc_list_file_path 209 global ffdc_report_list_path 210 global ffdc_summary_list_path 211 global boot_table 212 global valid_boot_types 213 214 if ffdc_dir_path_style == "": 215 ffdc_dir_path_style = int(os.environ.get('FFDC_DIR_PATH_STYLE', '0')) 216 217 # Convert these program parms to lists for easier processing.. 218 boot_list = list(filter(None, boot_list.split(":"))) 219 boot_stack = list(filter(None, boot_stack.split(":"))) 220 221 boot_table = create_boot_table(boot_table_path, os_host=os_host) 222 valid_boot_types = create_valid_boot_list(boot_table) 223 224 cleanup_boot_results_file() 225 boot_results_file_path = create_boot_results_file_path(pgm_name, 226 openbmc_nickname, 227 master_pid) 228 229 if os.path.isfile(boot_results_file_path): 230 # We've been called before in this run so we'll load the saved 231 # boot_results and boot_history objects. 232 boot_results, boot_history =\ 233 pickle.load(open(boot_results_file_path, 'rb')) 234 else: 235 boot_results = boot_results(boot_table, boot_pass, boot_fail) 236 237 ffdc_list_file_path = base_tool_dir_path + openbmc_nickname +\ 238 "/FFDC_FILE_LIST" 239 ffdc_report_list_path = base_tool_dir_path + openbmc_nickname +\ 240 "/FFDC_REPORT_FILE_LIST" 241 242 ffdc_summary_list_path = base_tool_dir_path + openbmc_nickname +\ 243 "/FFDC_SUMMARY_FILE_LIST" 244 245 246def initial_plug_in_setup(): 247 r""" 248 Initialize all plug-in environment variables which do not change for the 249 duration of the program. 250 251 """ 252 253 global LOG_LEVEL 254 BuiltIn().set_log_level("NONE") 255 256 BuiltIn().set_global_variable("${master_pid}", master_pid) 257 BuiltIn().set_global_variable("${FFDC_DIR_PATH}", ffdc_dir_path) 258 BuiltIn().set_global_variable("${STATUS_DIR_PATH}", status_dir_path) 259 BuiltIn().set_global_variable("${BASE_TOOL_DIR_PATH}", base_tool_dir_path) 260 BuiltIn().set_global_variable("${FFDC_LIST_FILE_PATH}", 261 ffdc_list_file_path) 262 BuiltIn().set_global_variable("${FFDC_REPORT_LIST_PATH}", 263 ffdc_report_list_path) 264 BuiltIn().set_global_variable("${FFDC_SUMMARY_LIST_PATH}", 265 ffdc_summary_list_path) 266 267 BuiltIn().set_global_variable("${FFDC_DIR_PATH_STYLE}", 268 ffdc_dir_path_style) 269 BuiltIn().set_global_variable("${FFDC_CHECK}", 270 ffdc_check) 271 272 # For each program parameter, set the corresponding AUTOBOOT_ environment 273 # variable value. Also, set an AUTOBOOT_ environment variable for every 274 # element in additional_values. 275 additional_values = ["program_pid", "master_pid", "ffdc_dir_path", 276 "status_dir_path", "base_tool_dir_path", 277 "ffdc_list_file_path", "ffdc_report_list_path", 278 "ffdc_summary_list_path", "execdir", "redfish_supported", 279 "redfish_rest_supported", "redfish_support_trans_state"] 280 281 plug_in_vars = parm_list + additional_values 282 283 for var_name in plug_in_vars: 284 var_value = BuiltIn().get_variable_value("${" + var_name + "}") 285 var_name = var_name.upper() 286 if var_value is None: 287 var_value = "" 288 os.environ["AUTOBOOT_" + var_name] = str(var_value) 289 290 BuiltIn().set_log_level(LOG_LEVEL) 291 292 # Make sure the ffdc list directory exists. 293 ffdc_list_dir_path = os.path.dirname(ffdc_list_file_path) + os.sep 294 if not os.path.exists(ffdc_list_dir_path): 295 os.makedirs(ffdc_list_dir_path) 296 297 298def plug_in_setup(): 299 r""" 300 Initialize all changing plug-in environment variables for use by the 301 plug-in programs. 302 """ 303 304 global LOG_LEVEL 305 global test_really_running 306 307 BuiltIn().set_log_level("NONE") 308 309 boot_pass, boot_fail = boot_results.return_total_pass_fail() 310 if boot_pass > 1: 311 test_really_running = 1 312 else: 313 test_really_running = 0 314 315 BuiltIn().set_global_variable("${test_really_running}", 316 test_really_running) 317 BuiltIn().set_global_variable("${boot_type_desc}", next_boot) 318 BuiltIn().set_global_variable("${boot_pass}", boot_pass) 319 BuiltIn().set_global_variable("${boot_fail}", boot_fail) 320 BuiltIn().set_global_variable("${boot_success}", boot_success) 321 BuiltIn().set_global_variable("${ffdc_prefix}", ffdc_prefix) 322 BuiltIn().set_global_variable("${boot_start_time}", boot_start_time) 323 BuiltIn().set_global_variable("${boot_end_time}", boot_end_time) 324 325 # For each program parameter, set the corresponding AUTOBOOT_ environment 326 # variable value. Also, set an AUTOBOOT_ environment variable for every 327 # element in additional_values. 328 additional_values = ["boot_type_desc", "boot_success", "boot_pass", 329 "boot_fail", "test_really_running", "ffdc_prefix", 330 "boot_start_time", "boot_end_time"] 331 332 plug_in_vars = additional_values 333 334 for var_name in plug_in_vars: 335 var_value = BuiltIn().get_variable_value("${" + var_name + "}") 336 var_name = var_name.upper() 337 if var_value is None: 338 var_value = "" 339 os.environ["AUTOBOOT_" + var_name] = str(var_value) 340 341 if debug: 342 shell_rc, out_buf = \ 343 gc.cmd_fnc_u("printenv | egrep AUTOBOOT_ | sort -u") 344 345 BuiltIn().set_log_level(LOG_LEVEL) 346 347 348def pre_boot_plug_in_setup(): 349 350 # Clear the ffdc_list_file_path file. Plug-ins may now write to it. 351 try: 352 os.remove(ffdc_list_file_path) 353 except OSError: 354 pass 355 356 # Clear the ffdc_report_list_path file. Plug-ins may now write to it. 357 try: 358 os.remove(ffdc_report_list_path) 359 except OSError: 360 pass 361 362 # Clear the ffdc_summary_list_path file. Plug-ins may now write to it. 363 try: 364 os.remove(ffdc_summary_list_path) 365 except OSError: 366 pass 367 368 global ffdc_prefix 369 370 seconds = time.time() 371 loc_time = time.localtime(seconds) 372 time_string = time.strftime("%y%m%d.%H%M%S.", loc_time) 373 374 ffdc_prefix = openbmc_nickname + "." + time_string 375 376 377def default_sigusr1(signal_number=0, 378 frame=None): 379 r""" 380 Handle SIGUSR1 by doing nothing. 381 382 This function assists in debugging SIGUSR1 processing by printing messages 383 to stdout and to the log.html file. 384 385 Description of argument(s): 386 signal_number The signal number (should always be 10 for SIGUSR1). 387 frame The frame data. 388 """ 389 390 gp.qprintn() 391 gp.qprint_executing() 392 gp.lprint_executing() 393 394 395def set_default_siguser1(): 396 r""" 397 Set the default_sigusr1 function to be the SIGUSR1 handler. 398 """ 399 400 gp.qprintn() 401 gp.qprint_executing() 402 gp.lprint_executing() 403 signal.signal(signal.SIGUSR1, default_sigusr1) 404 405 406def setup(): 407 r""" 408 Do general program setup tasks. 409 """ 410 411 global cp_setup_called 412 global transitional_boot_selected 413 414 gp.qprintn() 415 416 if redfish_supported: 417 redfish.login() 418 419 set_default_siguser1() 420 transitional_boot_selected = False 421 422 robot_pgm_dir_path = os.path.dirname(__file__) + os.sep 423 repo_bin_path = robot_pgm_dir_path.replace("/lib/", "/bin/") 424 # If we can't find process_plug_in_packages.py, ssh_pw or 425 # validate_plug_ins.py, then we don't have our repo bin in PATH. 426 shell_rc, out_buf = gc.cmd_fnc_u("which process_plug_in_packages.py" 427 + " ssh_pw validate_plug_ins.py", quiet=1, 428 print_output=0, show_err=0) 429 if shell_rc != 0: 430 os.environ['PATH'] = repo_bin_path + ":" + os.environ.get('PATH', "") 431 # Likewise, our repo lib subdir needs to be in sys.path and PYTHONPATH. 432 if robot_pgm_dir_path not in sys.path: 433 sys.path.append(robot_pgm_dir_path) 434 PYTHONPATH = os.environ.get("PYTHONPATH", "") 435 if PYTHONPATH == "": 436 os.environ['PYTHONPATH'] = robot_pgm_dir_path 437 else: 438 os.environ['PYTHONPATH'] = robot_pgm_dir_path + ":" + PYTHONPATH 439 440 validate_parms() 441 442 gp.qprint_pgm_header() 443 444 grk.run_key_u(default_set_power_policy) 445 446 initial_plug_in_setup() 447 448 plug_in_setup() 449 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 450 call_point='setup') 451 if rc != 0: 452 error_message = "Plug-in setup failed.\n" 453 gp.print_error_report(error_message) 454 BuiltIn().fail(error_message) 455 # Setting cp_setup_called lets our Teardown know that it needs to call 456 # the cleanup plug-in call point. 457 cp_setup_called = 1 458 459 # Keyword "FFDC" will fail if TEST_MESSAGE is not set. 460 BuiltIn().set_global_variable("${TEST_MESSAGE}", "${EMPTY}") 461 # FFDC_LOG_PATH is used by "FFDC" keyword. 462 BuiltIn().set_global_variable("${FFDC_LOG_PATH}", ffdc_dir_path) 463 464 # Also printed by FFDC. 465 global host_name 466 global host_ip 467 host = socket.gethostname() 468 host_name, host_ip = gm.get_host_name_ip(host) 469 470 gp.dprint_var(boot_table) 471 gp.dprint_var(boot_lists) 472 473 474def validate_parms(): 475 r""" 476 Validate all program parameters. 477 """ 478 479 process_pgm_parms() 480 481 gp.qprintn() 482 483 global openbmc_model 484 if openbmc_model == "": 485 status, ret_values =\ 486 grk.run_key_u("Get BMC System Model") 487 openbmc_model = ret_values 488 BuiltIn().set_global_variable("${openbmc_model}", openbmc_model) 489 gv.set_exit_on_error(True) 490 gv.valid_value(openbmc_host) 491 gv.valid_value(openbmc_username) 492 gv.valid_value(openbmc_password) 493 gv.valid_value(rest_username) 494 gv.valid_value(rest_password) 495 gv.valid_value(ipmi_username) 496 gv.valid_value(ipmi_password) 497 if os_host != "": 498 gv.valid_value(os_username) 499 gv.valid_value(os_password) 500 if pdu_host != "": 501 gv.valid_value(pdu_username) 502 gv.valid_value(pdu_password) 503 gv.valid_integer(pdu_slot_no) 504 if openbmc_serial_host != "": 505 gv.valid_integer(openbmc_serial_port) 506 gv.valid_value(openbmc_model) 507 gv.valid_integer(max_num_tests) 508 gv.valid_integer(boot_pass) 509 gv.valid_integer(boot_fail) 510 plug_in_packages_list = grpi.rvalidate_plug_ins(plug_in_dir_paths) 511 BuiltIn().set_global_variable("${plug_in_packages_list}", 512 plug_in_packages_list) 513 gv.valid_value(stack_mode, valid_values=['normal', 'skip']) 514 gv.set_exit_on_error(False) 515 if len(boot_list) == 0 and len(boot_stack) == 0 and not ffdc_only: 516 error_message = "You must provide either a value for either the" +\ 517 " boot_list or the boot_stack parm.\n" 518 BuiltIn().fail(gp.sprint_error(error_message)) 519 valid_boot_list(boot_list, valid_boot_types) 520 valid_boot_list(boot_stack, valid_boot_types) 521 selected_PDU_boots = list(set(boot_list + boot_stack) 522 & set(boot_lists['PDU_reboot'])) 523 if len(selected_PDU_boots) > 0 and pdu_host == "": 524 error_message = "You have selected the following boots which" +\ 525 " require a PDU host but no value for pdu_host:\n" 526 error_message += gp.sprint_var(selected_PDU_boots) 527 error_message += gp.sprint_var(pdu_host, fmt=gp.blank()) 528 BuiltIn().fail(gp.sprint_error(error_message)) 529 530 return 531 532 533def my_get_state(): 534 r""" 535 Get the system state plus a little bit of wrapping. 536 """ 537 538 global state 539 540 req_states = ['epoch_seconds'] + st.default_req_states 541 542 gp.qprint_timen("Getting system state.") 543 if test_mode: 544 state['epoch_seconds'] = int(time.time()) 545 else: 546 state = st.get_state(req_states=req_states, quiet=quiet) 547 gp.qprint_var(state) 548 549 550def valid_state(): 551 r""" 552 Verify that our state dictionary contains no blank values. If we don't get 553 valid state data, we cannot continue to work. 554 """ 555 556 if st.compare_states(state, st.invalid_state_match, 'or'): 557 error_message = "The state dictionary contains blank fields which" +\ 558 " is illegal.\n" + gp.sprint_var(state) 559 BuiltIn().fail(gp.sprint_error(error_message)) 560 561 562def select_boot(): 563 r""" 564 Select a boot test to be run based on our current state and return the 565 chosen boot type. 566 567 Description of arguments: 568 state The state of the machine. 569 """ 570 571 global transitional_boot_selected 572 global boot_stack 573 574 gp.qprint_timen("Selecting a boot test.") 575 576 if transitional_boot_selected and not boot_success: 577 prior_boot = next_boot 578 boot_candidate = boot_stack.pop() 579 gp.qprint_timen("The prior '" + next_boot + "' was chosen to" 580 + " transition to a valid state for '" + boot_candidate 581 + "' which was at the top of the boot_stack. Since" 582 + " the '" + next_boot + "' failed, the '" 583 + boot_candidate + "' has been removed from the stack" 584 + " to avoid and endless failure loop.") 585 if len(boot_stack) == 0: 586 return "" 587 588 my_get_state() 589 valid_state() 590 591 transitional_boot_selected = False 592 stack_popped = 0 593 if len(boot_stack) > 0: 594 stack_popped = 1 595 gp.qprint_dashes() 596 gp.qprint_var(boot_stack) 597 gp.qprint_dashes() 598 skip_boot_printed = 0 599 while len(boot_stack) > 0: 600 boot_candidate = boot_stack.pop() 601 if stack_mode == 'normal': 602 break 603 else: 604 if st.compare_states(state, boot_table[boot_candidate]['end']): 605 if not skip_boot_printed: 606 gp.qprint_var(stack_mode) 607 gp.qprintn() 608 gp.qprint_timen("Skipping the following boot tests" 609 + " which are unnecessary since their" 610 + " required end states match the" 611 + " current machine state:") 612 skip_boot_printed = 1 613 gp.qprint_var(boot_candidate) 614 boot_candidate = "" 615 if boot_candidate == "": 616 gp.qprint_dashes() 617 gp.qprint_var(boot_stack) 618 gp.qprint_dashes() 619 return boot_candidate 620 if st.compare_states(state, boot_table[boot_candidate]['start']): 621 gp.qprint_timen("The machine state is valid for a '" 622 + boot_candidate + "' boot test.") 623 gp.qprint_dashes() 624 gp.qprint_var(boot_stack) 625 gp.qprint_dashes() 626 return boot_candidate 627 else: 628 gp.qprint_timen("The machine state does not match the required" 629 + " starting state for a '" + boot_candidate 630 + "' boot test:") 631 gp.qprint_varx("boot_table_start_entry", 632 boot_table[boot_candidate]['start']) 633 boot_stack.append(boot_candidate) 634 transitional_boot_selected = True 635 popped_boot = boot_candidate 636 637 # Loop through your list selecting a boot_candidates 638 boot_candidates = [] 639 for boot_candidate in boot_list: 640 if st.compare_states(state, boot_table[boot_candidate]['start']): 641 if stack_popped: 642 if st.compare_states(boot_table[boot_candidate]['end'], 643 boot_table[popped_boot]['start']): 644 boot_candidates.append(boot_candidate) 645 else: 646 boot_candidates.append(boot_candidate) 647 648 if len(boot_candidates) == 0: 649 gp.qprint_timen("The user's boot list contained no boot tests" 650 + " which are valid for the current machine state.") 651 boot_candidate = default_power_on 652 if not st.compare_states(state, boot_table[default_power_on]['start']): 653 boot_candidate = default_power_off 654 boot_candidates.append(boot_candidate) 655 gp.qprint_timen("Using default '" + boot_candidate 656 + "' boot type to transition to valid state.") 657 658 gp.dprint_var(boot_candidates) 659 660 # Randomly select a boot from the candidate list. 661 boot = random.choice(boot_candidates) 662 663 return boot 664 665 666def print_defect_report(ffdc_file_list): 667 r""" 668 Print a defect report. 669 670 Description of argument(s): 671 ffdc_file_list A list of files which were collected by our ffdc functions. 672 """ 673 674 # Making deliberate choice to NOT run plug_in_setup(). We don't want 675 # ffdc_prefix updated. 676 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 677 call_point='ffdc_report', stop_on_plug_in_failure=0) 678 679 # Get additional header data which may have been created by ffdc plug-ins. 680 # Also, delete the individual header files to cleanup. 681 cmd_buf = "file_list=$(cat " + ffdc_report_list_path + " 2>/dev/null)" +\ 682 " ; [ ! -z \"${file_list}\" ] && cat ${file_list}" +\ 683 " 2>/dev/null ; rm -rf ${file_list} 2>/dev/null || :" 684 shell_rc, more_header_info = gc.cmd_fnc_u(cmd_buf, print_output=0, 685 show_err=0) 686 687 # Get additional summary data which may have been created by ffdc plug-ins. 688 # Also, delete the individual header files to cleanup. 689 cmd_buf = "file_list=$(cat " + ffdc_summary_list_path + " 2>/dev/null)" +\ 690 " ; [ ! -z \"${file_list}\" ] && cat ${file_list}" +\ 691 " 2>/dev/null ; rm -rf ${file_list} 2>/dev/null || :" 692 shell_rc, ffdc_summary_info = gc.cmd_fnc_u(cmd_buf, print_output=0, 693 show_err=0) 694 695 # ffdc_list_file_path contains a list of any ffdc files created by plug- 696 # ins, etc. Read that data into a list. 697 try: 698 plug_in_ffdc_list = \ 699 open(ffdc_list_file_path, 'r').read().rstrip("\n").split("\n") 700 plug_in_ffdc_list = list(filter(None, plug_in_ffdc_list)) 701 except IOError: 702 plug_in_ffdc_list = [] 703 704 # Combine the files from plug_in_ffdc_list with the ffdc_file_list passed 705 # in. Eliminate duplicates and sort the list. 706 ffdc_file_list = sorted(set(ffdc_file_list + plug_in_ffdc_list)) 707 708 if status_file_path != "": 709 ffdc_file_list.insert(0, status_file_path) 710 711 # Convert the list to a printable list. 712 printable_ffdc_file_list = "\n".join(ffdc_file_list) 713 714 # Open ffdc_file_list for writing. We will write a complete list of 715 # FFDC files to it for possible use by plug-ins like cp_stop_check. 716 ffdc_list_file = open(ffdc_list_file_path, 'w') 717 ffdc_list_file.write(printable_ffdc_file_list + "\n") 718 ffdc_list_file.close() 719 720 indent = 0 721 width = 90 722 linefeed = 1 723 char = "=" 724 725 gp.qprintn() 726 gp.qprint_dashes(indent, width, linefeed, char) 727 gp.qprintn("Copy this data to the defect:\n") 728 729 if len(more_header_info) > 0: 730 gp.qprintn(more_header_info) 731 gp.qpvars(host_name, host_ip, openbmc_nickname, openbmc_host, 732 openbmc_host_name, openbmc_ip, openbmc_username, 733 openbmc_password, rest_username, rest_password, ipmi_username, 734 ipmi_password, os_host, os_host_name, os_ip, os_username, 735 os_password, pdu_host, pdu_host_name, pdu_ip, pdu_username, 736 pdu_password, pdu_slot_no, openbmc_serial_host, 737 openbmc_serial_host_name, openbmc_serial_ip, openbmc_serial_port) 738 739 gp.qprintn() 740 print_boot_history(boot_history) 741 gp.qprintn() 742 gp.qprint_var(state) 743 gp.qprintn() 744 gp.qprintn("FFDC data files:") 745 gp.qprintn(printable_ffdc_file_list) 746 gp.qprintn() 747 748 if len(ffdc_summary_info) > 0: 749 gp.qprintn(ffdc_summary_info) 750 751 gp.qprint_dashes(indent, width, linefeed, char) 752 753 754def my_ffdc(): 755 r""" 756 Collect FFDC data. 757 """ 758 759 global state 760 761 plug_in_setup() 762 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 763 call_point='ffdc', stop_on_plug_in_failure=0) 764 765 AUTOBOOT_FFDC_PREFIX = os.environ['AUTOBOOT_FFDC_PREFIX'] 766 status, ffdc_file_list = grk.run_key_u("FFDC ffdc_prefix=" 767 + AUTOBOOT_FFDC_PREFIX 768 + " ffdc_function_list=" 769 + ffdc_function_list, ignore=1) 770 if status != 'PASS': 771 gp.qprint_error("Call to ffdc failed.\n") 772 if type(ffdc_file_list) is not list: 773 ffdc_file_list = [] 774 # Leave a record for caller that "soft" errors occurred. 775 soft_errors = 1 776 gpu.save_plug_in_value(soft_errors, pgm_name) 777 778 my_get_state() 779 780 print_defect_report(ffdc_file_list) 781 782 783def print_test_start_message(boot_keyword): 784 r""" 785 Print a message indicating what boot test is about to run. 786 787 Description of arguments: 788 boot_keyword The name of the boot which is to be run 789 (e.g. "BMC Power On"). 790 """ 791 792 global boot_history 793 global boot_start_time 794 795 doing_msg = gp.sprint_timen("Doing \"" + boot_keyword + "\".") 796 797 # Set boot_start_time for use by plug-ins. 798 boot_start_time = doing_msg[1:33] 799 gp.qprint_var(boot_start_time) 800 801 gp.qprint(doing_msg) 802 803 update_boot_history(boot_history, doing_msg, max_boot_history) 804 805 806def stop_boot_test(signal_number=0, 807 frame=None): 808 r""" 809 Handle SIGUSR1 by aborting the boot test that is running. 810 811 Description of argument(s): 812 signal_number The signal number (should always be 10 for SIGUSR1). 813 frame The frame data. 814 """ 815 816 gp.qprintn() 817 gp.qprint_executing() 818 gp.lprint_executing() 819 820 # Restore original sigusr1 handler. 821 set_default_siguser1() 822 823 message = "The caller has asked that the boot test be stopped and marked" 824 message += " as a failure." 825 826 function_stack = gm.get_function_stack() 827 if "wait_state" in function_stack: 828 st.set_exit_wait_early_message(message) 829 else: 830 BuiltIn().fail(gp.sprint_error(message)) 831 832 833def run_boot(boot): 834 r""" 835 Run the specified boot. 836 837 Description of arguments: 838 boot The name of the boot test to be performed. 839 """ 840 841 global state 842 843 signal.signal(signal.SIGUSR1, stop_boot_test) 844 gp.qprint_timen("stop_boot_test is armed.") 845 846 print_test_start_message(boot) 847 848 plug_in_setup() 849 rc, shell_rc, failed_plug_in_name = \ 850 grpi.rprocess_plug_in_packages(call_point="pre_boot") 851 if rc != 0: 852 error_message = "Plug-in failed with non-zero return code.\n" +\ 853 gp.sprint_var(rc, fmt=gp.hexa()) 854 set_default_siguser1() 855 BuiltIn().fail(gp.sprint_error(error_message)) 856 857 if test_mode: 858 # In test mode, we'll pretend the boot worked by assigning its 859 # required end state to the default state value. 860 state = st.strip_anchor_state(boot_table[boot]['end']) 861 else: 862 # Assertion: We trust that the state data was made fresh by the 863 # caller. 864 865 gp.qprintn() 866 867 if boot_table[boot]['method_type'] == "keyword": 868 rk.my_run_keywords(boot_table[boot].get('lib_file_path', ''), 869 boot_table[boot]['method'], 870 quiet=quiet) 871 872 if boot_table[boot]['bmc_reboot']: 873 st.wait_for_comm_cycle(int(state['epoch_seconds'])) 874 plug_in_setup() 875 rc, shell_rc, failed_plug_in_name = \ 876 grpi.rprocess_plug_in_packages(call_point="post_reboot") 877 if rc != 0: 878 error_message = "Plug-in failed with non-zero return code.\n" 879 error_message += gp.sprint_var(rc, fmt=gp.hexa()) 880 set_default_siguser1() 881 BuiltIn().fail(gp.sprint_error(error_message)) 882 else: 883 match_state = st.anchor_state(state) 884 del match_state['epoch_seconds'] 885 # Wait for the state to change in any way. 886 st.wait_state(match_state, wait_time=state_change_timeout, 887 interval="10 seconds", invert=1) 888 889 gp.qprintn() 890 if boot_table[boot]['end']['chassis'] == "Off": 891 boot_timeout = power_off_timeout 892 else: 893 boot_timeout = power_on_timeout 894 st.wait_state(boot_table[boot]['end'], wait_time=boot_timeout, 895 interval="10 seconds") 896 897 plug_in_setup() 898 rc, shell_rc, failed_plug_in_name = \ 899 grpi.rprocess_plug_in_packages(call_point="post_boot") 900 if rc != 0: 901 error_message = "Plug-in failed with non-zero return code.\n" +\ 902 gp.sprint_var(rc, fmt=gp.hexa()) 903 set_default_siguser1() 904 BuiltIn().fail(gp.sprint_error(error_message)) 905 906 # Restore original sigusr1 handler. 907 set_default_siguser1() 908 909 910def test_loop_body(): 911 r""" 912 The main loop body for the loop in main_py. 913 914 Description of arguments: 915 boot_count The iteration number (starts at 1). 916 """ 917 918 global boot_count 919 global state 920 global next_boot 921 global boot_success 922 global boot_end_time 923 924 gp.qprintn() 925 926 next_boot = select_boot() 927 if next_boot == "": 928 return True 929 930 boot_count += 1 931 gp.qprint_timen("Starting boot " + str(boot_count) + ".") 932 933 pre_boot_plug_in_setup() 934 935 cmd_buf = ["run_boot", next_boot] 936 boot_status, msg = BuiltIn().run_keyword_and_ignore_error(*cmd_buf) 937 if boot_status == "FAIL": 938 gp.qprint(msg) 939 940 gp.qprintn() 941 if boot_status == "PASS": 942 boot_success = 1 943 completion_msg = gp.sprint_timen("BOOT_SUCCESS: \"" + next_boot 944 + "\" succeeded.") 945 else: 946 boot_success = 0 947 completion_msg = gp.sprint_timen("BOOT_FAILED: \"" + next_boot 948 + "\" failed.") 949 950 # Set boot_end_time for use by plug-ins. 951 boot_end_time = completion_msg[1:33] 952 gp.qprint_var(boot_end_time) 953 954 gp.qprint(completion_msg) 955 956 boot_results.update(next_boot, boot_status) 957 958 plug_in_setup() 959 # NOTE: A post_test_case call point failure is NOT counted as a boot 960 # failure. 961 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 962 call_point='post_test_case', stop_on_plug_in_failure=0) 963 964 plug_in_setup() 965 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 966 call_point='ffdc_check', shell_rc=dump_ffdc_rc(), 967 stop_on_plug_in_failure=1, stop_on_non_zero_rc=1) 968 if ffdc_check == "All" or\ 969 shell_rc == dump_ffdc_rc(): 970 status, ret_values = grk.run_key_u("my_ffdc", ignore=1) 971 if status != 'PASS': 972 gp.qprint_error("Call to my_ffdc failed.\n") 973 # Leave a record for caller that "soft" errors occurred. 974 soft_errors = 1 975 gpu.save_plug_in_value(soft_errors, pgm_name) 976 977 if delete_errlogs: 978 # print error logs before delete 979 if redfish_support_trans_state: 980 status, error_logs = grk.run_key_u("Get Redfish Event Logs") 981 log.print_error_logs(error_logs, "AdditionalDataURI Message Severity") 982 else: 983 status, error_logs = grk.run_key_u("Get Error Logs") 984 log.print_error_logs(error_logs, "AdditionalData Message Severity") 985 pels = pel.peltool("-l", ignore_err=1) 986 gp.qprint_var(pels) 987 988 # We need to purge error logs between boots or they build up. 989 grk.run_key(delete_errlogs_cmd, ignore=1) 990 grk.run_key(delete_bmcdump_cmd, ignore=1) 991 if redfish_support_trans_state: 992 grk.run_key(delete_sysdump_cmd, ignore=1) 993 994 boot_results.print_report() 995 gp.qprint_timen("Finished boot " + str(boot_count) + ".") 996 997 plug_in_setup() 998 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 999 call_point='stop_check', shell_rc=stop_test_rc(), 1000 stop_on_non_zero_rc=1) 1001 if shell_rc == stop_test_rc(): 1002 message = "Stopping as requested by user.\n" 1003 gp.qprint_time(message) 1004 BuiltIn().fail(message) 1005 1006 # This should help prevent ConnectionErrors. 1007 # Purge all redfish and REST connection sessions. 1008 if redfish_delete_sessions: 1009 grk.run_key_u("Close All Connections", ignore=1) 1010 grk.run_key_u("Delete All Redfish Sessions", ignore=1) 1011 1012 return True 1013 1014 1015def obmc_boot_test_teardown(): 1016 r""" 1017 Clean up after the main keyword. 1018 """ 1019 gp.qprint_executing() 1020 1021 if ga.psutil_imported: 1022 ga.terminate_descendants() 1023 1024 if cp_setup_called: 1025 plug_in_setup() 1026 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 1027 call_point='cleanup', stop_on_plug_in_failure=0) 1028 1029 if 'boot_results_file_path' in globals(): 1030 # Save boot_results and boot_history objects to a file in case they are 1031 # needed again. 1032 gp.qprint_timen("Saving boot_results to the following path.") 1033 gp.qprint_var(boot_results_file_path) 1034 pickle.dump((boot_results, boot_history), 1035 open(boot_results_file_path, 'wb'), 1036 pickle.HIGHEST_PROTOCOL) 1037 1038 global save_stack 1039 # Restore any global values saved on the save_stack. 1040 for parm_name in main_func_parm_list: 1041 # Get the parm_value if it was saved on the stack. 1042 try: 1043 parm_value = save_stack.pop(parm_name) 1044 except BaseException: 1045 # If it was not saved, no further action is required. 1046 continue 1047 1048 # Restore the saved value. 1049 cmd_buf = "BuiltIn().set_global_variable(\"${" + parm_name +\ 1050 "}\", parm_value)" 1051 gp.dpissuing(cmd_buf) 1052 exec(cmd_buf) 1053 1054 gp.dprintn(save_stack.sprint_obj()) 1055 1056 1057def test_teardown(): 1058 r""" 1059 Clean up after this test case. 1060 """ 1061 1062 gp.qprintn() 1063 gp.qprint_executing() 1064 1065 if ga.psutil_imported: 1066 ga.terminate_descendants() 1067 1068 cmd_buf = ["Print Error", 1069 "A keyword timeout occurred ending this program.\n"] 1070 BuiltIn().run_keyword_if_timeout_occurred(*cmd_buf) 1071 1072 if redfish_supported: 1073 redfish.logout() 1074 1075 gp.qprint_pgm_footer() 1076 1077 1078def post_stack(): 1079 r""" 1080 Process post_stack plug-in programs. 1081 """ 1082 1083 if not call_post_stack_plug: 1084 # The caller does not wish to have post_stack plug-in processing done. 1085 return 1086 1087 global boot_success 1088 1089 # NOTE: A post_stack call-point failure is NOT counted as a boot failure. 1090 pre_boot_plug_in_setup() 1091 # For the purposes of the following plug-ins, mark the "boot" as a success. 1092 boot_success = 1 1093 plug_in_setup() 1094 rc, shell_rc, failed_plug_in_name, history =\ 1095 grpi.rprocess_plug_in_packages(call_point='post_stack', 1096 stop_on_plug_in_failure=0, 1097 return_history=True) 1098 for doing_msg in history: 1099 update_boot_history(boot_history, doing_msg, max_boot_history) 1100 if rc != 0: 1101 boot_success = 0 1102 1103 plug_in_setup() 1104 rc, shell_rc, failed_plug_in_name =\ 1105 grpi.rprocess_plug_in_packages(call_point='ffdc_check', 1106 shell_rc=dump_ffdc_rc(), 1107 stop_on_plug_in_failure=1, 1108 stop_on_non_zero_rc=1) 1109 if shell_rc == dump_ffdc_rc(): 1110 status, ret_values = grk.run_key_u("my_ffdc", ignore=1) 1111 if status != 'PASS': 1112 gp.qprint_error("Call to my_ffdc failed.\n") 1113 # Leave a record for caller that "soft" errors occurred. 1114 soft_errors = 1 1115 gpu.save_plug_in_value(soft_errors, pgm_name) 1116 1117 plug_in_setup() 1118 rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages( 1119 call_point='stop_check', shell_rc=stop_test_rc(), 1120 stop_on_non_zero_rc=1) 1121 if shell_rc == stop_test_rc(): 1122 message = "Stopping as requested by user.\n" 1123 gp.qprint_time(message) 1124 BuiltIn().fail(message) 1125 1126 1127def obmc_boot_test_py(loc_boot_stack=None, 1128 loc_stack_mode=None, 1129 loc_quiet=None): 1130 r""" 1131 Do main program processing. 1132 """ 1133 1134 global save_stack 1135 1136 ga.set_term_options(term_requests={'pgm_names': ['process_plug_in_packages.py']}) 1137 1138 gp.dprintn() 1139 # Process function parms. 1140 for parm_name in main_func_parm_list: 1141 # Get parm's value. 1142 parm_value = eval("loc_" + parm_name) 1143 gp.dpvars(parm_name, parm_value) 1144 1145 if parm_value is not None: 1146 # Save the global value on a stack. 1147 cmd_buf = "save_stack.push(BuiltIn().get_variable_value(\"${" +\ 1148 parm_name + "}\"), \"" + parm_name + "\")" 1149 gp.dpissuing(cmd_buf) 1150 exec(cmd_buf) 1151 1152 # Set the global value to the passed value. 1153 cmd_buf = "BuiltIn().set_global_variable(\"${" + parm_name +\ 1154 "}\", loc_" + parm_name + ")" 1155 gp.dpissuing(cmd_buf) 1156 exec(cmd_buf) 1157 1158 gp.dprintn(save_stack.sprint_obj()) 1159 1160 setup() 1161 1162 init_boot_pass, init_boot_fail = boot_results.return_total_pass_fail() 1163 1164 if ffdc_only: 1165 gp.qprint_timen("Caller requested ffdc_only.") 1166 if do_pre_boot_plug_in_setup: 1167 pre_boot_plug_in_setup() 1168 grk.run_key_u("my_ffdc") 1169 return 1170 1171 if delete_errlogs: 1172 # print error logs before delete 1173 if redfish_support_trans_state: 1174 status, error_logs = grk.run_key_u("Get Redfish Event Logs") 1175 log.print_error_logs(error_logs, "AdditionalDataURI Message Severity") 1176 else: 1177 status, error_logs = grk.run_key_u("Get Error Logs") 1178 log.print_error_logs(error_logs, "AdditionalData Message Severity") 1179 pels = pel.peltool("-l", ignore_err=1) 1180 gp.qprint_var(pels) 1181 1182 # Delete errlogs prior to doing any boot tests. 1183 grk.run_key(delete_errlogs_cmd, ignore=1) 1184 grk.run_key(delete_bmcdump_cmd, ignore=1) 1185 if redfish_support_trans_state: 1186 grk.run_key(delete_sysdump_cmd, ignore=1) 1187 1188 # Process caller's boot_stack. 1189 while (len(boot_stack) > 0): 1190 test_loop_body() 1191 1192 gp.qprint_timen("Finished processing stack.") 1193 1194 post_stack() 1195 1196 # Process caller's boot_list. 1197 if len(boot_list) > 0: 1198 for ix in range(1, max_num_tests + 1): 1199 test_loop_body() 1200 1201 gp.qprint_timen("Completed all requested boot tests.") 1202 1203 boot_pass, boot_fail = boot_results.return_total_pass_fail() 1204 new_fail = boot_fail - init_boot_fail 1205 if new_fail > boot_fail_threshold: 1206 error_message = "Boot failures exceed the boot failure" +\ 1207 " threshold:\n" +\ 1208 gp.sprint_var(new_fail) +\ 1209 gp.sprint_var(boot_fail_threshold) 1210 BuiltIn().fail(gp.sprint_error(error_message)) 1211