blob: c802065c690278e71ca02636e985baa27da92bff [file] [log] [blame]
George Keishing04d93452017-05-03 09:14:15 -05001#!/usr/bin/env python
2
3r"""
4This module contains keyword functions to supplement robot's built in
5functions and use in test where generic robot keywords don't support.
George Keishing04d93452017-05-03 09:14:15 -05006"""
Steven Sombar130a04f2017-07-16 10:02:37 -05007
8try:
9 from robot.libraries.BuiltIn import BuiltIn
10 from robot.libraries import DateTime
11except ImportError:
12 pass
George Keishing04d93452017-05-03 09:14:15 -050013import time
Steven Sombar130a04f2017-07-16 10:02:37 -050014import os
15import difflib
16
17
18##########################################################################
19def json_inv_file_diff_check(file1_path,
20 file2_path,
21 diff_file_path,
Steven Sombar48ad01d2017-08-17 14:17:37 -050022 skip_string):
Steven Sombar130a04f2017-07-16 10:02:37 -050023 r"""
24 Compare the contents of two files which contain inventory data in
25 JSON format. The comparison is similar to the unix 'diff' command but
26 the output lists the hardware subsystem (category) where differences
27 are found, and some differences are selectively ignored. The items
Steven Sombar48ad01d2017-08-17 14:17:37 -050028 ignored are defined by the skip_string.
Steven Sombar130a04f2017-07-16 10:02:37 -050029
30 Description of arguments:
31 file1_path File containing JSON formatted data.
32 file2_path File to compare to file1 to.
33 diff_file_path File which will contain the resulting difference report.
Steven Sombar48ad01d2017-08-17 14:17:37 -050034 skip_string String which defines what inventory items
Steven Sombar130a04f2017-07-16 10:02:37 -050035 to ignore if there are differences in inventory
36 files -- some differences are expected or
Steven Sombar48ad01d2017-08-17 14:17:37 -050037 immaterial. For example, assigned processor
Steven Sombar130a04f2017-07-16 10:02:37 -050038 speed routinely varies depending upon the
39 needs of OCC/tmgt/ondemand governor.
40 Back-to-back inventory runs may show
41 processor speed differences even if nothing
42 else was run between them.
Steven Sombar48ad01d2017-08-17 14:17:37 -050043 Each item in this string is of the form
Steven Sombar130a04f2017-07-16 10:02:37 -050044 category:leafname where category is a JSON
45 hardware category such as "processor", "memory",
46 "disk", "display", "network", etc.,
47 and leafname is a leaf node (atribute name)
48 within that category.
Steven Sombar48ad01d2017-08-17 14:17:37 -050049 For example: "processor:size", or
50 "processor:size,network:speed,display:id".
Steven Sombar130a04f2017-07-16 10:02:37 -050051
52 Sample difference report:
53 Difference at line 102 (in section "memory":)
54 102 - "slot": "UOPWR.BAR.1315ACA-DIMM0",
55 102 + "slot": "0"
56 Difference at line 126 (in section "processor":)
57 126 - "size": 2151000000, +++ NOTE! This is an ignore item
58 126 + "size": 2201000000, +++ NOTE! This is an ignore item
59
60 Returns:
61 0 if both files contain the same information or they differ only in
62 items specified as those to ignore.
63 2 if FILES_DO_NOT_MATCH.
64 3 if INPUT_FILE_DOES_NOT_EXIST.
65 4 if IO_EXCEPTION_READING_FILE.
66 5 if IO_EXCEPTION_WRITING_FILE.
67 """
68
69 FILES_MATCH = 0
70 FILES_DO_NOT_MATCH = 2
71 INPUT_FILE_DOES_NOT_EXIST = 3
72 IO_EXCEPTION_READING_FILE = 4
73 IO_EXCEPTION_WRITING_FILE = 5
74
75 # Hardware categories which are reported in the JSON inventory files.
76 hardware_categories = ['\"processor\":', '\"memory\":', '\"disk\":',
77 '\"I/O\":', '\"display\":', '\"generic\":',
78 '\"network\":', '\"communication\":',
79 '\"printer\":', '\"input\":', '\"multimedia\":',
80 '\"tape\":']
81
82 # The minimum size in bytes a JSON file must be.
83 min_json_byte_size = 16
84
85 now = time.strftime("At %Y-%m-%d %H:%M:%S")
86
87 if (not os.path.exists(file1_path) or (not os.path.exists(file2_path))):
88 return INPUT_FILE_DOES_NOT_EXIST
89 try:
90 with open(file1_path, 'r') as file:
91 initial = file.readlines()
92 with open(file2_path, 'r') as file:
93 final = file.readlines()
94 except IOError:
95 file.close()
96 return IO_EXCEPTION_READING_FILE
97 except ValueError:
98 file.close()
99 return INPUT_FILE_MALFORMED
100 else:
101 file.close()
102
103 # Must have more than a trivial number of bytes.
104 if len(initial) <= min_json_byte_size:
105 return INPUT_FILE_MALFORMED
106
107 if (initial == final):
108 try:
109 file = open(diff_file_path, 'w')
110 except IOError:
111 file.close()
Steven Sombar48ad01d2017-08-17 14:17:37 -0500112 line_to_print = "Specified skip (ignore) string = " + \
113 skip_string + "\n\n"
114 file.write(line_to_print)
Steven Sombar130a04f2017-07-16 10:02:37 -0500115 line_to_print = now + " found no difference between file " + \
116 file1_path + " and " + \
117 file2_path + "\n"
118 file.write(line_to_print)
119 file.close()
120 return FILES_MATCH
121
122 # Find the differences and write difference report to diff_file_path file.
123 try:
124 file = open(diff_file_path, 'w')
125 except IOError:
126 file.close()
127 return IO_EXCEPTION_WRITING_FILE
128
Steven Sombar48ad01d2017-08-17 14:17:37 -0500129 line_to_print = "Specified skip (ignore) string = " + skip_string + "\n\n"
130 file.write(line_to_print)
Steven Sombar130a04f2017-07-16 10:02:37 -0500131 line_to_print = now + " compared files " + \
132 file1_path + " and " + \
133 file2_path + "\n"
134 file.write(line_to_print)
135
136 diff = difflib.ndiff(initial, final)
137 # The diff array contains all lines that match in the
138 # initial and final arrays, and also all lines that differ.
139 # The first two characters of each line
140 # are prefixed with two letters, defined as:
141 # '- ' This line is unique to initial
142 # '+ ' This line is line unique to final
143 # ' ' This line is common to both, and
144 # '? ' This line indicates approximate differences.
145 # For example, comparing two three-line files:
146 # This line is in both initial and final.
147 # This line is too but the next line is different in each file.
148 # - "size": 2101000000,
149 # ? - ^
150 # + "size": 2002000000,
151 # ? ^^
152
153 print_header_flag = False
154 category = ""
155 row_num = 1
156 item_we_cannot_ignore = False
157
158 for my_line in diff:
159 diff_item = my_line.strip('\n')
160 # If it's a Category, such as processor or memory,
161 # save it. We will print it out later.
162 # Most lines do not explicitly contain a category. As such the
163 # category for that line is found in a previous line, which
164 # will be the category we last found.
165 for hdw_cat in hardware_categories:
166 if (hdw_cat in diff_item):
167 # If we don't have a match we will reuse
168 # the prvious category.
169 category = hdw_cat
170 # Lines beginning with minus or plus or q-mark are
171 # true difference items.
172 # We want to look at those in more detail.
173 if diff_item.startswith('? '):
174 # we can ignore these
175 continue
176 if (diff_item.startswith('- ') or diff_item.startswith('+ ')):
177 # If we have not printed the header line for this
178 # difference, print it now.
179 if print_header_flag is False:
180 line_to_print = "Difference at line " + \
181 str(row_num) + " (in section " + \
182 category + ")\n"
183 file.write(line_to_print)
Steven Sombar48ad01d2017-08-17 14:17:37 -0500184 # If this is in the ignore string, we'll print
Steven Sombar130a04f2017-07-16 10:02:37 -0500185 # it but also add text that it is an ignore item.
186 skipitem = False
Steven Sombar48ad01d2017-08-17 14:17:37 -0500187 # If a skip_string is specified, check if category and item are
188 # in the skip_string.
189 if skip_string:
190 skip_list = skip_string.split(",")
191 for item in skip_list:
192 cat_and_value = item.split(":")
193 ignore_category = cat_and_value[0].lower().strip()
194 ignore_value = cat_and_value[1].lower().strip()
195 if ((ignore_category in category.lower().strip()) and
196 (ignore_value in diff_item.lower().strip())):
197 line_to_print = " " + \
198 str(row_num) + " " + diff_item + \
199 " +++ NOTE! This line matches" + \
200 " the inventory ignore list and" + \
201 " can be ignored. +++\n"
202 # Set flag indicating this item is a skip item.
203 skipitem = True
204 break
Steven Sombar130a04f2017-07-16 10:02:37 -0500205 if skipitem is False:
206 # Its not a skip item, that is,
207 # this is not on the ignore list.
208 # Print the item and set the item_we_canot_ignore flag
209 # indicating we have an item not on the ignore list. The
210 # flag will determine the return code we
211 # pass back to the user at the end.
212 item_we_cannot_ignore = True
213 line_to_print = " " + \
214 str(row_num) + " " + diff_item + "\n"
215 file.write(line_to_print)
216 print_header_flag = True
217
218 else:
219 # Adjust row numbering as a difference is only one line
220 # but it takes several lines in the diff file.
221 if print_header_flag is True:
222 row_num = row_num + 1
223 print_header_flag = False
224 row_num = row_num + 1
225
226 # Make sure we end the file.
227 file.write("\n")
228 file.close()
229
230 if item_we_cannot_ignore:
231 # We have at least one diff_item not on the ignore list.
232 return FILES_DO_NOT_MATCH
233 else:
234 # Any differences were on the ignore list.
235 return FILES_MATCH
236###############################################################################
237
George Keishing04d93452017-05-03 09:14:15 -0500238
239###############################################################################
George Keishing04d93452017-05-03 09:14:15 -0500240
George Keishing4bbf5202017-05-18 06:55:53 -0500241
Steven Sombar130a04f2017-07-16 10:02:37 -0500242def run_until_keyword_fails(retry,
243 retry_interval,
244 name,
245 *args):
George Keishing04d93452017-05-03 09:14:15 -0500246 r"""
247 Execute a robot keyword repeatedly until it either fails or the timeout
248 value is exceeded.
249 Note: Opposite of robot keyword "Wait Until Keyword Succeeds".
250
251 Description of argument(s):
252 retry Max timeout time in hour(s).
253 retry_interval Time interval in minute(s) for looping.
254 name Robot keyword to execute.
255 args Robot keyword arguments.
256 """
257
258 # Convert the retry time in seconds
George Keishing4bbf5202017-05-18 06:55:53 -0500259 retry_seconds = DateTime.convert_time(retry)
George Keishing04d93452017-05-03 09:14:15 -0500260 timeout = time.time() + int(retry_seconds)
261
262 # Convert the interval time in seconds
George Keishing4bbf5202017-05-18 06:55:53 -0500263 interval_seconds = DateTime.convert_time(retry_interval)
George Keishing04d93452017-05-03 09:14:15 -0500264 interval = int(interval_seconds)
265
266 BuiltIn().log(timeout)
267 BuiltIn().log(interval)
268
269 while True:
George Keishing4bbf5202017-05-18 06:55:53 -0500270 status = BuiltIn().run_keyword_and_return_status(name, *args)
George Keishing04d93452017-05-03 09:14:15 -0500271
272 # Return if keywords returns as failure.
George Keishing4bbf5202017-05-18 06:55:53 -0500273 if status is False:
George Keishing04d93452017-05-03 09:14:15 -0500274 BuiltIn().log("Failed as expected")
George Keishing4bbf5202017-05-18 06:55:53 -0500275 return False
George Keishing04d93452017-05-03 09:14:15 -0500276 # Return if retry timeout as success.
277 elif time.time() > timeout > 0:
278 BuiltIn().log("Max retry timeout")
George Keishing4bbf5202017-05-18 06:55:53 -0500279 return True
George Keishing04d93452017-05-03 09:14:15 -0500280 time.sleep(interval)
281 BuiltIn().log(time.time())
282
George Keishing4bbf5202017-05-18 06:55:53 -0500283 return True
284###############################################################################
285
286
287###############################################################################
288def htx_error_log_to_list(htx_error_log_output):
289
290 r"""
291 Parse htx error log output string and return list of strings in the form
292 "<field name>:<field value>".
293 The output of this function may be passed to the build_error_dict function.
294
295 Description of argument(s):
296 htx_error_log_output Error entry string containing the stdout
297 generated by "htxcmdline -geterrlog".
298
299 Example of htx_error_log_output contents:
300
301 ######################## Result Starts Here ###############################
302 Currently running ECG/MDT : /usr/lpp/htx/mdt/mdt.whit
303 ===========================
304 ---------------------------------------------------------------------
305 Device id:/dev/nvidia0
306 Timestamp:Mar 29 19:41:54 2017
307 err=00000027
308 sev=1
309 Exerciser Name:hxenvidia
310 Serial No:Not Available
311 Part No:Not Available
312 Location:Not Available
313 FRU Number:Not Available
314 Device:Not Available
315 Error Text:cudaEventSynchronize for stopEvent returned err = 0039 from file
316 , line 430.
317 ---------------------------------------------------------------------
318 ---------------------------------------------------------------------
319 Device id:/dev/nvidia0
320 Timestamp:Mar 29 19:41:54 2017
321 err=00000027
322 sev=1
323 Exerciser Name:hxenvidia
324 Serial No:Not Available
325 Part No:Not Available
326 Location:Not Available
327 FRU Number:Not Available
328 Device:Not Available
329 Error Text:Hardware Exerciser stopped on error
330 ---------------------------------------------------------------------
331 ######################### Result Ends Here ################################
332
333 Example output:
334 Returns the lists of error string per entry
335 ['Device id:/dev/nvidia0',
336 'Timestamp:Mar 29 19:41:54 2017',
337 'err=00000027',
338 'sev=1',
339 'Exerciser Name:hxenvidia',
340 'Serial No:Not Available',
341 'Part No:Not Available',
342 'Location:Not Available',
343 'FRU Number:Not Available',
344 'Device:Not Available',
345 'Error Text:cudaEventSynchronize for stopEvent returned err = 0039
346 from file , line 430.']
347 """
348
349 # List which will hold all the list of entries.
350 error_list = []
351
352 temp_error_list = []
353 parse_walk = False
354
355 for line in htx_error_log_output.splitlines():
356 # Skip lines starting with "#"
357 if line.startswith("#"):
358 continue
359
360 # Mark line starting with "-" and set parse flag.
361 if line.startswith("-") and parse_walk is False:
362 parse_walk = True
363 continue
364 # Mark line starting with "-" and reset parse flag.
365 # Set temp error list to EMPTY.
366 elif line.startswith("-"):
367 error_list.append(temp_error_list)
368 parse_walk = False
369 temp_error_list = []
370 # Add entry to list if line is not emtpy
371 elif parse_walk:
372 temp_error_list.append(str(line))
373
374 return error_list
375###############################################################################
376
377
378###############################################################################
379def build_error_dict(htx_error_log_output):
380
381 r"""
382 Builds error list into a list of dictionary entries.
383
384 Description of argument(s):
385 error_list Error list entries.
386
387 Example output dictionary:
388 {
389 0:
390 {
391 'sev': '1',
392 'err': '00000027',
393 'Timestamp': 'Mar 29 19:41:54 2017',
394 'Part No': 'Not Available',
395 'Serial No': 'Not Available',
396 'Device': 'Not Available',
397 'FRU Number': 'Not Available',
398 'Location': 'Not Available',
399 'Device id': '/dev/nvidia0',
400 'Error Text': 'cudaEventSynchronize for stopEvent returned err = 0039
401 from file , line 430.',
402 'Exerciser Name': 'hxenvidia'
403 },
404 1:
405 {
406 'sev': '1',
407 'err': '00000027',
408 'Timestamp': 'Mar 29 19:41:54 2017',
409 'Part No': 'Not Available',
410 'Serial No': 'Not Available',
411 'Device': 'Not Available',
412 'FRU Number': 'Not Available',
413 'Location': 'Not Available',
414 'Device id': '/dev/nvidia0',
415 'Error Text': 'Hardware Exerciser stopped on error',
416 'Exerciser Name': 'hxenvidia'
417 }
418 },
419
420 """
421
422 # List which will hold all the list of entries.
423 error_list = []
424 error_list = htx_error_log_to_list(htx_error_log_output)
425
426 # dictionary which holds the error dictionry entry.
427 error_dict = {}
428
429 temp_error_dict = {}
430 error_index = 0
431
432 # Loop through the error list.
433 for entry_list in error_list:
434 # Loop through the first error list entry.
435 for entry in entry_list:
436 # Split string into list for key value update.
437 # Example: 'Device id:/dev/nvidia0'
438 # Example: 'err=00000027'
439 parm_split = re.split("[:=]", entry)
440 # Populate temp dictionary with key value pair data.
441 temp_error_dict[str(parm_split[0])] = parm_split[1]
442
443 # Update the master dictionary per entry index.
444 error_dict[error_index] = temp_error_dict
445 # Reset temp dict to EMPTY and increment index count.
446 temp_error_dict = {}
447 error_index += 1
448
449 return error_dict
George Keishing04d93452017-05-03 09:14:15 -0500450
451###############################################################################