From 64a9d1826b7b4a66750f7ecaaff7bcf7e0d23932 Mon Sep 17 00:00:00 2001 From: AppaRao Puli Date: Wed, 17 Mar 2021 01:16:50 +0000 Subject: [PATCH] Add EventService SSE filter support This commit implements the Event Service SSE stream filters support. As per redfish specification: The SSE streams have these formats: - Metric report SSE stream - Event message SSE stream To reduce the amount of data, service supports $filter query parameter in SSE URI. Below properties support as filter criteria: - EventFormatType( Event & MetricReport) - MessageId - RegistryPrefix - MetricReportDefinition For more details, refer Redfish specification section 13.5.2 Tested: Created SSE stream with different filters and observed desired events on SSE stream client(browser), some examples - To get all Redfish events, URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event) - To get Redfish events with RegistryPrefix "OpenBMC" URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(RegistryPrefix%20eq%20OpenBMC) - To get only DC power of Events, URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)%20and%20(MessageId%20eq%20DCPowerOff) Signed-off-by: AppaRao Puli Signed-off-by: P Dheeraj Srujan Kumar Change-Id: I55c6f53bb5e57aa1f2d1601f1a16525a33b13bd2 --- include/eventservice_sse.hpp | 146 +++++++++++++++++- redfish-core/include/error_messages.hpp | 9 ++ .../include/event_service_manager.hpp | 5 + redfish-core/lib/event_service.hpp | 5 - redfish-core/src/error_messages.cpp | 26 ++++ 5 files changed, 182 insertions(+), 9 deletions(-) diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp index 14daf00..fed7fec 100644 --- a/include/eventservice_sse.hpp +++ b/include/eventservice_sse.hpp @@ -23,16 +23,154 @@ static bool createSubscription(std::shared_ptr& conn, } BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size(); + // EventService SSE supports only "$filter" query param. + if (req.urlParams.size() > 1) + { + messages::invalidQueryFilter(res); + res.end(); + return false; + } + std::string eventFormatType; + std::string queryFilters; + if (req.urlParams.size()) + { + boost::urls::query_params_view::iterator it = + req.urlParams.find("$filter"); + if (it == req.urlParams.end()) + { + messages::invalidQueryFilter(res); + res.end(); + return false; + } + queryFilters = it->value(); + } + else + { + eventFormatType = "Event"; + } + + std::vector msgIds; + std::vector regPrefixes; + std::vector mrdsArray; + if (!queryFilters.empty()) + { + // Reading from query params. + bool status = readSSEQueryParams(queryFilters, eventFormatType, msgIds, + regPrefixes, mrdsArray); + if (!status) + { + messages::invalidObject(res, queryFilters); + res.end(); + return false; + } + + // RegsitryPrefix and messageIds are mutuly exclusive as per redfish + // specification. + if (regPrefixes.size() && msgIds.size()) + { + messages::mutualExclusiveProperties(res, "RegistryPrefix", + "MessageId"); + res.end(); + return false; + } + + if (!eventFormatType.empty()) + { + if (std::find(supportedEvtFormatTypes.begin(), + supportedEvtFormatTypes.end(), + eventFormatType) == supportedEvtFormatTypes.end()) + { + messages::propertyValueNotInList(res, eventFormatType, + "EventFormatType"); + res.end(); + return false; + } + } + else + { + // If nothing specified, using default "Event" + eventFormatType = "Event"; + } + + if (!regPrefixes.empty()) + { + for (const std::string& it : regPrefixes) + { + if (std::find(supportedRegPrefixes.begin(), + supportedRegPrefixes.end(), + it) == supportedRegPrefixes.end()) + { + messages::propertyValueNotInList(res, it, "RegistryPrefix"); + res.end(); + return false; + } + } + } + + if (!msgIds.empty()) + { + std::vector registryPrefix; + + // If no registry prefixes are mentioned, consider all supported + // prefixes to validate message ID + if (regPrefixes.empty()) + { + registryPrefix.assign(supportedRegPrefixes.begin(), + supportedRegPrefixes.end()); + } + else + { + registryPrefix = regPrefixes; + } + + for (const std::string& id : msgIds) + { + bool validId = false; + + // Check for Message ID in each of the selected Registry + for (const std::string& it : registryPrefix) + { + const std::span< + const redfish::message_registries::MessageEntry> + registry = + redfish::message_registries::getRegistryFromPrefix( + it); + + if (std::any_of( + registry.begin(), registry.end(), + [&id]( + const redfish::message_registries::MessageEntry& + messageEntry) { + return !id.compare(messageEntry.first); + })) + { + validId = true; + break; + } + } + + if (!validId) + { + messages::propertyValueNotInList(res, id, "MessageIds"); + res.end(); + return false; + } + } + } + } + std::shared_ptr subValue = std::make_shared(std::move(conn)); // GET on this URI means, Its SSE subscriptionType. - subValue->subscriptionType = redfish::subscriptionTypeSSE; - - // TODO: parse $filter query params and fill config. + subValue->subscriptionType = subscriptionTypeSSE; subValue->protocol = "Redfish"; subValue->retryPolicy = "TerminateAfterRetries"; - subValue->eventFormatType = "Event"; + subValue->eventFormatType = eventFormatType; + subValue->registryMsgIds = msgIds; + subValue->registryPrefixes = regPrefixes; + subValue->metricReportDefinitions = mrdsArray; + subValue->subscriptionOwner = req.session->username; std::string id = redfish::EventServiceManager::getInstance().addSubscription(subValue, diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp index 7f53fa4..9c688b4 100644 --- a/redfish-core/include/error_messages.hpp +++ b/redfish-core/include/error_messages.hpp @@ -985,6 +985,15 @@ nlohmann::json mutualExclusiveProperties(const std::string& arg1, void mutualExclusiveProperties(crow::Response& res, const std::string& arg1, const std::string& arg2); +/** + * @brief Formats InvalidQueryFilter message into JSON + * Message body: "The requested URL contains the invalid query filters" + * + * @returns Message InvalidQueryFilter formatted to JSON */ +nlohmann::json invalidQueryFilter(); + +void invalidQueryFilter(crow::Response& res); + } // namespace messages } // namespace redfish diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp index 57a2ab8..aae9f02 100644 --- a/redfish-core/include/event_service_manager.hpp +++ b/redfish-core/include/event_service_manager.hpp @@ -57,6 +57,11 @@ static constexpr const char* eventServiceFile = static constexpr const uint8_t maxNoOfSubscriptions = 20; static constexpr const uint8_t maxNoOfSSESubscriptions = 10; +static constexpr const std::array supportedEvtFormatTypes = { + eventFormatType, metricReportFormatType}; +static constexpr const std::array supportedRegPrefixes = { + "OpenBMC", "TaskEvent"}; + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES static std::optional inotifyConn; static constexpr const char* redfishEventLogDir = "/var/log"; diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index 9ad2bbd..99c3bfd 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -24,11 +24,6 @@ namespace redfish { - -static constexpr const std::array supportedEvtFormatTypes = { - eventFormatType, metricReportFormatType}; -static constexpr const std::array supportedRegPrefixes = { - "Base", "OpenBMC", "TaskEvent"}; static constexpr const std::array supportedRetryPolicies = { "TerminateAfterRetries", "SuspendRetries", "RetryForever"}; diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp index 2087a67..ecb3721 100644 --- a/redfish-core/src/error_messages.cpp +++ b/redfish-core/src/error_messages.cpp @@ -2203,6 +2203,32 @@ void mutualExclusiveProperties(crow::Response& res, const std::string& arg1, addMessageToErrorJson(res.jsonValue, mutualExclusiveProperties(arg1, arg2)); } +/** + * @internal + * @brief Formats InvalidQueryFilter into JSON + * + * See header file for more information + * @endinternal + */ +nlohmann::json invalidQueryFilter() +{ + return nlohmann::json{ + {"@odata.type", "#Message.v1_0_0.Message"}, + {"MessageId", "Base.1.5.0.InvalidQueryFilter"}, + {"Message", "The requested url contains the invalid query filter."}, + {"MessageArgs", nlohmann::json::array()}, + {"Severity", "Warning"}, + {"Resolution", + "Ensure the correct query filter is specified in requested url " + "and resubmit the request."}}; +} + +void invalidQueryFilter(crow::Response& res) +{ + res.result(boost::beast::http::status::bad_request); + addMessageToErrorJson(res.jsonValue, invalidQueryFilter()); +} + } // namespace messages } // namespace redfish -- 2.17.1