diff options
Diffstat (limited to 'redfish-core/lib/event_service.hpp')
-rw-r--r-- | redfish-core/lib/event_service.hpp | 1128 |
1 files changed, 587 insertions, 541 deletions
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index aebc2824b0..ff3de8298a 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -16,6 +16,7 @@ #pragma once #include "app.hpp" #include "event_service_manager.hpp" +#include "generated/enums/event_service.hpp" #include "http/utility.hpp" #include "logging.hpp" #include "query.hpp" @@ -51,118 +52,123 @@ inline void requestRoutesEventService(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/EventService/") .privileges(redfish::privileges::getEventService) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + .methods( + boost::beast::http::verb:: + get)([&app]( + const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService"; - asyncResp->res.jsonValue["@odata.type"] = - "#EventService.v1_5_0.EventService"; - asyncResp->res.jsonValue["Id"] = "EventService"; - asyncResp->res.jsonValue["Name"] = "Event Service"; - asyncResp->res.jsonValue["ServerSentEventUri"] = - "/redfish/v1/EventService/SSE"; - - asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] = - "/redfish/v1/EventService/Subscriptions"; - asyncResp->res - .jsonValue["Actions"]["#EventService.SubmitTestEvent"]["target"] = - "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent"; - - const persistent_data::EventServiceConfig eventServiceConfig = - persistent_data::EventServiceStore::getInstance() - .getEventServiceConfig(); - - asyncResp->res.jsonValue["Status"]["State"] = - (eventServiceConfig.enabled ? "Enabled" : "Disabled"); - asyncResp->res.jsonValue["ServiceEnabled"] = eventServiceConfig.enabled; - asyncResp->res.jsonValue["DeliveryRetryAttempts"] = - eventServiceConfig.retryAttempts; - asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = - eventServiceConfig.retryTimeoutInterval; - asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes; - asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes; - asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes; - - nlohmann::json::object_t supportedSSEFilters; - supportedSSEFilters["EventFormatType"] = true; - supportedSSEFilters["MessageId"] = true; - supportedSSEFilters["MetricReportDefinition"] = true; - supportedSSEFilters["RegistryPrefix"] = true; - supportedSSEFilters["OriginResource"] = false; - supportedSSEFilters["ResourceType"] = false; - - asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = - std::move(supportedSSEFilters); - }); + asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService"; + asyncResp->res.jsonValue["@odata.type"] = + "#EventService.v1_5_0.EventService"; + asyncResp->res.jsonValue["Id"] = "EventService"; + asyncResp->res.jsonValue["Name"] = "Event Service"; + asyncResp->res.jsonValue["ServerSentEventUri"] = + "/redfish/v1/EventService/SSE"; + + asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] = + "/redfish/v1/EventService/Subscriptions"; + asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"] + ["target"] = + "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent"; + + const persistent_data::EventServiceConfig eventServiceConfig = + persistent_data::EventServiceStore::getInstance() + .getEventServiceConfig(); + + asyncResp->res.jsonValue["Status"]["State"] = + (eventServiceConfig.enabled ? "Enabled" : "Disabled"); + asyncResp->res.jsonValue["ServiceEnabled"] = + eventServiceConfig.enabled; + asyncResp->res.jsonValue["DeliveryRetryAttempts"] = + eventServiceConfig.retryAttempts; + asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = + eventServiceConfig.retryTimeoutInterval; + asyncResp->res.jsonValue["EventFormatTypes"] = + supportedEvtFormatTypes; + asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes; + asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes; + + nlohmann::json::object_t supportedSSEFilters; + supportedSSEFilters["EventFormatType"] = true; + supportedSSEFilters["MessageId"] = true; + supportedSSEFilters["MetricReportDefinition"] = true; + supportedSSEFilters["RegistryPrefix"] = true; + supportedSSEFilters["OriginResource"] = false; + supportedSSEFilters["ResourceType"] = false; + + asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = + std::move(supportedSSEFilters); + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/") .privileges(redfish::privileges::patchEventService) .methods(boost::beast::http::verb::patch)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - std::optional<bool> serviceEnabled; - std::optional<uint32_t> retryAttemps; - std::optional<uint32_t> retryInterval; - - if (!json_util::readJsonPatch( - req, asyncResp->res, "ServiceEnabled", serviceEnabled, - "DeliveryRetryAttempts", retryAttemps, - "DeliveryRetryIntervalSeconds", retryInterval)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + std::optional<bool> serviceEnabled; + std::optional<uint32_t> retryAttemps; + std::optional<uint32_t> retryInterval; + + if (!json_util::readJsonPatch( + req, asyncResp->res, "ServiceEnabled", serviceEnabled, + "DeliveryRetryAttempts", retryAttemps, + "DeliveryRetryIntervalSeconds", retryInterval)) + { + return; + } - persistent_data::EventServiceConfig eventServiceConfig = - persistent_data::EventServiceStore::getInstance() - .getEventServiceConfig(); + persistent_data::EventServiceConfig eventServiceConfig = + persistent_data::EventServiceStore::getInstance() + .getEventServiceConfig(); - if (serviceEnabled) - { - eventServiceConfig.enabled = *serviceEnabled; - } + if (serviceEnabled) + { + eventServiceConfig.enabled = *serviceEnabled; + } - if (retryAttemps) - { - // Supported range [1-3] - if ((*retryAttemps < 1) || (*retryAttemps > 3)) - { - messages::queryParameterOutOfRange( - asyncResp->res, std::to_string(*retryAttemps), - "DeliveryRetryAttempts", "[1-3]"); - } - else - { - eventServiceConfig.retryAttempts = *retryAttemps; - } - } + if (retryAttemps) + { + // Supported range [1-3] + if ((*retryAttemps < 1) || (*retryAttemps > 3)) + { + messages::queryParameterOutOfRange( + asyncResp->res, std::to_string(*retryAttemps), + "DeliveryRetryAttempts", "[1-3]"); + } + else + { + eventServiceConfig.retryAttempts = *retryAttemps; + } + } - if (retryInterval) - { - // Supported range [5 - 180] - if ((*retryInterval < 5) || (*retryInterval > 180)) - { - messages::queryParameterOutOfRange( - asyncResp->res, std::to_string(*retryInterval), - "DeliveryRetryIntervalSeconds", "[5-180]"); - } - else - { - eventServiceConfig.retryTimeoutInterval = *retryInterval; - } - } + if (retryInterval) + { + // Supported range [5 - 180] + if ((*retryInterval < 5) || (*retryInterval > 180)) + { + messages::queryParameterOutOfRange( + asyncResp->res, std::to_string(*retryInterval), + "DeliveryRetryIntervalSeconds", "[5-180]"); + } + else + { + eventServiceConfig.retryTimeoutInterval = + *retryInterval; + } + } - EventServiceManager::getInstance().setEventServiceConfig( - eventServiceConfig); - }); + EventServiceManager::getInstance().setEventServiceConfig( + eventServiceConfig); + }); } inline void requestRoutesSubmitTestEvent(App& app) @@ -173,18 +179,18 @@ inline void requestRoutesSubmitTestEvent(App& app) .methods(boost::beast::http::verb::post)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (!EventServiceManager::getInstance().sendTestEventLog()) - { - messages::serviceDisabled(asyncResp->res, - "/redfish/v1/EventService/"); - return; - } - asyncResp->res.result(boost::beast::http::status::no_content); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (!EventServiceManager::getInstance().sendTestEventLog()) + { + messages::serviceDisabled(asyncResp->res, + "/redfish/v1/EventService/"); + return; + } + asyncResp->res.result(boost::beast::http::status::no_content); + }); } inline void doSubscriptionCollection( @@ -227,402 +233,417 @@ inline void requestRoutesEventDestinationCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#EventDestinationCollection.EventDestinationCollection"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/EventService/Subscriptions"; - asyncResp->res.jsonValue["Name"] = "Event Destination Collections"; - - nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; - - std::vector<std::string> subscripIds = - EventServiceManager::getInstance().getAllIDs(); - memberArray = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size(); - - for (const std::string& id : subscripIds) - { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/EventService/Subscriptions/{}" + id); - memberArray.emplace_back(std::move(member)); - } - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& resp) { - doSubscriptionCollection(ec, asyncResp, resp); - }, - "xyz.openbmc_project.Network.SNMP", - "/xyz/openbmc_project/network/snmp/manager", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#EventDestinationCollection.EventDestinationCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/EventService/Subscriptions"; + asyncResp->res.jsonValue["Name"] = + "Event Destination Collections"; + + nlohmann::json& memberArray = + asyncResp->res.jsonValue["Members"]; + + std::vector<std::string> subscripIds = + EventServiceManager::getInstance().getAllIDs(); + memberArray = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = + subscripIds.size(); + + for (const std::string& id : subscripIds) + { + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}" + id); + memberArray.emplace_back(std::move(member)); + } + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::ManagedObjectType& resp) { + doSubscriptionCollection(ec, asyncResp, resp); + }, + "xyz.openbmc_project.Network.SNMP", + "/xyz/openbmc_project/network/snmp/manager", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/") .privileges(redfish::privileges::postEventDestinationCollection) - .methods(boost::beast::http::verb::post)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= - maxNoOfSubscriptions) - { - messages::eventSubscriptionLimitExceeded(asyncResp->res); - return; - } - std::string destUrl; - std::string protocol; - std::optional<std::string> context; - std::optional<std::string> subscriptionType; - std::optional<std::string> eventFormatType2; - 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>> resTypes; - std::optional<std::vector<nlohmann::json::object_t>> headers; - std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray; - - if (!json_util::readJsonPatch( - req, asyncResp->res, "Destination", destUrl, "Context", context, - "Protocol", protocol, "SubscriptionType", subscriptionType, - "EventFormatType", eventFormatType2, "HttpHeaders", headers, - "RegistryPrefixes", regPrefixes, "MessageIds", msgIds, - "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions", - mrdJsonArray, "ResourceTypes", resTypes)) - { - return; - } - - // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers - static constexpr const uint16_t maxDestinationSize = 2000; - if (destUrl.size() > maxDestinationSize) - { - messages::stringValueTooLong(asyncResp->res, "Destination", - maxDestinationSize); - return; - } - - if (regPrefixes && msgIds) - { - if (!regPrefixes->empty() && !msgIds->empty()) + .methods( + boost::beast::http::verb:: + post)([&app]( + const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - messages::propertyValueConflict(asyncResp->res, "MessageIds", - "RegistryPrefixes"); return; } - } - - boost::system::result<boost::urls::url> url = - boost::urls::parse_absolute_uri(destUrl); - if (!url) - { - BMCWEB_LOG_WARNING("Failed to validate and split destination url"); - messages::propertyValueFormatError(asyncResp->res, destUrl, - "Destination"); - return; - } - url->normalize(); - crow::utility::setProtocolDefaults(*url, protocol); - crow::utility::setPortDefaults(*url); - - if (url->path().empty()) - { - url->set_path("/"); - } - - if (url->has_userinfo()) - { - messages::propertyValueFormatError(asyncResp->res, destUrl, - "Destination"); - return; - } - - if (protocol == "SNMPv2c") - { - if (context) + if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= + maxNoOfSubscriptions) { - messages::propertyValueConflict(asyncResp->res, "Context", - "Protocol"); + messages::eventSubscriptionLimitExceeded(asyncResp->res); return; } - if (eventFormatType2) + std::string destUrl; + std::string protocol; + std::optional<bool> verifyCertificate; + std::optional<std::string> context; + std::optional<std::string> subscriptionType; + std::optional<std::string> eventFormatType2; + 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>> resTypes; + std::optional<std::vector<nlohmann::json::object_t>> headers; + std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray; + + if (!json_util::readJsonPatch( + req, asyncResp->res, "Destination", destUrl, "Context", + context, "Protocol", protocol, "SubscriptionType", + subscriptionType, "EventFormatType", eventFormatType2, + "HttpHeaders", headers, "RegistryPrefixes", regPrefixes, + "MessageIds", msgIds, "DeliveryRetryPolicy", retryPolicy, + "MetricReportDefinitions", mrdJsonArray, "ResourceTypes", + resTypes, "VerifyCertificate", verifyCertificate)) { - messages::propertyValueConflict(asyncResp->res, - "EventFormatType", "Protocol"); return; } - if (retryPolicy) - { - messages::propertyValueConflict(asyncResp->res, "RetryPolicy", - "Protocol"); - return; - } - if (msgIds) + + // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers + static constexpr const uint16_t maxDestinationSize = 2000; + if (destUrl.size() > maxDestinationSize) { - messages::propertyValueConflict(asyncResp->res, "MessageIds", - "Protocol"); + messages::stringValueTooLong(asyncResp->res, "Destination", + maxDestinationSize); return; } - if (regPrefixes) + + if (regPrefixes && msgIds) { - messages::propertyValueConflict(asyncResp->res, - "RegistryPrefixes", "Protocol"); - return; + if (!regPrefixes->empty() && !msgIds->empty()) + { + messages::propertyValueConflict( + asyncResp->res, "MessageIds", "RegistryPrefixes"); + return; + } } - if (resTypes) + + boost::system::result<boost::urls::url> url = + boost::urls::parse_absolute_uri(destUrl); + if (!url) { - messages::propertyValueConflict(asyncResp->res, "ResourceTypes", - "Protocol"); + BMCWEB_LOG_WARNING( + "Failed to validate and split destination url"); + messages::propertyValueFormatError(asyncResp->res, destUrl, + "Destination"); return; } - if (headers) + url->normalize(); + crow::utility::setProtocolDefaults(*url, protocol); + crow::utility::setPortDefaults(*url); + + if (url->path().empty()) { - messages::propertyValueConflict(asyncResp->res, "HttpHeaders", - "Protocol"); - return; + url->set_path("/"); } - if (mrdJsonArray) + + if (url->has_userinfo()) { - messages::propertyValueConflict( - asyncResp->res, "MetricReportDefinitions", "Protocol"); + messages::propertyValueFormatError(asyncResp->res, destUrl, + "Destination"); return; } - if (url->scheme() != "snmp") + + if (protocol == "SNMPv2c") { - messages::propertyValueConflict(asyncResp->res, "Destination", - "Protocol"); + if (context) + { + messages::propertyValueConflict(asyncResp->res, "Context", + "Protocol"); + return; + } + if (eventFormatType2) + { + messages::propertyValueConflict( + asyncResp->res, "EventFormatType", "Protocol"); + return; + } + if (retryPolicy) + { + messages::propertyValueConflict(asyncResp->res, + "RetryPolicy", "Protocol"); + return; + } + if (msgIds) + { + messages::propertyValueConflict(asyncResp->res, + "MessageIds", "Protocol"); + return; + } + if (regPrefixes) + { + messages::propertyValueConflict( + asyncResp->res, "RegistryPrefixes", "Protocol"); + return; + } + if (resTypes) + { + messages::propertyValueConflict( + asyncResp->res, "ResourceTypes", "Protocol"); + return; + } + if (headers) + { + messages::propertyValueConflict(asyncResp->res, + "HttpHeaders", "Protocol"); + return; + } + if (mrdJsonArray) + { + messages::propertyValueConflict( + asyncResp->res, "MetricReportDefinitions", "Protocol"); + return; + } + if (url->scheme() != "snmp") + { + messages::propertyValueConflict(asyncResp->res, + "Destination", "Protocol"); + return; + } + + addSnmpTrapClient(asyncResp, url->host_address(), + url->port_number()); return; } - addSnmpTrapClient(asyncResp, url->host_address(), - url->port_number()); - return; - } + std::shared_ptr<Subscription> subValue = + std::make_shared<Subscription>(*url, app.ioContext()); - std::shared_ptr<Subscription> subValue = - std::make_shared<Subscription>(*url, app.ioContext()); + subValue->destinationUrl = std::move(*url); - subValue->destinationUrl = std::move(*url); - - if (subscriptionType) - { - if (*subscriptionType != "RedfishEvent") + if (subscriptionType) { - messages::propertyValueNotInList( - asyncResp->res, *subscriptionType, "SubscriptionType"); - return; + if (*subscriptionType != "RedfishEvent") + { + messages::propertyValueNotInList( + asyncResp->res, *subscriptionType, "SubscriptionType"); + return; + } + subValue->subscriptionType = *subscriptionType; } - subValue->subscriptionType = *subscriptionType; - } - else - { - subValue->subscriptionType = "RedfishEvent"; // Default - } - - if (protocol != "Redfish") - { - messages::propertyValueNotInList(asyncResp->res, protocol, - "Protocol"); - return; - } - subValue->protocol = protocol; - - if (eventFormatType2) - { - if (std::ranges::find(supportedEvtFormatTypes, *eventFormatType2) == - supportedEvtFormatTypes.end()) + else { - messages::propertyValueNotInList( - asyncResp->res, *eventFormatType2, "EventFormatType"); - return; + subValue->subscriptionType = "RedfishEvent"; // Default } - subValue->eventFormatType = *eventFormatType2; - } - else - { - // If not specified, use default "Event" - subValue->eventFormatType = "Event"; - } - if (context) - { - // This value is selected arbitrarily. - constexpr const size_t maxContextSize = 256; - if (context->size() > maxContextSize) + if (protocol != "Redfish") { - messages::stringValueTooLong(asyncResp->res, "Context", - maxContextSize); + messages::propertyValueNotInList(asyncResp->res, protocol, + "Protocol"); return; } - subValue->customText = *context; - } + subValue->protocol = protocol; - if (headers) - { - size_t cumulativeLen = 0; + if (verifyCertificate) + { + subValue->verifyCertificate = *verifyCertificate; + } - for (const nlohmann::json::object_t& headerChunk : *headers) + if (eventFormatType2) { - for (const auto& item : headerChunk) + if (std::ranges::find(supportedEvtFormatTypes, + *eventFormatType2) == + supportedEvtFormatTypes.end()) { - const std::string* value = - item.second.get_ptr<const std::string*>(); - if (value == nullptr) - { - messages::propertyValueFormatError( - asyncResp->res, item.second, - "HttpHeaders/" + item.first); - return; - } - // Adding a new json value is the size of the key, + - // the size of the value + 2 * 2 quotes for each, + - // the colon and space between. example: - // "key": "value" - cumulativeLen += item.first.size() + value->size() + 6; - // This value is selected to mirror http_connection.hpp - constexpr const uint16_t maxHeaderSizeED = 8096; - if (cumulativeLen > maxHeaderSizeED) - { - messages::arraySizeTooLong( - asyncResp->res, "HttpHeaders", maxHeaderSizeED); - return; - } - subValue->httpHeaders.set(item.first, *value); + messages::propertyValueNotInList( + asyncResp->res, *eventFormatType2, "EventFormatType"); + return; } + subValue->eventFormatType = *eventFormatType2; + } + else + { + // If not specified, use default "Event" + subValue->eventFormatType = "Event"; } - } - if (regPrefixes) - { - for (const std::string& it : *regPrefixes) + if (context) { - if (std::ranges::find(supportedRegPrefixes, it) == - supportedRegPrefixes.end()) + // This value is selected arbitrarily. + constexpr const size_t maxContextSize = 256; + if (context->size() > maxContextSize) { - messages::propertyValueNotInList(asyncResp->res, it, - "RegistryPrefixes"); + messages::stringValueTooLong(asyncResp->res, "Context", + maxContextSize); return; } + subValue->customText = *context; } - subValue->registryPrefixes = *regPrefixes; - } - if (resTypes) - { - for (const std::string& it : *resTypes) + if (headers) { - if (std::ranges::find(supportedResourceTypes, it) == - supportedResourceTypes.end()) + size_t cumulativeLen = 0; + + for (const nlohmann::json::object_t& headerChunk : *headers) { - messages::propertyValueNotInList(asyncResp->res, it, - "ResourceTypes"); - return; + for (const auto& item : headerChunk) + { + const std::string* value = + item.second.get_ptr<const std::string*>(); + if (value == nullptr) + { + messages::propertyValueFormatError( + asyncResp->res, item.second, + "HttpHeaders/" + item.first); + return; + } + // Adding a new json value is the size of the key, + + // the size of the value + 2 * 2 quotes for each, + + // the colon and space between. example: + // "key": "value" + cumulativeLen += item.first.size() + value->size() + 6; + // This value is selected to mirror http_connection.hpp + constexpr const uint16_t maxHeaderSizeED = 8096; + if (cumulativeLen > maxHeaderSizeED) + { + messages::arraySizeTooLong( + asyncResp->res, "HttpHeaders", maxHeaderSizeED); + return; + } + subValue->httpHeaders.set(item.first, *value); + } } } - subValue->resourceTypes = *resTypes; - } - - if (msgIds) - { - std::vector<std::string> registryPrefix; - // If no registry prefixes are mentioned, consider all - // supported prefixes - if (subValue->registryPrefixes.empty()) + if (regPrefixes) { - registryPrefix.assign(supportedRegPrefixes.begin(), - supportedRegPrefixes.end()); + for (const std::string& it : *regPrefixes) + { + if (std::ranges::find(supportedRegPrefixes, it) == + supportedRegPrefixes.end()) + { + messages::propertyValueNotInList(asyncResp->res, it, + "RegistryPrefixes"); + return; + } + } + subValue->registryPrefixes = *regPrefixes; } - else + + if (resTypes) { - registryPrefix = subValue->registryPrefixes; + for (const std::string& it : *resTypes) + { + if (std::ranges::find(supportedResourceTypes, it) == + supportedResourceTypes.end()) + { + messages::propertyValueNotInList(asyncResp->res, it, + "ResourceTypes"); + return; + } + } + subValue->resourceTypes = *resTypes; } - for (const std::string& id : *msgIds) + if (msgIds) { - bool validId = false; + std::vector<std::string> registryPrefix; - // Check for Message ID in each of the selected Registry - for (const std::string& it : registryPrefix) + // If no registry prefixes are mentioned, consider all + // supported prefixes + if (subValue->registryPrefixes.empty()) { - const std::span<const redfish::registries::MessageEntry> - registry = - redfish::registries::getRegistryFromPrefix(it); + registryPrefix.assign(supportedRegPrefixes.begin(), + supportedRegPrefixes.end()); + } + else + { + registryPrefix = subValue->registryPrefixes; + } + + for (const std::string& id : *msgIds) + { + bool validId = false; - if (std::ranges::any_of( - registry, - [&id](const redfish::registries::MessageEntry& - messageEntry) { - return id == messageEntry.first; - })) + // Check for Message ID in each of the selected Registry + for (const std::string& it : registryPrefix) { - validId = true; - break; + const std::span<const redfish::registries::MessageEntry> + registry = + redfish::registries::getRegistryFromPrefix(it); + + if (std::ranges::any_of( + registry, + [&id](const redfish::registries::MessageEntry& + messageEntry) { + return id == messageEntry.first; + })) + { + validId = true; + break; + } + } + + if (!validId) + { + messages::propertyValueNotInList(asyncResp->res, id, + "MessageIds"); + return; } } - if (!validId) + subValue->registryMsgIds = *msgIds; + } + + if (retryPolicy) + { + if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == + supportedRetryPolicies.end()) { - messages::propertyValueNotInList(asyncResp->res, id, - "MessageIds"); + messages::propertyValueNotInList( + asyncResp->res, *retryPolicy, "DeliveryRetryPolicy"); return; } + subValue->retryPolicy = *retryPolicy; } - - subValue->registryMsgIds = *msgIds; - } - - if (retryPolicy) - { - if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == - supportedRetryPolicies.end()) + else { - messages::propertyValueNotInList(asyncResp->res, *retryPolicy, - "DeliveryRetryPolicy"); - return; + // Default "TerminateAfterRetries" + subValue->retryPolicy = "TerminateAfterRetries"; } - subValue->retryPolicy = *retryPolicy; - } - else - { - // Default "TerminateAfterRetries" - subValue->retryPolicy = "TerminateAfterRetries"; - } - if (mrdJsonArray) - { - for (nlohmann::json::object_t& mrdObj : *mrdJsonArray) + if (mrdJsonArray) { - std::string mrdUri; + for (nlohmann::json::object_t& mrdObj : *mrdJsonArray) + { + std::string mrdUri; - if (!json_util::readJsonObject(mrdObj, asyncResp->res, - "@odata.id", mrdUri)) + if (!json_util::readJsonObject(mrdObj, asyncResp->res, + "@odata.id", mrdUri)) - { - return; + { + return; + } + subValue->metricReportDefinitions.emplace_back(mrdUri); } - subValue->metricReportDefinitions.emplace_back(mrdUri); } - } - std::string id = - EventServiceManager::getInstance().addSubscription(subValue); - if (id.empty()) - { - messages::internalError(asyncResp->res); - return; - } + std::string id = + EventServiceManager::getInstance().addPushSubscription( + subValue); + if (id.empty()) + { + messages::internalError(asyncResp->res); + return; + } - messages::created(asyncResp->res); - asyncResp->res.addHeader( - "Location", "/redfish/v1/EventService/Subscriptions/" + id); - }); + messages::created(asyncResp->res); + asyncResp->res.addHeader( + "Location", "/redfish/v1/EventService/Subscriptions/" + id); + }); } inline void requestRoutesEventDestination(App& app) @@ -633,55 +654,66 @@ inline void requestRoutesEventDestination(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (param.starts_with("snmp")) - { - getSnmpTrapClient(asyncResp, param); - return; - } + if (param.starts_with("snmp")) + { + getSnmpTrapClient(asyncResp, param); + return; + } - std::shared_ptr<Subscription> subValue = - EventServiceManager::getInstance().getSubscription(param); - if (subValue == nullptr) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - const std::string& id = param; - - asyncResp->res.jsonValue["@odata.type"] = - "#EventDestination.v1_8_0.EventDestination"; - asyncResp->res.jsonValue["Protocol"] = "Redfish"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/EventService/Subscriptions/{}", id); - asyncResp->res.jsonValue["Id"] = id; - asyncResp->res.jsonValue["Name"] = "Event Destination " + id; - asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl; - asyncResp->res.jsonValue["Context"] = subValue->customText; - asyncResp->res.jsonValue["SubscriptionType"] = - subValue->subscriptionType; - asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array(); - asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType; - asyncResp->res.jsonValue["RegistryPrefixes"] = - subValue->registryPrefixes; - asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes; - - asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds; - asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy; - - nlohmann::json::array_t mrdJsonArray; - for (const auto& mdrUri : subValue->metricReportDefinitions) - { - nlohmann::json::object_t mdr; - mdr["@odata.id"] = mdrUri; - mrdJsonArray.emplace_back(std::move(mdr)); - } - asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray; - }); + std::shared_ptr<Subscription> subValue = + EventServiceManager::getInstance().getSubscription(param); + if (subValue == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + const std::string& id = param; + + asyncResp->res.jsonValue["@odata.type"] = + "#EventDestination.v1_8_0.EventDestination"; + asyncResp->res.jsonValue["Protocol"] = + event_destination::EventDestinationProtocol::Redfish; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}", id); + asyncResp->res.jsonValue["Id"] = id; + asyncResp->res.jsonValue["Name"] = "Event Destination " + id; + asyncResp->res.jsonValue["Destination"] = + subValue->destinationUrl; + asyncResp->res.jsonValue["Context"] = subValue->customText; + asyncResp->res.jsonValue["SubscriptionType"] = + subValue->subscriptionType; + asyncResp->res.jsonValue["HttpHeaders"] = + nlohmann::json::array(); + asyncResp->res.jsonValue["EventFormatType"] = + subValue->eventFormatType; + asyncResp->res.jsonValue["RegistryPrefixes"] = + subValue->registryPrefixes; + asyncResp->res.jsonValue["ResourceTypes"] = + subValue->resourceTypes; + + asyncResp->res.jsonValue["MessageIds"] = + subValue->registryMsgIds; + asyncResp->res.jsonValue["DeliveryRetryPolicy"] = + subValue->retryPolicy; + asyncResp->res.jsonValue["VerifyCertificate"] = + subValue->verifyCertificate; + + nlohmann::json::array_t mrdJsonArray; + for (const auto& mdrUri : subValue->metricReportDefinitions) + { + nlohmann::json::object_t mdr; + mdr["@odata.id"] = mdrUri; + mrdJsonArray.emplace_back(std::move(mdr)); + } + asyncResp->res.jsonValue["MetricReportDefinitions"] = + mrdJsonArray; + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") // The below privilege is wrong, it should be ConfigureManager OR // ConfigureSelf @@ -692,70 +724,81 @@ inline void requestRoutesEventDestination(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - std::shared_ptr<Subscription> subValue = - EventServiceManager::getInstance().getSubscription(param); - if (subValue == nullptr) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + std::shared_ptr<Subscription> subValue = + EventServiceManager::getInstance().getSubscription(param); + if (subValue == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } - std::optional<std::string> context; - std::optional<std::string> retryPolicy; - std::optional<std::vector<nlohmann::json::object_t>> headers; + std::optional<std::string> context; + std::optional<std::string> retryPolicy; + std::optional<bool> verifyCertificate; + std::optional<std::vector<nlohmann::json::object_t>> headers; - if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context, - "DeliveryRetryPolicy", retryPolicy, - "HttpHeaders", headers)) - { - return; - } + if (!json_util::readJsonPatch( + req, asyncResp->res, "Context", context, + "VerifyCertificate", verifyCertificate, + "DeliveryRetryPolicy", retryPolicy, "HttpHeaders", + headers)) + { + return; + } - if (context) - { - subValue->customText = *context; - } + if (context) + { + subValue->customText = *context; + } - if (headers) - { - boost::beast::http::fields fields; - for (const nlohmann::json::object_t& headerChunk : *headers) - { - for (const auto& it : headerChunk) + if (headers) { - const std::string* value = - it.second.get_ptr<const std::string*>(); - if (value == nullptr) + boost::beast::http::fields fields; + for (const nlohmann::json::object_t& headerChunk : *headers) { - messages::propertyValueFormatError( - asyncResp->res, it.second, - "HttpHeaders/" + it.first); + for (const auto& it : headerChunk) + { + const std::string* value = + it.second.get_ptr<const std::string*>(); + if (value == nullptr) + { + messages::propertyValueFormatError( + asyncResp->res, it.second, + "HttpHeaders/" + it.first); + return; + } + fields.set(it.first, *value); + } + } + subValue->httpHeaders = std::move(fields); + } + + if (retryPolicy) + { + if (std::ranges::find(supportedRetryPolicies, + *retryPolicy) == + supportedRetryPolicies.end()) + { + messages::propertyValueNotInList(asyncResp->res, + *retryPolicy, + "DeliveryRetryPolicy"); return; } - fields.set(it.first, *value); + subValue->retryPolicy = *retryPolicy; } - } - subValue->httpHeaders = std::move(fields); - } - if (retryPolicy) - { - if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == - supportedRetryPolicies.end()) - { - messages::propertyValueNotInList(asyncResp->res, *retryPolicy, - "DeliveryRetryPolicy"); - return; - } - subValue->retryPolicy = *retryPolicy; - } + if (verifyCertificate) + { + subValue->verifyCertificate = *verifyCertificate; + } - EventServiceManager::getInstance().updateSubscriptionData(); - }); + EventServiceManager::getInstance().updateSubscriptionData(); + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") // The below privilege is wrong, it should be ConfigureManager OR // ConfigureSelf @@ -766,25 +809,28 @@ inline void requestRoutesEventDestination(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (param.starts_with("snmp")) - { - deleteSnmpTrapClient(asyncResp, param); - EventServiceManager::getInstance().deleteSubscription(param); - return; - } + if (param.starts_with("snmp")) + { + deleteSnmpTrapClient(asyncResp, param); + EventServiceManager::getInstance().deleteSubscription( + param); + return; + } - if (!EventServiceManager::getInstance().isSubscriptionExist(param)) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - EventServiceManager::getInstance().deleteSubscription(param); - }); + if (!EventServiceManager::getInstance().isSubscriptionExist( + param)) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + EventServiceManager::getInstance().deleteSubscription(param); + }); } } // namespace redfish |