sdbus++: events: generate Redfish registry

Create an initial Redfish Message Registry from an events.yaml
file.  This includes a few OEM properties under "OpenBMC_Mapping"
in order to give hints to bmcweb on how to do:
    - Map OpenBMC Events to Redfish Events.
    - Map AdditionalData fields to MessageArgs.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I7260c2344a8509bbcad7a6653ccf2b3578427e45
diff --git a/tools/sdbusplus/event.py b/tools/sdbusplus/event.py
index 03137c5..2c8d935 100644
--- a/tools/sdbusplus/event.py
+++ b/tools/sdbusplus/event.py
@@ -1,3 +1,6 @@
+import datetime
+import itertools
+import json
 import os
 
 import jsonschema
@@ -41,9 +44,10 @@
         self.metadata = [
             EventMetadata(**n) for n in kwargs.pop("metadata", [])
         ]
-        self.severity = EventElement.syslog_severity(
-            kwargs.pop("severity", "informational")
-        )
+
+        self.severity = kwargs.pop("severity", "informational")
+        self.syslog_sev = EventElement.syslog_severity(self.severity)
+        self.registry_sev = EventElement.registry_severity(self.severity)
 
         super(EventElement, self).__init__(**kwargs)
 
@@ -53,6 +57,55 @@
             includes.extend(m.enum_headers(interface))
         return sorted(set(includes))
 
+    def registry_event(self, interface, language):
+        if language not in self.languages:
+            language = "en"
+        language_data = self.languages[language]
+
+        args = [x for x in self.metadata if x.primary]
+
+        for i, arg in enumerate(args):
+            language_data.message = language_data.message.replace(
+                f"{{{arg.name}}}", f"%{i+1}"
+            )
+
+        result = {}
+        if language_data.description:
+            result["Description"] = language_data.description
+        result["Message"] = language_data.message
+        if language_data.resolution:
+            result["Resolution"] = language_data.resolution
+
+        result["MessageSeverity"] = self.registry_sev
+
+        result["NumberOfArgs"] = len(args)
+        result["ParamTypes"] = [x.registry_type() for x in args]
+
+        result["OEM"] = {
+            "OpenBMC_Mapping": {
+                "Event": interface + "." + self.name,
+                "Args": self.registry_args_mapping(interface),
+            }
+        }
+
+        return result
+
+    def registry_mapping(self, interface):
+        return {
+            "RedfishEvent": self.redfish_map,
+            "Args": self.registry_args_mapping(interface),
+        }
+
+    def registry_args_mapping(self, interface):
+        args = [x for x in self.metadata if x.primary]
+        return [
+            {
+                "Name": x.SNAKE_CASE,
+                "Type": x.cppTypeParam(interface, full=True),
+            }
+            for x in args
+        ]
+
     def __getattribute__(self, name):
         lam = {"description": lambda: self.__description()}.get(name)
 
@@ -85,6 +138,19 @@
             "debug": "LOG_DEBUG",
         }[severity]
 
+    @staticmethod
+    def registry_severity(severity: str) -> str:
+        return {
+            "emergency": "Critical",
+            "alert": "Critical",
+            "critical": "Critical",
+            "error": "Warning",
+            "warning": "Warning",
+            "notice": "Warning",
+            "informational": "OK",
+            "debug": "OK",
+        }[severity]
+
 
 class Event(NamedElement, Renderer):
     @staticmethod
@@ -137,3 +203,35 @@
 
     def exception_cpp(self, loader):
         return self.render(loader, "events.cpp.mako", events=self)
+
+    def exception_registry(self, loader):
+        language = "en"
+        registryName = self.registryPrefix("OpenBMC")
+
+        messages = {}
+        mappings = {}
+
+        for e in itertools.chain(self.errors, self.events):
+            if e.redfish_map:
+                mappings[self.name + "." + e.name] = e.registry_mapping(
+                    self.name
+                )
+            else:
+                messages[e.name] = e.registry_event(self.name, language)
+
+        result = {
+            "@Redfish.Copyright": f"Copyright 2024-{datetime.date.today().year} OpenBMC.",
+            "@odata.type": "#MessageRegistry.v1_6_3.MessageRegistry",
+            "Id": f"{registryName}.{self.version}",
+            "Language": f"{language}",
+            "Message": messages,
+            "Name": f"OpenBMC Message Registry for {self.name}",
+            "OwningEntity": "OpenBMC",
+            "RegistryPrefix": f"{registryName}",
+            "RegistryVersion": f"{self.version}",
+        }
+
+        if len(mappings) != 0:
+            result["OEM"] = ({"OpenBMC_Mapping": mappings},)
+
+        return json.dumps(result, indent=4)
diff --git a/tools/sdbusplus/main.py b/tools/sdbusplus/main.py
index ac66f99..15a94ce 100644
--- a/tools/sdbusplus/main.py
+++ b/tools/sdbusplus/main.py
@@ -20,6 +20,7 @@
         "common-header": "common_header",
         "exception-cpp": "exception_cpp",
         "exception-header": "exception_header",
+        "exception-registry": "exception_registry",
         "markdown": "markdown",
         "server-cpp": "server_cpp",
         "server-header": "server_header",
diff --git a/tools/sdbusplus/namedelement.py b/tools/sdbusplus/namedelement.py
index 87cb86d..c8afb31 100644
--- a/tools/sdbusplus/namedelement.py
+++ b/tools/sdbusplus/namedelement.py
@@ -55,6 +55,15 @@
     def cppNamespacedClass(self):
         return self.cppNamespace() + "::" + self.classname
 
+    def registryPrefix(self, root_prefix):
+        name = inflection.camelize(
+            self.name.replace("xyz.openbmc_project.", "")
+            .replace("com.", "")
+            .replace(".", "_"),
+            uppercase_first_letter=True,
+        )
+        return root_prefix + "_" + name
+
     """ Some names are reserved in some languages.  Fixup names to avoid using
         reserved words.
     """
diff --git a/tools/sdbusplus/property.py b/tools/sdbusplus/property.py
index 898aa28..4d79cb6 100644
--- a/tools/sdbusplus/property.py
+++ b/tools/sdbusplus/property.py
@@ -202,47 +202,77 @@
 
         return result
 
+    propertyMap = {
+        "byte": {"cppName": "uint8_t", "params": 0},
+        "boolean": {"cppName": "bool", "params": 0},
+        "int16": {"cppName": "int16_t", "params": 0},
+        "uint16": {"cppName": "uint16_t", "params": 0},
+        "int32": {"cppName": "int32_t", "params": 0},
+        "uint32": {"cppName": "uint32_t", "params": 0},
+        "int64": {
+            "cppName": "int64_t",
+            "params": 0,
+            "registryName": "number",
+        },
+        "uint64": {
+            "cppName": "uint64_t",
+            "params": 0,
+            "registryName": "number",
+        },
+        "size": {
+            "cppName": "size_t",
+            "params": 0,
+            "registryName": "number",
+        },
+        "ssize": {"cppName": "ssize_t", "params": 0},
+        "double": {
+            "cppName": "double",
+            "params": 0,
+            "registryName": "number",
+        },
+        "unixfd": {"cppName": "sdbusplus::message::unix_fd", "params": 0},
+        "string": {
+            "cppName": "std::string",
+            "params": 0,
+            "registryName": "string",
+        },
+        "object_path": {
+            "cppName": "sdbusplus::message::object_path",
+            "params": 0,
+            "registryName": "string",
+        },
+        "signature": {
+            "cppName": "sdbusplus::message::signature",
+            "params": 0,
+        },
+        "array": {"cppName": "std::vector", "params": 1},
+        "set": {"cppName": "std::set", "params": 1},
+        "struct": {"cppName": "std::tuple", "params": -1},
+        "variant": {"cppName": "std::variant", "params": -1},
+        "dict": {"cppName": "std::map", "params": 2},
+        "enum": {
+            "cppName": "enum",
+            "params": 1,
+            "registryName": "number",
+        },
+    }
+
+    """ Get the registry type of the property. """
+
+    def registry_type(self):
+        return Property.propertyMap[self.typeName]["registryName"]
+
     """ Take a list of dbus types and perform validity checking, such as:
             [ variant [ dict [ int32, int32 ], double ] ]
         This function then converts the type-list into a C++ type string.
     """
 
     def __parse_cpp_type__(self, typeTuple):
-        propertyMap = {
-            "byte": {"cppName": "uint8_t", "params": 0},
-            "boolean": {"cppName": "bool", "params": 0},
-            "int16": {"cppName": "int16_t", "params": 0},
-            "uint16": {"cppName": "uint16_t", "params": 0},
-            "int32": {"cppName": "int32_t", "params": 0},
-            "uint32": {"cppName": "uint32_t", "params": 0},
-            "int64": {"cppName": "int64_t", "params": 0},
-            "uint64": {"cppName": "uint64_t", "params": 0},
-            "size": {"cppName": "size_t", "params": 0},
-            "ssize": {"cppName": "ssize_t", "params": 0},
-            "double": {"cppName": "double", "params": 0},
-            "unixfd": {"cppName": "sdbusplus::message::unix_fd", "params": 0},
-            "string": {"cppName": "std::string", "params": 0},
-            "object_path": {
-                "cppName": "sdbusplus::message::object_path",
-                "params": 0,
-            },
-            "signature": {
-                "cppName": "sdbusplus::message::signature",
-                "params": 0,
-            },
-            "array": {"cppName": "std::vector", "params": 1},
-            "set": {"cppName": "std::set", "params": 1},
-            "struct": {"cppName": "std::tuple", "params": -1},
-            "variant": {"cppName": "std::variant", "params": -1},
-            "dict": {"cppName": "std::map", "params": 2},
-            "enum": {"cppName": "enum", "params": 1},
-        }
-
         if len(typeTuple) != 2:
             raise RuntimeError("Invalid typeTuple %s" % typeTuple)
 
         first = typeTuple[0]
-        entry = propertyMap[first]
+        entry = Property.propertyMap[first]
 
         result = entry["cppName"]
 
diff --git a/tools/sdbusplus/templates/event.hpp.mako b/tools/sdbusplus/templates/event.hpp.mako
index 8cf6e31..21171ea 100644
--- a/tools/sdbusplus/templates/event.hpp.mako
+++ b/tools/sdbusplus/templates/event.hpp.mako
@@ -43,7 +43,7 @@
         "${event.description}";
     static constexpr auto errWhat =
         "${events.name}.${event.name}: ${event.description}";
-    static constexpr int errSeverity = ${event.severity};
+    static constexpr int errSeverity = ${event.syslog_sev};
 
     static constexpr auto errErrno = ${event.errno};
 };