blob: 74f13707539fcc2d07bfcff14e916de1d2b9838a [file] [log] [blame]
#!/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/bmc#/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)