1#!/usr/bin/env python 2 3r""" 4This module has functions to support various data structures such as the 5boot_table, valid_boot_list and boot_results_table. 6""" 7 8import os 9import tempfile 10import json 11from tally_sheet import * 12 13from robot.libraries.BuiltIn import BuiltIn 14try: 15 from robot.utils import DotDict 16except ImportError: 17 import collections 18 19import gen_print as gp 20import gen_robot_print as grp 21import gen_valid as gv 22import gen_misc as gm 23import gen_cmd as gc 24 25# The code base directory will be one level up from the directory containing 26# this module. 27code_base_dir_path = os.path.dirname(os.path.dirname(__file__)) + os.sep 28 29 30############################################################################### 31def create_boot_table(file_path=None): 32 33 r""" 34 Read the boot table JSON file, convert it to an object and return it. 35 36 Note that if the user is running without a global OS_HOST robot variable 37 specified, this function will remove all of the "os_" start and end state 38 requirements from the JSON data. 39 40 Description of arguments: 41 file_path The path to the boot_table file. If this value is not 42 specified, it will be obtained from the "BOOT_TABLE_PATH" 43 environment variable, if set. Otherwise, it will default to 44 "data/boot_table.json". If this value is a relative path, 45 this function will use the code_base_dir_path as the base 46 directory (see definition above). 47 """ 48 if file_path is None: 49 file_path = os.environ.get('BOOT_TABLE_PATH', 'data/boot_table.json') 50 51 if not file_path.startswith("/"): 52 file_path = code_base_dir_path + file_path 53 54 # Pre-process the file by removing blank lines and comment lines. 55 temp = tempfile.NamedTemporaryFile() 56 temp_file_path = temp.name 57 58 cmd_buf = "egrep -v '^[ ]*$|^[ ]*#' " + file_path + " > " + temp_file_path 59 gc.cmd_fnc_u(cmd_buf, quiet=1) 60 61 boot_file = open(temp_file_path) 62 boot_table = json.load(boot_file, object_hook=DotDict) 63 64 # If the user is running without an OS_HOST, we remove os starting and 65 # ending state requirements from the boot entries. 66 os_host = BuiltIn().get_variable_value("${OS_HOST}", default="") 67 if os_host == "": 68 for boot in boot_table: 69 state_keys = ['start', 'end'] 70 for state_key in state_keys: 71 for sub_state in boot_table[boot][state_key]: 72 if sub_state.startswith("os_"): 73 boot_table[boot][state_key].pop(sub_state, None) 74 75 # For every boot_type we should have a corresponding mfg mode boot type. 76 enhanced_boot_table = DotDict() 77 for key, value in boot_table.iteritems(): 78 enhanced_boot_table[key] = value 79 enhanced_boot_table[key + " (mfg)"] = value 80 81 return enhanced_boot_table 82 83############################################################################### 84 85 86############################################################################### 87def create_valid_boot_list(boot_table): 88 89 r""" 90 Return a list of all of the valid boot types (e.g. ['BMC Power On', 91 'BMC Power Off', ....] 92 93 Description of arguments: 94 boot_table A boot table such as is returned by the create_boot_table 95 function. 96 """ 97 98 return list(boot_table.keys()) 99 100############################################################################### 101 102 103############################################################################### 104def read_boot_lists(dir_path="data/boot_lists/"): 105 106 r""" 107 Read the contents of all the boot lists files found in the given boot lists 108 directory and return dictionary of the lists. 109 110 Boot lists are simply files containing a boot test name on each line. 111 These files are useful for categorizing and organizing boot tests. For 112 example, there may be a "Power_on" list, a "Power_off" list, etc. 113 114 The names of the boot list files will be the keys to the top level 115 dictionary. Each dictionary entry is a list of all the boot tests found 116 in the corresponding file. 117 118 Here is an abbreviated look at the resulting boot_lists dictionary. 119 120 boot_lists: 121 boot_lists[All]: 122 boot_lists[All][0]: BMC Power On 123 boot_lists[All][1]: BMC Power Off 124 ... 125 boot_lists[Code_update]: 126 boot_lists[Code_update][0]: BMC oob hpm 127 boot_lists[Code_update][1]: BMC ib hpm 128 ... 129 130 Description of arguments: 131 dir_path The path to the directory containing the boot list files. If 132 this value is a relative path, this function will use the 133 code_base_dir_path as the base directory (see definition above). 134 """ 135 136 if not dir_path.startswith("/"): 137 # Dir path is relative. 138 dir_path = code_base_dir_path + dir_path 139 140 # Get a list of all file names in the directory. 141 boot_file_names = os.listdir(dir_path) 142 143 boot_lists = DotDict() 144 for boot_category in boot_file_names: 145 file_path = gm.which(dir_path + boot_category) 146 boot_list = gm.file_to_list(file_path, newlines=0, comments=0, trim=1) 147 boot_lists[boot_category] = boot_list 148 149 return boot_lists 150 151############################################################################### 152 153 154############################################################################### 155def valid_boot_list(boot_list, 156 valid_boot_types): 157 158 r""" 159 Verify that each entry in boot_list is a supported boot test. 160 161 Description of arguments: 162 boot_list An array (i.e. list) of boot test types 163 (e.g. "BMC Power On"). 164 valid_boot_types A list of valid boot types such as that returned by 165 create_valid_boot_list. 166 """ 167 168 for boot_name in boot_list: 169 boot_name = boot_name.strip(" ") 170 error_message = gv.svalid_value(boot_name, 171 valid_values=valid_boot_types, 172 var_name="boot_name") 173 if error_message != "": 174 BuiltIn().fail(gp.sprint_error(error_message)) 175 176############################################################################### 177 178 179############################################################################### 180class boot_results: 181 182 r""" 183 This class defines a boot_results table. 184 """ 185 186 def __init__(self, 187 boot_table, 188 boot_pass=0, 189 boot_fail=0, 190 obj_name='boot_results'): 191 192 r""" 193 Initialize the boot results object. 194 195 Description of arguments: 196 boot_table Boot table object (see definition above). The boot table 197 contains all of the valid boot test types. It can be 198 created with the create_boot_table function. 199 boot_pass An initial boot_pass value. This program may be called 200 as part of a larger test suite. As such there may already 201 have been some successful boot tests that we need to 202 keep track of. 203 boot_fail An initial boot_fail value. This program may be called 204 as part of a larger test suite. As such there may already 205 have been some unsuccessful boot tests that we need to 206 keep track of. 207 obj_name The name of this object. 208 """ 209 210 # Store the method parms as class data. 211 self.__obj_name = obj_name 212 self.__initial_boot_pass = boot_pass 213 self.__initial_boot_fail = boot_fail 214 215 # Create boot_results_fields for use in creating boot_results table. 216 boot_results_fields = DotDict([('total', 0), ('pass', 0), ('fail', 0)]) 217 # Create boot_results table. 218 self.__boot_results = tally_sheet('boot type', 219 boot_results_fields, 220 'boot_test_results') 221 self.__boot_results.set_sum_fields(['total', 'pass', 'fail']) 222 self.__boot_results.set_calc_fields(['total=pass+fail']) 223 # Create one row in the result table for each kind of boot test 224 # in the boot_table (i.e. for all supported boot tests). 225 for boot_name in list(boot_table.keys()): 226 self.__boot_results.add_row(boot_name) 227 228 def return_total_pass_fail(self): 229 230 r""" 231 Return the total boot_pass and boot_fail values. This information is 232 comprised of the pass/fail values from the table plus the initial 233 pass/fail values. 234 """ 235 236 totals_line = self.__boot_results.calc() 237 return totals_line['pass'] + self.__initial_boot_pass,\ 238 totals_line['fail'] + self.__initial_boot_fail 239 240 def update(self, 241 boot_type, 242 boot_status): 243 244 r""" 245 Update our boot_results_table. This includes: 246 - Updating the record for the given boot_type by incrementing the pass 247 or fail field. 248 - Calling the calc method to have the totals calculated. 249 250 Description of arguments: 251 boot_type The type of boot test just done (e.g. "BMC Power On"). 252 boot_status The status of the boot just done. This should be equal to 253 either "pass" or "fail" (case-insensitive). 254 """ 255 256 self.__boot_results.inc_row_field(boot_type, boot_status.lower()) 257 self.__boot_results.calc() 258 259 def sprint_report(self, 260 header_footer="\n"): 261 262 r""" 263 String-print the formatted boot_resuls_table and return them. 264 265 Description of arguments: 266 header_footer This indicates whether a header and footer are to be 267 included in the report. 268 """ 269 270 buffer = "" 271 272 buffer += gp.sprint(header_footer) 273 buffer += self.__boot_results.sprint_report() 274 buffer += gp.sprint(header_footer) 275 276 return buffer 277 278 def print_report(self, 279 header_footer="\n"): 280 281 r""" 282 Print the formatted boot_resuls_table to the console. 283 284 See sprint_report for details. 285 """ 286 287 grp.rqprint(self.sprint_report(header_footer)) 288 289 def sprint_obj(self): 290 291 r""" 292 sprint the fields of this object. This would normally be for debug 293 purposes only. 294 """ 295 296 buffer = "" 297 298 buffer += "class name: " + self.__class__.__name__ + "\n" 299 buffer += gp.sprint_var(self.__obj_name) 300 buffer += self.__boot_results.sprint_obj() 301 buffer += gp.sprint_var(self.__initial_boot_pass) 302 buffer += gp.sprint_var(self.__initial_boot_fail) 303 304 return buffer 305 306 def print_obj(self): 307 308 r""" 309 Print the fields of this object to stdout. This would normally be for 310 debug purposes. 311 """ 312 313 grp.rprint(self.sprint_obj()) 314 315############################################################################### 316