1#!/usr/bin/env python3
2import argparse
3import json
4import os
5from collections import OrderedDict
6
7import requests
8
9PRAGMA_ONCE = """#pragma once
10"""
11
12WARNING = """/****************************************************************
13 *                 READ THIS WARNING FIRST
14 * This is an auto-generated header which contains definitions
15 * for Redfish DMTF defined messages.
16 * DO NOT modify this registry outside of running the
17 * parse_registries.py script.  The definitions contained within
18 * this file are owned by DMTF.  Any modifications to these files
19 * should be first pushed to the relevant registry in the DMTF
20 * github organization.
21 ***************************************************************/"""
22
23REGISTRY_HEADER = (
24    PRAGMA_ONCE
25    + WARNING
26    + """
27#include "registries.hpp"
28
29#include <array>
30
31// clang-format off
32
33namespace redfish::registries::{}
34{{
35"""
36)
37
38SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
39
40include_path = os.path.realpath(
41    os.path.join(SCRIPT_DIR, "..", "redfish-core", "include", "registries")
42)
43
44proxies = {"https": os.environ.get("https_proxy", None)}
45
46
47def make_getter(dmtf_name, header_name, type_name):
48    url = "https://redfish.dmtf.org/registries/{}".format(dmtf_name)
49    dmtf = requests.get(url, proxies=proxies)
50    dmtf.raise_for_status()
51    json_file = json.loads(dmtf.text, object_pairs_hook=OrderedDict)
52    path = os.path.join(include_path, header_name)
53    return (path, json_file, type_name, url)
54
55
56def openbmc_local_getter():
57    url = "https://github.com/openbmc/bmcweb/blob/master/redfish-core/include/registries/openbmc.json"
58    with open(
59        os.path.join(
60            SCRIPT_DIR,
61            "..",
62            "redfish-core",
63            "include",
64            "registries",
65            "openbmc.json",
66        ),
67        "rb",
68    ) as json_file:
69        json_file = json.load(json_file)
70
71    path = os.path.join(include_path, "openbmc_message_registry.hpp")
72    return (path, json_file, "openbmc", url)
73
74
75def update_registries(files):
76    # Remove the old files
77    for file, json_dict, namespace, url in files:
78        try:
79            os.remove(file)
80        except BaseException:
81            print("{} not found".format(file))
82
83        with open(file, "w") as registry:
84
85            version_split = json_dict["RegistryVersion"].split(".")
86
87            registry.write(REGISTRY_HEADER.format(namespace))
88            # Parse the Registry header info
89            registry.write(
90                "const Header header = {{\n"
91                '    "{json_dict[@Redfish.Copyright]}",\n'
92                '    "{json_dict[@odata.type]}",\n'
93                "    {version_split[0]},\n"
94                "    {version_split[1]},\n"
95                "    {version_split[2]},\n"
96                '    "{json_dict[Name]}",\n'
97                '    "{json_dict[Language]}",\n'
98                '    "{json_dict[Description]}",\n'
99                '    "{json_dict[RegistryPrefix]}",\n'
100                '    "{json_dict[OwningEntity]}",\n'
101                "}};\n"
102                "constexpr const char* url =\n"
103                '    "{url}";\n'
104                "\n"
105                "constexpr std::array registry =\n"
106                "{{\n".format(
107                    json_dict=json_dict,
108                    url=url,
109                    version_split=version_split,
110                )
111            )
112
113            messages_sorted = sorted(json_dict["Messages"].items())
114            for messageId, message in messages_sorted:
115                registry.write(
116                    "    MessageEntry{{\n"
117                    '        "{messageId}",\n'
118                    "        {{\n"
119                    '            "{message[Description]}",\n'
120                    '            "{message[Message]}",\n'
121                    '            "{message[MessageSeverity]}",\n'
122                    "            {message[NumberOfArgs]},\n"
123                    "            {{".format(
124                        messageId=messageId, message=message
125                    )
126                )
127                paramTypes = message.get("ParamTypes")
128                if paramTypes:
129                    for paramType in paramTypes:
130                        registry.write(
131                            '\n                "{}",'.format(paramType)
132                        )
133                    registry.write("\n            },\n")
134                else:
135                    registry.write("},\n")
136                registry.write(
137                    '            "{message[Resolution]}",\n'
138                    "        }}}},\n".format(message=message)
139                )
140
141            registry.write("\n};\n\nenum class Index\n{\n")
142            for index, (messageId, message) in enumerate(messages_sorted):
143                messageId = messageId[0].lower() + messageId[1:]
144                registry.write("    {} = {},\n".format(messageId, index))
145            registry.write(
146                "}};\n}} // namespace redfish::registries::{}\n".format(
147                    namespace
148                )
149            )
150
151
152def get_privilege_string_from_list(privilege_list):
153    privilege_string = "{{\n"
154    for privilege_json in privilege_list:
155        privileges = privilege_json["Privilege"]
156        privilege_string += "    {"
157        for privilege in privileges:
158            if privilege == "NoAuth":
159                continue
160            privilege_string += '"'
161            privilege_string += privilege
162            privilege_string += '",\n'
163        if privilege != "NoAuth":
164            privilege_string = privilege_string[:-2]
165        privilege_string += "}"
166        privilege_string += ",\n"
167    privilege_string = privilege_string[:-2]
168    privilege_string += "\n}}"
169    return privilege_string
170
171
172def get_variable_name_for_privilege_set(privilege_list):
173    names = []
174    for privilege_json in privilege_list:
175        privileges = privilege_json["Privilege"]
176        names.append("And".join(privileges))
177    return "Or".join(names)
178
179
180PRIVILEGE_HEADER = (
181    PRAGMA_ONCE
182    + WARNING
183    + """
184#include "privileges.hpp"
185
186#include <array>
187
188// clang-format off
189
190namespace redfish::privileges
191{
192"""
193)
194
195
196def get_response_code(entry_id, entry):
197    codes = {
198        "InternalError": "internal_server_error",
199        "OperationTimeout": "internal_server_error",
200        "PropertyValueResourceConflict": "conflict",
201        "ResourceInUse": "service_unavailable",
202        "ServiceTemporarilyUnavailable": "service_unavailable",
203        "ResourceCannotBeDeleted": "method_not_allowed",
204        "PropertyValueModified": "ok",
205        "InsufficientPrivilege": "forbidden",
206        "AccountForSessionNoLongerExists": "forbidden",
207        "ServiceDisabled": "service_unavailable",
208        "ServiceInUnknownState": "service_unavailable",
209        "EventSubscriptionLimitExceeded": "service_unavailable",
210        "ResourceAtUriUnauthorized": "unauthorized",
211        "SessionTerminated": "ok",
212        "SubscriptionTerminated": "ok",
213        "PropertyNotWritable": "forbidden",
214        "MaximumErrorsExceeded": "internal_server_error",
215        "GeneralError": "internal_server_error",
216        "PreconditionFailed": "precondition_failed",
217        "OperationFailed": "bad_gateway",
218        "ServiceShuttingDown": "service_unavailable",
219        "AccountRemoved": "ok",
220        "PropertyValueExternalConflict": "conflict",
221        "InsufficientStorage": "insufficient_storage",
222        "OperationNotAllowed": "method_not_allowed",
223        "ResourceNotFound": "not_found",
224        "CouldNotEstablishConnection": "not_found",
225        "AccessDenied": "forbidden",
226        "Success": None,
227        "Created": "created",
228        "NoValidSession": "forbidden",
229        "SessionLimitExceeded": "service_unavailable",
230        "ResourceExhaustion": "service_unavailable",
231        "AccountModified": "ok",
232        "PasswordChangeRequired": None,
233        "ResourceInStandby": "service_unavailable",
234        "GenerateSecretKeyRequired": "forbidden",
235    }
236
237    code = codes.get(entry_id, "NOCODE")
238    if code != "NOCODE":
239        return code
240
241    return "bad_request"
242
243
244def make_error_function(
245    entry_id, entry, is_header, registry_name, namespace_name
246):
247    arg_nonstring_types = {
248        "const boost::urls::url_view_base&": {
249            "AccessDenied": [1],
250            "CouldNotEstablishConnection": [1],
251            "GenerateSecretKeyRequired": [1],
252            "InvalidObject": [1],
253            "PasswordChangeRequired": [1],
254            "PropertyValueResourceConflict": [3],
255            "ResetRequired": [1],
256            "ResourceAtUriInUnknownFormat": [1],
257            "ResourceAtUriUnauthorized": [1],
258            "ResourceCreationConflict": [1],
259            "ResourceMissingAtURI": [1],
260            "SourceDoesNotSupportProtocol": [1],
261        },
262        "const nlohmann::json&": {
263            "ActionParameterValueError": [1],
264            "ActionParameterValueFormatError": [1],
265            "ActionParameterValueTypeError": [1],
266            "PropertyValueExternalConflict": [2],
267            "PropertyValueFormatError": [1],
268            "PropertyValueIncorrect": [2],
269            "PropertyValueModified": [2],
270            "PropertyValueNotInList": [1],
271            "PropertyValueOutOfRange": [1],
272            "PropertyValueResourceConflict": [2],
273            "PropertyValueTypeError": [1],
274            "QueryParameterValueFormatError": [1],
275            "QueryParameterValueTypeError": [1],
276        },
277        "uint64_t": {
278            "ArraySizeTooLong": [2],
279            "InvalidIndex": [1],
280            "StringValueTooLong": [2],
281            "TaskProgressChanged": [2],
282        },
283    }
284
285    out = ""
286    args = []
287    argtypes = []
288    for arg_index, arg in enumerate(entry.get("ParamTypes", [])):
289        arg_index += 1
290        typename = "std::string_view"
291        for typestring, entries in arg_nonstring_types.items():
292            if arg_index in entries.get(entry_id, []):
293                typename = typestring
294
295        argtypes.append(typename)
296        args.append(f"{typename} arg{arg_index}")
297    function_name = entry_id[0].lower() + entry_id[1:]
298    arg = ", ".join(args)
299    out += f"nlohmann::json {function_name}({arg})"
300
301    if is_header:
302        out += ";\n\n"
303    else:
304        out += "\n{\n"
305        to_array_type = ""
306        if argtypes:
307            outargs = []
308            for index, typename in enumerate(argtypes):
309                index += 1
310                if typename == "const nlohmann::json&":
311                    out += f"std::string arg{index}Str = arg{index}.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);\n"
312                elif typename == "uint64_t":
313                    out += f"std::string arg{index}Str = std::to_string(arg{index});\n"
314
315            for index, typename in enumerate(argtypes):
316                index += 1
317                if typename == "const boost::urls::url_view_base&":
318                    outargs.append(f"arg{index}.buffer()")
319                    to_array_type = "<std::string_view>"
320                elif typename == "const nlohmann::json&":
321                    outargs.append(f"arg{index}Str")
322                    to_array_type = "<std::string_view>"
323                elif typename == "uint64_t":
324                    outargs.append(f"arg{index}Str")
325                    to_array_type = "<std::string_view>"
326                else:
327                    outargs.append(f"arg{index}")
328            argstring = ", ".join(outargs)
329
330        if argtypes:
331            arg_param = f"std::to_array{to_array_type}({{{argstring}}})"
332        else:
333            arg_param = "{}"
334        out += f"    return getLog(redfish::registries::{namespace_name}::Index::{function_name}, {arg_param});"
335        out += "\n}\n\n"
336    if registry_name == "Base":
337        args.insert(0, "crow::Response& res")
338        if entry_id == "InternalError":
339            if is_header:
340                args.append(
341                    "std::source_location location = std::source_location::current()"
342                )
343            else:
344                args.append("const std::source_location location")
345        arg = ", ".join(args)
346        out += f"void {function_name}({arg})"
347        if is_header:
348            out += ";\n"
349        else:
350            out += "\n{\n"
351            if entry_id == "InternalError":
352                out += """BMCWEB_LOG_CRITICAL("Internal Error {}({}:{}) `{}`: ", location.file_name(),
353                            location.line(), location.column(),
354                            location.function_name());\n"""
355
356            if entry_id == "ServiceTemporarilyUnavailable":
357                out += "res.addHeader(boost::beast::http::field::retry_after, arg1);"
358
359            res = get_response_code(entry_id, entry)
360            if res:
361                out += f"    res.result(boost::beast::http::status::{res});\n"
362            args_out = ", ".join([f"arg{x+1}" for x in range(len(argtypes))])
363
364            addMessageToJson = {
365                "PropertyDuplicate": 1,
366                "ResourceAlreadyExists": 2,
367                "CreateFailedMissingReqProperties": 1,
368                "PropertyValueFormatError": 2,
369                "PropertyValueNotInList": 2,
370                "PropertyValueTypeError": 2,
371                "PropertyValueError": 1,
372                "PropertyNotWritable": 1,
373                "PropertyValueModified": 1,
374                "PropertyMissing": 1,
375            }
376
377            addMessageToRoot = [
378                "SessionTerminated",
379                "SubscriptionTerminated",
380                "AccountRemoved",
381                "Created",
382                "Success",
383                "PasswordChangeRequired",
384            ]
385
386            if entry_id in addMessageToJson:
387                out += f"    addMessageToJson(res.jsonValue, {function_name}({args_out}), arg{addMessageToJson[entry_id]});\n"
388            elif entry_id in addMessageToRoot:
389                out += f"    addMessageToJsonRoot(res.jsonValue, {function_name}({args_out}));\n"
390            else:
391                out += f"    addMessageToErrorJson(res.jsonValue, {function_name}({args_out}));\n"
392            out += "}\n"
393    out += "\n"
394    return out
395
396
397def create_error_registry(
398    entry, registry_version, registry_name, namespace_name, filename
399):
400    file, json_dict, namespace, url = entry
401    base_filename = filename + "_messages"
402
403    error_messages_hpp = os.path.join(
404        SCRIPT_DIR, "..", "redfish-core", "include", f"{base_filename}.hpp"
405    )
406    messages = json_dict["Messages"]
407
408    with open(
409        error_messages_hpp,
410        "w",
411    ) as out:
412        out.write(PRAGMA_ONCE)
413        out.write(WARNING)
414        out.write(
415            """
416
417#include "http_response.hpp"
418
419#include <boost/url/url_view_base.hpp>
420#include <nlohmann/json.hpp>
421
422#include <source_location>
423#include <string_view>
424
425// IWYU pragma: no_forward_declare crow::Response
426
427namespace redfish
428{
429
430namespace messages
431{
432"""
433        )
434        if registry_name == "Base":
435            out.write(
436                f'constexpr const char* messageVersionPrefix = "{registry_name}.{registry_version}.";'
437            )
438            out.write(
439                """
440            constexpr const char* messageAnnotation = "@Message.ExtendedInfo";
441
442            /**
443            * @brief Moves all error messages from the |source| JSON to |target|
444            */
445            void moveErrorsToErrorJson(nlohmann::json& target, nlohmann::json& source);
446
447        """
448            )
449        for entry_id, entry in messages.items():
450            message = entry["Message"]
451            for index in range(1, 10):
452                message = message.replace(f"'%{index}'", f"<arg{index}>")
453                message = message.replace(f"%{index}", f"<arg{index}>")
454
455            if registry_name == "Base":
456                out.write("/**\n")
457                out.write(f"* @brief Formats {entry_id} message into JSON\n")
458                out.write(f'* Message body: "{message}"\n')
459                out.write("*\n")
460                arg_index = 0
461                for arg_index, arg in enumerate(entry.get("ParamTypes", [])):
462                    arg_index += 1
463
464                    out.write(
465                        f"* @param[in] arg{arg_index} Parameter of message that will replace %{arg_index} in its body.\n"
466                    )
467                out.write("*\n")
468                out.write(
469                    f"* @returns Message {entry_id} formatted to JSON */\n"
470                )
471
472            out.write(
473                make_error_function(
474                    entry_id, entry, True, registry_name, namespace_name
475                )
476            )
477        out.write("    }\n")
478        out.write("}\n")
479
480    error_messages_cpp = os.path.join(
481        SCRIPT_DIR, "..", "redfish-core", "src", f"{base_filename}.cpp"
482    )
483    with open(
484        error_messages_cpp,
485        "w",
486    ) as out:
487        out.write(WARNING)
488        out.write(f'\n#include "{base_filename}.hpp"\n')
489        headers = []
490
491        headers.append('"registries.hpp"')
492        if registry_name == "Base":
493            reg_name_lower = "base"
494            headers.append('"http_response.hpp"')
495            headers.append('"logging.hpp"')
496            headers.append("<boost/beast/http/field.hpp>")
497            headers.append("<boost/beast/http/status.hpp>")
498            headers.append("<boost/url/url_view_base.hpp>")
499            headers.append("<source_location>")
500        else:
501            reg_name_lower = namespace_name.lower()
502        headers.append(f'"registries/{reg_name_lower}_message_registry.hpp"')
503
504        headers.append("<nlohmann/json.hpp>")
505        headers.append("<array>")
506        headers.append("<cstddef>")
507        headers.append("<span>")
508
509        if registry_name not in ("ResourceEvent", "HeartbeatEvent"):
510            headers.append("<cstdint>")
511            headers.append("<string>")
512        headers.append("<string_view>")
513
514        for header in headers:
515            out.write(f"#include {header}\n")
516
517        out.write(
518            """
519// Clang can't seem to decide whether this header needs to be included or not,
520// and is inconsistent.  Include it for now
521// NOLINTNEXTLINE(misc-include-cleaner)
522#include <utility>
523
524namespace redfish
525{
526
527namespace messages
528{
529"""
530        )
531
532        if registry_name == "Base":
533            out.write(
534                """
535static void addMessageToErrorJson(nlohmann::json& target,
536                                  const nlohmann::json& message)
537{
538    auto& error = target["error"];
539
540    // If this is the first error message, fill in the information from the
541    // first error message to the top level struct
542    if (!error.is_object())
543    {
544        auto messageIdIterator = message.find("MessageId");
545        if (messageIdIterator == message.end())
546        {
547            BMCWEB_LOG_CRITICAL(
548                "Attempt to add error message without MessageId");
549            return;
550        }
551
552        auto messageFieldIterator = message.find("Message");
553        if (messageFieldIterator == message.end())
554        {
555            BMCWEB_LOG_CRITICAL("Attempt to add error message without Message");
556            return;
557        }
558        error["code"] = *messageIdIterator;
559        error["message"] = *messageFieldIterator;
560    }
561    else
562    {
563        // More than 1 error occurred, so the message has to be generic
564        error["code"] = std::string(messageVersionPrefix) + "GeneralError";
565        error["message"] = "A general error has occurred. See Resolution for "
566                           "information on how to resolve the error.";
567    }
568
569    // This check could technically be done in the default construction
570    // branch above, but because we need the pointer to the extended info field
571    // anyway, it's more efficient to do it here.
572    auto& extendedInfo = error[messages::messageAnnotation];
573    if (!extendedInfo.is_array())
574    {
575        extendedInfo = nlohmann::json::array();
576    }
577
578    extendedInfo.push_back(message);
579}
580
581void moveErrorsToErrorJson(nlohmann::json& target, nlohmann::json& source)
582{
583    if (!source.is_object())
584    {
585        return;
586    }
587    auto errorIt = source.find("error");
588    if (errorIt == source.end())
589    {
590        // caller puts error message in root
591        messages::addMessageToErrorJson(target, source);
592        source.clear();
593        return;
594    }
595    auto extendedInfoIt = errorIt->find(messages::messageAnnotation);
596    if (extendedInfoIt == errorIt->end())
597    {
598        return;
599    }
600    const nlohmann::json::array_t* extendedInfo =
601        (*extendedInfoIt).get_ptr<const nlohmann::json::array_t*>();
602    if (extendedInfo == nullptr)
603    {
604        source.erase(errorIt);
605        return;
606    }
607    for (const nlohmann::json& message : *extendedInfo)
608    {
609        addMessageToErrorJson(target, message);
610    }
611    source.erase(errorIt);
612}
613
614static void addMessageToJsonRoot(nlohmann::json& target,
615                                 const nlohmann::json& message)
616{
617    if (!target[messages::messageAnnotation].is_array())
618    {
619        // Force object to be an array
620        target[messages::messageAnnotation] = nlohmann::json::array();
621    }
622
623    target[messages::messageAnnotation].push_back(message);
624}
625
626static void addMessageToJson(nlohmann::json& target,
627                             const nlohmann::json& message,
628                             std::string_view fieldPath)
629{
630    std::string extendedInfo(fieldPath);
631    extendedInfo += messages::messageAnnotation;
632
633    nlohmann::json& field = target[extendedInfo];
634    if (!field.is_array())
635    {
636        // Force object to be an array
637        field = nlohmann::json::array();
638    }
639
640    // Object exists and it is an array so we can just push in the message
641    field.push_back(message);
642}
643"""
644            )
645        out.write(
646            """
647static nlohmann::json getLog(redfish::registries::{namespace_name}::Index name,
648                             std::span<const std::string_view> args)
649{{
650    size_t index = static_cast<size_t>(name);
651    if (index >= redfish::registries::{namespace_name}::registry.size())
652    {{
653        return {{}};
654    }}
655    return getLogFromRegistry(redfish::registries::{namespace_name}::header,
656                              redfish::registries::{namespace_name}::registry, index, args);
657}}
658
659""".format(
660                namespace_name=namespace_name
661            )
662        )
663        for entry_id, entry in messages.items():
664            out.write(
665                f"""/**
666 * @internal
667 * @brief Formats {entry_id} message into JSON
668 *
669 * See header file for more information
670 * @endinternal
671 */
672"""
673            )
674            message = entry["Message"]
675            out.write(
676                make_error_function(
677                    entry_id, entry, False, registry_name, namespace_name
678                )
679            )
680
681        out.write("    }\n")
682        out.write("}\n")
683    os.system(f"clang-format -i {error_messages_hpp} {error_messages_cpp}")
684
685
686def make_privilege_registry():
687    path, json_file, type_name, url = make_getter(
688        "Redfish_1.5.0_PrivilegeRegistry.json",
689        "privilege_registry.hpp",
690        "privilege",
691    )
692    with open(path, "w") as registry:
693        registry.write(PRIVILEGE_HEADER)
694
695        privilege_dict = {}
696        for mapping in json_file["Mappings"]:
697            # first pass, identify all the unique privilege sets
698            for operation, privilege_list in mapping["OperationMap"].items():
699                privilege_dict[
700                    get_privilege_string_from_list(privilege_list)
701                ] = (privilege_list,)
702        for index, key in enumerate(privilege_dict):
703            (privilege_list,) = privilege_dict[key]
704            name = get_variable_name_for_privilege_set(privilege_list)
705            registry.write(
706                "const std::array<Privileges, {length}> "
707                "privilegeSet{name} = {key};\n".format(
708                    length=len(privilege_list), name=name, key=key
709                )
710            )
711            privilege_dict[key] = (privilege_list, name)
712
713        for mapping in json_file["Mappings"]:
714            entity = mapping["Entity"]
715            registry.write("// {}\n".format(entity))
716            for operation, privilege_list in mapping["OperationMap"].items():
717                privilege_string = get_privilege_string_from_list(
718                    privilege_list
719                )
720                operation = operation.lower()
721
722                registry.write(
723                    "const static auto& {}{} = privilegeSet{};\n".format(
724                        operation, entity, privilege_dict[privilege_string][1]
725                    )
726                )
727            registry.write("\n")
728        registry.write(
729            "} // namespace redfish::privileges\n// clang-format on\n"
730        )
731
732
733def to_pascal_case(text):
734    s = text.replace("_", " ")
735    s = s.split()
736    if len(text) == 0:
737        return text
738    return "".join(i.capitalize() for i in s[0:])
739
740
741def main():
742    dmtf_registries = (
743        ("base", "1.19.0"),
744        ("composition", "1.1.2"),
745        ("environmental", "1.0.1"),
746        ("ethernet_fabric", "1.0.1"),
747        ("fabric", "1.0.2"),
748        ("heartbeat_event", "1.0.1"),
749        ("job_event", "1.0.1"),
750        ("license", "1.0.3"),
751        ("log_service", "1.0.1"),
752        ("network_device", "1.0.3"),
753        ("platform", "1.0.1"),
754        ("power", "1.0.1"),
755        ("resource_event", "1.3.0"),
756        ("sensor_event", "1.0.1"),
757        ("storage_device", "1.2.1"),
758        ("task_event", "1.0.3"),
759        ("telemetry", "1.0.0"),
760        ("update", "1.0.2"),
761    )
762
763    parser = argparse.ArgumentParser()
764    parser.add_argument(
765        "--registries",
766        type=str,
767        default="privilege,openbmc,"
768        + ",".join([dmtf[0] for dmtf in dmtf_registries]),
769        help="Comma delimited list of registries to update",
770    )
771
772    args = parser.parse_args()
773
774    registries = set(args.registries.split(","))
775    files = []
776
777    for registry, version in dmtf_registries:
778        if registry in registries:
779            registry_pascal_case = to_pascal_case(registry)
780            files.append(
781                make_getter(
782                    f"{registry_pascal_case}.{version}.json",
783                    f"{registry}_message_registry.hpp",
784                    registry,
785                )
786            )
787    if "openbmc" in registries:
788        files.append(openbmc_local_getter())
789
790    update_registries(files)
791
792    create_error_registry(
793        files[0], dmtf_registries[0][1], "Base", "base", "error"
794    )
795    create_error_registry(
796        files[5],
797        dmtf_registries[5][1],
798        "HeartbeatEvent",
799        "heartbeat_event",
800        "heartbeat",
801    )
802    create_error_registry(
803        files[12],
804        dmtf_registries[12][1],
805        "ResourceEvent",
806        "resource_event",
807        "resource",
808    )
809    create_error_registry(
810        files[15], dmtf_registries[15][1], "TaskEvent", "task_event", "task"
811    )
812
813    if "privilege" in registries:
814        make_privilege_registry()
815
816
817if __name__ == "__main__":
818    main()
819