|  | #!/usr/bin/env python | 
|  |  | 
|  | r""" | 
|  | This module has functions to support various data structures such as the | 
|  | boot_table, valid_boot_list and boot_results_table. | 
|  | """ | 
|  |  | 
|  | import os | 
|  | import tempfile | 
|  | import json | 
|  | from tally_sheet import * | 
|  |  | 
|  | from robot.libraries.BuiltIn import BuiltIn | 
|  | try: | 
|  | from robot.utils import DotDict | 
|  | except ImportError: | 
|  | import collections | 
|  |  | 
|  | import gen_print as gp | 
|  | import gen_robot_print as grp | 
|  | import gen_valid as gv | 
|  | import gen_misc as gm | 
|  | import gen_cmd as gc | 
|  |  | 
|  | # The code base directory will be one level up from the directory containing | 
|  | # this module. | 
|  | code_base_dir_path = os.path.dirname(os.path.dirname(__file__)) + os.sep | 
|  |  | 
|  |  | 
|  | ############################################################################### | 
|  | def create_boot_table(file_path=None): | 
|  |  | 
|  | r""" | 
|  | Read the boot table JSON file, convert it to an object and return it. | 
|  |  | 
|  | Note that if the user is running without a global OS_HOST robot variable | 
|  | specified, this function will remove all of the "os_" start and end state | 
|  | requirements from the JSON data. | 
|  |  | 
|  | Description of arguments: | 
|  | file_path  The path to the boot_table file.  If this value is not | 
|  | specified, it will be obtained from the "BOOT_TABLE_PATH" | 
|  | environment variable, if set.  Otherwise, it will default to | 
|  | "data/boot_table.json".  If this value is a relative path, | 
|  | this function will use the code_base_dir_path as the base | 
|  | directory (see definition above). | 
|  | """ | 
|  | if file_path is None: | 
|  | file_path = os.environ.get('BOOT_TABLE_PATH', 'data/boot_table.json') | 
|  |  | 
|  | if not file_path.startswith("/"): | 
|  | file_path = code_base_dir_path + file_path | 
|  |  | 
|  | # Pre-process the file by removing blank lines and comment lines. | 
|  | temp = tempfile.NamedTemporaryFile() | 
|  | temp_file_path = temp.name | 
|  |  | 
|  | cmd_buf = "egrep -v '^[ ]*$|^[ ]*#' " + file_path + " > " + temp_file_path | 
|  | gc.cmd_fnc_u(cmd_buf, quiet=1) | 
|  |  | 
|  | boot_file = open(temp_file_path) | 
|  | boot_table = json.load(boot_file, object_hook=DotDict) | 
|  |  | 
|  | # If the user is running without an OS_HOST, we remove os starting and | 
|  | # ending state requirements from the boot entries. | 
|  | os_host = BuiltIn().get_variable_value("${OS_HOST}", default="") | 
|  | if os_host == "": | 
|  | for boot in boot_table: | 
|  | state_keys = ['start', 'end'] | 
|  | for state_key in state_keys: | 
|  | for sub_state in boot_table[boot][state_key]: | 
|  | if sub_state.startswith("os_"): | 
|  | boot_table[boot][state_key].pop(sub_state, None) | 
|  |  | 
|  | # For every boot_type we should have a corresponding mfg mode boot type. | 
|  | enhanced_boot_table = DotDict() | 
|  | for key, value in boot_table.iteritems(): | 
|  | enhanced_boot_table[key] = value | 
|  | enhanced_boot_table[key + " (mfg)"] = value | 
|  |  | 
|  | return enhanced_boot_table | 
|  |  | 
|  | ############################################################################### | 
|  |  | 
|  |  | 
|  | ############################################################################### | 
|  | def create_valid_boot_list(boot_table): | 
|  |  | 
|  | r""" | 
|  | Return a list of all of the valid boot types (e.g. ['BMC Power On', | 
|  | 'BMC Power Off', ....] | 
|  |  | 
|  | Description of arguments: | 
|  | boot_table  A boot table such as is returned by the create_boot_table | 
|  | function. | 
|  | """ | 
|  |  | 
|  | return list(boot_table.keys()) | 
|  |  | 
|  | ############################################################################### | 
|  |  | 
|  |  | 
|  | ############################################################################### | 
|  | def read_boot_lists(dir_path="data/boot_lists/"): | 
|  |  | 
|  | r""" | 
|  | Read the contents of all the boot lists files found in the given boot lists | 
|  | directory and return dictionary of the lists. | 
|  |  | 
|  | Boot lists are simply files containing a boot test name on each line. | 
|  | These files are useful for categorizing and organizing boot tests.  For | 
|  | example, there may be a "Power_on" list, a "Power_off" list, etc. | 
|  |  | 
|  | The names of the boot list files will be the keys to the top level | 
|  | dictionary.  Each dictionary entry is a list of all the boot tests found | 
|  | in the corresponding file. | 
|  |  | 
|  | Here is an abbreviated look at the resulting boot_lists dictionary. | 
|  |  | 
|  | boot_lists: | 
|  | boot_lists[All]: | 
|  | boot_lists[All][0]:                           BMC Power On | 
|  | boot_lists[All][1]:                           BMC Power Off | 
|  | ... | 
|  | boot_lists[Code_update]: | 
|  | boot_lists[Code_update][0]:                   BMC oob hpm | 
|  | boot_lists[Code_update][1]:                   BMC ib hpm | 
|  | ... | 
|  |  | 
|  | Description of arguments: | 
|  | dir_path  The path to the directory containing the boot list files.  If | 
|  | this value is a relative path, this function will use the | 
|  | code_base_dir_path as the base directory (see definition above). | 
|  | """ | 
|  |  | 
|  | if not dir_path.startswith("/"): | 
|  | # Dir path is relative. | 
|  | dir_path = code_base_dir_path + dir_path | 
|  |  | 
|  | # Get a list of all file names in the directory. | 
|  | boot_file_names = os.listdir(dir_path) | 
|  |  | 
|  | boot_lists = DotDict() | 
|  | for boot_category in boot_file_names: | 
|  | file_path = gm.which(dir_path + boot_category) | 
|  | boot_list = gm.file_to_list(file_path, newlines=0, comments=0, trim=1) | 
|  | boot_lists[boot_category] = boot_list | 
|  |  | 
|  | return boot_lists | 
|  |  | 
|  | ############################################################################### | 
|  |  | 
|  |  | 
|  | ############################################################################### | 
|  | def valid_boot_list(boot_list, | 
|  | valid_boot_types): | 
|  |  | 
|  | r""" | 
|  | Verify that each entry in boot_list is a supported boot test. | 
|  |  | 
|  | Description of arguments: | 
|  | boot_list         An array (i.e. list) of boot test types | 
|  | (e.g. "BMC Power On"). | 
|  | valid_boot_types  A list of valid boot types such as that returned by | 
|  | create_valid_boot_list. | 
|  | """ | 
|  |  | 
|  | for boot_name in boot_list: | 
|  | boot_name = boot_name.strip(" ") | 
|  | error_message = gv.svalid_value(boot_name, | 
|  | valid_values=valid_boot_types, | 
|  | var_name="boot_name") | 
|  | if error_message != "": | 
|  | BuiltIn().fail(gp.sprint_error(error_message)) | 
|  |  | 
|  | ############################################################################### | 
|  |  | 
|  |  | 
|  | ############################################################################### | 
|  | class boot_results: | 
|  |  | 
|  | r""" | 
|  | This class defines a boot_results table. | 
|  | """ | 
|  |  | 
|  | def __init__(self, | 
|  | boot_table, | 
|  | boot_pass=0, | 
|  | boot_fail=0, | 
|  | obj_name='boot_results'): | 
|  |  | 
|  | r""" | 
|  | Initialize the boot results object. | 
|  |  | 
|  | Description of arguments: | 
|  | boot_table  Boot table object (see definition above).  The boot table | 
|  | contains all of the valid boot test types.  It can be | 
|  | created with the create_boot_table function. | 
|  | boot_pass   An initial boot_pass value.  This program may be called | 
|  | as part of a larger test suite.  As such there may already | 
|  | have been some successful boot tests that we need to | 
|  | keep track of. | 
|  | boot_fail   An initial boot_fail value.  This program may be called | 
|  | as part of a larger test suite.  As such there may already | 
|  | have been some unsuccessful boot tests that we need to | 
|  | keep track of. | 
|  | obj_name    The name of this object. | 
|  | """ | 
|  |  | 
|  | # Store the method parms as class data. | 
|  | self.__obj_name = obj_name | 
|  | self.__initial_boot_pass = boot_pass | 
|  | self.__initial_boot_fail = boot_fail | 
|  |  | 
|  | # Create boot_results_fields for use in creating boot_results table. | 
|  | boot_results_fields = DotDict([('total', 0), ('pass', 0), ('fail', 0)]) | 
|  | # Create boot_results table. | 
|  | self.__boot_results = tally_sheet('boot type', | 
|  | boot_results_fields, | 
|  | 'boot_test_results') | 
|  | self.__boot_results.set_sum_fields(['total', 'pass', 'fail']) | 
|  | self.__boot_results.set_calc_fields(['total=pass+fail']) | 
|  | # Create one row in the result table for each kind of boot test | 
|  | # in the boot_table (i.e. for all supported boot tests). | 
|  | for boot_name in list(boot_table.keys()): | 
|  | self.__boot_results.add_row(boot_name) | 
|  |  | 
|  | def return_total_pass_fail(self): | 
|  |  | 
|  | r""" | 
|  | Return the total boot_pass and boot_fail values.  This information is | 
|  | comprised of the pass/fail values from the table plus the initial | 
|  | pass/fail values. | 
|  | """ | 
|  |  | 
|  | totals_line = self.__boot_results.calc() | 
|  | return totals_line['pass'] + self.__initial_boot_pass,\ | 
|  | totals_line['fail'] + self.__initial_boot_fail | 
|  |  | 
|  | def update(self, | 
|  | boot_type, | 
|  | boot_status): | 
|  |  | 
|  | 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 calculated. | 
|  |  | 
|  | Description of arguments: | 
|  | boot_type    The type of boot test 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). | 
|  | """ | 
|  |  | 
|  | self.__boot_results.inc_row_field(boot_type, boot_status.lower()) | 
|  | self.__boot_results.calc() | 
|  |  | 
|  | def sprint_report(self, | 
|  | header_footer="\n"): | 
|  |  | 
|  | r""" | 
|  | String-print the formatted boot_resuls_table and return them. | 
|  |  | 
|  | Description of arguments: | 
|  | header_footer  This indicates whether a header and footer are to be | 
|  | included in the report. | 
|  | """ | 
|  |  | 
|  | buffer = "" | 
|  |  | 
|  | buffer += gp.sprint(header_footer) | 
|  | buffer += self.__boot_results.sprint_report() | 
|  | buffer += gp.sprint(header_footer) | 
|  |  | 
|  | return buffer | 
|  |  | 
|  | def print_report(self, | 
|  | header_footer="\n"): | 
|  |  | 
|  | r""" | 
|  | Print the formatted boot_resuls_table to the console. | 
|  |  | 
|  | See sprint_report for details. | 
|  | """ | 
|  |  | 
|  | grp.rqprint(self.sprint_report(header_footer)) | 
|  |  | 
|  | def sprint_obj(self): | 
|  |  | 
|  | r""" | 
|  | sprint the fields of this object.  This would normally be for debug | 
|  | purposes only. | 
|  | """ | 
|  |  | 
|  | buffer = "" | 
|  |  | 
|  | buffer += "class name: " + self.__class__.__name__ + "\n" | 
|  | buffer += gp.sprint_var(self.__obj_name) | 
|  | buffer += self.__boot_results.sprint_obj() | 
|  | buffer += gp.sprint_var(self.__initial_boot_pass) | 
|  | buffer += gp.sprint_var(self.__initial_boot_fail) | 
|  |  | 
|  | return buffer | 
|  |  | 
|  | def print_obj(self): | 
|  |  | 
|  | r""" | 
|  | Print the fields of this object to stdout.  This would normally be for | 
|  | debug purposes. | 
|  | """ | 
|  |  | 
|  | grp.rprint(self.sprint_obj()) | 
|  |  | 
|  | ############################################################################### |