Changes to obmc_boot_test.robot/obmc_boot_test.py:

- I re-wrote nearly all robot code in python.

Change-Id: Ie68b2714fe107f10486c9b07cc29f4afd2af264c
Signed-off-by: Michael Walsh <micwalsh@us.ibm.com>
diff --git a/extended/obmc_boot_test.robot b/extended/obmc_boot_test.robot
index 8c4714b..3b509bc 100644
--- a/extended/obmc_boot_test.robot
+++ b/extended/obmc_boot_test.robot
@@ -1,23 +1,10 @@
 *** Settings ***
 Documentation  Do random repeated boots based on the state of the BMC machine.
-...  The number of repetitions is designated by ${max_num_tests}. Keyword
-...  names that are listed in @{AVAIL_BOOTS} become the selection of possible
-...  boots for the test.
 
-Resource  ../lib/dvt/obmc_driver_vars.txt
-Resource  ../lib/list_utils.robot
-Resource  ../lib/openbmc_ffdc.robot
+Library   state.py
+Library   obmc_boot_test.py
 
-Library   ../lib/gen_robot_print.py
-Library   ../lib/gen_misc.py
-Library   ../lib/gen_robot_plug_in.py
-Library   ../lib/gen_robot_valid.py
-Library   ../lib/state.py
-Library   ../lib/boot/powerons.py
-Library   ../lib/boot/poweroffs.py
-Library   ../lib/obmc_boot_test.py
-
-#  WITH NAME  boot_results
+Resource  openbmc_ffdc.robot
 
 *** Variables ***
 # Initialize program parameters variables.
@@ -29,11 +16,11 @@
 ...  openbmc_serial_port  boot_stack  boot_list  max_num_tests
 ...  plug_in_dir_paths  status_file_path  openbmc_model  boot_pass  boot_fail
 ...  ffdc_dir_path_style  ffdc_check  state_change_timeout  power_on_timeout
-...  power_on_timeout  test_mode  quiet  debug
+...  power_off_timeout  test_mode  quiet  debug
 
 # Initialize each program parameter.
-${openbmc_nickname}         ${EMPTY}
 ${openbmc_host}             ${EMPTY}
+${openbmc_nickname}         ${openbmc_host}
 ${openbmc_username}         root
 ${openbmc_password}         0penBmc
 ${os_host}                  ${EMPTY}
@@ -65,25 +52,14 @@
 ${quiet}                    0
 ${debug}                    0
 
-
-# Plug-in variables.
-${shell_rc}                 0x00000000
-${fail_on_plug_in_failure}  1
-${return_on_non_zero_rc}    0
-
-${next_boot}                ${EMPTY}
-# State dictionary.  Initializing to a realistic state for testing in
-# test_mode.
-
 # Flag variables.
-${cp_setup_called}          ${0}
 # test_really_running is needed by DB_Logging plug-in.
 ${test_really_running}      ${1}
 
 *** Test Cases ***
-Randomized Boot Testing
-    [Documentation]  Performs random, repeated boots.
-    [Tags]  Randomized_boot_testing
+General Boot Testing
+    [Documentation]  Performs repeated boot tests.
+    [Tags]  General_boot_testing
 
     # Call the Main keyword to prevent any dots from appearing in the console
     # due to top level keywords.
@@ -94,215 +70,10 @@
 Main
     [Teardown]  Program Teardown
 
-    Setup
+    # This is the "Main" keyword.  The advantages of having this keyword vs
+    # just putting the code in the *** Test Cases *** table are:
+    # 1) You won't get a green dot in the output every time you run a keyword.
 
-    :For  ${BOOT_COUNT}  IN RANGE  ${max_num_tests}
-    \  Test Loop Body  ${BOOT_COUNT}
-
-    Rqprint Timen  Completed all requested boot tests.
+    Main Py
 
 ###############################################################################
-
-
-###############################################################################
-Setup
-    [Documentation]  Do general program setup tasks.
-
-    Validate Parms
-
-    Rqprint Pgm Header
-
-    Create Boot Results Table
-
-    # Preserve the values of boot_pass/boot_fail that were passed in.
-    Set Global Variable  ${initial_boot_pass}  ${boot_pass}
-    Set Global Variable  ${initial_boot_fail}  ${boot_fail}
-
-    # Call "setup" plug-ins, if any.
-    Plug In Setup
-    ${rc}  ${shell_rc}  ${failed_plug_in_name}=  Rprocess Plug In Packages
-    ...  call_point=setup
-    Should Be Equal  '${rc}'  '${0}'
-
-    # Keyword "FFDC" will fail if TEST_MESSAGE is not set.
-    Set Global Variable  ${TEST_MESSAGE}  ${EMPTY}
-
-    # Setting cp_setup_called lets our Teardown know that it needs to call
-    # the cleanup plug-in call point.
-    Set Global Variable  ${cp_setup_called}  ${1}
-
-    Rqprint Timen  Getting system state.
-    # The state dictionary must be primed before calling Test Loop Body.
-    ${temp_state}=  Run Keyword If  '${test_mode}' == '0'  Get State
-    ...  ELSE  Create Dictionary  &{default_state}
-    Set Global Variable  &{state}  &{temp_state}
-    Rqpvars  state
-
-###############################################################################
-
-
-###############################################################################
-Validate Parms
-    [Documentation]  Validate all program parameters.
-
-    Rqprintn
-
-    Rvalid Value  AVAIL_BOOTS
-    Rvalid Value  openbmc_host
-    Rvalid Value  openbmc_username
-    Rvalid Value  openbmc_password
-    # os_host is optional so no validation is being done.
-    Run Keyword If  '${OS_HOST}' != '${EMPTY}'  Run Keywords
-    ...  Rvalid Value  os_username  AND
-    ...  Rvalid Value  os_password
-    Rvalid Value  pdu_host
-    Rvalid Value  pdu_username
-    Rvalid Value  pdu_password
-    Rvalid Integer  pdu_slot_no
-    Rvalid Value  openbmc_serial_host
-    Rvalid Integer  openbmc_serial_port
-    Rvalid Integer  max_num_tests
-    Rvalid Value  openbmc_model
-    Rvalid Integer  boot_pass
-    Rvalid Integer  boot_fail
-
-    ${boot_pass_temp}=  Convert To Integer  ${boot_pass}
-    Set Global Variable  ${boot_pass}  ${boot_pass_temp}
-    ${boot_fail_temp}=  Convert To Integer  ${boot_fail}
-    Set Global Variable  ${boot_fail}  ${boot_fail_temp}
-
-    ${temp_arr}=  Rvalidate Plug Ins  ${plug_in_dir_paths}
-    Set Global Variable  @{plug_in_packages_list}  @{temp_arr}
-
-    Run Keyword If  '${openbmc_nickname}' == '${EMPTY}'
-    ...  Set Global Variable  ${openbmc_nickname}  ${openbmc_host}
-
-    Set FFDC Dir Path Style
-
-    ${default_state}=  Return Default State
-    Set Global Variable  ${state}  ${default_state}
-    Set Global Variable  ${default_state}  ${default_state}
-
-###############################################################################
-
-
-###############################################################################
-Set FFDC Dir Path Style
-
-    Run Keyword If  '${ffdc_dir_path_style}' != '${EMPTY}'  Return from Keyword
-
-    ${temp}=  Run Keyword and Continue On Failure  Get Environment Variable
-    ...  FFDC_DIR_PATH_STYLE  ${0}
-
-    Set Global Variable  ${ffdc_dir_path_style}  ${temp}
-
-###############################################################################
-
-
-###############################################################################
-Program Teardown
-    [Documentation]  Clean up after this program.
-
-    Run Keyword If  '${cp_setup_called}' == '1'  Run Keywords
-    ...  Plug In Setup  AND
-    ...  Rprocess Plug In Packages  call_point=cleanup
-    ...  stop_on_plug_in_failure=1
-
-    Rqprint Pgm Footer
-
-###############################################################################
-
-
-###############################################################################
-Test Loop Body
-    [Documentation]  The main loop body for the loop in "main".
-    [Arguments]  ${BOOT_COUNT}
-
-    Rqprintn
-    Rqprint Timen  Starting boot ${BOOT_COUNT+1} of ${max_num_tests}.
-
-    Rqpvars  state
-
-    ${loc_next_boot}=  Select Boot  ${state}
-    Set Global Variable  ${next_boot}  ${loc_next_boot}
-
-    # Clear this file.  Plug-ins may now write to it.
-    Remove File  ${FFDC_LIST_FILE_PATH}
-
-    ${status}  ${msg}=  Run Keyword And Ignore Error  Run Boot  ${next_boot}
-    Run Keyword If  '${status}' == 'FAIL'  rprint  ${msg}
-
-    Rqprintn
-    Run Keyword If  '${BOOT_STATUS}' == 'PASS'  Run Keywords
-    ...    Set Global Variable  ${boot_success}  ${1}  AND
-    ...    Rqprint Timen  BOOT_SUCCESS: "${next_boot}" succeeded.
-    ...  ELSE  Run Keywords
-    ...    Set Global Variable  ${boot_success}  ${0}  AND
-    ...      Rqprint Timen  BOOT_FAILED: ${next_boot} failed.
-
-    Update Boot Results Table  ${next_boot}  ${BOOT_STATUS}
-
-    # NOTE: A post_test_case call point failure is NOT counted as a boot
-    # failure.
-    Plug In Setup
-    ${rc}  ${shell_rc}  ${failed_plug_in_name}=  Rprocess Plug In Packages
-    ...  call_point=post_test_case  stop_on_plug_in_failure=1
-
-    ${rc}  ${shell_rc}  ${failed_plug_in_name}=  Rprocess Plug In Packages
-    ...  call_point=ffdc_check  shell_rc=${0x00000200}
-    ...  stop_on_plug_in_failure=1  stop_on_non_zero_rc=1
-
-    Run Keyword If
-    ...  '${BOOT_STATUS}' != 'PASS' or '${FFDC_CHECK}' == 'All' or '${shell_rc}' == '${0x00000200}'
-    ...  Run Keyword and Continue On Failure  My FFDC
-
-    # Run plug-ins to see if we ought to stop execution.
-    Plug In Setup
-    ${rc}  ${shell_rc}  ${failed_plug_in_name}=  Rprocess Plug In Packages
-    ...  call_point=stop_check
-    Run Keyword If  '${rc}' != '${0}'  Run Keywords
-    ...  Rprint Error Report  Stopping as requested by user.
-    ...  Fail
-
-    Print Boot Results Table
-    Rqprint Timen  Finished boot ${BOOT_COUNT+1} of ${max_num_tests}.
-
-    Rqprint Timen  Getting system state.
-    # The state must be refreshed before calling Test Loop Body again.
-    ${temp_state}=  Run Keyword If  '${test_mode}' == '0'  Get State
-    ...  quiet=${1}
-    ...  ELSE  Create Dictionary  &{default_state}
-    Set Global Variable  &{state}  &{temp_state}
-    Rqpvars  state
-
-###############################################################################
-
-
-###############################################################################
-Run Boot
-    [Documentation]  Run the selected boot and mark the status when complete.
-    [Arguments]  ${boot_keyword}
-    [Teardown]  Set Global Variable  ${BOOT_STATUS}  ${KEYWORD STATUS}
-
-    # boot_keyword     The name of the boot to run, which corresponds to the
-    #                  keyword to run. (i.e "BMC Power On")
-
-    Print Test Start Message  ${boot_keyword}
-
-    Plug In Setup
-    ${rc}  ${shell_rc}  ${failed_plug_in_name}=  Rprocess Plug In Packages
-    ...  call_point=pre_boot
-    Should Be Equal  '${rc}'  '${0}'
-
-    @{cmd_buf}=  Create List  ${boot_keyword}
-    rqpissuing_keyword  ${cmd_buf}  ${test_mode}
-    Run Keyword If  '${test_mode}' == '0'  Run Keyword  @{cmd_buf}
-
-    Plug In Setup
-    ${rc}  ${shell_rc}  ${failed_plug_in_name}=  Rprocess Plug In Packages
-    ...  call_point=post_boot
-    Should Be Equal  '${rc}'  '${0}'
-
-###############################################################################
-
-
diff --git a/lib/obmc_boot_test.py b/lib/obmc_boot_test.py
index 653dde1..d5dabe0 100755
--- a/lib/obmc_boot_test.py
+++ b/lib/obmc_boot_test.py
@@ -4,29 +4,67 @@
 This module is the python counterpart to obmc_boot_test.
 """
 
-from tally_sheet import *
+from boot_data import *
 import gen_robot_print as grp
 import gen_robot_plug_in as grpi
+import gen_robot_valid as grv
+import gen_misc as gm
+import gen_cmd as gc
 import state as st
+import random
 
 import os
 import time
-import subprocess
 import glob
 
 from robot.utils import DotDict
 from robot.libraries.BuiltIn import BuiltIn
-from robot.libraries.OperatingSystem import OperatingSystem
 
-# Create boot_results_fields for use in creating boot_results.
-boot_results_fields = DotDict([('total', 0), ('pass', 0), ('fail', 0)])
-# Create boot_results which is global to this module.
-boot_results = tally_sheet('boot type',
-                           boot_results_fields,
-                           'boot_test_results')
+# Program parameter processing.
+# Assign all program parms to python variables which are global to this module.
+parm_list = BuiltIn().get_variable_value("${parm_list}")
+int_list = ['max_num_tests', 'boot_pass', 'boot_fail', 'quiet', 'test_mode',
+            'debug']
+for parm in parm_list:
+    if parm in int_list:
+        sub_cmd = "int(BuiltIn().get_variable_value(\"${" + parm +\
+                  "}\", \"0\"))"
+    else:
+        sub_cmd = "BuiltIn().get_variable_value(\"${" + parm + "}\")"
+    cmd_buf = parm + " = " + sub_cmd
+    exec(cmd_buf)
 
-boot_results.set_sum_fields(['total', 'pass', 'fail'])
-boot_results.set_calc_fields(['total=pass+fail'])
+if ffdc_dir_path_style == "":
+    ffdc_dir_path_style = int(os.environ.get('FFDC_DIR_PATH_STYLE', '0'))
+
+# Set up boot data structures.
+boot_table = create_boot_table()
+valid_boot_types = create_valid_boot_list(boot_table)
+boot_results = boot_results(boot_table, boot_pass, boot_fail)
+boot_lists = read_boot_lists()
+last_ten = []
+# Convert these program parms to more useable lists.
+boot_list = filter(None, boot_list.split(":"))
+boot_stack = filter(None, boot_stack.split(":"))
+
+state = st.return_default_state()
+cp_setup_called = 0
+next_boot = ""
+base_tool_dir_path = os.path.normpath(os.environ.get(
+    'AUTOBOOT_BASE_TOOL_DIR_PATH', "/tmp")) + os.sep
+ffdc_dir_path = os.path.normpath(os.environ.get('FFDC_DIR_PATH', '')) + os.sep
+ffdc_list_file_path = base_tool_dir_path + openbmc_nickname + "/FFDC_FILE_LIST"
+boot_success = 0
+# Setting master_pid correctly influences the behavior of plug-ins like
+# DB_Logging
+program_pid = os.getpid()
+master_pid = os.environ.get('AUTOBOOT_MASTER_PID', program_pid)
+status_dir_path = os.environ.get('STATUS_DIR_PATH', "")
+if status_dir_path != "":
+    status_dir_path = os.path.normpath(status_dir_path) + os.sep
+default_power_on = "BMC Power On"
+default_power_off = "BMC Power Off"
+boot_count = 0
 
 
 ###############################################################################
@@ -37,66 +75,35 @@
     programs.
     """
 
-    boot_pass = int(BuiltIn().get_variable_value("${boot_pass}"))
+    boot_pass, boot_fail = boot_results.return_total_pass_fail()
     if boot_pass > 1:
         test_really_running = 1
     else:
         test_really_running = 0
 
-    BuiltIn().set_global_variable("${test_really_running}",
-                                  test_really_running)
-
-    next_boot = BuiltIn().get_variable_value("${next_boot}")
-    BuiltIn().set_global_variable("${boot_type_desc}", next_boot)
-
-    # Setting master_pid correctly influences the behavior of plug-ins like
-    # DB_Logging
-    program_pid = BuiltIn().get_variable_value("${program_pid}")
-    try:
-        master_pid = OperatingSystem().get_environment_variable(
-            "AUTOBOOT_MASTER_PID")
-    except RuntimeError:
-        master_pid = program_pid
-    if master_pid == "":
-        master_pid = program_pid
-
-    BuiltIn().set_global_variable("${master_pid}", master_pid)
-
     seconds = time.time()
     loc_time = time.localtime(seconds)
     time_string = time.strftime("%y%m%d.%H%M%S.", loc_time)
 
-    openbmc_nickname = BuiltIn().get_variable_value("${openbmc_nickname}")
-    openbmc_host = BuiltIn().get_variable_value("${openbmc_host}")
-    if openbmc_nickname == "":
-        openbmc_nickname = openbmc_host
-    ffdc_prefix = openbmc_nickname
+    ffdc_prefix = openbmc_nickname + "." + time_string
 
-    ffdc_prefix += "." + time_string
-
-    try:
-        ffdc_dir_path = os.environ['FFDC_DIR_PATH']
-        # Add trailing slash.
-        ffdc_dir_path = os.path.normpath(ffdc_dir_path) + os.sep
-    except KeyError:
-        ffdc_dir_path = ""
+    BuiltIn().set_global_variable("${test_really_running}",
+                                  test_really_running)
+    BuiltIn().set_global_variable("${boot_type_desc}", next_boot)
+    BuiltIn().set_global_variable("${master_pid}", master_pid)
     BuiltIn().set_global_variable("${FFDC_DIR_PATH}", ffdc_dir_path)
-
-    status_dir_path = os.environ.get('STATUS_DIR_PATH', "")
-    if status_dir_path != "":
-        # Add trailing slash.
-        status_dir_path = os.path.normpath(status_dir_path) + os.sep
     BuiltIn().set_global_variable("${STATUS_DIR_PATH}", status_dir_path)
-
-    base_tool_dir_path = os.environ.get('AUTOBOOT_BASE_TOOL_DIR_PATH', "/tmp")
-    base_tool_dir_path = os.path.normpath(base_tool_dir_path) + os.sep
     BuiltIn().set_global_variable("${BASE_TOOL_DIR_PATH}", base_tool_dir_path)
-
-    ffdc_list_file_path = base_tool_dir_path + openbmc_nickname +\
-        "/FFDC_FILE_LIST"
-
     BuiltIn().set_global_variable("${FFDC_LIST_FILE_PATH}",
                                   ffdc_list_file_path)
+    BuiltIn().set_global_variable("${FFDC_DIR_PATH_STYLE}",
+                                  ffdc_dir_path_style)
+    BuiltIn().set_global_variable("${FFDC_CHECK}",
+                                  ffdc_check)
+    BuiltIn().set_global_variable("${boot_pass}", boot_pass)
+    BuiltIn().set_global_variable("${boot_fail}", boot_fail)
+    BuiltIn().set_global_variable("${boot_success}", boot_success)
+    BuiltIn().set_global_variable("${ffdc_prefix}", ffdc_prefix)
 
     # For each program parameter, set the corresponding AUTOBOOT_ environment
     # variable value.  Also, set an AUTOBOOT_ environment variable for every
@@ -106,9 +113,6 @@
                          "master_pid", "ffdc_prefix", "ffdc_dir_path",
                          "status_dir_path", "base_tool_dir_path",
                          "ffdc_list_file_path"]
-    BuiltIn().set_global_variable("${ffdc_prefix}", ffdc_prefix)
-
-    parm_list = BuiltIn().get_variable_value("@{parm_list}")
 
     plug_in_vars = parm_list + additional_values
 
@@ -117,113 +121,175 @@
         var_name = var_name.upper()
         if var_value is None:
             var_value = ""
-        OperatingSystem().set_environment_variable(
-            "AUTOBOOT_" + var_name, var_value)
+        os.environ["AUTOBOOT_" + var_name] = str(var_value)
 
-    debug = int(BuiltIn().get_variable_value("${debug}"))
     if debug:
-        cmd_buf = "printenv | egrep AUTOBOOT_ | sort -u"
-        grp.rpissuing(cmd_buf)
-        sub_proc = subprocess.Popen(cmd_buf, shell=True,
-                                    stdout=subprocess.PIPE,
-                                    stderr=subprocess.STDOUT)
-        out_buf, err_buf = sub_proc.communicate()
-        shell_rc = sub_proc.returncode
-        grp.rprint(out_buf)
+        shell_rc, out_buf = \
+            gc.cmd_fnc_u("printenv | egrep AUTOBOOT_ | sort -u")
 
 ###############################################################################
 
 
 ###############################################################################
-def create_boot_results_table():
+def setup():
 
     r"""
-    Create our boot_results_table.
+    Do general program setup tasks.
     """
 
-    # At some point we'll want to change to reading in our boot types from
-    # some external source (e.g. file).
+    global cp_setup_called
 
-    boot_results.add_row('BMC Power On')
-    boot_results.add_row('BMC Power Off')
+    grp.rqprintn()
+
+    validate_parms()
+
+    grp.rqprint_pgm_header()
+
+    plug_in_setup()
+    rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
+        call_point='setup')
+    if rc != 0:
+        error_message = "Plug-in setup failed.\n"
+        grp.rprint_error_report(error_message)
+        BuiltIn().fail(error_message)
+    # Setting cp_setup_called lets our Teardown know that it needs to call
+    # the cleanup plug-in call point.
+    cp_setup_called = 1
+
+    # Keyword "FFDC" will fail if TEST_MESSAGE is not set.
+    BuiltIn().set_global_variable("${TEST_MESSAGE}", "${EMPTY}")
+
+    grp.rdprint_var(boot_table, 1)
+    grp.rdprint_var(boot_lists)
 
 ###############################################################################
 
 
 ###############################################################################
-def update_boot_results_table(boot_type,
-                              boot_status):
+def validate_parms():
 
     r"""
-    Update our boot_results_table.  This includes:
-    - Updating the record for the given boot_type by incrementing the pass or
-      fail field.
-    - Calling the calc method to have the totals, etc. calculated.
-    - Updating global variables boot_pass/boot_fail.
-
-    Description of arguments:
-    boot_type    The type of boot just done (e.g. "BMC Power On").
-    boot_status  The status of the boot just done.  This should be equal to
-                 either "pass" or "fail" (case-insensitive).
+    Validate all program parameters.
     """
 
-    boot_results.inc_row_field(boot_type, boot_status.lower())
-    totals_line = boot_results.calc()
+    grp.rqprintn()
 
-    # The caller of obmc_boot_test can pass boot_pass/boot_fail values because
-    # the caller may have already done some testing (e.g. "BMC OOB").  For the
-    # sake of DB logging done by plug-ins, we want to include these in our
-    # overall totals.
-    initial_boot_pass = int(BuiltIn().get_variable_value(
-        "${initial_boot_pass}"))
-    initial_boot_fail = int(BuiltIn().get_variable_value(
-        "${initial_boot_fail}"))
+    grv.rvalid_value("openbmc_host")
+    grv.rvalid_value("openbmc_username")
+    grv.rvalid_value("openbmc_password")
+    if os_host != "":
+        grv.rvalid_value("os_username")
+        grv.rvalid_value("os_password")
 
-    BuiltIn().set_global_variable("${boot_pass}",
-                                  totals_line['pass'] + initial_boot_pass)
-    BuiltIn().set_global_variable("${boot_fail}",
-                                  totals_line['fail'] + initial_boot_fail)
+    if pdu_host != "":
+        grv.rvalid_value("pdu_username")
+        grv.rvalid_value("pdu_password")
+    grv.rvalid_integer("pdu_slot_no")
+    if openbmc_serial_host != "":
+        grv.rvalid_integer("openbmc_serial_port")
+    grv.rvalid_integer("max_num_tests")
+    grv.rvalid_value("openbmc_model")
+    grv.rvalid_integer("boot_pass")
+    grv.rvalid_integer("boot_fail")
+
+    plug_in_packages_list = grpi.rvalidate_plug_ins(plug_in_dir_paths)
+    BuiltIn().set_global_variable("${plug_in_packages_list}",
+                                  plug_in_packages_list)
+
+    if len(boot_list) == 0 and len(boot_stack) == 0:
+        error_message = "You must provide either a value for either the" +\
+            " boot_list or the boot_stack parm.\n"
+        BuiltIn().fail(gp.sprint_error(error_message))
+
+    valid_boot_list(boot_list, valid_boot_types)
+    valid_boot_list(boot_stack, valid_boot_types)
+
+    return
 
 ###############################################################################
 
 
 ###############################################################################
-def print_boot_results_table(header_footer="\n"):
+def my_get_state():
 
     r"""
-    Print the formatted boot_resuls_table to the console.
+    Get the system state plus a little bit of wrapping.
     """
 
-    grp.rqprint(header_footer)
-    grp.rqprint(boot_results.sprint_report())
-    grp.rqprint(header_footer)
+    global state
+
+    req_states = ['epoch_seconds'] + st.default_req_states
+
+    grp.rqprint_timen("Getting system state.")
+    if test_mode:
+        state['epoch_seconds'] = int(time.time())
+    else:
+        state = st.get_state(req_states=req_states, quiet=0)
+    grp.rprint_var(state)
 
 ###############################################################################
 
 
 ###############################################################################
-def select_boot(state):
+def select_boot():
 
     r"""
     Select a boot test to be run based on our current state and return the
     chosen boot type.
 
     Description of arguments:
-    state  The state of the machine, which will include the power state..
+    state  The state of the machine.
     """
 
-    if 'chassis' in state:
-        # New style state.
-        if state['chassis'] == 'Off':
-            boot = 'BMC Power On'
+    grp.rprint_timen("Selecting a boot test.")
+
+    my_get_state()
+
+    stack_popped = 0
+    if len(boot_stack) > 0:
+        stack_popped = 1
+        grp.rprint_dashes()
+        grp.rprint_var(boot_stack)
+        grp.rprint_dashes()
+        boot_candidate = boot_stack.pop()
+        if st.compare_states(state, boot_table[boot_candidate]['start']):
+            grp.rprint_timen("The machine state is valid for a '" +
+                             boot_candidate + "' boot test.")
+            grp.rprint_dashes()
+            grp.rprint_var(boot_stack)
+            grp.rprint_dashes()
+            return boot_candidate
         else:
-            boot = 'BMC Power Off'
-    else:
-        # Old style state.
-        if state['power'] == 0:
-            boot = 'BMC Power On'
-        else:
-            boot = 'BMC Power Off'
+            grp.rprint_timen("The machine state is not valid for a '" +
+                             boot_candidate + "' boot test.")
+            boot_stack.append(boot_candidate)
+            popped_boot = boot_candidate
+
+    # Loop through your list selecting a boot_candidates
+    boot_candidates = []
+    for boot_candidate in boot_list:
+        if st.compare_states(state, boot_table[boot_candidate]['start']):
+            if stack_popped:
+                if st.compare_states(boot_table[boot_candidate]['end'],
+                   boot_table[popped_boot]['start']):
+                    boot_candidates.append(boot_candidate)
+            else:
+                boot_candidates.append(boot_candidate)
+
+    if len(boot_candidates) == 0:
+        grp.rprint_timen("The user's boot list contained no boot tests" +
+                         " which are valid for the current machine state.")
+        boot_candidate = default_power_on
+        if not st.compare_states(state, boot_table[default_power_on]['start']):
+            boot_candidate = default_power_off
+        boot_candidates.append(boot_candidate)
+        grp.rprint_timen("Using default '" + boot_candidate +
+                         "' boot type to transtion to valid state.")
+
+    grp.rdprint_var(boot_candidates)
+
+    # Randomly select a boot from the candidate list.
+    boot = random.choice(boot_candidates)
 
     return boot
 
@@ -231,39 +297,6 @@
 
 
 ###############################################################################
-def my_ffdc():
-
-    r"""
-    Collect FFDC data.
-    """
-
-    plug_in_setup()
-    rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
-        call_point='ffdc', stop_on_plug_in_failure=1)
-
-    AUTOBOOT_FFDC_PREFIX = os.environ['AUTOBOOT_FFDC_PREFIX']
-
-    # FFDC_LOG_PATH is used by "FFDC" keyword.
-    FFDC_DIR_PATH = BuiltIn().get_variable_value("${FFDC_DIR_PATH}")
-    BuiltIn().set_global_variable("${FFDC_LOG_PATH}",
-                                  FFDC_DIR_PATH)
-
-    cmd_buf = ["FFDC", "ffdc_prefix=" + AUTOBOOT_FFDC_PREFIX]
-    grp.rpissuing_keyword(cmd_buf)
-    BuiltIn().run_keyword(*cmd_buf)
-
-    state = st.get_state()
-    BuiltIn().set_global_variable("${state}",
-                                  state)
-
-    cmd_buf = ["Print Defect Report"]
-    grp.rpissuing_keyword(cmd_buf)
-    BuiltIn().run_keyword(*cmd_buf)
-
-###############################################################################
-
-
-###############################################################################
 def print_last_boots():
 
     r"""
@@ -273,7 +306,6 @@
     # indent 0, 90 chars wide, linefeed, char is "="
     grp.rqprint_dashes(0, 90)
     grp.rqprintn("Last 10 boots:\n")
-    last_ten = BuiltIn().get_variable_value("${LAST_TEN}")
 
     for boot_entry in last_ten:
         grp.rqprint(boot_entry)
@@ -283,29 +315,6 @@
 
 
 ###############################################################################
-def print_test_start_message(boot_keyword):
-
-    r"""
-    Print a message indicating what boot test is about to run.
-
-    Description of arguments:
-    boot_keyword  The name of the boot which is to be run
-                  (e.g. "BMC Power On").
-    """
-
-    doing_msg = gp.sprint_timen("Doing \"" + boot_keyword + "\".")
-    grp.rqprint(doing_msg)
-
-    last_ten = BuiltIn().get_variable_value("${LAST_TEN}")
-    last_ten.append(doing_msg)
-
-    if len(last_ten) > 10:
-        del last_ten[0]
-
-###############################################################################
-
-
-###############################################################################
 def print_defect_report():
 
     r"""
@@ -317,15 +326,12 @@
     grp.rqprint_dashes(0, 90, 1, "=")
     grp.rqprintn("Copy this data to the defect:\n")
 
-    parm_list = BuiltIn().get_variable_value("${parm_list}")
-
     grp.rqpvars(*parm_list)
 
     grp.rqprintn()
 
     print_last_boots()
     grp.rqprintn()
-    state = BuiltIn().get_variable_value("${state}")
     grp.rqpvar(state)
 
     # At some point I'd like to have the 'Call FFDC Methods' return a list
@@ -337,17 +343,11 @@
     LOG_PREFIX = BuiltIn().get_variable_value("${LOG_PREFIX}")
 
     output = '\n'.join(glob.glob(LOG_PREFIX + '*'))
-
-    FFDC_LIST_FILE_PATH = \
-        BuiltIn().get_variable_value("${FFDC_LIST_FILE_PATH}")
-
     try:
-        ffdc_list = open(FFDC_LIST_FILE_PATH, 'r')
+        ffdc_list = open(ffdc_list_file_path, 'r')
     except IOError:
         ffdc_list = ""
 
-    status_file_path = BuiltIn().get_variable_value("${status_file_path}")
-
     grp.rqprintn()
     grp.rqprintn("FFDC data files:")
     if status_file_path != "":
@@ -360,3 +360,234 @@
     grp.rqprint_dashes(0, 90, 1, "=")
 
 ###############################################################################
+
+
+###############################################################################
+def my_ffdc():
+
+    r"""
+    Collect FFDC data.
+    """
+
+    global state
+
+    plug_in_setup()
+    rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
+        call_point='ffdc', stop_on_plug_in_failure=1)
+
+    AUTOBOOT_FFDC_PREFIX = os.environ['AUTOBOOT_FFDC_PREFIX']
+
+    # FFDC_LOG_PATH is used by "FFDC" keyword.
+    BuiltIn().set_global_variable("${FFDC_LOG_PATH}", ffdc_dir_path)
+
+    cmd_buf = ["FFDC", "ffdc_prefix=" + AUTOBOOT_FFDC_PREFIX]
+    grp.rpissuing_keyword(cmd_buf)
+    BuiltIn().run_keyword(*cmd_buf)
+
+    my_get_state()
+
+    print_defect_report()
+
+###############################################################################
+
+
+###############################################################################
+def print_test_start_message(boot_keyword):
+
+    r"""
+    Print a message indicating what boot test is about to run.
+
+    Description of arguments:
+    boot_keyword  The name of the boot which is to be run
+                  (e.g. "BMC Power On").
+    """
+
+    global last_ten
+
+    doing_msg = gp.sprint_timen("Doing \"" + boot_keyword + "\".")
+    grp.rqprint(doing_msg)
+
+    last_ten.append(doing_msg)
+
+    if len(last_ten) > 10:
+        del last_ten[0]
+
+###############################################################################
+
+
+###############################################################################
+def run_boot(boot):
+
+    r"""
+    Run the specified boot.
+
+    Description of arguments:
+    boot  The name of the boot test to be performed.
+    """
+
+    global state
+
+    print_test_start_message(boot)
+
+    plug_in_setup()
+    rc, shell_rc, failed_plug_in_name = \
+        grpi.rprocess_plug_in_packages(call_point="pre_boot")
+    if rc != 0:
+        error_message = "Plug-in failed with non-zero return code.\n" +\
+            gp.sprint_var(rc, 1)
+        BuiltIn().fail(gp.sprint_error(error_message))
+
+    if test_mode:
+        # In test mode, we'll pretend the boot worked by assigning its
+        # required end state to the default state value.
+        state = boot_table[boot]['end']
+    else:
+        # Assertion:  We trust that the state data was made fresh by the
+        # caller.
+
+        grp.rprintn()
+
+        if boot_table[boot]['method_type'] == "keyword":
+            cmd_buf = boot_table[boot]['method'].split("  ")
+            grp.rpissuing_keyword(cmd_buf)
+            BuiltIn().run_keyword(*cmd_buf)
+
+        if boot_table[boot]['bmc_reboot']:
+            st.wait_for_comm_cycle(int(state['epoch_seconds']))
+        else:
+            match_state = st.anchor_state(state)
+            del match_state['epoch_seconds']
+            # Wait for the state to change in any way.
+            st.wait_state(match_state, wait_time=state_change_timeout,
+                          interval="3 seconds", invert=1)
+
+        grp.rprintn()
+        if boot_table[boot]['end']['chassis'] == "Off":
+            boot_timeout = power_off_timeout
+        else:
+            boot_timeout = power_on_timeout
+        st.wait_state(boot_table[boot]['end'], wait_time=boot_timeout,
+                      interval="3 seconds")
+
+    plug_in_setup()
+    rc, shell_rc, failed_plug_in_name = \
+        grpi.rprocess_plug_in_packages(call_point="post_boot")
+    if rc != 0:
+        error_message = "Plug-in failed with non-zero return code.\n" +\
+            gp.sprint_var(rc, 1)
+        BuiltIn().fail(gp.sprint_error(error_message))
+
+###############################################################################
+
+
+###############################################################################
+def test_loop_body():
+
+    r"""
+    The main loop body for the loop in main_py.
+
+    Description of arguments:
+    boot_count  The iteration number (starts at 1).
+    """
+
+    global boot_count
+    global state
+    global next_boot
+    global boot_success
+
+    grp.rqprintn()
+
+    boot_count += 1
+
+    next_boot = select_boot()
+
+    grp.rqprint_timen("Starting boot " + str(boot_count) + ".")
+
+    # Clear the ffdc_list_file_path file.  Plug-ins may now write to it.
+    try:
+        os.remove(ffdc_list_file_path)
+    except OSError:
+        pass
+
+    cmd_buf = ["run_boot", next_boot]
+    boot_status, msg = BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
+    if boot_status == "FAIL":
+        grp.rprint(msg)
+
+    grp.rqprintn()
+    if boot_status == "PASS":
+        boot_success = 1
+        grp.rqprint_timen("BOOT_SUCCESS: \"" + next_boot + "\" succeeded.")
+    else:
+        boot_success = 0
+        grp.rqprint_timen("BOOT_FAILED: \"" + next_boot + "\" failed.")
+
+    boot_results.update(next_boot, boot_status)
+
+    plug_in_setup()
+    # NOTE: A post_test_case call point failure is NOT counted as a boot
+    # failure.
+    rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
+        call_point='post_test_case', stop_on_plug_in_failure=1)
+
+    plug_in_setup()
+    rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
+        call_point='ffdc_check', shell_rc=0x00000200,
+        stop_on_plug_in_failure=1, stop_on_non_zero_rc=1)
+    if boot_status != "PASS" or ffdc_check == "All" or shell_rc == 0x00000200:
+        cmd_buf = ["my_ffdc"]
+        grp.rpissuing_keyword(cmd_buf)
+        BuiltIn().run_keyword_and_continue_on_failure(*cmd_buf)
+
+    plug_in_setup()
+    rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
+        call_point='stop_check')
+    if rc != 0:
+        error_message = "Stopping as requested by user.\n"
+        grp.rprint_error_report(error_message)
+        BuiltIn().fail(error_message)
+
+    boot_results.print_report()
+    grp.rqprint_timen("Finished boot " + str(boot_count) + ".")
+
+    return True
+
+###############################################################################
+
+
+###############################################################################
+def program_teardown():
+
+    r"""
+    Clean up after this program.
+    """
+
+    if cp_setup_called:
+        plug_in_setup()
+        rc, shell_rc, failed_plug_in_name = grpi.rprocess_plug_in_packages(
+            call_point='cleanup', stop_on_plug_in_failure=1)
+
+###############################################################################
+
+
+###############################################################################
+def main_py():
+
+    r"""
+    Do main program processing.
+    """
+
+    setup()
+
+    # Process caller's boot_stack.
+    while (len(boot_stack) > 0):
+        test_loop_body()
+
+    # Process caller's boot_list.
+    if len(boot_list) > 0:
+        for ix in range(1, max_num_tests + 1):
+            test_loop_body()
+
+    grp.rqprint_timen("Completed all requested boot tests.")
+
+###############################################################################