Add extra subscription params support
OriginResource allows filtering messages on a per-device basis. This
was already listed as supported in our docs.
RegistryPrefixes is also added.
Tested: Unit tests pass.
Change-Id: Idfde8416f2f466ce11957177e052b540fc669888
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/Redfish.md b/Redfish.md
index 9c6d153..2ffaa5e 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -479,6 +479,7 @@
- EventTypes
- Context
- OriginResources
+- RegistryPrefixes
- Protocol
### /redfish/v1/JsonSchemas/
diff --git a/include/event_service_store.hpp b/include/event_service_store.hpp
index 8501fff..9761eb5 100644
--- a/include/event_service_store.hpp
+++ b/include/event_service_store.hpp
@@ -25,6 +25,7 @@
std::vector<std::string> resourceTypes;
boost::beast::http::fields httpHeaders;
std::vector<std::string> metricReportDefinitions;
+ std::vector<std::string> originResources;
static std::shared_ptr<UserSubscription> fromJson(
const nlohmann::json::object_t& j, const bool loadFromOldConfig = false)
@@ -215,6 +216,25 @@
subvalue->metricReportDefinitions.emplace_back(*value);
}
}
+ else if (element.first == "OriginResources")
+ {
+ const nlohmann::json::array_t* obj =
+ element.second.get_ptr<const nlohmann::json::array_t*>();
+ if (obj == nullptr)
+ {
+ continue;
+ }
+ for (const auto& val : *obj)
+ {
+ const std::string* value =
+ val.get_ptr<const std::string*>();
+ if (value == nullptr)
+ {
+ continue;
+ }
+ subvalue->originResources.emplace_back(*value);
+ }
+ }
else
{
BMCWEB_LOG_ERROR(
diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp
index 0f14143..bcecf9f 100644
--- a/include/persistent_data.hpp
+++ b/include/persistent_data.hpp
@@ -297,6 +297,7 @@
subscription["MessageIds"] = subValue.registryMsgIds;
subscription["Protocol"] = subValue.protocol;
subscription["RegistryPrefixes"] = subValue.registryPrefixes;
+ subscription["OriginResources"] = subValue.originResources;
subscription["ResourceTypes"] = subValue.resourceTypes;
subscription["SubscriptionType"] = subValue.subscriptionType;
subscription["MetricReportDefinitions"] =
diff --git a/meson.build b/meson.build
index b10bc2a..299ce55 100644
--- a/meson.build
+++ b/meson.build
@@ -414,6 +414,7 @@
'test/include/ossl_random.cpp',
'test/include/ssl_key_handler_test.cpp',
'test/include/str_utility_test.cpp',
+ 'test/redfish-core/include/event_service_manager_test.cpp',
'test/redfish-core/include/filter_expr_executor_test.cpp',
'test/redfish-core/include/filter_expr_parser_test.cpp',
'test/redfish-core/include/privileges_test.cpp',
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
index 6a936cb..8fd0157 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -43,10 +43,12 @@
#include <algorithm>
#include <cstdlib>
#include <ctime>
+#include <format>
#include <fstream>
#include <memory>
#include <ranges>
#include <span>
+#include <string>
namespace redfish
{
@@ -336,6 +338,60 @@
resType);
}
+ // If registryPrefixes list is empty, don't filter events
+ // send everything.
+ if (!registryPrefixes.empty())
+ {
+ auto eventJson = eventMessage.find("MessageId");
+ if (eventJson == eventMessage.end())
+ {
+ return false;
+ }
+
+ const std::string* messageId =
+ eventJson->second.get_ptr<const std::string*>();
+ if (messageId == nullptr)
+ {
+ BMCWEB_LOG_ERROR("MessageId wasn't a string???");
+ return false;
+ }
+
+ std::string registry;
+ std::string messageKey;
+ event_log::getRegistryAndMessageKey(*messageId, registry,
+ messageKey);
+
+ auto obj = std::ranges::find(registryPrefixes, registry);
+ if (obj == registryPrefixes.end())
+ {
+ return false;
+ }
+ }
+
+ if (!originResources.empty())
+ {
+ auto eventJson = eventMessage.find("OriginOfCondition");
+ if (eventJson == eventMessage.end())
+ {
+ return false;
+ }
+
+ const std::string* originOfCondition =
+ eventJson->second.get_ptr<const std::string*>();
+ if (originOfCondition == nullptr)
+ {
+ BMCWEB_LOG_ERROR("OriginOfCondition wasn't a string???");
+ return false;
+ }
+
+ auto obj = std::ranges::find(originResources, *originOfCondition);
+
+ if (obj == originResources.end())
+ {
+ return false;
+ }
+ }
+
// If registryMsgIds list is empty, assume all
if (!registryMsgIds.empty())
{
@@ -362,7 +418,8 @@
BMCWEB_LOG_DEBUG("extracted registry {}", registry);
BMCWEB_LOG_DEBUG("extracted message key {}", messageKey);
- auto obj = std::ranges::find(registryMsgIds, registry);
+ auto obj = std::ranges::find(
+ registryMsgIds, std::format("{}.{}", registry, messageKey));
if (obj == registryMsgIds.end())
{
BMCWEB_LOG_DEBUG("did not find registry {} in registryMsgIds",
@@ -648,6 +705,7 @@
subValue->resourceTypes = newSub->resourceTypes;
subValue->httpHeaders = newSub->httpHeaders;
subValue->metricReportDefinitions = newSub->metricReportDefinitions;
+ subValue->originResources = newSub->originResources;
if (subValue->id.empty())
{
@@ -909,6 +967,8 @@
newSub->resourceTypes = subValue->resourceTypes;
newSub->httpHeaders = subValue->httpHeaders;
newSub->metricReportDefinitions = subValue->metricReportDefinitions;
+ newSub->originResources = subValue->originResources;
+
persistent_data::EventServiceStore::getInstance()
.subscriptionsConfigMap.emplace(newSub->id, newSub);
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index aa88b09..76f3c5a 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -34,6 +34,7 @@
#include <ranges>
#include <span>
#include <string>
+#include <vector>
namespace redfish
{
@@ -296,6 +297,7 @@
std::optional<std::string> retryPolicy;
std::optional<std::vector<std::string>> msgIds;
std::optional<std::vector<std::string>> regPrefixes;
+ std::optional<std::vector<std::string>> originResources;
std::optional<std::vector<std::string>> resTypes;
std::optional<std::vector<nlohmann::json::object_t>> headers;
std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
@@ -305,7 +307,8 @@
context, "Protocol", protocol, "SubscriptionType",
subscriptionType, "EventFormatType", eventFormatType2,
"HttpHeaders", headers, "RegistryPrefixes", regPrefixes,
- "MessageIds", msgIds, "DeliveryRetryPolicy", retryPolicy,
+ "OriginResources", originResources, "MessageIds", msgIds,
+ "DeliveryRetryPolicy", retryPolicy,
"MetricReportDefinitions", mrdJsonArray, "ResourceTypes",
resTypes, "VerifyCertificate", verifyCertificate))
{
@@ -533,6 +536,11 @@
subValue->registryPrefixes = *regPrefixes;
}
+ if (originResources)
+ {
+ subValue->originResources = *originResources;
+ }
+
if (resTypes)
{
for (const std::string& it : *resTypes)
diff --git a/test/redfish-core/include/event_service_manager_test.cpp b/test/redfish-core/include/event_service_manager_test.cpp
new file mode 100644
index 0000000..bb86e77
--- /dev/null
+++ b/test/redfish-core/include/event_service_manager_test.cpp
@@ -0,0 +1,102 @@
+#include "event_service_manager.hpp"
+#include "filter_expr_printer.hpp"
+
+#include <boost/asio/io_context.hpp>
+#include <boost/url/url.hpp>
+#include <nlohmann/json.hpp>
+
+#include <optional>
+#include <string_view>
+
+#include <gtest/gtest.h>
+
+namespace redfish
+{
+
+TEST(EventServiceManager, eventMatchesFilter)
+{
+ boost::asio::io_context io;
+ boost::urls::url url;
+
+ {
+ Subscription sub(url, io);
+ nlohmann::json::object_t event;
+
+ // Default constructed should always pass
+ EXPECT_TRUE(sub.eventMatchesFilter(event, "Event"));
+ }
+ {
+ nlohmann::json::object_t event;
+ // Resource types filter
+ Subscription sub(url, io);
+ sub.resourceTypes.emplace_back("Task");
+ EXPECT_FALSE(sub.eventMatchesFilter(event, "Event"));
+ EXPECT_TRUE(sub.eventMatchesFilter(event, "Task"));
+ }
+ {
+ nlohmann::json::object_t event;
+ // Resource types filter
+ Subscription sub(url, io);
+ sub.registryMsgIds.emplace_back("OpenBMC.PostComplete");
+
+ // Correct message registry
+ event["MessageId"] = "OpenBMC.0.1.PostComplete";
+ EXPECT_TRUE(sub.eventMatchesFilter(event, "Event"));
+
+ // Different message registry
+ event["MessageId"] = "Task.0.1.PostComplete";
+ EXPECT_FALSE(sub.eventMatchesFilter(event, "Event"));
+
+ // Different MessageId
+ event["MessageId"] = "OpenBMC.0.1.NoMatch";
+ EXPECT_FALSE(sub.eventMatchesFilter(event, "Event"));
+ }
+ {
+ nlohmann::json::object_t event;
+ // Resource types filter
+ Subscription sub(url, io);
+ event["MessageId"] = "OpenBMC.0.1.PostComplete";
+
+ // Correct message registry
+ sub.filter = parseFilter("MessageId eq 'OpenBMC.0.1.PostComplete'");
+ EXPECT_TRUE(sub.eventMatchesFilter(event, "Event"));
+
+ // Different message registry
+ sub.filter = parseFilter("MessageId ne 'OpenBMC.0.1.PostComplete'");
+ EXPECT_FALSE(sub.eventMatchesFilter(event, "Event"));
+ }
+ {
+ nlohmann::json::object_t event;
+ // Resource types filter
+ Subscription sub(url, io);
+ event["MessageId"] = "OpenBMC.0.1.PostComplete";
+
+ // Correct message registry
+ sub.registryPrefixes.emplace_back("OpenBMC");
+ EXPECT_TRUE(sub.eventMatchesFilter(event, "Event"));
+
+ // Different message registry
+ event["MessageId"] = "Task.0.1.PostComplete";
+ EXPECT_FALSE(sub.eventMatchesFilter(event, "Event"));
+ }
+ {
+ nlohmann::json::object_t event;
+ // Resource types filter
+ {
+ Subscription sub(url, io);
+ event["OriginOfCondition"] = "/redfish/v1/Managers/bmc";
+
+ // Correct origin
+ sub.originResources.emplace_back("/redfish/v1/Managers/bmc");
+ EXPECT_TRUE(sub.eventMatchesFilter(event, "Event"));
+ }
+ {
+ Subscription sub(url, io);
+ // Incorrect origin
+ sub.originResources.clear();
+ sub.originResources.emplace_back("/redfish/v1/Managers/bmc_not");
+ EXPECT_FALSE(sub.eventMatchesFilter(event, "Event"));
+ }
+ }
+}
+} // namespace redfish