blob: c036dc84c59537d399066ac4e03a31128aeea90e [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
75 return boot_table
76
77###############################################################################
78
79
80###############################################################################
81def create_valid_boot_list(boot_table):
82
83 r"""
84 Return a list of all of the valid boot types (e.g. ['BMC Power On',
85 'BMC Power Off', ....]
86
87 Description of arguments:
88 boot_table A boot table such as is returned by the create_boot_table
89 function.
90 """
91
92 return list(boot_table.keys())
93
94###############################################################################
95
96
97###############################################################################
98def read_boot_lists(dir_path="data/boot_lists/"):
99
100 r"""
101 Read the contents of all the boot lists files found in the given boot lists
102 directory and return dictionary of the lists.
103
104 Boot lists are simply files containing a boot test name on each line.
105 These files are useful for categorizing and organizing boot tests. For
106 example, there may be a "Power_on" list, a "Power_off" list, etc.
107
108 The names of the boot list files will be the keys to the top level
109 dictionary. Each dictionary entry is a list of all the boot tests found
110 in the corresponding file.
111
112 Here is an abbreviated look at the resulting boot_lists dictionary.
113
114 boot_lists:
115 boot_lists[All]:
116 boot_lists[All][0]: BMC Power On
117 boot_lists[All][1]: BMC Power Off
118 ...
119 boot_lists[Code_update]:
120 boot_lists[Code_update][0]: BMC oob hpm
121 boot_lists[Code_update][1]: BMC ib hpm
122 ...
123
124 Description of arguments:
125 dir_path The path to the directory containing the boot list files. If
126 this value is a relative path, this function will use the
127 code_base_dir_path as the base directory (see definition above).
128 """
129
130 if not dir_path.startswith("/"):
131 # Dir path is relative.
132 dir_path = code_base_dir_path + dir_path
133
134 # Get a list of all file names in the directory.
135 boot_file_names = os.listdir(dir_path)
136
137 boot_lists = DotDict()
138 for boot_category in boot_file_names:
139 file_path = gm.which(dir_path + boot_category)
140 boot_list = gm.file_to_list(file_path, newlines=0, comments=0, trim=1)
141 boot_lists[boot_category] = boot_list
142
143 return boot_lists
144
145###############################################################################
146
147
148###############################################################################
149def valid_boot_list(boot_list,
150 valid_boot_types):
151
152 r"""
153 Verify that each entry in boot_list is a supported boot test.
154
155 Description of arguments:
156 boot_list An array (i.e. list) of boot test types
157 (e.g. "BMC Power On").
158 valid_boot_types A list of valid boot types such as that returned by
159 create_valid_boot_list.
160 """
161
162 for boot_name in boot_list:
163 boot_name = boot_name.strip(" ")
164 error_message = gv.svalid_value(boot_name,
165 valid_values=valid_boot_types,
166 var_name="boot_name")
167 if error_message != "":
168 BuiltIn().fail(gp.sprint_error(error_message))
169
170###############################################################################
171
172
173###############################################################################
174class boot_results:
175
176 r"""
177 This class defines a boot_results table.
178 """
179
180 def __init__(self,
181 boot_table,
182 boot_pass=0,
183 boot_fail=0,
184 obj_name='boot_results'):
185
186 r"""
187 Initialize the boot results object.
188
189 Description of arguments:
190 boot_table Boot table object (see definition above). The boot table
191 contains all of the valid boot test types. It can be
192 created with the create_boot_table function.
193 boot_pass An initial boot_pass value. This program may be called
194 as part of a larger test suite. As such there may already
195 have been some successful boot tests that we need to
196 keep track of.
197 boot_fail An initial boot_fail value. This program may be called
198 as part of a larger test suite. As such there may already
199 have been some unsuccessful boot tests that we need to
200 keep track of.
201 obj_name The name of this object.
202 """
203
204 # Store the method parms as class data.
205 self.__obj_name = obj_name
206 self.__initial_boot_pass = boot_pass
207 self.__initial_boot_fail = boot_fail
208
209 # Create boot_results_fields for use in creating boot_results table.
210 boot_results_fields = DotDict([('total', 0), ('pass', 0), ('fail', 0)])
211 # Create boot_results table.
212 self.__boot_results = tally_sheet('boot type',
213 boot_results_fields,
214 'boot_test_results')
215 self.__boot_results.set_sum_fields(['total', 'pass', 'fail'])
216 self.__boot_results.set_calc_fields(['total=pass+fail'])
217 # Create one row in the result table for each kind of boot test
218 # in the boot_table (i.e. for all supported boot tests).
219 for boot_name in list(boot_table.keys()):
220 self.__boot_results.add_row(boot_name)
221
222 def return_total_pass_fail(self):
223
224 r"""
225 Return the total boot_pass and boot_fail values. This information is
226 comprised of the pass/fail values from the table plus the initial
227 pass/fail values.
228 """
229
230 totals_line = self.__boot_results.calc()
231 return totals_line['pass'] + self.__initial_boot_pass,\
232 totals_line['fail'] + self.__initial_boot_fail
233
234 def update(self,
235 boot_type,
236 boot_status):
237
238 r"""
239 Update our boot_results_table. This includes:
240 - Updating the record for the given boot_type by incrementing the pass
241 or fail field.
242 - Calling the calc method to have the totals calculated.
243
244 Description of arguments:
245 boot_type The type of boot test just done (e.g. "BMC Power On").
246 boot_status The status of the boot just done. This should be equal to
247 either "pass" or "fail" (case-insensitive).
248 """
249
250 self.__boot_results.inc_row_field(boot_type, boot_status.lower())
251
252 def sprint_report(self,
253 header_footer="\n"):
254
255 r"""
256 String-print the formatted boot_resuls_table and return them.
257
258 Description of arguments:
259 header_footer This indicates whether a header and footer are to be
260 included in the report.
261 """
262
263 buffer = ""
264
265 buffer += gp.sprint(header_footer)
266 buffer += self.__boot_results.sprint_report()
267 buffer += gp.sprint(header_footer)
268
269 return buffer
270
271 def print_report(self,
272 header_footer="\n"):
273
274 r"""
275 Print the formatted boot_resuls_table to the console.
276
277 See sprint_report for details.
278 """
279
280 grp.rqprint(self.sprint_report(header_footer))
281
282 def sprint_obj(self):
283
284 r"""
285 sprint the fields of this object. This would normally be for debug
286 purposes only.
287 """
288
289 buffer = ""
290
291 buffer += "class name: " + self.__class__.__name__ + "\n"
292 buffer += gp.sprint_var(self.__obj_name)
293 buffer += self.__boot_results.sprint_obj()
294 buffer += gp.sprint_var(self.__initial_boot_pass)
295 buffer += gp.sprint_var(self.__initial_boot_fail)
296
297 return buffer
298
299 def print_obj(self):
300
301 r"""
302 Print the fields of this object to stdout. This would normally be for
303 debug purposes.
304 """
305
306 grp.rprint(self.sprint_obj())
307
308###############################################################################