1#!/usr/bin/env python3 2 3r""" 4This module contains functions having to do with redfish path walking. 5""" 6 7import os 8import subprocess 9import json 10 11ERROR_RESPONSE = { 12 "404": 'Response Error: status_code: 404 -- Not Found', 13 "500": 'Response Error: status_code: 500 -- Internal Server Error', 14} 15 16# Variable to hold enumerated data. 17result = {} 18 19# Variable to hold the pending list of resources for which enumeration. 20# is yet to be obtained. 21pending_enumeration = set() 22 23 24def execute_redfish_cmd(parms, json_type="json"): 25 r""" 26 Run CLI standard redfish tool. 27 28 Description of variable: 29 parms_string Command to execute from the current SHELL. 30 quiet do not print tool error message if True 31 """ 32 resp = subprocess.run([parms], 33 stdout=subprocess.PIPE, 34 stderr=subprocess.PIPE, 35 shell=True, 36 universal_newlines=True) 37 38 if resp.stderr: 39 print('\n\t\tERROR with %s ' % parms) 40 print('\t\t' + resp.stderr) 41 return resp.stderr 42 elif json_type == "json": 43 json_data = json.loads(resp.stdout) 44 return json_data 45 else: 46 return resp.stdout 47 48 49def enumerate_request(hostname, username, password, url, return_json="json"): 50 r""" 51 Perform a GET enumerate request and return available resource paths. 52 53 Description of argument(s): 54 url URI resource absolute path (e.g. 55 "/redfish/v1/SessionService/Sessions"). 56 return_json Indicates whether the result should be 57 returned as a json string or as a 58 dictionary. 59 """ 60 parms = 'redfishtool -u ' + username + ' -p ' + password + ' -r ' + \ 61 hostname + ' -S Always raw GET ' 62 63 pending_enumeration.add(url) 64 65 # Variable having resources for which enumeration is completed. 66 enumerated_resources = set() 67 68 resources_to_be_enumerated = (url,) 69 70 while resources_to_be_enumerated: 71 for resource in resources_to_be_enumerated: 72 # JsonSchemas, SessionService or URLs containing # are not 73 # required in enumeration. 74 # Example: '/redfish/v1/JsonSchemas/' and sub resources. 75 # '/redfish/v1/SessionService' 76 # '/redfish/v1/Managers/bmc#/Oem' 77 if ('JsonSchemas' in resource) or ('SessionService' in resource)\ 78 or ('PostCodes' in resource) or ('Registries' in resource)\ 79 or ('#' in resource): 80 continue 81 82 response = execute_redfish_cmd(parms + resource) 83 # Enumeration is done for available resources ignoring the 84 # ones for which response is not obtained. 85 if 'Error getting response' in response: 86 continue 87 88 walk_nested_dict(response, url=resource) 89 90 enumerated_resources.update(set(resources_to_be_enumerated)) 91 resources_to_be_enumerated = \ 92 tuple(pending_enumeration - enumerated_resources) 93 94 if return_json == "json": 95 return json.dumps(result, sort_keys=True, 96 indent=4, separators=(',', ': ')) 97 else: 98 return result 99 100 101def walk_nested_dict(data, url=''): 102 r""" 103 Parse through the nested dictionary and get the resource id paths. 104 105 Description of argument(s): 106 data Nested dictionary data from response message. 107 url Resource for which the response is obtained in data. 108 """ 109 url = url.rstrip('/') 110 111 for key, value in data.items(): 112 113 # Recursion if nested dictionary found. 114 if isinstance(value, dict): 115 walk_nested_dict(value) 116 else: 117 # Value contains a list of dictionaries having member data. 118 if 'Members' == key: 119 if isinstance(value, list): 120 for memberDict in value: 121 if isinstance(memberDict, str): 122 pending_enumeration.add(memberDict) 123 else: 124 pending_enumeration.add(memberDict['@odata.id']) 125 126 if '@odata.id' == key: 127 value = value.rstrip('/') 128 # Data for the given url. 129 if value == url: 130 result[url] = data 131 # Data still needs to be looked up, 132 else: 133 pending_enumeration.add(value) 134 135 136def get_key_value_nested_dict(data, key): 137 r""" 138 Parse through the nested dictionary and get the searched key value. 139 140 Description of argument(s): 141 data Nested dictionary data from response message. 142 key Search dictionary key element. 143 """ 144 145 for k, v in data.items(): 146 if isinstance(v, dict): 147 get_key_value_nested_dict(v, key) 148 149 if k == key: 150 target_list.append(v) 151