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 <cstdint>
423#include <source_location>
424#include <string>
425#include <string_view>
426
427// IWYU pragma: no_forward_declare crow::Response
428
429namespace redfish
430{
431
432namespace messages
433{
434"""
435        )
436        if registry_name == "Base":
437            out.write(
438                f'constexpr const char* messageVersionPrefix = "{registry_name}.{registry_version}.";'
439            )
440            out.write(
441                """
442            constexpr const char* messageAnnotation = "@Message.ExtendedInfo";
443
444            /**
445            * @brief Moves all error messages from the |source| JSON to |target|
446            */
447            void moveErrorsToErrorJson(nlohmann::json& target, nlohmann::json& source);
448
449        """
450            )
451        for entry_id, entry in messages.items():
452            message = entry["Message"]
453            for index in range(1, 10):
454                message = message.replace(f"'%{index}'", f"<arg{index}>")
455                message = message.replace(f"%{index}", f"<arg{index}>")
456
457            if registry_name == "Base":
458                out.write("/**\n")
459                out.write(f"* @brief Formats {entry_id} message into JSON\n")
460                out.write(f'* Message body: "{message}"\n')
461                out.write("*\n")
462                arg_index = 0
463                for arg_index, arg in enumerate(entry.get("ParamTypes", [])):
464                    arg_index += 1
465
466                    out.write(
467                        f"* @param[in] arg{arg_index} Parameter of message that will replace %{arg_index} in its body.\n"
468                    )
469                out.write("*\n")
470                out.write(
471                    f"* @returns Message {entry_id} formatted to JSON */\n"
472                )
473
474            out.write(
475                make_error_function(
476                    entry_id, entry, True, registry_name, namespace_name
477                )
478            )
479        out.write("    }\n")
480        out.write("}\n")
481
482    error_messages_cpp = os.path.join(
483        SCRIPT_DIR, "..", "redfish-core", "src", f"{base_filename}.cpp"
484    )
485    with open(
486        error_messages_cpp,
487        "w",
488    ) as out:
489        out.write(WARNING)
490        out.write(f'\n#include "{base_filename}.hpp"\n')
491        headers = []
492
493        headers.append('"registries.hpp"')
494        if registry_name == "Base":
495            reg_name_lower = "base"
496            headers.append('"http_response.hpp"')
497            headers.append('"logging.hpp"')
498            headers.append("<boost/beast/http/field.hpp>")
499            headers.append("<boost/beast/http/status.hpp>")
500            headers.append("<boost/url/url_view_base.hpp>")
501            headers.append("<source_location>")
502        else:
503            reg_name_lower = namespace_name.lower()
504        headers.append(f'"registries/{reg_name_lower}_message_registry.hpp"')
505
506        headers.append("<nlohmann/json.hpp>")
507        headers.append("<array>")
508        headers.append("<cstddef>")
509        headers.append("<span>")
510
511        if registry_name != "ResourceEvent":
512            headers.append("<cstdint>")
513            headers.append("<string>")
514        headers.append("<string_view>")
515
516        for header in headers:
517            out.write(f"#include {header}\n")
518
519        out.write(
520            """
521// Clang can't seem to decide whether this header needs to be included or not,
522// and is inconsistent.  Include it for now
523// NOLINTNEXTLINE(misc-include-cleaner)
524#include <utility>
525
526namespace redfish
527{
528
529namespace messages
530{
531"""
532        )
533
534        if registry_name == "Base":
535            out.write(
536                """
537static void addMessageToErrorJson(nlohmann::json& target,
538                                  const nlohmann::json& message)
539{
540    auto& error = target["error"];
541
542    // If this is the first error message, fill in the information from the
543    // first error message to the top level struct
544    if (!error.is_object())
545    {
546        auto messageIdIterator = message.find("MessageId");
547        if (messageIdIterator == message.end())
548        {
549            BMCWEB_LOG_CRITICAL(
550                "Attempt to add error message without MessageId");
551            return;
552        }
553
554        auto messageFieldIterator = message.find("Message");
555        if (messageFieldIterator == message.end())
556        {
557            BMCWEB_LOG_CRITICAL("Attempt to add error message without Message");
558            return;
559        }
560        error["code"] = *messageIdIterator;
561        error["message"] = *messageFieldIterator;
562    }
563    else
564    {
565        // More than 1 error occurred, so the message has to be generic
566        error["code"] = std::string(messageVersionPrefix) + "GeneralError";
567        error["message"] = "A general error has occurred. See Resolution for "
568                           "information on how to resolve the error.";
569    }
570
571    // This check could technically be done in the default construction
572    // branch above, but because we need the pointer to the extended info field
573    // anyway, it's more efficient to do it here.
574    auto& extendedInfo = error[messages::messageAnnotation];
575    if (!extendedInfo.is_array())
576    {
577        extendedInfo = nlohmann::json::array();
578    }
579
580    extendedInfo.push_back(message);
581}
582
583void moveErrorsToErrorJson(nlohmann::json& target, nlohmann::json& source)
584{
585    if (!source.is_object())
586    {
587        return;
588    }
589    auto errorIt = source.find("error");
590    if (errorIt == source.end())
591    {
592        // caller puts error message in root
593        messages::addMessageToErrorJson(target, source);
594        source.clear();
595        return;
596    }
597    auto extendedInfoIt = errorIt->find(messages::messageAnnotation);
598    if (extendedInfoIt == errorIt->end())
599    {
600        return;
601    }
602    const nlohmann::json::array_t* extendedInfo =
603        (*extendedInfoIt).get_ptr<const nlohmann::json::array_t*>();
604    if (extendedInfo == nullptr)
605    {
606        source.erase(errorIt);
607        return;
608    }
609    for (const nlohmann::json& message : *extendedInfo)
610    {
611        addMessageToErrorJson(target, message);
612    }
613    source.erase(errorIt);
614}
615
616static void addMessageToJsonRoot(nlohmann::json& target,
617                                 const nlohmann::json& message)
618{
619    if (!target[messages::messageAnnotation].is_array())
620    {
621        // Force object to be an array
622        target[messages::messageAnnotation] = nlohmann::json::array();
623    }
624
625    target[messages::messageAnnotation].push_back(message);
626}
627
628static void addMessageToJson(nlohmann::json& target,
629                             const nlohmann::json& message,
630                             std::string_view fieldPath)
631{
632    std::string extendedInfo(fieldPath);
633    extendedInfo += messages::messageAnnotation;
634
635    nlohmann::json& field = target[extendedInfo];
636    if (!field.is_array())
637    {
638        // Force object to be an array
639        field = nlohmann::json::array();
640    }
641
642    // Object exists and it is an array so we can just push in the message
643    field.push_back(message);
644}
645"""
646            )
647        out.write(
648            """
649static nlohmann::json getLog(redfish::registries::{namespace_name}::Index name,
650                             std::span<const std::string_view> args)
651{{
652    size_t index = static_cast<size_t>(name);
653    if (index >= redfish::registries::{namespace_name}::registry.size())
654    {{
655        return {{}};
656    }}
657    return getLogFromRegistry(redfish::registries::{namespace_name}::header,
658                              redfish::registries::{namespace_name}::registry, index, args);
659}}
660
661""".format(
662                namespace_name=namespace_name
663            )
664        )
665        for entry_id, entry in messages.items():
666            out.write(
667                f"""/**
668 * @internal
669 * @brief Formats {entry_id} message into JSON
670 *
671 * See header file for more information
672 * @endinternal
673 */
674"""
675            )
676            message = entry["Message"]
677            out.write(
678                make_error_function(
679                    entry_id, entry, False, registry_name, namespace_name
680                )
681            )
682
683        out.write("    }\n")
684        out.write("}\n")
685    os.system(f"clang-format -i {error_messages_hpp} {error_messages_cpp}")
686
687
688def make_privilege_registry():
689    path, json_file, type_name, url = make_getter(
690        "Redfish_1.5.0_PrivilegeRegistry.json",
691        "privilege_registry.hpp",
692        "privilege",
693    )
694    with open(path, "w") as registry:
695        registry.write(PRIVILEGE_HEADER)
696
697        privilege_dict = {}
698        for mapping in json_file["Mappings"]:
699            # first pass, identify all the unique privilege sets
700            for operation, privilege_list in mapping["OperationMap"].items():
701                privilege_dict[
702                    get_privilege_string_from_list(privilege_list)
703                ] = (privilege_list,)
704        for index, key in enumerate(privilege_dict):
705            (privilege_list,) = privilege_dict[key]
706            name = get_variable_name_for_privilege_set(privilege_list)
707            registry.write(
708                "const std::array<Privileges, {length}> "
709                "privilegeSet{name} = {key};\n".format(
710                    length=len(privilege_list), name=name, key=key
711                )
712            )
713            privilege_dict[key] = (privilege_list, name)
714
715        for mapping in json_file["Mappings"]:
716            entity = mapping["Entity"]
717            registry.write("// {}\n".format(entity))
718            for operation, privilege_list in mapping["OperationMap"].items():
719                privilege_string = get_privilege_string_from_list(
720                    privilege_list
721                )
722                operation = operation.lower()
723
724                registry.write(
725                    "const static auto& {}{} = privilegeSet{};\n".format(
726                        operation, entity, privilege_dict[privilege_string][1]
727                    )
728                )
729            registry.write("\n")
730        registry.write(
731            "} // namespace redfish::privileges\n// clang-format on\n"
732        )
733
734
735def to_pascal_case(text):
736    s = text.replace("_", " ")
737    s = s.split()
738    if len(text) == 0:
739        return text
740    return "".join(i.capitalize() for i in s[0:])
741
742
743def main():
744    dmtf_registries = (
745        ("base", "1.19.0"),
746        ("composition", "1.1.2"),
747        ("environmental", "1.0.1"),
748        ("ethernet_fabric", "1.0.1"),
749        ("fabric", "1.0.2"),
750        ("heartbeat_event", "1.0.1"),
751        ("job_event", "1.0.1"),
752        ("license", "1.0.3"),
753        ("log_service", "1.0.1"),
754        ("network_device", "1.0.3"),
755        ("platform", "1.0.1"),
756        ("power", "1.0.1"),
757        ("resource_event", "1.3.0"),
758        ("sensor_event", "1.0.1"),
759        ("storage_device", "1.2.1"),
760        ("task_event", "1.0.3"),
761        ("telemetry", "1.0.0"),
762        ("update", "1.0.2"),
763    )
764
765    parser = argparse.ArgumentParser()
766    parser.add_argument(
767        "--registries",
768        type=str,
769        default="privilege,openbmc,"
770        + ",".join([dmtf[0] for dmtf in dmtf_registries]),
771        help="Comma delimited list of registries to update",
772    )
773
774    args = parser.parse_args()
775
776    registries = set(args.registries.split(","))
777    files = []
778
779    for registry, version in dmtf_registries:
780        if registry in registries:
781            registry_pascal_case = to_pascal_case(registry)
782            files.append(
783                make_getter(
784                    f"{registry_pascal_case}.{version}.json",
785                    f"{registry}_message_registry.hpp",
786                    registry,
787                )
788            )
789    if "openbmc" in registries:
790        files.append(openbmc_local_getter())
791
792    update_registries(files)
793
794    create_error_registry(
795        files[0], dmtf_registries[0][1], "Base", "base", "error"
796    )
797    create_error_registry(
798        files[12],
799        dmtf_registries[12][1],
800        "ResourceEvent",
801        "resource_event",
802        "resource",
803    )
804    create_error_registry(
805        files[15], dmtf_registries[15][1], "TaskEvent", "task_event", "task"
806    )
807
808    if "privilege" in registries:
809        make_privilege_registry()
810
811
812if __name__ == "__main__":
813    main()
814