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