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