1e7e9171eSGeorge Keishing#!/usr/bin/env python3 2ced4eb0aSMichael Walsh 3ced4eb0aSMichael Walshr""" 4ced4eb0aSMichael WalshDefine variable manipulation functions. 5ced4eb0aSMichael Walsh""" 6ced4eb0aSMichael Walsh 7ced4eb0aSMichael Walshimport os 805c68d98SMichael Walshimport re 9ced4eb0aSMichael Walsh 10ced4eb0aSMichael Walshtry: 11ced4eb0aSMichael Walsh from robot.utils import DotDict 12ced4eb0aSMichael Walshexcept ImportError: 13ced4eb0aSMichael Walsh pass 14ced4eb0aSMichael Walsh 15ced4eb0aSMichael Walshimport collections 16ced4eb0aSMichael Walsh 17e635ddc0SGeorge Keishingimport func_args as fa 18*20f38712SPatrick Williamsimport gen_misc as gm 19*20f38712SPatrick Williamsimport gen_print as gp 20ced4eb0aSMichael Walsh 21ced4eb0aSMichael Walsh 22ced4eb0aSMichael Walshdef create_var_dict(*args): 23ced4eb0aSMichael Walsh r""" 24410b1787SMichael Walsh Create a dictionary whose keys/values are the arg names/arg values passed to it and return it to the 25410b1787SMichael Walsh caller. 26ced4eb0aSMichael Walsh 27ced4eb0aSMichael Walsh Note: The resulting dictionary will be ordered. 28ced4eb0aSMichael Walsh 29ced4eb0aSMichael Walsh Description of argument(s): 30ced4eb0aSMichael Walsh *args An unlimited number of arguments to be processed. 31ced4eb0aSMichael Walsh 32ced4eb0aSMichael Walsh Example use: 33ced4eb0aSMichael Walsh 34ced4eb0aSMichael Walsh first_name = 'Steve' 35ced4eb0aSMichael Walsh last_name = 'Smith' 36ced4eb0aSMichael Walsh var_dict = create_var_dict(first_name, last_name) 37ced4eb0aSMichael Walsh 38ced4eb0aSMichael Walsh gp.print_var(var_dict) 39ced4eb0aSMichael Walsh 40ced4eb0aSMichael Walsh The print-out of the resulting var dictionary is: 41ced4eb0aSMichael Walsh var_dict: 42ced4eb0aSMichael Walsh var_dict[first_name]: Steve 43ced4eb0aSMichael Walsh var_dict[last_name]: Smith 44ced4eb0aSMichael Walsh """ 45ced4eb0aSMichael Walsh 46ced4eb0aSMichael Walsh try: 47ced4eb0aSMichael Walsh result_dict = collections.OrderedDict() 48ced4eb0aSMichael Walsh except AttributeError: 49ced4eb0aSMichael Walsh result_dict = DotDict() 50ced4eb0aSMichael Walsh 51ced4eb0aSMichael Walsh arg_num = 1 52ced4eb0aSMichael Walsh for arg in args: 53ced4eb0aSMichael Walsh arg_name = gp.get_arg_name(None, arg_num, stack_frame_ix=2) 54ced4eb0aSMichael Walsh result_dict[arg_name] = arg 55ced4eb0aSMichael Walsh arg_num += 1 56ced4eb0aSMichael Walsh 57ced4eb0aSMichael Walsh return result_dict 58ced4eb0aSMichael Walsh 59ced4eb0aSMichael Walsh 60*20f38712SPatrick Williamsdefault_record_delim = ":" 61*20f38712SPatrick Williamsdefault_key_val_delim = "." 62ced4eb0aSMichael Walsh 63ced4eb0aSMichael Walsh 64*20f38712SPatrick Williamsdef join_dict( 65*20f38712SPatrick Williams dict, 66ced4eb0aSMichael Walsh record_delim=default_record_delim, 67*20f38712SPatrick Williams key_val_delim=default_key_val_delim, 68*20f38712SPatrick Williams): 69ced4eb0aSMichael Walsh r""" 70ced4eb0aSMichael Walsh Join a dictionary's keys and values into a string and return the string. 71ced4eb0aSMichael Walsh 72ced4eb0aSMichael Walsh Description of argument(s): 73410b1787SMichael Walsh dict The dictionary whose keys and values are to be joined. 74410b1787SMichael Walsh record_delim The delimiter to be used to separate dictionary pairs in the resulting 75410b1787SMichael Walsh string. 76410b1787SMichael Walsh key_val_delim The delimiter to be used to separate keys from values in the resulting 77410b1787SMichael Walsh string. 78ced4eb0aSMichael Walsh 79ced4eb0aSMichael Walsh Example use: 80ced4eb0aSMichael Walsh 81ced4eb0aSMichael Walsh gp.print_var(var_dict) 82ced4eb0aSMichael Walsh str1 = join_dict(var_dict) 83c2762f62SMichael Walsh gp.print_var(str1) 84ced4eb0aSMichael Walsh 85ced4eb0aSMichael Walsh Program output. 86ced4eb0aSMichael Walsh var_dict: 87ced4eb0aSMichael Walsh var_dict[first_name]: Steve 88ced4eb0aSMichael Walsh var_dict[last_name]: Smith 89410b1787SMichael Walsh str1: first_name.Steve:last_name.Smith 90ced4eb0aSMichael Walsh """ 91ced4eb0aSMichael Walsh 92*20f38712SPatrick Williams format_str = "%s" + key_val_delim + "%s" 93*20f38712SPatrick Williams return record_delim.join( 94*20f38712SPatrick Williams [format_str % (key, value) for (key, value) in dict.items()] 95*20f38712SPatrick Williams ) 96ced4eb0aSMichael Walsh 97ced4eb0aSMichael Walsh 98*20f38712SPatrick Williamsdef split_to_dict( 99*20f38712SPatrick Williams string, 100ced4eb0aSMichael Walsh record_delim=default_record_delim, 101*20f38712SPatrick Williams key_val_delim=default_key_val_delim, 102*20f38712SPatrick Williams): 103ced4eb0aSMichael Walsh r""" 104ced4eb0aSMichael Walsh Split a string into a dictionary and return it. 105ced4eb0aSMichael Walsh 106ced4eb0aSMichael Walsh This function is the complement to join_dict. 107ced4eb0aSMichael Walsh 108ced4eb0aSMichael Walsh Description of argument(s): 109410b1787SMichael Walsh string The string to be split into a dictionary. The string must have the 110410b1787SMichael Walsh proper delimiters in it. A string created by join_dict would qualify. 111410b1787SMichael Walsh record_delim The delimiter to be used to separate dictionary pairs in the input string. 112410b1787SMichael Walsh key_val_delim The delimiter to be used to separate keys/values in the input string. 113ced4eb0aSMichael Walsh 114ced4eb0aSMichael Walsh Example use: 115ced4eb0aSMichael Walsh 116ced4eb0aSMichael Walsh gp.print_var(str1) 117ced4eb0aSMichael Walsh new_dict = split_to_dict(str1) 118ced4eb0aSMichael Walsh gp.print_var(new_dict) 119ced4eb0aSMichael Walsh 120ced4eb0aSMichael Walsh 121ced4eb0aSMichael Walsh Program output. 122410b1787SMichael Walsh str1: first_name.Steve:last_name.Smith 123ced4eb0aSMichael Walsh new_dict: 124ced4eb0aSMichael Walsh new_dict[first_name]: Steve 125ced4eb0aSMichael Walsh new_dict[last_name]: Smith 126ced4eb0aSMichael Walsh """ 127ced4eb0aSMichael Walsh 128ced4eb0aSMichael Walsh try: 129ced4eb0aSMichael Walsh result_dict = collections.OrderedDict() 130ced4eb0aSMichael Walsh except AttributeError: 131ced4eb0aSMichael Walsh result_dict = DotDict() 132ced4eb0aSMichael Walsh 133ced4eb0aSMichael Walsh raw_keys_values = string.split(record_delim) 134ced4eb0aSMichael Walsh for key_value in raw_keys_values: 135ced4eb0aSMichael Walsh key_value_list = key_value.split(key_val_delim) 136ced4eb0aSMichael Walsh try: 137ced4eb0aSMichael Walsh result_dict[key_value_list[0]] = key_value_list[1] 138ced4eb0aSMichael Walsh except IndexError: 139ced4eb0aSMichael Walsh result_dict[key_value_list[0]] = "" 140ced4eb0aSMichael Walsh 141ced4eb0aSMichael Walsh return result_dict 142ced4eb0aSMichael Walsh 143ced4eb0aSMichael Walsh 144*20f38712SPatrick Williamsdef create_file_path(file_name_dict, dir_path="/tmp/", file_suffix=""): 145ced4eb0aSMichael Walsh r""" 146ced4eb0aSMichael Walsh Create a file path using the given parameters and return it. 147ced4eb0aSMichael Walsh 148ced4eb0aSMichael Walsh Description of argument(s): 149410b1787SMichael Walsh file_name_dict A dictionary with keys/values which are to appear as part of the file 150410b1787SMichael Walsh name. 151410b1787SMichael Walsh dir_path The dir_path that is to appear as part of the file name. 152410b1787SMichael Walsh file_suffix A suffix to be included as part of the file name. 153ced4eb0aSMichael Walsh """ 154ced4eb0aSMichael Walsh 155ced4eb0aSMichael Walsh dir_path = gm.add_trailing_slash(dir_path) 156ced4eb0aSMichael Walsh return dir_path + join_dict(file_name_dict) + file_suffix 157ced4eb0aSMichael Walsh 158ced4eb0aSMichael Walsh 159ced4eb0aSMichael Walshdef parse_file_path(file_path): 160ced4eb0aSMichael Walsh r""" 161410b1787SMichael Walsh Parse a file path created by create_file_path and return the result as a dictionary. 162ced4eb0aSMichael Walsh 163ced4eb0aSMichael Walsh This function is the complement to create_file_path. 164ced4eb0aSMichael Walsh 165ced4eb0aSMichael Walsh Description of argument(s): 166ced4eb0aSMichael Walsh file_path The file_path. 167ced4eb0aSMichael Walsh 168ced4eb0aSMichael Walsh Example use: 169c2762f62SMichael Walsh gp.print_var(boot_results_file_path) 170ced4eb0aSMichael Walsh file_path_data = parse_file_path(boot_results_file_path) 171c2762f62SMichael Walsh gp.print_var(file_path_data) 172ced4eb0aSMichael Walsh 173ced4eb0aSMichael Walsh Program output. 174ced4eb0aSMichael Walsh 175ced4eb0aSMichael Walsh boot_results_file_path: 176410b1787SMichael Walsh /tmp/pgm_name.obmc_boot_test:openbmc_nickname.beye6:master_pid.2039:boot_results 177ced4eb0aSMichael Walsh file_path_data: 178ced4eb0aSMichael Walsh file_path_data[dir_path]: /tmp/ 179ced4eb0aSMichael Walsh file_path_data[pgm_name]: obmc_boot_test 180ced4eb0aSMichael Walsh file_path_data[openbmc_nickname]: beye6 181ced4eb0aSMichael Walsh file_path_data[master_pid]: 2039 182ced4eb0aSMichael Walsh file_path_data[boot_results]: 183ced4eb0aSMichael Walsh """ 184ced4eb0aSMichael Walsh 185ced4eb0aSMichael Walsh try: 186ced4eb0aSMichael Walsh result_dict = collections.OrderedDict() 187ced4eb0aSMichael Walsh except AttributeError: 188ced4eb0aSMichael Walsh result_dict = DotDict() 189ced4eb0aSMichael Walsh 190ced4eb0aSMichael Walsh dir_path = os.path.dirname(file_path) + os.sep 191ced4eb0aSMichael Walsh file_path = os.path.basename(file_path) 192ced4eb0aSMichael Walsh 193*20f38712SPatrick Williams result_dict["dir_path"] = dir_path 194ced4eb0aSMichael Walsh 195ced4eb0aSMichael Walsh result_dict.update(split_to_dict(file_path)) 196ced4eb0aSMichael Walsh 197ced4eb0aSMichael Walsh return result_dict 19805c68d98SMichael Walsh 19905c68d98SMichael Walsh 200*20f38712SPatrick Williamsdef parse_key_value(string, delim=":", strip=" ", to_lower=1, underscores=1): 20105c68d98SMichael Walsh r""" 20205c68d98SMichael Walsh Parse a key/value string and return as a key/value tuple. 20305c68d98SMichael Walsh 204410b1787SMichael Walsh This function is useful for parsing a line of program output or data that is in the following form: 20505c68d98SMichael Walsh <key or variable name><delimiter><value> 20605c68d98SMichael Walsh 20705c68d98SMichael Walsh An example of a key/value string would be as follows: 20805c68d98SMichael Walsh 20905c68d98SMichael Walsh Current Limit State: No Active Power Limit 21005c68d98SMichael Walsh 211410b1787SMichael Walsh In the example shown, the delimiter is ":". The resulting key would be as follows: 21205c68d98SMichael Walsh Current Limit State 21305c68d98SMichael Walsh 214410b1787SMichael Walsh Note: If one were to take the default values of to_lower=1 and underscores=1, the resulting key would be 215410b1787SMichael Walsh as follows: 21605c68d98SMichael Walsh current_limit_state 21705c68d98SMichael Walsh 218410b1787SMichael Walsh The to_lower and underscores arguments are provided for those who wish to have their key names have the 219410b1787SMichael Walsh look and feel of python variable names. 22005c68d98SMichael Walsh 22105c68d98SMichael Walsh The resulting value for the example above would be as follows: 22205c68d98SMichael Walsh No Active Power Limit 22305c68d98SMichael Walsh 22405c68d98SMichael Walsh Another example: 22505c68d98SMichael Walsh name=Mike 22605c68d98SMichael Walsh 227410b1787SMichael Walsh In this case, the delim would be "=", the key is "name" and the value is "Mike". 22805c68d98SMichael Walsh 22905c68d98SMichael Walsh Description of argument(s): 23005c68d98SMichael Walsh string The string to be parsed. 231410b1787SMichael Walsh delim The delimiter which separates the key from the value. 232410b1787SMichael Walsh strip The characters (if any) to strip from the beginning and end of both the 233410b1787SMichael Walsh key and the value. 23405c68d98SMichael Walsh to_lower Change the key name to lower case. 235410b1787SMichael Walsh underscores Change any blanks found in the key name to underscores. 23605c68d98SMichael Walsh """ 23705c68d98SMichael Walsh 23805c68d98SMichael Walsh pair = string.split(delim) 23905c68d98SMichael Walsh 24005c68d98SMichael Walsh key = pair[0].strip(strip) 24105c68d98SMichael Walsh if len(pair) == 0: 24205c68d98SMichael Walsh value = "" 24305c68d98SMichael Walsh else: 2449509a0ffSMICHAEL J. WALSH value = delim.join(pair[1:]).strip(strip) 24505c68d98SMichael Walsh 24605c68d98SMichael Walsh if to_lower: 24705c68d98SMichael Walsh key = key.lower() 24805c68d98SMichael Walsh if underscores: 24905c68d98SMichael Walsh key = re.sub(r" ", "_", key) 25005c68d98SMichael Walsh 25105c68d98SMichael Walsh return key, value 25205c68d98SMichael Walsh 25305c68d98SMichael Walsh 254*20f38712SPatrick Williamsdef key_value_list_to_dict(key_value_list, process_indent=0, **args): 25505c68d98SMichael Walsh r""" 256410b1787SMichael Walsh Convert a list containing key/value strings or tuples to a dictionary and return it. 25705c68d98SMichael Walsh 25805c68d98SMichael Walsh See docstring of parse_key_value function for details on key/value strings. 25905c68d98SMichael Walsh 26005c68d98SMichael Walsh Example usage: 26105c68d98SMichael Walsh 26219df7aaaSMichael Walsh For the following value of key_value_list: 26305c68d98SMichael Walsh 26419df7aaaSMichael Walsh key_value_list: 26519df7aaaSMichael Walsh [0]: Current Limit State: No Active Power Limit 26619df7aaaSMichael Walsh [1]: Exception actions: Hard Power Off & Log Event to SEL 26719df7aaaSMichael Walsh [2]: Power Limit: 0 Watts 26819df7aaaSMichael Walsh [3]: Correction time: 0 milliseconds 26919df7aaaSMichael Walsh [4]: Sampling period: 0 seconds 27005c68d98SMichael Walsh 27105c68d98SMichael Walsh And the following call in python: 27205c68d98SMichael Walsh 27319df7aaaSMichael Walsh power_limit = key_value_outbuf_to_dict(key_value_list) 27405c68d98SMichael Walsh 27505c68d98SMichael Walsh The resulting power_limit directory would look like this: 27605c68d98SMichael Walsh 27705c68d98SMichael Walsh power_limit: 27805c68d98SMichael Walsh [current_limit_state]: No Active Power Limit 27905c68d98SMichael Walsh [exception_actions]: Hard Power Off & Log Event to SEL 28005c68d98SMichael Walsh [power_limit]: 0 Watts 28105c68d98SMichael Walsh [correction_time]: 0 milliseconds 28205c68d98SMichael Walsh [sampling_period]: 0 seconds 28305c68d98SMichael Walsh 2841db8687dSMichael Walsh For the following list: 2851db8687dSMichael Walsh 2861db8687dSMichael Walsh headers: 2871db8687dSMichael Walsh headers[0]: 2881db8687dSMichael Walsh headers[0][0]: content-length 2891db8687dSMichael Walsh headers[0][1]: 559 2901db8687dSMichael Walsh headers[1]: 2911db8687dSMichael Walsh headers[1][0]: x-xss-protection 2921db8687dSMichael Walsh headers[1][1]: 1; mode=block 2931db8687dSMichael Walsh 2941db8687dSMichael Walsh And the following call in python: 2951db8687dSMichael Walsh 2961db8687dSMichael Walsh headers_dict = key_value_list_to_dict(headers) 2971db8687dSMichael Walsh 2981db8687dSMichael Walsh The resulting headers_dict would look like this: 2991db8687dSMichael Walsh 3001db8687dSMichael Walsh headers_dict: 3011db8687dSMichael Walsh [content-length]: 559 3021db8687dSMichael Walsh [x-xss-protection]: 1; mode=block 3031db8687dSMichael Walsh 304410b1787SMichael Walsh Another example containing a sub-list (see process_indent description below): 305cad0713eSMichael Walsh 306cad0713eSMichael Walsh Provides Device SDRs : yes 307cad0713eSMichael Walsh Additional Device Support : 308cad0713eSMichael Walsh Sensor Device 309cad0713eSMichael Walsh SEL Device 310cad0713eSMichael Walsh FRU Inventory Device 311cad0713eSMichael Walsh Chassis Device 312cad0713eSMichael Walsh 313410b1787SMichael Walsh Note that the 2 qualifications for containing a sub-list are met: 1) 'Additional Device Support' has no 314410b1787SMichael Walsh value and 2) The entries below it are indented. In this case those entries contain no delimiters (":") 315410b1787SMichael Walsh so they will be processed as a list rather than as a dictionary. The result would be as follows: 316cad0713eSMichael Walsh 317cad0713eSMichael Walsh mc_info: 318cad0713eSMichael Walsh mc_info[provides_device_sdrs]: yes 319cad0713eSMichael Walsh mc_info[additional_device_support]: 320cad0713eSMichael Walsh mc_info[additional_device_support][0]: Sensor Device 321cad0713eSMichael Walsh mc_info[additional_device_support][1]: SEL Device 322cad0713eSMichael Walsh mc_info[additional_device_support][2]: FRU Inventory Device 323cad0713eSMichael Walsh mc_info[additional_device_support][3]: Chassis Device 324cad0713eSMichael Walsh 32505c68d98SMichael Walsh Description of argument(s): 32619df7aaaSMichael Walsh key_value_list A list of key/value strings. (See docstring of parse_key_value function 327410b1787SMichael Walsh for details). 328410b1787SMichael Walsh process_indent This indicates that indented sub-dictionaries and sub-lists are to be 329410b1787SMichael Walsh processed as such. An entry may have a sub-dict or sub-list if 1) It has 330410b1787SMichael Walsh no value other than blank 2) There are entries below it that are 331410b1787SMichael Walsh indented. Note that process_indent is not allowed for a list of tuples 332410b1787SMichael Walsh (vs. a list of key/value strings). 333410b1787SMichael Walsh **args Arguments to be interpreted by parse_key_value. (See docstring of 33405c68d98SMichael Walsh parse_key_value function for details). 33505c68d98SMichael Walsh """ 33605c68d98SMichael Walsh 33705c68d98SMichael Walsh try: 33805c68d98SMichael Walsh result_dict = collections.OrderedDict() 33905c68d98SMichael Walsh except AttributeError: 34005c68d98SMichael Walsh result_dict = DotDict() 34105c68d98SMichael Walsh 342cad0713eSMichael Walsh if not process_indent: 34319df7aaaSMichael Walsh for entry in key_value_list: 3441db8687dSMichael Walsh if type(entry) is tuple: 3451db8687dSMichael Walsh key, value = entry 3461db8687dSMichael Walsh else: 347c1dfc781SMichael Walsh key, value = parse_key_value(entry, **args) 34805c68d98SMichael Walsh result_dict[key] = value 349cad0713eSMichael Walsh return result_dict 350cad0713eSMichael Walsh 351cad0713eSMichael Walsh # Process list while paying heed to indentation. 352cad0713eSMichael Walsh delim = args.get("delim", ":") 353cad0713eSMichael Walsh # Initialize "parent_" indentation level variables. 35419df7aaaSMichael Walsh parent_indent = len(key_value_list[0]) - len(key_value_list[0].lstrip()) 355cad0713eSMichael Walsh sub_list = [] 35619df7aaaSMichael Walsh for entry in key_value_list: 357cad0713eSMichael Walsh key, value = parse_key_value(entry, **args) 358cad0713eSMichael Walsh 359cad0713eSMichael Walsh indent = len(entry) - len(entry.lstrip()) 360cad0713eSMichael Walsh 361cad0713eSMichael Walsh if indent > parent_indent and parent_value == "": 362410b1787SMichael Walsh # This line is indented compared to the parent entry and the parent entry has no value. 363cad0713eSMichael Walsh # Append the entry to sub_list for later processing. 364cad0713eSMichael Walsh sub_list.append(str(entry)) 365cad0713eSMichael Walsh continue 366cad0713eSMichael Walsh 367410b1787SMichael Walsh # Process any outstanding sub_list and add it to result_dict[parent_key]. 368cad0713eSMichael Walsh if len(sub_list) > 0: 369cad0713eSMichael Walsh if any(delim in word for word in sub_list): 370410b1787SMichael Walsh # If delim is found anywhere in the sub_list, we'll process as a sub-dictionary. 371*20f38712SPatrick Williams result_dict[parent_key] = key_value_list_to_dict( 372*20f38712SPatrick Williams sub_list, **args 373*20f38712SPatrick Williams ) 374cad0713eSMichael Walsh else: 37519df7aaaSMichael Walsh result_dict[parent_key] = list(map(str.strip, sub_list)) 376cad0713eSMichael Walsh del sub_list[:] 377cad0713eSMichael Walsh 378cad0713eSMichael Walsh result_dict[key] = value 379cad0713eSMichael Walsh 380cad0713eSMichael Walsh parent_key = key 381cad0713eSMichael Walsh parent_value = value 382cad0713eSMichael Walsh parent_indent = indent 383cad0713eSMichael Walsh 384cad0713eSMichael Walsh # Any outstanding sub_list to be processed? 385cad0713eSMichael Walsh if len(sub_list) > 0: 386cad0713eSMichael Walsh if any(delim in word for word in sub_list): 387410b1787SMichael Walsh # If delim is found anywhere in the sub_list, we'll process as a sub-dictionary. 388cad0713eSMichael Walsh result_dict[parent_key] = key_value_list_to_dict(sub_list, **args) 389cad0713eSMichael Walsh else: 39019df7aaaSMichael Walsh result_dict[parent_key] = list(map(str.strip, sub_list)) 39105c68d98SMichael Walsh 39205c68d98SMichael Walsh return result_dict 39305c68d98SMichael Walsh 39405c68d98SMichael Walsh 395*20f38712SPatrick Williamsdef key_value_outbuf_to_dict(out_buf, **args): 39605c68d98SMichael Walsh r""" 397410b1787SMichael Walsh Convert a buffer with a key/value string on each line to a dictionary and return it. 39805c68d98SMichael Walsh 39905c68d98SMichael Walsh Each line in the out_buf should end with a \n. 40005c68d98SMichael Walsh 40105c68d98SMichael Walsh See docstring of parse_key_value function for details on key/value strings. 40205c68d98SMichael Walsh 40305c68d98SMichael Walsh Example usage: 40405c68d98SMichael Walsh 40505c68d98SMichael Walsh For the following value of out_buf: 40605c68d98SMichael Walsh 40705c68d98SMichael Walsh Current Limit State: No Active Power Limit 40805c68d98SMichael Walsh Exception actions: Hard Power Off & Log Event to SEL 40905c68d98SMichael Walsh Power Limit: 0 Watts 41005c68d98SMichael Walsh Correction time: 0 milliseconds 41105c68d98SMichael Walsh Sampling period: 0 seconds 41205c68d98SMichael Walsh 41305c68d98SMichael Walsh And the following call in python: 41405c68d98SMichael Walsh 41505c68d98SMichael Walsh power_limit = key_value_outbuf_to_dict(out_buf) 41605c68d98SMichael Walsh 41705c68d98SMichael Walsh The resulting power_limit directory would look like this: 41805c68d98SMichael Walsh 41905c68d98SMichael Walsh power_limit: 42005c68d98SMichael Walsh [current_limit_state]: No Active Power Limit 42105c68d98SMichael Walsh [exception_actions]: Hard Power Off & Log Event to SEL 42205c68d98SMichael Walsh [power_limit]: 0 Watts 42305c68d98SMichael Walsh [correction_time]: 0 milliseconds 42405c68d98SMichael Walsh [sampling_period]: 0 seconds 42505c68d98SMichael Walsh 42605c68d98SMichael Walsh Description of argument(s): 427410b1787SMichael Walsh out_buf A buffer with a key/value string on each line. (See docstring of 428410b1787SMichael Walsh parse_key_value function for details). 429410b1787SMichael Walsh **args Arguments to be interpreted by parse_key_value. (See docstring of 43005c68d98SMichael Walsh parse_key_value function for details). 43105c68d98SMichael Walsh """ 43205c68d98SMichael Walsh 43305c68d98SMichael Walsh # Create key_var_list and remove null entries. 43405c68d98SMichael Walsh key_var_list = list(filter(None, out_buf.split("\n"))) 435c1dfc781SMichael Walsh return key_value_list_to_dict(key_var_list, **args) 436db560d46SMichael Walsh 437db560d46SMichael Walsh 438*20f38712SPatrick Williamsdef key_value_outbuf_to_dicts(out_buf, **args): 43979af4395SMichael Walsh r""" 44079af4395SMichael Walsh Convert a buffer containing multiple sections with key/value strings on each line to a list of 44179af4395SMichael Walsh dictionaries and return it. 44279af4395SMichael Walsh 44379af4395SMichael Walsh Sections in the output are delimited by blank lines. 44479af4395SMichael Walsh 44579af4395SMichael Walsh Example usage: 44679af4395SMichael Walsh 44779af4395SMichael Walsh For the following value of out_buf: 44879af4395SMichael Walsh 44979af4395SMichael Walsh Maximum User IDs : 15 45079af4395SMichael Walsh Enabled User IDs : 1 45179af4395SMichael Walsh 45279af4395SMichael Walsh User ID : 1 45379af4395SMichael Walsh User Name : root 45479af4395SMichael Walsh Fixed Name : No 45579af4395SMichael Walsh Access Available : callback 45679af4395SMichael Walsh Link Authentication : enabled 45779af4395SMichael Walsh IPMI Messaging : enabled 45879af4395SMichael Walsh Privilege Level : ADMINISTRATOR 45979af4395SMichael Walsh Enable Status : enabled 46079af4395SMichael Walsh 46179af4395SMichael Walsh User ID : 2 46279af4395SMichael Walsh User Name : 46379af4395SMichael Walsh Fixed Name : No 46479af4395SMichael Walsh Access Available : call-in / callback 46579af4395SMichael Walsh Link Authentication : disabled 46679af4395SMichael Walsh IPMI Messaging : disabled 46779af4395SMichael Walsh Privilege Level : NO ACCESS 46879af4395SMichael Walsh Enable Status : disabled 46979af4395SMichael Walsh 47079af4395SMichael Walsh And the following call in python: 47179af4395SMichael Walsh 47279af4395SMichael Walsh user_info = key_value_outbuf_to_dicts(out_buf) 47379af4395SMichael Walsh 47479af4395SMichael Walsh The resulting user_info list would look like this: 47579af4395SMichael Walsh 47679af4395SMichael Walsh user_info: 47779af4395SMichael Walsh [0]: 47879af4395SMichael Walsh [maximum_user_ids]: 15 47979af4395SMichael Walsh [enabled_user_ids]: 1 48079af4395SMichael Walsh [1]: 48179af4395SMichael Walsh [user_id]: 1 48279af4395SMichael Walsh [user_name]: root 48379af4395SMichael Walsh [fixed_name]: No 48479af4395SMichael Walsh [access_available]: callback 48579af4395SMichael Walsh [link_authentication]: enabled 48679af4395SMichael Walsh [ipmi_messaging]: enabled 48779af4395SMichael Walsh [privilege_level]: ADMINISTRATOR 48879af4395SMichael Walsh [enable_status]: enabled 48979af4395SMichael Walsh [2]: 49079af4395SMichael Walsh [user_id]: 2 49179af4395SMichael Walsh [user_name]: 49279af4395SMichael Walsh [fixed_name]: No 49379af4395SMichael Walsh [access_available]: call-in / callback 49479af4395SMichael Walsh [link_authentication]: disabled 49579af4395SMichael Walsh [ipmi_messaging]: disabled 49679af4395SMichael Walsh [privilege_level]: NO ACCESS 49779af4395SMichael Walsh [enable_status]: disabled 49879af4395SMichael Walsh 49979af4395SMichael Walsh Description of argument(s): 50079af4395SMichael Walsh out_buf A buffer with multiple secionts of key/value strings on each line. 50179af4395SMichael Walsh Sections are delimited by one or more blank lines (i.e. line feeds). (See 50279af4395SMichael Walsh docstring of parse_key_value function for details). 50379af4395SMichael Walsh **args Arguments to be interpreted by parse_key_value. (See docstring of 50479af4395SMichael Walsh parse_key_value function for details). 50579af4395SMichael Walsh """ 506*20f38712SPatrick Williams return [ 507*20f38712SPatrick Williams key_value_outbuf_to_dict(x, **args) 508*20f38712SPatrick Williams for x in re.split("\n[\n]+", out_buf) 509*20f38712SPatrick Williams ] 51079af4395SMichael Walsh 51179af4395SMichael Walsh 512dc97882eSMichael Walshdef create_field_desc_regex(line): 513dc97882eSMichael Walsh r""" 514410b1787SMichael Walsh Create a field descriptor regular expression based on the input line and return it. 515dc97882eSMichael Walsh 516410b1787SMichael Walsh This function is designed for use by the list_to_report function (defined below). 517dc97882eSMichael Walsh 518dc97882eSMichael Walsh Example: 519dc97882eSMichael Walsh 520dc97882eSMichael Walsh Given the following input line: 521dc97882eSMichael Walsh 522dc97882eSMichael Walsh -------- ------------ ------------------ ------------------------ 523dc97882eSMichael Walsh 524dc97882eSMichael Walsh This function will return this regular expression: 525dc97882eSMichael Walsh 526dc97882eSMichael Walsh (.{8}) (.{12}) (.{18}) (.{24}) 527dc97882eSMichael Walsh 528410b1787SMichael Walsh This means that other report lines interpreted using the regular expression are expected to have: 529dc97882eSMichael Walsh - An 8 character field 530dc97882eSMichael Walsh - 3 spaces 531dc97882eSMichael Walsh - A 12 character field 532dc97882eSMichael Walsh - One space 533dc97882eSMichael Walsh - An 18 character field 534dc97882eSMichael Walsh - One space 535dc97882eSMichael Walsh - A 24 character field 536dc97882eSMichael Walsh 537dc97882eSMichael Walsh Description of argument(s): 538410b1787SMichael Walsh line A line consisting of dashes to represent fields and spaces to delimit 539410b1787SMichael Walsh fields. 540dc97882eSMichael Walsh """ 541dc97882eSMichael Walsh 542dc97882eSMichael Walsh # Split the line into a descriptors list. Example: 543dc97882eSMichael Walsh # descriptors: 544dc97882eSMichael Walsh # descriptors[0]: -------- 545dc97882eSMichael Walsh # descriptors[1]: 546dc97882eSMichael Walsh # descriptors[2]: 547dc97882eSMichael Walsh # descriptors[3]: ------------ 548dc97882eSMichael Walsh # descriptors[4]: ------------------ 549dc97882eSMichael Walsh # descriptors[5]: ------------------------ 550dc97882eSMichael Walsh descriptors = line.split(" ") 551dc97882eSMichael Walsh 552dc97882eSMichael Walsh # Create regexes list. Example: 553dc97882eSMichael Walsh # regexes: 554dc97882eSMichael Walsh # regexes[0]: (.{8}) 555dc97882eSMichael Walsh # regexes[1]: 556dc97882eSMichael Walsh # regexes[2]: 557dc97882eSMichael Walsh # regexes[3]: (.{12}) 558dc97882eSMichael Walsh # regexes[4]: (.{18}) 559dc97882eSMichael Walsh # regexes[5]: (.{24}) 560dc97882eSMichael Walsh regexes = [] 561dc97882eSMichael Walsh for descriptor in descriptors: 562dc97882eSMichael Walsh if descriptor == "": 563dc97882eSMichael Walsh regexes.append("") 564dc97882eSMichael Walsh else: 565dc97882eSMichael Walsh regexes.append("(.{" + str(len(descriptor)) + "})") 566dc97882eSMichael Walsh 567dc97882eSMichael Walsh # Join the regexes list into a regex string. 568*20f38712SPatrick Williams field_desc_regex = " ".join(regexes) 569dc97882eSMichael Walsh 570dc97882eSMichael Walsh return field_desc_regex 571dc97882eSMichael Walsh 572dc97882eSMichael Walsh 573*20f38712SPatrick Williamsdef list_to_report(report_list, to_lower=1, field_delim=None): 574db560d46SMichael Walsh r""" 575410b1787SMichael Walsh Convert a list containing report text lines to a report "object" and return it. 576db560d46SMichael Walsh 577410b1787SMichael Walsh The first entry in report_list must be a header line consisting of column names delimited by white space. 578410b1787SMichael Walsh No column name may contain white space. The remaining report_list entries should contain tabular data 579410b1787SMichael Walsh which corresponds to the column names. 580db560d46SMichael Walsh 581410b1787SMichael Walsh A report object is a list where each entry is a dictionary whose keys are the field names from the first 582410b1787SMichael Walsh entry in report_list. 583db560d46SMichael Walsh 584db560d46SMichael Walsh Example: 585db560d46SMichael Walsh Given the following report_list as input: 586db560d46SMichael Walsh 587db560d46SMichael Walsh rl: 588db560d46SMichael Walsh rl[0]: Filesystem 1K-blocks Used Available Use% Mounted on 589db560d46SMichael Walsh rl[1]: dev 247120 0 247120 0% /dev 590db560d46SMichael Walsh rl[2]: tmpfs 248408 79792 168616 32% /run 591db560d46SMichael Walsh 592db560d46SMichael Walsh This function will return a list of dictionaries as shown below: 593db560d46SMichael Walsh 594db560d46SMichael Walsh df_report: 595db560d46SMichael Walsh df_report[0]: 596db560d46SMichael Walsh [filesystem]: dev 597db560d46SMichael Walsh [1k-blocks]: 247120 598db560d46SMichael Walsh [used]: 0 599db560d46SMichael Walsh [available]: 247120 600db560d46SMichael Walsh [use%]: 0% 601db560d46SMichael Walsh [mounted]: /dev 602db560d46SMichael Walsh df_report[1]: 603db560d46SMichael Walsh [filesystem]: dev 604db560d46SMichael Walsh [1k-blocks]: 247120 605db560d46SMichael Walsh [used]: 0 606db560d46SMichael Walsh [available]: 247120 607db560d46SMichael Walsh [use%]: 0% 608db560d46SMichael Walsh [mounted]: /dev 609db560d46SMichael Walsh 610410b1787SMichael Walsh Notice that because "Mounted on" contains a space, "on" would be considered the 7th field. In this case, 611410b1787SMichael Walsh there is never any data in field 7 so things work out nicely. A caller could do some pre-processing if 612db560d46SMichael Walsh desired (e.g. change "Mounted on" to "Mounted_on"). 613db560d46SMichael Walsh 614dc97882eSMichael Walsh Example 2: 615dc97882eSMichael Walsh 616410b1787SMichael Walsh If the 2nd line of report data is a series of dashes and spaces as in the following example, that line 617410b1787SMichael Walsh will serve to delineate columns. 618dc97882eSMichael Walsh 619dc97882eSMichael Walsh The 2nd line of data is like this: 620410b1787SMichael Walsh ID status size tool,clientid,userid 621dc97882eSMichael Walsh -------- ------------ ------------------ ------------------------ 622dc97882eSMichael Walsh 20000001 in progress 0x7D0 ,, 623dc97882eSMichael Walsh 624db560d46SMichael Walsh Description of argument(s): 625410b1787SMichael Walsh report_list A list where each entry is one line of output from a report. The first 626410b1787SMichael Walsh entry must be a header line which contains column names. Column names 627410b1787SMichael Walsh may not contain spaces. 628410b1787SMichael Walsh to_lower Change the resulting key names to lower case. 629410b1787SMichael Walsh field_delim Indicates that there are field delimiters in report_list entries (which 630410b1787SMichael Walsh should be removed). 631db560d46SMichael Walsh """ 632db560d46SMichael Walsh 633dc97882eSMichael Walsh if len(report_list) <= 1: 634410b1787SMichael Walsh # If we don't have at least a descriptor line and one line of data, return an empty array. 635dc97882eSMichael Walsh return [] 636dc97882eSMichael Walsh 63764043d54SMichael Walsh if field_delim is not None: 63864043d54SMichael Walsh report_list = [re.sub("\\|", "", line) for line in report_list] 63964043d54SMichael Walsh 640db560d46SMichael Walsh header_line = report_list[0] 641db560d46SMichael Walsh if to_lower: 642db560d46SMichael Walsh header_line = header_line.lower() 643dc97882eSMichael Walsh 644dc97882eSMichael Walsh field_desc_regex = "" 645dc97882eSMichael Walsh if re.match(r"^-[ -]*$", report_list[1]): 646dc97882eSMichael Walsh # We have a field descriptor line (as shown in example 2 above). 647dc97882eSMichael Walsh field_desc_regex = create_field_desc_regex(report_list[1]) 648dc97882eSMichael Walsh field_desc_len = len(report_list[1]) 649dc97882eSMichael Walsh pad_format_string = "%-" + str(field_desc_len) + "s" 650dc97882eSMichael Walsh # The field descriptor line has served its purpose. Deleting it. 651dc97882eSMichael Walsh del report_list[1] 652dc97882eSMichael Walsh 653dc97882eSMichael Walsh # Process the header line by creating a list of column names. 654dc97882eSMichael Walsh if field_desc_regex == "": 655db560d46SMichael Walsh columns = header_line.split() 656dc97882eSMichael Walsh else: 657410b1787SMichael Walsh # Pad the line with spaces on the right to facilitate processing with field_desc_regex. 658dc97882eSMichael Walsh header_line = pad_format_string % header_line 659*20f38712SPatrick Williams columns = list( 660*20f38712SPatrick Williams map(str.strip, re.findall(field_desc_regex, header_line)[0]) 661*20f38712SPatrick Williams ) 662db560d46SMichael Walsh 663db560d46SMichael Walsh report_obj = [] 664db560d46SMichael Walsh for report_line in report_list[1:]: 665dc97882eSMichael Walsh if field_desc_regex == "": 666dc97882eSMichael Walsh line = report_line.split() 667dc97882eSMichael Walsh else: 668410b1787SMichael Walsh # Pad the line with spaces on the right to facilitate processing with field_desc_regex. 669dc97882eSMichael Walsh report_line = pad_format_string % report_line 670*20f38712SPatrick Williams line = list( 671*20f38712SPatrick Williams map(str.strip, re.findall(field_desc_regex, report_line)[0]) 672*20f38712SPatrick Williams ) 673db560d46SMichael Walsh try: 674db560d46SMichael Walsh line_dict = collections.OrderedDict(zip(columns, line)) 675db560d46SMichael Walsh except AttributeError: 676db560d46SMichael Walsh line_dict = DotDict(zip(columns, line)) 677db560d46SMichael Walsh report_obj.append(line_dict) 678db560d46SMichael Walsh 679db560d46SMichael Walsh return report_obj 680db560d46SMichael Walsh 681db560d46SMichael Walsh 682*20f38712SPatrick Williamsdef outbuf_to_report(out_buf, **args): 683db560d46SMichael Walsh r""" 684410b1787SMichael Walsh Convert a text buffer containing report lines to a report "object" and return it. 685db560d46SMichael Walsh 686db560d46SMichael Walsh Refer to list_to_report (above) for more details. 687db560d46SMichael Walsh 688db560d46SMichael Walsh Example: 689db560d46SMichael Walsh 690db560d46SMichael Walsh Given the following out_buf: 691db560d46SMichael Walsh 692410b1787SMichael Walsh Filesystem 1K-blocks Used Available Use% Mounted on 693db560d46SMichael Walsh dev 247120 0 247120 0% /dev 694db560d46SMichael Walsh tmpfs 248408 79792 168616 32% /run 695db560d46SMichael Walsh 696db560d46SMichael Walsh This function will return a list of dictionaries as shown below: 697db560d46SMichael Walsh 698db560d46SMichael Walsh df_report: 699db560d46SMichael Walsh df_report[0]: 700db560d46SMichael Walsh [filesystem]: dev 701db560d46SMichael Walsh [1k-blocks]: 247120 702db560d46SMichael Walsh [used]: 0 703db560d46SMichael Walsh [available]: 247120 704db560d46SMichael Walsh [use%]: 0% 705db560d46SMichael Walsh [mounted]: /dev 706db560d46SMichael Walsh df_report[1]: 707db560d46SMichael Walsh [filesystem]: dev 708db560d46SMichael Walsh [1k-blocks]: 247120 709db560d46SMichael Walsh [used]: 0 710db560d46SMichael Walsh [available]: 247120 711db560d46SMichael Walsh [use%]: 0% 712db560d46SMichael Walsh [mounted]: /dev 713db560d46SMichael Walsh 714db560d46SMichael Walsh Other possible uses: 715db560d46SMichael Walsh - Process the output of a ps command. 716410b1787SMichael Walsh - Process the output of an ls command (the caller would need to supply column names) 717db560d46SMichael Walsh 718db560d46SMichael Walsh Description of argument(s): 719410b1787SMichael Walsh out_buf A text report. The first line must be a header line which contains 720410b1787SMichael Walsh column names. Column names may not contain spaces. 721410b1787SMichael Walsh **args Arguments to be interpreted by list_to_report. (See docstring of 722db560d46SMichael Walsh list_to_report function for details). 723db560d46SMichael Walsh """ 724db560d46SMichael Walsh 725255181c1SMichael Walsh report_list = list(filter(None, out_buf.split("\n"))) 726db560d46SMichael Walsh return list_to_report(report_list, **args) 7277822b9e9SMichael Walsh 7287822b9e9SMichael Walsh 72946ef0a24SMichael Walshdef nested_get(key_name, structure): 7307822b9e9SMichael Walsh r""" 731410b1787SMichael Walsh Return a list of all values from the nested structure that have the given key name. 7327822b9e9SMichael Walsh 7337822b9e9SMichael Walsh Example: 7347822b9e9SMichael Walsh 73546ef0a24SMichael Walsh Given a dictionary structure named "personnel" with the following contents: 7367822b9e9SMichael Walsh 7377822b9e9SMichael Walsh personnel: 7387822b9e9SMichael Walsh [manager]: 7397822b9e9SMichael Walsh [last_name]: Doe 7407822b9e9SMichael Walsh [first_name]: John 7417822b9e9SMichael Walsh [accountant]: 7427822b9e9SMichael Walsh [last_name]: Smith 7437822b9e9SMichael Walsh [first_name]: Will 7447822b9e9SMichael Walsh 7457822b9e9SMichael Walsh The following code... 7467822b9e9SMichael Walsh 7477822b9e9SMichael Walsh last_names = nested_get('last_name', personnel) 7487822b9e9SMichael Walsh print_var(last_names) 7497822b9e9SMichael Walsh 75046ef0a24SMichael Walsh Would result in the following data returned: 7517822b9e9SMichael Walsh 7527822b9e9SMichael Walsh last_names: 7537822b9e9SMichael Walsh last_names[0]: Doe 7547822b9e9SMichael Walsh last_names[1]: Smith 7557822b9e9SMichael Walsh 7567822b9e9SMichael Walsh Description of argument(s): 75746ef0a24SMichael Walsh key_name The key name (e.g. 'last_name'). 758410b1787SMichael Walsh structure Any nested combination of lists or dictionaries (e.g. a dictionary, a 759410b1787SMichael Walsh dictionary of dictionaries, a list of dictionaries, etc.). This function 760410b1787SMichael Walsh will locate the given key at any level within the structure and include 761410b1787SMichael Walsh its value in the returned list. 7627822b9e9SMichael Walsh """ 7637822b9e9SMichael Walsh 7647822b9e9SMichael Walsh result = [] 765d882cdc5SMichael Walsh if type(structure) is list: 766d882cdc5SMichael Walsh for entry in structure: 76746ef0a24SMichael Walsh result += nested_get(key_name, entry) 768d882cdc5SMichael Walsh return result 76946ef0a24SMichael Walsh elif gp.is_dict(structure): 77046ef0a24SMichael Walsh for key, value in structure.items(): 77146ef0a24SMichael Walsh result += nested_get(key_name, value) 77246ef0a24SMichael Walsh if key == key_name: 77346ef0a24SMichael Walsh result.append(value) 7747822b9e9SMichael Walsh 7757822b9e9SMichael Walsh return result 776074b7654SMichael Walsh 777074b7654SMichael Walsh 77846ef0a24SMichael Walshdef match_struct(structure, match_dict, regex=False): 77946ef0a24SMichael Walsh r""" 780410b1787SMichael Walsh Return True or False to indicate whether the structure matches the match dictionary. 78146ef0a24SMichael Walsh 78246ef0a24SMichael Walsh Example: 78346ef0a24SMichael Walsh 78446ef0a24SMichael Walsh Given a dictionary structure named "personnel" with the following contents: 78546ef0a24SMichael Walsh 78646ef0a24SMichael Walsh personnel: 78746ef0a24SMichael Walsh [manager]: 78846ef0a24SMichael Walsh [last_name]: Doe 78946ef0a24SMichael Walsh [first_name]: John 79046ef0a24SMichael Walsh [accountant]: 79146ef0a24SMichael Walsh [last_name]: Smith 79246ef0a24SMichael Walsh [first_name]: Will 79346ef0a24SMichael Walsh 79446ef0a24SMichael Walsh The following call would return True. 79546ef0a24SMichael Walsh 79646ef0a24SMichael Walsh match_struct(personnel, {'last_name': '^Doe$'}, regex=True) 79746ef0a24SMichael Walsh 79846ef0a24SMichael Walsh Whereas the following call would return False. 79946ef0a24SMichael Walsh 80046ef0a24SMichael Walsh match_struct(personnel, {'last_name': 'Johnson'}, regex=True) 80146ef0a24SMichael Walsh 80246ef0a24SMichael Walsh Description of argument(s): 803410b1787SMichael Walsh structure Any nested combination of lists or dictionaries. See the prolog of 80446ef0a24SMichael Walsh get_nested() for details. 805410b1787SMichael Walsh match_dict Each key/value pair in match_dict must exist somewhere in the structure 806410b1787SMichael Walsh for the structure to be considered a match. A match value of None is 807410b1787SMichael Walsh considered a special case where the structure would be considered a match 808410b1787SMichael Walsh only if the key in question is found nowhere in the structure. 809410b1787SMichael Walsh regex Indicates whether the values in the match_dict should be interpreted as 81046ef0a24SMichael Walsh regular expressions. 81146ef0a24SMichael Walsh """ 81246ef0a24SMichael Walsh 813410b1787SMichael Walsh # The structure must match for each match_dict entry to be considered a match. Therefore, any failure 814410b1787SMichael Walsh # to match is grounds for returning False. 81546ef0a24SMichael Walsh for match_key, match_value in match_dict.items(): 81646ef0a24SMichael Walsh struct_key_values = nested_get(match_key, structure) 81746ef0a24SMichael Walsh if match_value is None: 81846ef0a24SMichael Walsh # Handle this as special case. 81946ef0a24SMichael Walsh if len(struct_key_values) != 0: 82046ef0a24SMichael Walsh return False 82146ef0a24SMichael Walsh else: 82246ef0a24SMichael Walsh if len(struct_key_values) == 0: 82346ef0a24SMichael Walsh return False 82446ef0a24SMichael Walsh if regex: 825*20f38712SPatrick Williams matches = [ 826*20f38712SPatrick Williams x 827*20f38712SPatrick Williams for x in struct_key_values 828*20f38712SPatrick Williams if re.search(match_value, str(x)) 829*20f38712SPatrick Williams ] 83046ef0a24SMichael Walsh if not matches: 83146ef0a24SMichael Walsh return False 83246ef0a24SMichael Walsh elif match_value not in struct_key_values: 83346ef0a24SMichael Walsh return False 83446ef0a24SMichael Walsh 83546ef0a24SMichael Walsh return True 83646ef0a24SMichael Walsh 83746ef0a24SMichael Walsh 838399df5a2SMichael Walshdef filter_struct(structure, filter_dict, regex=False, invert=False): 839074b7654SMichael Walsh r""" 840410b1787SMichael Walsh Filter the structure by removing any entries that do NOT contain the keys/values specified in filter_dict 841410b1787SMichael Walsh and return the result. 842074b7654SMichael Walsh 843410b1787SMichael Walsh The selection process is directed only at the first-level entries of the structure. 84446ef0a24SMichael Walsh 845074b7654SMichael Walsh Example: 846074b7654SMichael Walsh 847074b7654SMichael Walsh Given a dictionary named "properties" that has the following structure: 848074b7654SMichael Walsh 849074b7654SMichael Walsh properties: 850074b7654SMichael Walsh [/redfish/v1/Systems/system/Processors]: 851074b7654SMichael Walsh [Members]: 852074b7654SMichael Walsh [0]: 853410b1787SMichael Walsh [@odata.id]: /redfish/v1/Systems/system/Processors/cpu0 854074b7654SMichael Walsh [1]: 855410b1787SMichael Walsh [@odata.id]: /redfish/v1/Systems/system/Processors/cpu1 856074b7654SMichael Walsh [/redfish/v1/Systems/system/Processors/cpu0]: 857074b7654SMichael Walsh [Status]: 858074b7654SMichael Walsh [State]: Enabled 859074b7654SMichael Walsh [Health]: OK 860074b7654SMichael Walsh [/redfish/v1/Systems/system/Processors/cpu1]: 861074b7654SMichael Walsh [Status]: 862074b7654SMichael Walsh [State]: Enabled 863074b7654SMichael Walsh [Health]: Bad 864074b7654SMichael Walsh 865074b7654SMichael Walsh The following call: 866074b7654SMichael Walsh 867074b7654SMichael Walsh properties = filter_struct(properties, "[('Health', 'OK')]") 868074b7654SMichael Walsh 869074b7654SMichael Walsh Would return a new properties dictionary that looks like this: 870074b7654SMichael Walsh 871074b7654SMichael Walsh properties: 872074b7654SMichael Walsh [/redfish/v1/Systems/system/Processors/cpu0]: 873074b7654SMichael Walsh [Status]: 874074b7654SMichael Walsh [State]: Enabled 875074b7654SMichael Walsh [Health]: OK 876074b7654SMichael Walsh 877410b1787SMichael Walsh Note that the first item in the original properties directory had no key anywhere in the structure named 878410b1787SMichael Walsh "Health". Therefore, that item failed to make the cut. The next item did have a key named "Health" 879410b1787SMichael Walsh whose value was "OK" so it was included in the new structure. The third item had a key named "Health" 880410b1787SMichael Walsh but its value was not "OK" so it also failed to make the cut. 881074b7654SMichael Walsh 882074b7654SMichael Walsh Description of argument(s): 883410b1787SMichael Walsh structure Any nested combination of lists or dictionaries. See the prolog of 88446ef0a24SMichael Walsh get_nested() for details. 885410b1787SMichael Walsh filter_dict For each key/value pair in filter_dict, each entry in structure must 886410b1787SMichael Walsh contain the same key/value pair at some level. A filter_dict value of 887410b1787SMichael Walsh None is treated as a special case. Taking the example shown above, 888410b1787SMichael Walsh [('State', None)] would mean that the result should only contain records 88946ef0a24SMichael Walsh that have no State key at all. 890410b1787SMichael Walsh regex Indicates whether the values in the filter_dict should be interpreted as 89146ef0a24SMichael Walsh regular expressions. 892410b1787SMichael Walsh invert Invert the results. Instead of including only matching entries in the 893410b1787SMichael Walsh results, include only NON-matching entries in the results. 894074b7654SMichael Walsh """ 895074b7654SMichael Walsh 896410b1787SMichael Walsh # Convert filter_dict from a string containing a python object definition to an actual python object (if 897410b1787SMichael Walsh # warranted). 898074b7654SMichael Walsh filter_dict = fa.source_to_object(filter_dict) 899074b7654SMichael Walsh 900410b1787SMichael Walsh # Determine whether structure is a list or a dictionary and process accordingly. The result returned 901410b1787SMichael Walsh # will be of the same type as the structure. 902074b7654SMichael Walsh if type(structure) is list: 903074b7654SMichael Walsh result = [] 90446ef0a24SMichael Walsh for element in structure: 905399df5a2SMichael Walsh if match_struct(element, filter_dict, regex) != invert: 90646ef0a24SMichael Walsh result.append(element) 907074b7654SMichael Walsh else: 908074b7654SMichael Walsh try: 909074b7654SMichael Walsh result = collections.OrderedDict() 910074b7654SMichael Walsh except AttributeError: 911074b7654SMichael Walsh result = DotDict() 912074b7654SMichael Walsh for struct_key, struct_value in structure.items(): 913399df5a2SMichael Walsh if match_struct(struct_value, filter_dict, regex) != invert: 914074b7654SMichael Walsh result[struct_key] = struct_value 915074b7654SMichael Walsh 916074b7654SMichael Walsh return result 917f3a8ae10SMichael Walsh 918f3a8ae10SMichael Walsh 919f3a8ae10SMichael Walshdef split_dict_on_key(split_key, dictionary): 920f3a8ae10SMichael Walsh r""" 921f3a8ae10SMichael Walsh Split a dictionary into two dictionaries based on the first occurrence of the split key and return the 922f3a8ae10SMichael Walsh resulting sub-dictionaries. 923f3a8ae10SMichael Walsh 924f3a8ae10SMichael Walsh Example: 925f3a8ae10SMichael Walsh dictionary = {'one': 1, 'two': 2, 'three':3, 'four':4} 926f3a8ae10SMichael Walsh dict1, dict2 = split_dict_on_key('three', dictionary) 927f3a8ae10SMichael Walsh pvars(dictionary, dict1, dict2) 928f3a8ae10SMichael Walsh 929f3a8ae10SMichael Walsh Output: 930f3a8ae10SMichael Walsh dictionary: 931f3a8ae10SMichael Walsh [one]: 1 932f3a8ae10SMichael Walsh [two]: 2 933f3a8ae10SMichael Walsh [three]: 3 934f3a8ae10SMichael Walsh [four]: 4 935f3a8ae10SMichael Walsh dict1: 936f3a8ae10SMichael Walsh [one]: 1 937f3a8ae10SMichael Walsh [two]: 2 938f3a8ae10SMichael Walsh dict2: 939f3a8ae10SMichael Walsh [three]: 3 940f3a8ae10SMichael Walsh [four]: 4 941f3a8ae10SMichael Walsh 942f3a8ae10SMichael Walsh Description of argument(s): 943f3a8ae10SMichael Walsh split_key The key value to be used to determine where the dictionary should be 944f3a8ae10SMichael Walsh split. 945f3a8ae10SMichael Walsh dictionary The dictionary to be split. 946f3a8ae10SMichael Walsh """ 947f3a8ae10SMichael Walsh dict1 = {} 948f3a8ae10SMichael Walsh dict2 = {} 949f3a8ae10SMichael Walsh found_split_key = False 950f3a8ae10SMichael Walsh for key in list(dictionary.keys()): 951f3a8ae10SMichael Walsh if key == split_key: 952f3a8ae10SMichael Walsh found_split_key = True 953f3a8ae10SMichael Walsh if found_split_key: 954f3a8ae10SMichael Walsh dict2[key] = dictionary[key] 955f3a8ae10SMichael Walsh else: 956f3a8ae10SMichael Walsh dict1[key] = dictionary[key] 957f3a8ae10SMichael Walsh return dict1, dict2 958