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