| #!/usr/bin/env python3 |
| |
| r""" |
| This module contains functions having to do with redfish path walking. |
| """ |
| |
| import json |
| import os |
| import subprocess |
| |
| ERROR_RESPONSE = { |
| "404": "Response Error: status_code: 404 -- Not Found", |
| "500": "Response Error: status_code: 500 -- Internal Server Error", |
| } |
| |
| # Variable to hold enumerated data. |
| result = {} |
| |
| # Variable to hold the pending list of resources for which enumeration. |
| # is yet to be obtained. |
| pending_enumeration = set() |
| |
| |
| def execute_redfish_cmd(parms, json_type="json"): |
| r""" |
| Run CLI standard redfish tool. |
| |
| Description of variable: |
| parms_string Command to execute from the current SHELL. |
| quiet do not print tool error message if True |
| """ |
| resp = subprocess.run( |
| [parms], |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| shell=True, |
| universal_newlines=True, |
| ) |
| |
| if resp.stderr: |
| print("\n\t\tERROR with %s " % parms) |
| print("\t\t" + resp.stderr) |
| return resp.stderr |
| elif json_type == "json": |
| json_data = json.loads(resp.stdout) |
| return json_data |
| else: |
| return resp.stdout |
| |
| |
| def enumerate_request(hostname, username, password, url, return_json="json"): |
| r""" |
| Perform a GET enumerate request and return available resource paths. |
| |
| Description of argument(s): |
| url URI resource absolute path (e.g. |
| "/redfish/v1/SessionService/Sessions"). |
| return_json Indicates whether the result should be |
| returned as a json string or as a |
| dictionary. |
| """ |
| parms = ( |
| "redfishtool -u " |
| + username |
| + " -p " |
| + password |
| + " -r " |
| + hostname |
| + " -S Always raw GET " |
| ) |
| |
| pending_enumeration.add(url) |
| |
| # Variable having resources for which enumeration is completed. |
| enumerated_resources = set() |
| |
| resources_to_be_enumerated = (url,) |
| |
| while resources_to_be_enumerated: |
| for resource in resources_to_be_enumerated: |
| # JsonSchemas, SessionService or URLs containing # are not |
| # required in enumeration. |
| # Example: '/redfish/v1/JsonSchemas/' and sub resources. |
| # '/redfish/v1/SessionService' |
| # '/redfish/v1/Managers/${MANAGER_ID}#/Oem' |
| if ( |
| ("JsonSchemas" in resource) |
| or ("SessionService" in resource) |
| or ("PostCodes" in resource) |
| or ("Registries" in resource) |
| or ("#" in resource) |
| ): |
| continue |
| |
| response = execute_redfish_cmd(parms + resource) |
| # Enumeration is done for available resources ignoring the |
| # ones for which response is not obtained. |
| if "Error getting response" in response: |
| continue |
| |
| walk_nested_dict(response, url=resource) |
| |
| enumerated_resources.update(set(resources_to_be_enumerated)) |
| resources_to_be_enumerated = tuple( |
| pending_enumeration - enumerated_resources |
| ) |
| |
| if return_json == "json": |
| return json.dumps( |
| result, sort_keys=True, indent=4, separators=(",", ": ") |
| ) |
| else: |
| return result |
| |
| |
| def walk_nested_dict(data, url=""): |
| r""" |
| Parse through the nested dictionary and get the resource id paths. |
| |
| Description of argument(s): |
| data Nested dictionary data from response message. |
| url Resource for which the response is obtained in data. |
| """ |
| url = url.rstrip("/") |
| |
| for key, value in data.items(): |
| # Recursion if nested dictionary found. |
| if isinstance(value, dict): |
| walk_nested_dict(value) |
| else: |
| # Value contains a list of dictionaries having member data. |
| if "Members" == key: |
| if isinstance(value, list): |
| for memberDict in value: |
| if isinstance(memberDict, str): |
| pending_enumeration.add(memberDict) |
| else: |
| pending_enumeration.add(memberDict["@odata.id"]) |
| |
| if "@odata.id" == key: |
| value = value.rstrip("/") |
| # Data for the given url. |
| if value == url: |
| result[url] = data |
| # Data still needs to be looked up, |
| else: |
| pending_enumeration.add(value) |
| |
| |
| def get_key_value_nested_dict(data, key): |
| r""" |
| Parse through the nested dictionary and get the searched key value. |
| |
| Description of argument(s): |
| data Nested dictionary data from response message. |
| key Search dictionary key element. |
| """ |
| |
| for k, v in data.items(): |
| if isinstance(v, dict): |
| get_key_value_nested_dict(v, key) |
| |
| if k == key: |
| target_list.append(v) |