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 openbmc_local_getter():
56    url = ""
57    with open(
58        os.path.join(
59            SCRIPT_DIR,
60            "..",
61            "redfish-core",
62            "include",
63            "registries",
64            "openbmc.json",
65        ),
66        "rb",
67    ) as json_file:
68        json_file = json.load(json_file)
69
70    path = os.path.join(include_path, "openbmc_message_registry.hpp")
71    return (path, json_file, "openbmc", url)
72
73
74def update_registries(files):
75    # Remove the old files
76    for file, json_dict, namespace, url in files:
77        try:
78            os.remove(file)
79        except BaseException:
80            print("{} not found".format(file))
81
82        with open(file, "w") as registry:
83            registry.write(REGISTRY_HEADER.format(namespace))
84            # Parse the Registry header info
85            registry.write(
86                "const Header header = {{\n"
87                '    "{json_dict[@Redfish.Copyright]}",\n'
88                '    "{json_dict[@odata.type]}",\n'
89                '    "{json_dict[Id]}",\n'
90                '    "{json_dict[Name]}",\n'
91                '    "{json_dict[Language]}",\n'
92                '    "{json_dict[Description]}",\n'
93                '    "{json_dict[RegistryPrefix]}",\n'
94                '    "{json_dict[RegistryVersion]}",\n'
95                '    "{json_dict[OwningEntity]}",\n'
96                "}};\n"
97                "constexpr const char* url =\n"
98                '    "{url}";\n'
99                "\n"
100                "constexpr std::array registry =\n"
101                "{{\n".format(
102                    json_dict=json_dict,
103                    url=url,
104                )
105            )
106
107            messages_sorted = sorted(json_dict["Messages"].items())
108            for messageId, message in messages_sorted:
109                registry.write(
110                    "    MessageEntry{{\n"
111                    '        "{messageId}",\n'
112                    "        {{\n"
113                    '            "{message[Description]}",\n'
114                    '            "{message[Message]}",\n'
115                    '            "{message[MessageSeverity]}",\n'
116                    "            {message[NumberOfArgs]},\n"
117                    "            {{".format(
118                        messageId=messageId, message=message
119                    )
120                )
121                paramTypes = message.get("ParamTypes")
122                if paramTypes:
123                    for paramType in paramTypes:
124                        registry.write(
125                            '\n                "{}",'.format(paramType)
126                        )
127                    registry.write("\n            },\n")
128                else:
129                    registry.write("},\n")
130                registry.write(
131                    '            "{message[Resolution]}",\n'
132                    "        }}}},\n".format(message=message)
133                )
134
135            registry.write("\n};\n\nenum class Index\n{\n")
136            for index, (messageId, message) in enumerate(messages_sorted):
137                messageId = messageId[0].lower() + messageId[1:]
138                registry.write("    {} = {},\n".format(messageId, index))
139            registry.write(
140                "}};\n}} // namespace redfish::registries::{}\n".format(
141                    namespace
142                )
143            )
144
145
146def get_privilege_string_from_list(privilege_list):
147    privilege_string = "{{\n"
148    for privilege_json in privilege_list:
149        privileges = privilege_json["Privilege"]
150        privilege_string += "    {"
151        for privilege in privileges:
152            if privilege == "NoAuth":
153                continue
154            privilege_string += '"'
155            privilege_string += privilege
156            privilege_string += '",\n'
157        if privilege != "NoAuth":
158            privilege_string = privilege_string[:-2]
159        privilege_string += "}"
160        privilege_string += ",\n"
161    privilege_string = privilege_string[:-2]
162    privilege_string += "\n}}"
163    return privilege_string
164
165
166def get_variable_name_for_privilege_set(privilege_list):
167    names = []
168    for privilege_json in privilege_list:
169        privileges = privilege_json["Privilege"]
170        names.append("And".join(privileges))
171    return "Or".join(names)
172
173
174PRIVILEGE_HEADER = (
175    PRAGMA_ONCE
176    + WARNING
177    + """
178#include "privileges.hpp"
179
180#include <array>
181
182// clang-format off
183
184namespace redfish::privileges
185{
186"""
187)
188
189
190def make_privilege_registry():
191    path, json_file, type_name, url = make_getter(
192        "Redfish_1.5.0_PrivilegeRegistry.json",
193        "privilege_registry.hpp",
194        "privilege",
195    )
196    with open(path, "w") as registry:
197        registry.write(PRIVILEGE_HEADER)
198
199        privilege_dict = {}
200        for mapping in json_file["Mappings"]:
201            # first pass, identify all the unique privilege sets
202            for operation, privilege_list in mapping["OperationMap"].items():
203                privilege_dict[
204                    get_privilege_string_from_list(privilege_list)
205                ] = (privilege_list,)
206        for index, key in enumerate(privilege_dict):
207            (privilege_list,) = privilege_dict[key]
208            name = get_variable_name_for_privilege_set(privilege_list)
209            registry.write(
210                "const std::array<Privileges, {length}> "
211                "privilegeSet{name} = {key};\n".format(
212                    length=len(privilege_list), name=name, key=key
213                )
214            )
215            privilege_dict[key] = (privilege_list, name)
216
217        for mapping in json_file["Mappings"]:
218            entity = mapping["Entity"]
219            registry.write("// {}\n".format(entity))
220            for operation, privilege_list in mapping["OperationMap"].items():
221                privilege_string = get_privilege_string_from_list(
222                    privilege_list
223                )
224                operation = operation.lower()
225
226                registry.write(
227                    "const static auto& {}{} = privilegeSet{};\n".format(
228                        operation, entity, privilege_dict[privilege_string][1]
229                    )
230                )
231            registry.write("\n")
232        registry.write(
233            "} // namespace redfish::privileges\n// clang-format on\n"
234        )
235
236
237def to_pascal_case(text):
238    s = text.replace("_", " ")
239    s = s.split()
240    if len(text) == 0:
241        return text
242    return "".join(i.capitalize() for i in s[0:])
243
244
245def main():
246    dmtf_registries = (
247        ("base", "1.18.1"),
248        ("composition", "1.1.2"),
249        ("environmental", "1.0.1"),
250        ("ethernet_fabric", "1.0.1"),
251        ("fabric", "1.0.2"),
252        ("heartbeat_event", "1.0.1"),
253        ("job_event", "1.0.1"),
254        ("license", "1.0.3"),
255        ("log_service", "1.0.1"),
256        ("network_device", "1.0.3"),
257        ("platform", "1.0.1"),
258        ("power", "1.0.1"),
259        ("resource_event", "1.3.0"),
260        ("sensor_event", "1.0.1"),
261        ("storage_device", "1.2.1"),
262        ("task_event", "1.0.3"),
263        ("telemetry", "1.0.0"),
264        ("update", "1.0.2"),
265    )
266
267    parser = argparse.ArgumentParser()
268    parser.add_argument(
269        "--registries",
270        type=str,
271        default="privilege,openbmc,"
272        + ",".join([dmtf[0] for dmtf in dmtf_registries]),
273        help="Comma delimited list of registries to update",
274    )
275
276    args = parser.parse_args()
277
278    registries = set(args.registries.split(","))
279    files = []
280
281    for registry, version in dmtf_registries:
282        if registry in registries:
283            registry_pascal_case = to_pascal_case(registry)
284            files.append(
285                make_getter(
286                    f"{registry_pascal_case}.{version}.json",
287                    f"{registry}_message_registry.hpp",
288                    registry,
289                )
290            )
291    if "openbmc" in registries:
292        files.append(openbmc_local_getter())
293
294    update_registries(files)
295
296    if "privilege" in registries:
297        make_privilege_registry()
298
299
300if __name__ == "__main__":
301    main()
302