1#!/usr/bin/env python3
2import argparse
3import json
4import os
5
6import requests
7
8PRAGMA_ONCE = """#pragma once
9"""
10
11WARNING = """/****************************************************************
12 *                 READ THIS WARNING FIRST
13 * This is an auto-generated header which contains definitions
14 * for Redfish DMTF defined messages.
15 * DO NOT modify this registry outside of running the
16 * parse_registries.py script.  The definitions contained within
17 * this file are owned by DMTF.  Any modifications to these files
18 * should be first pushed to the relevant registry in the DMTF
19 * github organization.
20 ***************************************************************/"""
21
22REGISTRY_HEADER = (
23    PRAGMA_ONCE
24    + WARNING
25    + """
26#include "registries.hpp"
27
28#include <array>
29
30// clang-format off
31
32namespace redfish::registries::{}
33{{
34"""
35)
36
37SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
38
39include_path = os.path.realpath(
40    os.path.join(SCRIPT_DIR, "..", "redfish-core", "include", "registries")
41)
42
43proxies = {"https": os.environ.get("https_proxy", None)}
44
45
46def make_getter(dmtf_name, header_name, type_name):
47    url = "https://redfish.dmtf.org/registries/{}".format(dmtf_name)
48    dmtf = requests.get(url, proxies=proxies)
49    dmtf.raise_for_status()
50    json_file = json.loads(dmtf.text)
51    path = os.path.join(include_path, header_name)
52    return (path, json_file, type_name, url)
53
54
55def update_registries(files):
56    # Remove the old files
57    for file, json_dict, namespace, url in files:
58        try:
59            os.remove(file)
60        except BaseException:
61            print("{} not found".format(file))
62
63        with open(file, "w") as registry:
64            registry.write(REGISTRY_HEADER.format(namespace))
65            # Parse the Registry header info
66            registry.write(
67                "const Header header = {{\n"
68                '    "{json_dict[@Redfish.Copyright]}",\n'
69                '    "{json_dict[@odata.type]}",\n'
70                '    "{json_dict[Id]}",\n'
71                '    "{json_dict[Name]}",\n'
72                '    "{json_dict[Language]}",\n'
73                '    "{json_dict[Description]}",\n'
74                '    "{json_dict[RegistryPrefix]}",\n'
75                '    "{json_dict[RegistryVersion]}",\n'
76                '    "{json_dict[OwningEntity]}",\n'
77                "}};\n"
78                "constexpr const char* url =\n"
79                '    "{url}";\n'
80                "\n"
81                "constexpr std::array registry =\n"
82                "{{\n".format(
83                    json_dict=json_dict,
84                    url=url,
85                )
86            )
87
88            messages_sorted = sorted(json_dict["Messages"].items())
89            for messageId, message in messages_sorted:
90                registry.write(
91                    "    MessageEntry{{\n"
92                    '        "{messageId}",\n'
93                    "        {{\n"
94                    '            "{message[Description]}",\n'
95                    '            "{message[Message]}",\n'
96                    '            "{message[MessageSeverity]}",\n'
97                    "            {message[NumberOfArgs]},\n"
98                    "            {{".format(
99                        messageId=messageId, message=message
100                    )
101                )
102                paramTypes = message.get("ParamTypes")
103                if paramTypes:
104                    for paramType in paramTypes:
105                        registry.write(
106                            '\n                "{}",'.format(paramType)
107                        )
108                    registry.write("\n            },\n")
109                else:
110                    registry.write("},\n")
111                registry.write(
112                    '            "{message[Resolution]}",\n'
113                    "        }}}},\n".format(message=message)
114                )
115
116            registry.write("\n};\n\nenum class Index\n{\n")
117            for index, (messageId, message) in enumerate(messages_sorted):
118                messageId = messageId[0].lower() + messageId[1:]
119                registry.write("    {} = {},\n".format(messageId, index))
120            registry.write(
121                "}};\n}} // namespace redfish::registries::{}\n".format(
122                    namespace
123                )
124            )
125
126
127def get_privilege_string_from_list(privilege_list):
128    privilege_string = "{{\n"
129    for privilege_json in privilege_list:
130        privileges = privilege_json["Privilege"]
131        privilege_string += "    {"
132        for privilege in privileges:
133            if privilege == "NoAuth":
134                continue
135            privilege_string += '"'
136            privilege_string += privilege
137            privilege_string += '",\n'
138        if privilege != "NoAuth":
139            privilege_string = privilege_string[:-2]
140        privilege_string += "}"
141        privilege_string += ",\n"
142    privilege_string = privilege_string[:-2]
143    privilege_string += "\n}}"
144    return privilege_string
145
146
147def get_variable_name_for_privilege_set(privilege_list):
148    names = []
149    for privilege_json in privilege_list:
150        privileges = privilege_json["Privilege"]
151        names.append("And".join(privileges))
152    return "Or".join(names)
153
154
155PRIVILEGE_HEADER = (
156    PRAGMA_ONCE
157    + WARNING
158    + """
159#include "privileges.hpp"
160
161#include <array>
162
163// clang-format off
164
165namespace redfish::privileges
166{
167"""
168)
169
170
171def make_privilege_registry():
172    path, json_file, type_name, url = make_getter(
173        "Redfish_1.3.0_PrivilegeRegistry.json",
174        "privilege_registry.hpp",
175        "privilege",
176    )
177    with open(path, "w") as registry:
178        registry.write(PRIVILEGE_HEADER)
179
180        privilege_dict = {}
181        for mapping in json_file["Mappings"]:
182            # first pass, identify all the unique privilege sets
183            for operation, privilege_list in mapping["OperationMap"].items():
184                privilege_dict[
185                    get_privilege_string_from_list(privilege_list)
186                ] = (privilege_list,)
187        for index, key in enumerate(privilege_dict):
188            (privilege_list,) = privilege_dict[key]
189            name = get_variable_name_for_privilege_set(privilege_list)
190            registry.write(
191                "const std::array<Privileges, {length}> "
192                "privilegeSet{name} = {key};\n".format(
193                    length=len(privilege_list), name=name, key=key
194                )
195            )
196            privilege_dict[key] = (privilege_list, name)
197
198        for mapping in json_file["Mappings"]:
199            entity = mapping["Entity"]
200            registry.write("// {}\n".format(entity))
201            for operation, privilege_list in mapping["OperationMap"].items():
202                privilege_string = get_privilege_string_from_list(
203                    privilege_list
204                )
205                operation = operation.lower()
206
207                registry.write(
208                    "const static auto& {}{} = privilegeSet{};\n".format(
209                        operation, entity, privilege_dict[privilege_string][1]
210                    )
211                )
212            registry.write("\n")
213        registry.write(
214            "} // namespace redfish::privileges\n// clang-format on\n"
215        )
216
217
218def main():
219    parser = argparse.ArgumentParser()
220    parser.add_argument(
221        "--registries",
222        type=str,
223        default="base,task_event,resource_event,privilege",
224        help="Comma delimited list of registries to update",
225    )
226
227    args = parser.parse_args()
228
229    registries = set(args.registries.split(","))
230    files = []
231
232    if "base" in registries:
233        files.append(
234            make_getter(
235                "Base.1.16.0.json", "base_message_registry.hpp", "base"
236            )
237        )
238    if "task_event" in registries:
239        files.append(
240            make_getter(
241                "TaskEvent.1.0.3.json",
242                "task_event_message_registry.hpp",
243                "task_event",
244            )
245        )
246    if "resource_event" in registries:
247        files.append(
248            make_getter(
249                "ResourceEvent.1.3.0.json",
250                "resource_event_message_registry.hpp",
251                "resource_event",
252            )
253        )
254
255    update_registries(files)
256
257    if "privilege" in registries:
258        make_privilege_registry()
259
260
261if __name__ == "__main__":
262    main()
263