tools: add gen-eventfilter.py

Generate event filtering code for lg2::commit from an input json
file, so that system integrators can create default filtering rules
to omit undesired events and errors from their systems.

Tested:

Used `log-create` to create an event and observed it.  Modified the
default event filter as follows and saw log was not created.

```
@@ -3,6 +3,9 @@
         "default": "allowed"
     },
     "errors": {
-        "default": "allowed"
+        "default": "allowed",
+        "ids": [
+            "xyz.openbmc_project.State.SMC.SMCFailed"
+        ]
     }
 }
```

```
$ ./builddir/log-create xyz.openbmc_project.State.SMC.SMCFailed --json '{ "IDENTIFIER": "/xyz/openbmc_project/inventory/SomeSMC", "FAILURE_TYPE": "Timeout for the SMC" }'
```

Change-Id: Ib6041481075758b994bb27a816dbf5e9f26c2841
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/tools/gen-eventfilter.py b/tools/gen-eventfilter.py
new file mode 100755
index 0000000..56151d6
--- /dev/null
+++ b/tools/gen-eventfilter.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+
+import argparse
+import json
+import os
+import sys
+
+import jsonschema
+from mako.template import Template
+
+import yaml
+
+# Determine the script's directory to find the schema file relative to it.
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+SCHEMA_FILE = os.path.join(
+    SCRIPT_DIR, "phosphor-logging", "schemas", "eventfilter.schema.yaml"
+)
+TEMPLATE_FILE = os.path.join(
+    SCRIPT_DIR, "phosphor-logging", "templates", "event-filter.cpp.mako"
+)
+
+
+def main() -> int:
+    """
+    Validates a JSON filter file against the eventfilter schema.
+    """
+    parser = argparse.ArgumentParser(
+        description="Validate an event filter JSON file against the schema."
+    )
+    parser.add_argument(
+        "filter",
+        type=str,
+        help="Path to the JSON filter file to validate.",
+    )
+    args = parser.parse_args()
+
+    with open(args.filter, "r") as f:
+        filter_data = json.load(f)
+
+    with open(SCHEMA_FILE, "r") as f:
+        schema_data = yaml.safe_load(f)
+
+    jsonschema.validate(instance=filter_data, schema=schema_data)
+
+    template = Template(filename=TEMPLATE_FILE)
+    output = template.render(data=filter_data)
+    print(output)
+
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/tools/meson.build b/tools/meson.build
index bb08ffb..fcde0bf 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -1,4 +1,7 @@
 tool_dir = meson.current_source_dir()
 
 elog_gen = files('elog-gen.py')
+eventfilter_gen = files('gen-eventfilter.py')
+default_eventfilter_json = files('phosphor-logging/default-eventfilter.json')
+
 subdir('phosphor-logging/templates')
diff --git a/tools/phosphor-logging/default-eventfilter.json b/tools/phosphor-logging/default-eventfilter.json
new file mode 100644
index 0000000..501ace6
--- /dev/null
+++ b/tools/phosphor-logging/default-eventfilter.json
@@ -0,0 +1,8 @@
+{
+    "events": {
+        "default": "allowed"
+    },
+    "errors": {
+        "default": "allowed"
+    }
+}
diff --git a/tools/phosphor-logging/schemas/eventfilter.schema.yaml b/tools/phosphor-logging/schemas/eventfilter.schema.yaml
new file mode 100644
index 0000000..aa7e511
--- /dev/null
+++ b/tools/phosphor-logging/schemas/eventfilter.schema.yaml
@@ -0,0 +1,36 @@
+$id: "http://openbmc-project.xyz/phosphor-logging/eventdefault.yaml"
+$schema: "https://json-schema.org/draft/2020-12/schema"
+title: "Event and Error defaults"
+
+$defs:
+    base-event:
+        type: object
+        properties:
+            default:
+                description:
+                    The default setting for any event not specified in ids.
+                type: string
+                enum:
+                    - "blocked"
+                    - "allowed"
+            ids:
+                description: The list of ids to do the opposite of 'default'.
+                type: array
+                items:
+                    type: string
+                    description: Event ids from phosphor-dbus-interfaces.
+                    pattern: "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$"
+        required:
+            - default
+        additionalProperties: false
+
+type: object
+properties:
+    events:
+        $ref: "#/$defs/base-event"
+    errors:
+        $ref: "#/$defs/base-event"
+required:
+    - events
+    - errors
+additionalProperties: false
diff --git a/tools/phosphor-logging/templates/event-filter.cpp.mako b/tools/phosphor-logging/templates/event-filter.cpp.mako
new file mode 100644
index 0000000..0a4a4ef
--- /dev/null
+++ b/tools/phosphor-logging/templates/event-filter.cpp.mako
@@ -0,0 +1,62 @@
+## Note that this file is not auto generated, it is what generates the
+## lg2_eventfilter.cpp file
+// This file was autogenerated.  Do not edit!
+
+#include <string>
+#include <unordered_set>
+
+namespace lg2
+{
+namespace details
+{
+
+bool filterEvent([[maybe_unused]] const std::string& id)
+{
+    static constexpr bool default_return = \
+        % if data['events']['default'] == 'allowed':
+false;
+        % else:
+true;
+        % endif
+    % if "ids" in data['events'] and len(data['events']['ids']) != 0:
+    static const std::unordered_set<std::string> event_ids = {
+        % for item in data['events']['ids']:
+        "${item}",
+        % endfor
+    };
+
+    if (event_ids.contains(id))
+    {
+        return !default_return;
+    }
+    % endif
+
+    return default_return;
+}
+
+bool filterError([[maybe_unused]] const std::string& id)
+{
+    static constexpr bool default_return = \
+        % if data['errors']['default'] == 'allowed':
+false;
+        % else:
+true;
+        % endif
+    % if "ids" in data['errors'] and len(data['errors']['ids']) != 0:
+    static const std::unordered_set<std::string> error_ids = {
+        % for item in data['errors']['ids']:
+        "${item}",
+        % endfor
+    };
+
+    if (error_ids.contains(id))
+    {
+        return !default_return;
+    }
+    % endif
+
+    return default_return;
+}
+
+} // namespace details
+} // namespace lg2
diff --git a/tools/phosphor-logging/templates/meson.build b/tools/phosphor-logging/templates/meson.build
index e713aaf..ee9e24c 100644
--- a/tools/phosphor-logging/templates/meson.build
+++ b/tools/phosphor-logging/templates/meson.build
@@ -1 +1,2 @@
 template_elog_gen = files('elog-gen-template.hpp.mako')
+template_eventfilter_gen = files('event-filter.cpp.mako')