| #!/usr/bin/env python3 | |
| """Script to parse Redfish registries and generate C++ header files. | |
| This script fetches Redfish registries from DMTF, parses them, and generates | |
| C++ header files containing the definitions for the registries. | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import requests | |
| NOLINTBEGIN = """// NOLINTBEGIN | |
| """ | |
| NOLINTEND = """// NOLINTEND | |
| """ | |
| PRAGMA_ONCE = """#pragma once | |
| """ | |
| WARNING = """/**************************************************************** | |
| * READ THIS WARNING FIRST | |
| * This is an auto-generated header which contains definitions | |
| * for Redfish DMTF defined messages. | |
| * DO NOT modify this registry outside of running the | |
| * parse_registries.py script. The definitions contained within | |
| * this file are owned by DMTF. Any modifications to these files | |
| * should be first pushed to the relevant registry in the DMTF | |
| * github organization. | |
| ***************************************************************/""" | |
| REGISTRY_HEADER = PRAGMA_ONCE + WARNING + """ | |
| #include "registries.hpp" | |
| #include <array> | |
| // clang-format off | |
| namespace redfish::registries::{} | |
| {{ | |
| """ | |
| SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) | |
| include_path = os.path.realpath( | |
| os.path.join(SCRIPT_DIR, "..", "redfish-core", "include", "registries") | |
| ) | |
| proxies = {"https": os.environ.get("https_proxy", None)} | |
| def make_getter(dmtf_name, header_name, type_name): | |
| url = "https://redfish.dmtf.org/registries/{}".format(dmtf_name) | |
| dmtf = requests.get(url, proxies=proxies) | |
| dmtf.raise_for_status() | |
| json_file = json.loads(dmtf.text) | |
| path = os.path.join(include_path, header_name) | |
| return (path, json_file, type_name, url) | |
| def update_registries(files): | |
| """Updates the specified registry files. | |
| This function takes a list of registry file information, removes existing | |
| files, and generates new ones based on the provided JSON data. | |
| Args: | |
| files: A list of tuples, where each tuple contains: - The path to the | |
| registry file. - The JSON data for the registry. - The namespace for the | |
| registry. - The URL of the registry. | |
| """ | |
| # Remove the old files | |
| for file, json_dict, namespace, url in files: | |
| try: | |
| os.remove(file) | |
| except BaseException: # pylint: disable=broad-exception-caught | |
| print("{} not found".format(file)) | |
| with open(file, "w") as registry: | |
| registry.write(NOLINTBEGIN) | |
| registry.write(REGISTRY_HEADER.format(namespace)) | |
| # Parse the Registry header info | |
| registry.write( | |
| "const Header header = {{\n" | |
| ' "{json_dict[@Redfish.Copyright]}",\n' | |
| ' "{json_dict[@odata.type]}",\n' | |
| ' "{json_dict[Id]}",\n' | |
| ' "{json_dict[Name]}",\n' | |
| ' "{json_dict[Language]}",\n' | |
| ' "{json_dict[Description]}",\n' | |
| ' "{json_dict[RegistryPrefix]}",\n' | |
| ' "{json_dict[RegistryVersion]}",\n' | |
| ' "{json_dict[OwningEntity]}",\n' | |
| "}};\n" | |
| "constexpr const char* url =\n" | |
| ' "{url}";\n' | |
| "\n" | |
| "constexpr std::array registry =\n" | |
| "{{\n".format( | |
| json_dict=json_dict, | |
| url=url, | |
| ) | |
| ) | |
| messages_sorted = sorted(json_dict["Messages"].items()) | |
| for message_id, message in messages_sorted: | |
| registry.write( | |
| " MessageEntry{{\n" | |
| ' "{message_id}",\n' | |
| " {{\n" | |
| ' "{message[Description]}",\n' | |
| ' "{message[Message]}",\n' | |
| ' "{message[MessageSeverity]}",\n' | |
| " {message[NumberOfArgs]},\n" | |
| " {{".format(message_id=message_id, message=message) | |
| ) | |
| param_types = message.get("ParamTypes") | |
| if param_types: | |
| for param_type in param_types: | |
| registry.write('\n "{}",'.format(param_type)) | |
| registry.write("\n },\n") | |
| else: | |
| registry.write("},\n") | |
| registry.write( | |
| ' "{message[Resolution]}",\n }}}},\n'.format( | |
| message=message | |
| ) | |
| ) | |
| registry.write("\n};\n\nenum class Index : std::uint8_t\n{\n") | |
| for index, (message_id, _) in enumerate(messages_sorted): | |
| message_id = message_id[0].lower() + message_id[1:] | |
| registry.write(" {} = {},\n".format(message_id, index)) | |
| registry.write( | |
| "}};\n}} // namespace redfish::registries::{}\n".format(namespace) | |
| ) | |
| registry.write(NOLINTEND) | |
| def get_privilege_string_from_list(privilege_list): | |
| """Generates a string representation of a list of privileges. | |
| Args: | |
| privilege_list: A list of JSON objects, each containing a "Privilege" key | |
| with a list of privilege strings. | |
| Returns: | |
| A string representing the combined privileges, formatted for C++ code. | |
| """ | |
| privilege_string = "{{\n" | |
| for privilege_json in privilege_list: | |
| privileges = privilege_json["Privilege"] | |
| privilege_string += " {" | |
| for privilege in privileges: | |
| if privilege == "NoAuth": | |
| continue | |
| privilege_string += '"' | |
| privilege_string += privilege | |
| privilege_string += '",\n' | |
| if privilege != "NoAuth": # pylint: disable=undefined-loop-variable | |
| privilege_string = privilege_string[:-2] | |
| privilege_string += "}" | |
| privilege_string += ",\n" | |
| privilege_string = privilege_string[:-2] | |
| privilege_string += "\n}}" | |
| return privilege_string | |
| def get_variable_name_for_privilege_set(privilege_list): | |
| names = [] | |
| for privilege_json in privilege_list: | |
| privileges = privilege_json["Privilege"] | |
| names.append("And".join(privileges)) | |
| return "Or".join(names) | |
| PRIVILEGE_HEADER = PRAGMA_ONCE + WARNING + """ | |
| #include "privileges.hpp" | |
| #include <array> | |
| #include <cstdint> | |
| // clang-format off | |
| namespace redfish::privileges | |
| { | |
| """ | |
| def make_privilege_registry(): | |
| """Generates the privilege registry file. | |
| This function fetches the Redfish privilege registry JSON, parses it, | |
| and generates a C++ header file containing the privilege definitions. | |
| """ | |
| path, json_file, _, _ = make_getter( | |
| "Redfish_1.5.0_PrivilegeRegistry.json", | |
| "privilege_registry.hpp", | |
| "privilege", | |
| ) | |
| with open(path, "w") as registry: | |
| registry.write(NOLINTBEGIN) | |
| registry.write(PRIVILEGE_HEADER) | |
| privilege_dict = {} | |
| for mapping in json_file["Mappings"]: | |
| if mapping["Entity"] in [ | |
| "Session", | |
| "SessionCollection", | |
| "SessionService", | |
| "Triggers", | |
| "TriggersCollection", | |
| ]: | |
| continue | |
| # first pass, identify all the unique privilege sets | |
| for _, privilege_list in mapping["OperationMap"].items(): | |
| privilege_dict[get_privilege_string_from_list(privilege_list)] = ( | |
| privilege_list, | |
| ) | |
| for _, key in enumerate(privilege_dict): | |
| (privilege_list,) = privilege_dict[key] | |
| name = get_variable_name_for_privilege_set(privilege_list) | |
| registry.write( | |
| "const std::array<Privileges, {length}> " | |
| "privilegeSet{name} = {key};\n".format( | |
| length=len(privilege_list), name=name, key=key | |
| ) | |
| ) | |
| privilege_dict[key] = (privilege_list, name) | |
| for mapping in json_file["Mappings"]: | |
| entity = mapping["Entity"] | |
| if entity in [ | |
| "Session", | |
| "SessionCollection", | |
| "SessionService", | |
| "Triggers", | |
| "TriggersCollection", | |
| ]: | |
| continue | |
| registry.write("// {}\n".format(entity)) | |
| for operation, privilege_list in mapping["OperationMap"].items(): | |
| privilege_string = get_privilege_string_from_list(privilege_list) | |
| operation = operation.lower() | |
| registry.write( | |
| "const static auto& {}{} = privilegeSet{};\n".format( | |
| operation, entity, privilege_dict[privilege_string][1] | |
| ) | |
| ) | |
| registry.write("\n") | |
| registry.write("} // namespace redfish::privileges\n// clang-format on\n") | |
| registry.write(NOLINTEND) | |
| def main(): | |
| """Main function to parse arguments and update registries.""" | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument( | |
| "--registries", | |
| type=str, | |
| default="base,task_event,resource_event,privilege", | |
| help="Comma delimited list of registries to update", | |
| ) | |
| args = parser.parse_args() | |
| registries = set(args.registries.split(",")) | |
| files = [] | |
| if "base" in registries: | |
| files.append( | |
| make_getter("Base.1.13.0.json", "base_message_registry.hpp", "base") | |
| ) | |
| if "task_event" in registries: | |
| files.append( | |
| make_getter( | |
| "TaskEvent.1.0.3.json", | |
| "task_event_message_registry.hpp", | |
| "task_event", | |
| ) | |
| ) | |
| if "resource_event" in registries: | |
| files.append( | |
| make_getter( | |
| "ResourceEvent.1.0.3.json", | |
| "resource_event_message_registry.hpp", | |
| "resource_event", | |
| ) | |
| ) | |
| update_registries(files) | |
| if "privilege" in registries: | |
| make_privilege_registry() | |
| if __name__ == "__main__": | |
| main() |