| #!/usr/bin/env python | 
 |  | 
 | r""" | 
 | This module contains keyword functions to supplement robot's built in | 
 | functions and use in test where generic robot keywords don't support. | 
 | """ | 
 |  | 
 | try: | 
 |     from robot.libraries.BuiltIn import BuiltIn | 
 |     from robot.libraries import DateTime | 
 | except ImportError: | 
 |     pass | 
 | import time | 
 | import os | 
 | import difflib | 
 |  | 
 |  | 
 | ########################################################################## | 
 | def json_inv_file_diff_check(file1_path, | 
 |                              file2_path, | 
 |                              diff_file_path, | 
 |                              skip_string): | 
 |     r""" | 
 |     Compare the contents of two files which contain inventory data in | 
 |     JSON format.  The comparison is similar to the unix 'diff' command but | 
 |     the output lists the hardware subsystem (category) where differences | 
 |     are found, and some differences are selectively ignored.  The items | 
 |     ignored are defined by the skip_string. | 
 |  | 
 |     Description of arguments: | 
 |     file1_path       File containing JSON formatted data. | 
 |     file2_path       File to compare to file1 to. | 
 |     diff_file_path   File which will contain the resulting difference report. | 
 |     skip_string      String which defines what inventory items | 
 |                      to ignore if there are differences in inventory | 
 |                      files -- some differences are expected or | 
 |                      immaterial.  For example, assigned processor | 
 |                      speed routinely varies depending upon the | 
 |                      needs of OCC/tmgt/ondemand governor. | 
 |                      Back-to-back inventory runs may show | 
 |                      processor speed differences even if nothing | 
 |                      else was run between them. | 
 |                      Each item in this string is of the form | 
 |                      category:leafname where category is a JSON | 
 |                      hardware category such as "processor", "memory", | 
 |                      "disk", "display", "network", etc., | 
 |                      and leafname is a leaf node (atribute name) | 
 |                      within that category. | 
 |                      For example: "processor:size", or | 
 |                      "processor:size,network:speed,display:id". | 
 |  | 
 |     Sample difference report: | 
 |     Difference at line 102  (in section "memory":) | 
 |      102 -   "slot": "UOPWR.BAR.1315ACA-DIMM0", | 
 |      102 +   "slot": "0" | 
 |     Difference at line 126  (in section "processor":) | 
 |      126 -   "size": 2151000000,    +++ NOTE! This is an ignore item | 
 |      126 +   "size": 2201000000,    +++ NOTE! This is an ignore item | 
 |  | 
 |     Returns: | 
 |     0 if both files contain the same information or they differ only in | 
 |       items specified as those to ignore. | 
 |     2 if FILES_DO_NOT_MATCH. | 
 |     3 if INPUT_FILE_DOES_NOT_EXIST. | 
 |     4 if IO_EXCEPTION_READING_FILE. | 
 |     5 if IO_EXCEPTION_WRITING_FILE. | 
 |     """ | 
 |  | 
 |     FILES_MATCH = 0 | 
 |     FILES_DO_NOT_MATCH = 2 | 
 |     INPUT_FILE_DOES_NOT_EXIST = 3 | 
 |     IO_EXCEPTION_READING_FILE = 4 | 
 |     IO_EXCEPTION_WRITING_FILE = 5 | 
 |  | 
 |     # Hardware categories which are reported in the JSON inventory files. | 
 |     hardware_categories = ['\"processor\":', '\"memory\":', '\"disk\":', | 
 |                            '\"I/O\":', '\"display\":', '\"generic\":', | 
 |                            '\"network\":', '\"communication\":', | 
 |                            '\"printer\":', '\"input\":', '\"multimedia\":', | 
 |                            '\"tape\":'] | 
 |  | 
 |     # The minimum size in bytes a JSON file must be. | 
 |     min_json_byte_size = 16 | 
 |  | 
 |     now = time.strftime("At %Y-%m-%d %H:%M:%S") | 
 |  | 
 |     if (not os.path.exists(file1_path) or (not os.path.exists(file2_path))): | 
 |         return INPUT_FILE_DOES_NOT_EXIST | 
 |     try: | 
 |         with open(file1_path, 'r') as file: | 
 |             initial = file.readlines() | 
 |         with open(file2_path, 'r') as file: | 
 |             final = file.readlines() | 
 |     except IOError: | 
 |         file.close() | 
 |         return IO_EXCEPTION_READING_FILE | 
 |     except ValueError: | 
 |         file.close() | 
 |         return INPUT_FILE_MALFORMED | 
 |     else: | 
 |         file.close() | 
 |  | 
 |     # Must have more than a trivial number of bytes. | 
 |     if len(initial) <= min_json_byte_size: | 
 |         return INPUT_FILE_MALFORMED | 
 |  | 
 |     if (initial == final): | 
 |         try: | 
 |             file = open(diff_file_path, 'w') | 
 |         except IOError: | 
 |             file.close() | 
 |         line_to_print = "Specified skip (ignore) string = " + \ | 
 |             skip_string + "\n\n" | 
 |         file.write(line_to_print) | 
 |         line_to_print = now + " found no difference between file " + \ | 
 |             file1_path + " and " + \ | 
 |             file2_path + "\n" | 
 |         file.write(line_to_print) | 
 |         file.close() | 
 |         return FILES_MATCH | 
 |  | 
 |     # Find the differences and write difference report to diff_file_path file. | 
 |     try: | 
 |         file = open(diff_file_path, 'w') | 
 |     except IOError: | 
 |         file.close() | 
 |         return IO_EXCEPTION_WRITING_FILE | 
 |  | 
 |     line_to_print = "Specified skip (ignore) string = " + skip_string + "\n\n" | 
 |     file.write(line_to_print) | 
 |     line_to_print = now + " compared files " + \ | 
 |         file1_path + " and " + \ | 
 |         file2_path + "\n" | 
 |     file.write(line_to_print) | 
 |  | 
 |     diff = difflib.ndiff(initial, final) | 
 |     # The diff array contains all lines that match in the | 
 |     # initial and final arrays, and also all lines that differ. | 
 |     # The first two characters of each line | 
 |     # are prefixed with two letters, defined as: | 
 |     # '- ' This line is unique to initial | 
 |     # '+ ' This line is line unique to final | 
 |     # '  ' This line is common to both, and | 
 |     # '? ' This line indicates approximate differences. | 
 |     # For example,  comparing two three-line files: | 
 |     #  This line is in both initial and final. | 
 |     #  This line is too but the next line is different in each file. | 
 |     # -                     "size": 2101000000, | 
 |     # ?                              - ^ | 
 |     # +                     "size": 2002000000, | 
 |     # ?                               ^^ | 
 |  | 
 |     print_header_flag = False | 
 |     category = "" | 
 |     row_num = 1 | 
 |     item_we_cannot_ignore = False | 
 |  | 
 |     for my_line in diff: | 
 |         diff_item = my_line.strip('\n') | 
 |         # If it's a Category, such as processor or memory, | 
 |         # save it.  We will print it out later. | 
 |         # Most lines do not explicitly contain a category.  As such the | 
 |         # category for that line is found in a previous line, which | 
 |         # will be the category we last found. | 
 |         for hdw_cat in hardware_categories: | 
 |             if (hdw_cat in diff_item): | 
 |                 # If we don't have a match we will reuse | 
 |                 # the prvious category. | 
 |                 category = hdw_cat | 
 |         # Lines beginning with minus or plus or q-mark are | 
 |         # true difference items. | 
 |         # We want to look at those in more detail. | 
 |         if diff_item.startswith('? '): | 
 |             # we can ignore these | 
 |             continue | 
 |         if (diff_item.startswith('- ') or diff_item.startswith('+ ')): | 
 |             # If we have not printed the header line for this | 
 |             # difference, print it now. | 
 |             if print_header_flag is False: | 
 |                 line_to_print = "Difference at line " + \ | 
 |                  str(row_num) + "  (in section " + \ | 
 |                  category + ")\n" | 
 |                 file.write(line_to_print) | 
 |             # If this is in the ignore string, we'll print | 
 |             # it but also add text that it is an ignore item. | 
 |             skipitem = False | 
 |             # If a skip_string is specified, check if category and item are | 
 |             # in the skip_string. | 
 |             if skip_string: | 
 |                 skip_list = skip_string.split(",") | 
 |                 for item in skip_list: | 
 |                     cat_and_value = item.split(":") | 
 |                     ignore_category = cat_and_value[0].lower().strip() | 
 |                     ignore_value = cat_and_value[1].lower().strip() | 
 |                     if ((ignore_category in category.lower().strip()) and | 
 |                        (ignore_value in diff_item.lower().strip())): | 
 |                         line_to_print = "  " + \ | 
 |                             str(row_num) + " " + diff_item + \ | 
 |                             "    +++ NOTE! This line matches" + \ | 
 |                             " the inventory ignore list and" + \ | 
 |                             " can be ignored. +++\n" | 
 |                         # Set flag indicating this item is a skip item. | 
 |                         skipitem = True | 
 |                         break | 
 |             if skipitem is False: | 
 |                 # Its not a skip item, that is, | 
 |                 # this is not on the ignore list. | 
 |                 # Print the item and set the item_we_canot_ignore flag | 
 |                 # indicating we have an item not on the ignore list.  The | 
 |                 # flag will determine the return code we | 
 |                 # pass back to the user at the end. | 
 |                 item_we_cannot_ignore = True | 
 |                 line_to_print = "  " + \ | 
 |                     str(row_num) + " " + diff_item + "\n" | 
 |             file.write(line_to_print) | 
 |             print_header_flag = True | 
 |  | 
 |         else: | 
 |             # Adjust row numbering as a difference is only one line | 
 |             # but it takes several lines in the diff file. | 
 |             if print_header_flag is True: | 
 |                 row_num = row_num + 1 | 
 |                 print_header_flag = False | 
 |             row_num = row_num + 1 | 
 |  | 
 |     # Make sure we end the file. | 
 |     file.write("\n") | 
 |     file.close() | 
 |  | 
 |     if item_we_cannot_ignore: | 
 |         # We have at least one diff_item not on the ignore list. | 
 |         return FILES_DO_NOT_MATCH | 
 |     else: | 
 |         # Any differences were on the ignore list. | 
 |         return FILES_MATCH | 
 | ############################################################################### | 
 |  | 
 |  | 
 | ############################################################################### | 
 |  | 
 |  | 
 | def run_until_keyword_fails(retry, | 
 |                             retry_interval, | 
 |                             name, | 
 |                             *args): | 
 |     r""" | 
 |     Execute a robot keyword repeatedly until it either fails or the timeout | 
 |     value is exceeded. | 
 |     Note: Opposite of robot keyword "Wait Until Keyword Succeeds". | 
 |  | 
 |     Description of argument(s): | 
 |     retry              Max timeout time in hour(s). | 
 |     retry_interval     Time interval in minute(s) for looping. | 
 |     name               Robot keyword to execute. | 
 |     args               Robot keyword arguments. | 
 |     """ | 
 |  | 
 |     # Convert the retry time in seconds | 
 |     retry_seconds = DateTime.convert_time(retry) | 
 |     timeout = time.time() + int(retry_seconds) | 
 |  | 
 |     # Convert the interval time in seconds | 
 |     interval_seconds = DateTime.convert_time(retry_interval) | 
 |     interval = int(interval_seconds) | 
 |  | 
 |     BuiltIn().log(timeout) | 
 |     BuiltIn().log(interval) | 
 |  | 
 |     while True: | 
 |         status = BuiltIn().run_keyword_and_return_status(name, *args) | 
 |  | 
 |         # Return if keywords returns as failure. | 
 |         if status is False: | 
 |             BuiltIn().log("Failed as expected") | 
 |             return False | 
 |         # Return if retry timeout as success. | 
 |         elif time.time() > timeout > 0: | 
 |             BuiltIn().log("Max retry timeout") | 
 |             return True | 
 |         time.sleep(interval) | 
 |         BuiltIn().log(time.time()) | 
 |  | 
 |     return True | 
 | ############################################################################### | 
 |  | 
 |  | 
 | ############################################################################### | 
 | def htx_error_log_to_list(htx_error_log_output): | 
 |  | 
 |     r""" | 
 |     Parse htx error log output string and return list of strings in the form | 
 |     "<field name>:<field value>". | 
 |     The output of this function may be passed to the build_error_dict function. | 
 |  | 
 |     Description of argument(s): | 
 |     htx_error_log_output        Error entry string containing the stdout | 
 |                                 generated by "htxcmdline -geterrlog". | 
 |  | 
 |     Example of htx_error_log_output contents: | 
 |  | 
 |     ######################## Result Starts Here ############################### | 
 |     Currently running ECG/MDT : /usr/lpp/htx/mdt/mdt.whit | 
 |     =========================== | 
 |     --------------------------------------------------------------------- | 
 |     Device id:/dev/nvidia0 | 
 |     Timestamp:Mar 29 19:41:54 2017 | 
 |     err=00000027 | 
 |     sev=1 | 
 |     Exerciser Name:hxenvidia | 
 |     Serial No:Not Available | 
 |     Part No:Not Available | 
 |     Location:Not Available | 
 |     FRU Number:Not Available | 
 |     Device:Not Available | 
 |     Error Text:cudaEventSynchronize for stopEvent returned err = 0039 from file | 
 |                , line 430. | 
 |     --------------------------------------------------------------------- | 
 |     --------------------------------------------------------------------- | 
 |     Device id:/dev/nvidia0 | 
 |     Timestamp:Mar 29 19:41:54 2017 | 
 |     err=00000027 | 
 |     sev=1 | 
 |     Exerciser Name:hxenvidia | 
 |     Serial No:Not Available | 
 |     Part No:Not Available | 
 |     Location:Not Available | 
 |     FRU Number:Not Available | 
 |     Device:Not Available | 
 |     Error Text:Hardware Exerciser stopped on error | 
 |     --------------------------------------------------------------------- | 
 |     ######################### Result Ends Here ################################ | 
 |  | 
 |     Example output: | 
 |     Returns the lists of error string per entry | 
 |     ['Device id:/dev/nvidia0', | 
 |      'Timestamp:Mar 29 19:41:54 2017', | 
 |      'err=00000027', | 
 |      'sev=1', | 
 |      'Exerciser Name:hxenvidia', | 
 |      'Serial No:Not Available', | 
 |      'Part No:Not Available', | 
 |      'Location:Not Available', | 
 |      'FRU Number:Not Available', | 
 |      'Device:Not Available', | 
 |      'Error Text:cudaEventSynchronize for stopEvent returned err = 0039 | 
 |                  from file , line 430.'] | 
 |     """ | 
 |  | 
 |     # List which will hold all the list of entries. | 
 |     error_list = [] | 
 |  | 
 |     temp_error_list = [] | 
 |     parse_walk = False | 
 |  | 
 |     for line in htx_error_log_output.splitlines(): | 
 |         # Skip lines starting with "#" | 
 |         if line.startswith("#"): | 
 |             continue | 
 |  | 
 |         # Mark line starting with "-" and set parse flag. | 
 |         if line.startswith("-") and parse_walk is False: | 
 |             parse_walk = True | 
 |             continue | 
 |         # Mark line starting with "-" and reset parse flag. | 
 |         # Set temp error list to EMPTY. | 
 |         elif line.startswith("-"): | 
 |             error_list.append(temp_error_list) | 
 |             parse_walk = False | 
 |             temp_error_list = [] | 
 |         # Add entry to list if line is not emtpy | 
 |         elif parse_walk: | 
 |             temp_error_list.append(str(line)) | 
 |  | 
 |     return error_list | 
 | ############################################################################### | 
 |  | 
 |  | 
 | ############################################################################### | 
 | def build_error_dict(htx_error_log_output): | 
 |  | 
 |     r""" | 
 |     Builds error list into a list of dictionary entries. | 
 |  | 
 |     Description of argument(s): | 
 |     error_list        Error list entries. | 
 |  | 
 |     Example output dictionary: | 
 |     { | 
 |       0: | 
 |         { | 
 |           'sev': '1', | 
 |           'err': '00000027', | 
 |           'Timestamp': 'Mar 29 19:41:54 2017', | 
 |           'Part No': 'Not Available', | 
 |           'Serial No': 'Not Available', | 
 |           'Device': 'Not Available', | 
 |           'FRU Number': 'Not Available', | 
 |           'Location': 'Not Available', | 
 |           'Device id': '/dev/nvidia0', | 
 |           'Error Text': 'cudaEventSynchronize for stopEvent returned err = 0039 | 
 |                          from file , line 430.', | 
 |           'Exerciser Name': 'hxenvidia' | 
 |         }, | 
 |       1: | 
 |         { | 
 |           'sev': '1', | 
 |           'err': '00000027', | 
 |           'Timestamp': 'Mar 29 19:41:54 2017', | 
 |           'Part No': 'Not Available', | 
 |           'Serial No': 'Not Available', | 
 |           'Device': 'Not Available', | 
 |           'FRU Number': 'Not Available', | 
 |           'Location': 'Not Available', | 
 |           'Device id': '/dev/nvidia0', | 
 |           'Error Text': 'Hardware Exerciser stopped on error', | 
 |           'Exerciser Name': 'hxenvidia' | 
 |         } | 
 |     }, | 
 |  | 
 |     """ | 
 |  | 
 |     # List which will hold all the list of entries. | 
 |     error_list = [] | 
 |     error_list = htx_error_log_to_list(htx_error_log_output) | 
 |  | 
 |     # dictionary which holds the error dictionry entry. | 
 |     error_dict = {} | 
 |  | 
 |     temp_error_dict = {} | 
 |     error_index = 0 | 
 |  | 
 |     # Loop through the error list. | 
 |     for entry_list in error_list: | 
 |         # Loop through the first error list entry. | 
 |         for entry in entry_list: | 
 |             # Split string into list for key value update. | 
 |             # Example: 'Device id:/dev/nvidia0' | 
 |             # Example: 'err=00000027' | 
 |             parm_split = re.split("[:=]", entry) | 
 |             # Populate temp dictionary with key value pair data. | 
 |             temp_error_dict[str(parm_split[0])] = parm_split[1] | 
 |  | 
 |         # Update the master dictionary per entry index. | 
 |         error_dict[error_index] = temp_error_dict | 
 |         # Reset temp dict to EMPTY and increment index count. | 
 |         temp_error_dict = {} | 
 |         error_index += 1 | 
 |  | 
 |     return error_dict | 
 |  | 
 | ############################################################################### |