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