blob: 2b331d78de6eb17b2175b4115e6d62df0578625d [file] [log] [blame]
Michael Walshac29d062017-02-20 16:13:10 -06001#!/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
Michael Walsh07a01ef2017-02-27 14:20:22 -060075 # 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
Michael Walshac29d062017-02-20 16:13:10 -060082
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())
Michael Walsh8f1ef9e2017-03-02 14:31:24 -0600257 self.__boot_results.calc()
Michael Walshac29d062017-02-20 16:13:10 -0600258
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###############################################################################