xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 177612aaa0633cf9d5aef0b763a43135cf552d9b)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation
4e5aaf047SAppaRao Puli #pragma once
53ccb3adbSEd Tanous #include "app.hpp"
6d7857201SEd Tanous #include "async_resp.hpp"
7d7857201SEd Tanous #include "dbus_utility.hpp"
8d7857201SEd Tanous #include "error_messages.hpp"
9b52664e2SAppaRao Puli #include "event_service_manager.hpp"
10d7857201SEd Tanous #include "event_service_store.hpp"
11d7857201SEd Tanous #include "generated/enums/event_destination.hpp"
123ccb3adbSEd Tanous #include "http/utility.hpp"
13d7857201SEd Tanous #include "http_request.hpp"
149838eb20SEd Tanous #include "io_context_singleton.hpp"
153ccb3adbSEd Tanous #include "logging.hpp"
163ccb3adbSEd Tanous #include "query.hpp"
17d109e2b6SAlexander Hansen #include "registries.hpp"
183ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
19d109e2b6SAlexander Hansen #include "registries_selector.hpp"
203d30708fSChicago Duan #include "snmp_trap_event_clients.hpp"
21d7857201SEd Tanous #include "subscription.hpp"
22d109e2b6SAlexander Hansen #include "utils/json_utils.hpp"
23e5aaf047SAppaRao Puli 
24d7857201SEd Tanous #include <asm-generic/errno.h>
25ed398213SEd Tanous 
26d7857201SEd Tanous #include <boost/beast/http/fields.hpp>
27d7857201SEd Tanous #include <boost/beast/http/status.hpp>
28d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
29d7857201SEd Tanous #include <boost/system/error_code.hpp>
30d7857201SEd Tanous #include <boost/system/result.hpp>
31d7857201SEd Tanous #include <boost/url/format.hpp>
32d7857201SEd Tanous #include <boost/url/parse.hpp>
33d7857201SEd Tanous #include <boost/url/url.hpp>
34d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
35d7857201SEd Tanous 
36d7857201SEd Tanous #include <algorithm>
37d7857201SEd Tanous #include <array>
38d7857201SEd Tanous #include <cerrno>
39d7857201SEd Tanous #include <cstddef>
40d7857201SEd Tanous #include <cstdint>
413d30708fSChicago Duan #include <memory>
425064a25bSMyung Bae #include <optional>
433544d2a7SEd Tanous #include <ranges>
441e270c5fSPatrick Williams #include <span>
453d30708fSChicago Duan #include <string>
46d7857201SEd Tanous #include <utility>
47a14c9113SEd Tanous #include <vector>
481e270c5fSPatrick Williams 
49e5aaf047SAppaRao Puli namespace redfish
50e5aaf047SAppaRao Puli {
51e5aaf047SAppaRao Puli 
52156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
53156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
54fb546105SMyung Bae static constexpr const std::array<const char*, 4> supportedRegPrefixes = {
55fb546105SMyung Bae     "Base", "OpenBMC", "TaskEvent", "HeartbeatEvent"};
56e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
57e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
58e5aaf047SAppaRao Puli 
59fb546105SMyung Bae static constexpr const std::array<const char*, 2> supportedResourceTypes = {
60fb546105SMyung Bae     "Task", "Heartbeat"};
61e56f254cSSunitha Harish 
627e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
63e5aaf047SAppaRao Puli {
647e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
65ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
66bd79bce8SPatrick Williams         .methods(
67bd79bce8SPatrick Williams             boost::beast::http::verb::
68bd79bce8SPatrick Williams                 get)([&app](
69bd79bce8SPatrick Williams                          const crow::Request& req,
70002d39b4SEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
713ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
7245ca1b86SEd Tanous             {
7345ca1b86SEd Tanous                 return;
7445ca1b86SEd Tanous             }
751476687dSEd Tanous 
761476687dSEd Tanous             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
771476687dSEd Tanous             asyncResp->res.jsonValue["@odata.type"] =
781476687dSEd Tanous                 "#EventService.v1_5_0.EventService";
791476687dSEd Tanous             asyncResp->res.jsonValue["Id"] = "EventService";
801476687dSEd Tanous             asyncResp->res.jsonValue["Name"] = "Event Service";
815e44e3d8SAppaRao Puli             asyncResp->res.jsonValue["ServerSentEventUri"] =
825e44e3d8SAppaRao Puli                 "/redfish/v1/EventService/SSE";
835e44e3d8SAppaRao Puli 
841476687dSEd Tanous             asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
851476687dSEd Tanous                 "/redfish/v1/EventService/Subscriptions";
86bd79bce8SPatrick Williams             asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
87bd79bce8SPatrick Williams                                     ["target"] =
881476687dSEd Tanous                 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
89e5aaf047SAppaRao Puli 
9028afb49cSJunLin Chen             const persistent_data::EventServiceConfig eventServiceConfig =
9128afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
9228afb49cSJunLin Chen                     .getEventServiceConfig();
937d1cc387SAppaRao Puli 
947d1cc387SAppaRao Puli             asyncResp->res.jsonValue["Status"]["State"] =
9528afb49cSJunLin Chen                 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
96bd79bce8SPatrick Williams             asyncResp->res.jsonValue["ServiceEnabled"] =
97bd79bce8SPatrick Williams                 eventServiceConfig.enabled;
987e860f15SJohn Edward Broadbent             asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
9928afb49cSJunLin Chen                 eventServiceConfig.retryAttempts;
100e5aaf047SAppaRao Puli             asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
10128afb49cSJunLin Chen                 eventServiceConfig.retryTimeoutInterval;
102bd79bce8SPatrick Williams             asyncResp->res.jsonValue["EventFormatTypes"] =
103bd79bce8SPatrick Williams                 supportedEvtFormatTypes;
1040fda0f12SGeorge Liu             asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
1050fda0f12SGeorge Liu             asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
10607941a88SAyushi Smriti 
107613dabeaSEd Tanous             nlohmann::json::object_t supportedSSEFilters;
108613dabeaSEd Tanous             supportedSSEFilters["EventFormatType"] = true;
109613dabeaSEd Tanous             supportedSSEFilters["MessageId"] = true;
110613dabeaSEd Tanous             supportedSSEFilters["MetricReportDefinition"] = true;
111613dabeaSEd Tanous             supportedSSEFilters["RegistryPrefix"] = true;
112613dabeaSEd Tanous             supportedSSEFilters["OriginResource"] = false;
113613dabeaSEd Tanous             supportedSSEFilters["ResourceType"] = false;
11407941a88SAyushi Smriti 
11507941a88SAyushi Smriti             asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
116613dabeaSEd Tanous                 std::move(supportedSSEFilters);
1177e860f15SJohn Edward Broadbent         });
118e5aaf047SAppaRao Puli 
1197e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
120ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1217e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
12245ca1b86SEd Tanous             [&app](const crow::Request& req,
12345ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1243ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
125e5aaf047SAppaRao Puli                 {
12645ca1b86SEd Tanous                     return;
12745ca1b86SEd Tanous                 }
128e5aaf047SAppaRao Puli                 std::optional<bool> serviceEnabled;
129e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryAttemps;
130e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryInterval;
131afc474aeSMyung Bae                 if (!json_util::readJsonPatch(                         //
132afc474aeSMyung Bae                         req, asyncResp->res,                           //
133afc474aeSMyung Bae                         "DeliveryRetryAttempts", retryAttemps,         //
134afc474aeSMyung Bae                         "DeliveryRetryIntervalSeconds", retryInterval, //
135afc474aeSMyung Bae                         "ServiceEnabled", serviceEnabled               //
136afc474aeSMyung Bae                         ))
137e5aaf047SAppaRao Puli                 {
138e5aaf047SAppaRao Puli                     return;
139e5aaf047SAppaRao Puli                 }
140e5aaf047SAppaRao Puli 
14128afb49cSJunLin Chen                 persistent_data::EventServiceConfig eventServiceConfig =
14228afb49cSJunLin Chen                     persistent_data::EventServiceStore::getInstance()
14328afb49cSJunLin Chen                         .getEventServiceConfig();
1447d1cc387SAppaRao Puli 
145e5aaf047SAppaRao Puli                 if (serviceEnabled)
146e5aaf047SAppaRao Puli                 {
14728afb49cSJunLin Chen                     eventServiceConfig.enabled = *serviceEnabled;
148e5aaf047SAppaRao Puli                 }
149e5aaf047SAppaRao Puli 
150e5aaf047SAppaRao Puli                 if (retryAttemps)
151e5aaf047SAppaRao Puli                 {
152e5aaf047SAppaRao Puli                     // Supported range [1-3]
153e5aaf047SAppaRao Puli                     if ((*retryAttemps < 1) || (*retryAttemps > 3))
154e5aaf047SAppaRao Puli                     {
155e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
156e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryAttemps),
157e5aaf047SAppaRao Puli                             "DeliveryRetryAttempts", "[1-3]");
158e5aaf047SAppaRao Puli                     }
159e5aaf047SAppaRao Puli                     else
160e5aaf047SAppaRao Puli                     {
16128afb49cSJunLin Chen                         eventServiceConfig.retryAttempts = *retryAttemps;
162e5aaf047SAppaRao Puli                     }
163e5aaf047SAppaRao Puli                 }
164e5aaf047SAppaRao Puli 
165e5aaf047SAppaRao Puli                 if (retryInterval)
166e5aaf047SAppaRao Puli                 {
16733a32b34SGunnar Mills                     // Supported range [5 - 180]
16833a32b34SGunnar Mills                     if ((*retryInterval < 5) || (*retryInterval > 180))
169e5aaf047SAppaRao Puli                     {
170e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
171e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryInterval),
17233a32b34SGunnar Mills                             "DeliveryRetryIntervalSeconds", "[5-180]");
173e5aaf047SAppaRao Puli                     }
174e5aaf047SAppaRao Puli                     else
175e5aaf047SAppaRao Puli                     {
176bd79bce8SPatrick Williams                         eventServiceConfig.retryTimeoutInterval =
177bd79bce8SPatrick Williams                             *retryInterval;
178e5aaf047SAppaRao Puli                     }
179e5aaf047SAppaRao Puli                 }
180e5aaf047SAppaRao Puli 
1817d1cc387SAppaRao Puli                 EventServiceManager::getInstance().setEventServiceConfig(
18228afb49cSJunLin Chen                     eventServiceConfig);
1837e860f15SJohn Edward Broadbent             });
1840b4bdd93SAppaRao Puli }
1850b4bdd93SAppaRao Puli 
1867e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1870b4bdd93SAppaRao Puli {
1887e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1897e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
190ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1917e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
19245ca1b86SEd Tanous             [&app](const crow::Request& req,
1937e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1943ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
19545ca1b86SEd Tanous                 {
19645ca1b86SEd Tanous                     return;
19745ca1b86SEd Tanous                 }
19881ee0e74SChandramohan Harkude 
1994a19a7b5SEd Tanous                 // From the Redfish spec on EventId
2004a19a7b5SEd Tanous                 // A service can ignore this value and replace it with its own.
2014a19a7b5SEd Tanous                 // note that this parameter is intentionally ignored
2024a19a7b5SEd Tanous 
2034a19a7b5SEd Tanous                 std::optional<std::string> eventId;
20481ee0e74SChandramohan Harkude                 TestEvent testEvent;
20581ee0e74SChandramohan Harkude                 // clang-format off
20681ee0e74SChandramohan Harkude                 if (!json_util::readJsonAction(
20781ee0e74SChandramohan Harkude                         req, asyncResp->res,
20881ee0e74SChandramohan Harkude                         "EventGroupId", testEvent.eventGroupId,
2094a19a7b5SEd Tanous                         "EventId", eventId,
21081ee0e74SChandramohan Harkude                         "EventTimestamp", testEvent.eventTimestamp,
21181ee0e74SChandramohan Harkude                         "Message", testEvent.message,
21281ee0e74SChandramohan Harkude                         "MessageArgs", testEvent.messageArgs,
21381ee0e74SChandramohan Harkude                         "MessageId", testEvent.messageId,
21481ee0e74SChandramohan Harkude                         "OriginOfCondition", testEvent.originOfCondition,
21581ee0e74SChandramohan Harkude                         "Resolution", testEvent.resolution,
21681ee0e74SChandramohan Harkude                         "Severity", testEvent.severity))
21781ee0e74SChandramohan Harkude                 {
21881ee0e74SChandramohan Harkude                     return;
21981ee0e74SChandramohan Harkude                 }
22081ee0e74SChandramohan Harkude                 // clang-format on
22181ee0e74SChandramohan Harkude 
22281ee0e74SChandramohan Harkude                 if (!EventServiceManager::getInstance().sendTestEventLog(
22381ee0e74SChandramohan Harkude                         testEvent))
2246ba8c82eSsunharis_in                 {
2256ba8c82eSsunharis_in                     messages::serviceDisabled(asyncResp->res,
2266ba8c82eSsunharis_in                                               "/redfish/v1/EventService/");
2276ba8c82eSsunharis_in                     return;
2286ba8c82eSsunharis_in                 }
2298d1b46d7Szhanghch05                 asyncResp->res.result(boost::beast::http::status::no_content);
2307e860f15SJohn Edward Broadbent             });
231e5aaf047SAppaRao Puli }
232e5aaf047SAppaRao Puli 
2333d30708fSChicago Duan inline void doSubscriptionCollection(
234e81de512SEd Tanous     const boost::system::error_code& ec,
2353d30708fSChicago Duan     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2363d30708fSChicago Duan     const dbus::utility::ManagedObjectType& resp)
2373d30708fSChicago Duan {
2383d30708fSChicago Duan     if (ec)
2393d30708fSChicago Duan     {
2401306101eSEd Tanous         if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
2413d30708fSChicago Duan         {
2423d30708fSChicago Duan             // This is an optional process so just return if it isn't there
2433d30708fSChicago Duan             return;
2443d30708fSChicago Duan         }
2453d30708fSChicago Duan 
24662598e31SEd Tanous         BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
2473d30708fSChicago Duan         messages::internalError(asyncResp->res);
2483d30708fSChicago Duan         return;
2493d30708fSChicago Duan     }
2503d30708fSChicago Duan     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
2513d30708fSChicago Duan     for (const auto& objpath : resp)
2523d30708fSChicago Duan     {
2533d30708fSChicago Duan         sdbusplus::message::object_path path(objpath.first);
2543d30708fSChicago Duan         const std::string snmpId = path.filename();
2553d30708fSChicago Duan         if (snmpId.empty())
2563d30708fSChicago Duan         {
25762598e31SEd Tanous             BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
2583d30708fSChicago Duan             messages::internalError(asyncResp->res);
2593d30708fSChicago Duan             return;
2603d30708fSChicago Duan         }
2613d30708fSChicago Duan 
2623d30708fSChicago Duan         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
2633d30708fSChicago Duan     }
2643d30708fSChicago Duan }
2653d30708fSChicago Duan 
2667e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
267e5aaf047SAppaRao Puli {
2681ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
269ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
2707e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
27145ca1b86SEd Tanous             [&app](const crow::Request& req,
2727e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2733ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
27445ca1b86SEd Tanous                 {
27545ca1b86SEd Tanous                     return;
27645ca1b86SEd Tanous                 }
2771476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.type"] =
2781476687dSEd Tanous                     "#EventDestinationCollection.EventDestinationCollection";
2791476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.id"] =
2801476687dSEd Tanous                     "/redfish/v1/EventService/Subscriptions";
281bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Name"] =
282bd79bce8SPatrick Williams                     "Event Destination Collections";
283e5aaf047SAppaRao Puli 
284bd79bce8SPatrick Williams                 nlohmann::json& memberArray =
285bd79bce8SPatrick Williams                     asyncResp->res.jsonValue["Members"];
286e5aaf047SAppaRao Puli 
287b52664e2SAppaRao Puli                 std::vector<std::string> subscripIds =
288b52664e2SAppaRao Puli                     EventServiceManager::getInstance().getAllIDs();
289b52664e2SAppaRao Puli                 memberArray = nlohmann::json::array();
290bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Members@odata.count"] =
291bd79bce8SPatrick Williams                     subscripIds.size();
292b52664e2SAppaRao Puli 
293b52664e2SAppaRao Puli                 for (const std::string& id : subscripIds)
294e5aaf047SAppaRao Puli                 {
2951476687dSEd Tanous                     nlohmann::json::object_t member;
2963d30708fSChicago Duan                     member["@odata.id"] = boost::urls::format(
2973d30708fSChicago Duan                         "/redfish/v1/EventService/Subscriptions/{}" + id);
298b2ba3072SPatrick Williams                     memberArray.emplace_back(std::move(member));
299e5aaf047SAppaRao Puli                 }
300*177612aaSEd Tanous                 dbus::utility::async_method_call(
301*177612aaSEd Tanous                     asyncResp,
302e81de512SEd Tanous                     [asyncResp](const boost::system::error_code& ec,
3033d30708fSChicago Duan                                 const dbus::utility::ManagedObjectType& resp) {
3043d30708fSChicago Duan                         doSubscriptionCollection(ec, asyncResp, resp);
3053d30708fSChicago Duan                     },
3063d30708fSChicago Duan                     "xyz.openbmc_project.Network.SNMP",
3073d30708fSChicago Duan                     "/xyz/openbmc_project/network/snmp/manager",
3083d30708fSChicago Duan                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
3097e860f15SJohn Edward Broadbent             });
3103d30708fSChicago Duan 
3117e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
3127eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
313bd79bce8SPatrick Williams         .methods(
314bd79bce8SPatrick Williams             boost::beast::http::verb::
315bd79bce8SPatrick Williams                 post)([&app](
316bd79bce8SPatrick Williams                           const crow::Request& req,
3177e860f15SJohn Edward Broadbent                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3183ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
31945ca1b86SEd Tanous             {
32045ca1b86SEd Tanous                 return;
32145ca1b86SEd Tanous             }
322fffb8c1fSEd Tanous             if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
323fffb8c1fSEd Tanous                 maxNoOfSubscriptions)
324e5aaf047SAppaRao Puli             {
325e5aaf047SAppaRao Puli                 messages::eventSubscriptionLimitExceeded(asyncResp->res);
326e5aaf047SAppaRao Puli                 return;
327e5aaf047SAppaRao Puli             }
328e5aaf047SAppaRao Puli             std::string destUrl;
329e5aaf047SAppaRao Puli             std::string protocol;
33019bb362bSEd Tanous             std::optional<bool> verifyCertificate;
331e5aaf047SAppaRao Puli             std::optional<std::string> context;
332e5aaf047SAppaRao Puli             std::optional<std::string> subscriptionType;
33323a21a1cSEd Tanous             std::optional<std::string> eventFormatType2;
334e5aaf047SAppaRao Puli             std::optional<std::string> retryPolicy;
3355064a25bSMyung Bae             std::optional<bool> sendHeartbeat;
3365064a25bSMyung Bae             std::optional<uint64_t> hbIntervalMinutes;
337e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> msgIds;
338e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> regPrefixes;
339a14c9113SEd Tanous             std::optional<std::vector<std::string>> originResources;
340e56f254cSSunitha Harish             std::optional<std::vector<std::string>> resTypes;
34178d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> headers;
34278d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
343e5aaf047SAppaRao Puli 
344afc474aeSMyung Bae             if (!json_util::readJsonPatch(                         //
345afc474aeSMyung Bae                     req, asyncResp->res,                           //
346afc474aeSMyung Bae                     "Context", context,                            //
347afc474aeSMyung Bae                     "DeliveryRetryPolicy", retryPolicy,            //
348afc474aeSMyung Bae                     "Destination", destUrl,                        //
349afc474aeSMyung Bae                     "EventFormatType", eventFormatType2,           //
3505064a25bSMyung Bae                     "HeartbeatIntervalMinutes", hbIntervalMinutes, //
351afc474aeSMyung Bae                     "HttpHeaders", headers,                        //
352afc474aeSMyung Bae                     "MessageIds", msgIds,                          //
353afc474aeSMyung Bae                     "MetricReportDefinitions", mrdJsonArray,       //
354afc474aeSMyung Bae                     "OriginResources", originResources,            //
355afc474aeSMyung Bae                     "Protocol", protocol,                          //
356afc474aeSMyung Bae                     "RegistryPrefixes", regPrefixes,               //
357afc474aeSMyung Bae                     "ResourceTypes", resTypes,                     //
3585064a25bSMyung Bae                     "SendHeartbeat", sendHeartbeat,                //
359afc474aeSMyung Bae                     "SubscriptionType", subscriptionType,          //
360afc474aeSMyung Bae                     "VerifyCertificate", verifyCertificate         //
3614b712a29SEd Tanous                     ))
362e5aaf047SAppaRao Puli             {
363e5aaf047SAppaRao Puli                 return;
364e5aaf047SAppaRao Puli             }
3654b712a29SEd Tanous             // clang-format on
366e5aaf047SAppaRao Puli 
367600af5f1SAppaRao Puli             // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
368600af5f1SAppaRao Puli             static constexpr const uint16_t maxDestinationSize = 2000;
369600af5f1SAppaRao Puli             if (destUrl.size() > maxDestinationSize)
370600af5f1SAppaRao Puli             {
371600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Destination",
372600af5f1SAppaRao Puli                                              maxDestinationSize);
373600af5f1SAppaRao Puli                 return;
374600af5f1SAppaRao Puli             }
375600af5f1SAppaRao Puli 
376dd28ba82SAppaRao Puli             if (regPrefixes && msgIds)
377dd28ba82SAppaRao Puli             {
37826f6976fSEd Tanous                 if (!regPrefixes->empty() && !msgIds->empty())
379dd28ba82SAppaRao Puli                 {
380bd79bce8SPatrick Williams                     messages::propertyValueConflict(
381bd79bce8SPatrick Williams                         asyncResp->res, "MessageIds", "RegistryPrefixes");
382dd28ba82SAppaRao Puli                     return;
383dd28ba82SAppaRao Puli                 }
384dd28ba82SAppaRao Puli             }
385dd28ba82SAppaRao Puli 
3866fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
387a716aa74SEd Tanous                 boost::urls::parse_absolute_uri(destUrl);
388a716aa74SEd Tanous             if (!url)
389e5aaf047SAppaRao Puli             {
390bd79bce8SPatrick Williams                 BMCWEB_LOG_WARNING(
391bd79bce8SPatrick Williams                     "Failed to validate and split destination url");
392e5aaf047SAppaRao Puli                 messages::propertyValueFormatError(asyncResp->res, destUrl,
393e5aaf047SAppaRao Puli                                                    "Destination");
394e5aaf047SAppaRao Puli                 return;
395e5aaf047SAppaRao Puli             }
396a716aa74SEd Tanous             url->normalize();
397b07942e3SGeorge Liu 
398b07942e3SGeorge Liu             // port_number returns zero if it is not a valid representable port
399b07942e3SGeorge Liu             if (url->has_port() && url->port_number() == 0)
400b07942e3SGeorge Liu             {
401b07942e3SGeorge Liu                 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
402b07942e3SGeorge Liu                                    url->port());
403b07942e3SGeorge Liu                 messages::propertyValueFormatError(asyncResp->res, destUrl,
404b07942e3SGeorge Liu                                                    "Destination");
405b07942e3SGeorge Liu                 return;
406b07942e3SGeorge Liu             }
407b07942e3SGeorge Liu 
408a716aa74SEd Tanous             crow::utility::setProtocolDefaults(*url, protocol);
409a716aa74SEd Tanous             crow::utility::setPortDefaults(*url);
410a716aa74SEd Tanous 
411a716aa74SEd Tanous             if (url->path().empty())
412a716aa74SEd Tanous             {
413a716aa74SEd Tanous                 url->set_path("/");
414a716aa74SEd Tanous             }
415a716aa74SEd Tanous 
416a716aa74SEd Tanous             if (url->has_userinfo())
417a716aa74SEd Tanous             {
418a716aa74SEd Tanous                 messages::propertyValueFormatError(asyncResp->res, destUrl,
419a716aa74SEd Tanous                                                    "Destination");
420a716aa74SEd Tanous                 return;
421a716aa74SEd Tanous             }
422b52664e2SAppaRao Puli 
4233d30708fSChicago Duan             if (protocol == "SNMPv2c")
4243d30708fSChicago Duan             {
4253d30708fSChicago Duan                 if (context)
4263d30708fSChicago Duan                 {
4273d30708fSChicago Duan                     messages::propertyValueConflict(asyncResp->res, "Context",
4283d30708fSChicago Duan                                                     "Protocol");
4293d30708fSChicago Duan                     return;
4303d30708fSChicago Duan                 }
4313d30708fSChicago Duan                 if (eventFormatType2)
4323d30708fSChicago Duan                 {
433bd79bce8SPatrick Williams                     messages::propertyValueConflict(
434bd79bce8SPatrick Williams                         asyncResp->res, "EventFormatType", "Protocol");
4353d30708fSChicago Duan                     return;
4363d30708fSChicago Duan                 }
4373d30708fSChicago Duan                 if (retryPolicy)
4383d30708fSChicago Duan                 {
439bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
440bd79bce8SPatrick Williams                                                     "RetryPolicy", "Protocol");
4413d30708fSChicago Duan                     return;
4423d30708fSChicago Duan                 }
4435064a25bSMyung Bae                 if (sendHeartbeat)
4445064a25bSMyung Bae                 {
4455064a25bSMyung Bae                     messages::propertyValueConflict(
4465064a25bSMyung Bae                         asyncResp->res, "SendHeartbeat", "Protocol");
4475064a25bSMyung Bae                     return;
4485064a25bSMyung Bae                 }
4495064a25bSMyung Bae                 if (hbIntervalMinutes)
4505064a25bSMyung Bae                 {
4515064a25bSMyung Bae                     messages::propertyValueConflict(
4525064a25bSMyung Bae                         asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
4535064a25bSMyung Bae                     return;
4545064a25bSMyung Bae                 }
4553d30708fSChicago Duan                 if (msgIds)
4563d30708fSChicago Duan                 {
457bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
458bd79bce8SPatrick Williams                                                     "MessageIds", "Protocol");
4593d30708fSChicago Duan                     return;
4603d30708fSChicago Duan                 }
4613d30708fSChicago Duan                 if (regPrefixes)
4623d30708fSChicago Duan                 {
463bd79bce8SPatrick Williams                     messages::propertyValueConflict(
464bd79bce8SPatrick Williams                         asyncResp->res, "RegistryPrefixes", "Protocol");
4653d30708fSChicago Duan                     return;
4663d30708fSChicago Duan                 }
4673d30708fSChicago Duan                 if (resTypes)
4683d30708fSChicago Duan                 {
469bd79bce8SPatrick Williams                     messages::propertyValueConflict(
470bd79bce8SPatrick Williams                         asyncResp->res, "ResourceTypes", "Protocol");
4713d30708fSChicago Duan                     return;
4723d30708fSChicago Duan                 }
4733d30708fSChicago Duan                 if (headers)
4743d30708fSChicago Duan                 {
475bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
476bd79bce8SPatrick Williams                                                     "HttpHeaders", "Protocol");
4773d30708fSChicago Duan                     return;
4783d30708fSChicago Duan                 }
4793d30708fSChicago Duan                 if (mrdJsonArray)
4803d30708fSChicago Duan                 {
4813d30708fSChicago Duan                     messages::propertyValueConflict(
4823d30708fSChicago Duan                         asyncResp->res, "MetricReportDefinitions", "Protocol");
4833d30708fSChicago Duan                     return;
4843d30708fSChicago Duan                 }
485a716aa74SEd Tanous                 if (url->scheme() != "snmp")
486a716aa74SEd Tanous                 {
487bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
488bd79bce8SPatrick Williams                                                     "Destination", "Protocol");
4893d30708fSChicago Duan                     return;
4903d30708fSChicago Duan                 }
4913d30708fSChicago Duan 
492a716aa74SEd Tanous                 addSnmpTrapClient(asyncResp, url->host_address(),
493a716aa74SEd Tanous                                   url->port_number());
494a716aa74SEd Tanous                 return;
495b52664e2SAppaRao Puli             }
4963d30708fSChicago Duan 
497a716aa74SEd Tanous             std::shared_ptr<Subscription> subValue =
49821a94d5cSMyung Bae                 std::make_shared<Subscription>(
4995fe4ef35SMyung Bae                     std::make_shared<persistent_data::UserSubscription>(), *url,
5009838eb20SEd Tanous                     getIoContext());
501e5aaf047SAppaRao Puli 
502e5aaf047SAppaRao Puli             if (subscriptionType)
503e5aaf047SAppaRao Puli             {
504e5aaf047SAppaRao Puli                 if (*subscriptionType != "RedfishEvent")
505e5aaf047SAppaRao Puli                 {
506fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
507fffb8c1fSEd Tanous                         asyncResp->res, *subscriptionType, "SubscriptionType");
508e5aaf047SAppaRao Puli                     return;
509e5aaf047SAppaRao Puli                 }
5105fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = *subscriptionType;
511e5aaf047SAppaRao Puli             }
512e5aaf047SAppaRao Puli             else
513e5aaf047SAppaRao Puli             {
5144b712a29SEd Tanous                 // Default
5155fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = "RedfishEvent";
516e5aaf047SAppaRao Puli             }
517e5aaf047SAppaRao Puli 
518e5aaf047SAppaRao Puli             if (protocol != "Redfish")
519e5aaf047SAppaRao Puli             {
520e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, protocol,
521e5aaf047SAppaRao Puli                                                  "Protocol");
522e5aaf047SAppaRao Puli                 return;
523e5aaf047SAppaRao Puli             }
5245fe4ef35SMyung Bae             subValue->userSub->protocol = protocol;
525e5aaf047SAppaRao Puli 
52619bb362bSEd Tanous             if (verifyCertificate)
52719bb362bSEd Tanous             {
5285fe4ef35SMyung Bae                 subValue->userSub->verifyCertificate = *verifyCertificate;
52919bb362bSEd Tanous             }
53019bb362bSEd Tanous 
53123a21a1cSEd Tanous             if (eventFormatType2)
532e5aaf047SAppaRao Puli             {
533bd79bce8SPatrick Williams                 if (std::ranges::find(supportedEvtFormatTypes,
534bd79bce8SPatrick Williams                                       *eventFormatType2) ==
5353544d2a7SEd Tanous                     supportedEvtFormatTypes.end())
536e5aaf047SAppaRao Puli                 {
537fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
538fffb8c1fSEd Tanous                         asyncResp->res, *eventFormatType2, "EventFormatType");
539e5aaf047SAppaRao Puli                     return;
540e5aaf047SAppaRao Puli                 }
5415fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = *eventFormatType2;
542e5aaf047SAppaRao Puli             }
543e5aaf047SAppaRao Puli             else
544e5aaf047SAppaRao Puli             {
545e5aaf047SAppaRao Puli                 // If not specified, use default "Event"
5465fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = "Event";
547e5aaf047SAppaRao Puli             }
548e5aaf047SAppaRao Puli 
549e5aaf047SAppaRao Puli             if (context)
550e5aaf047SAppaRao Puli             {
5518ece0e45SEd Tanous                 // This value is selected arbitrarily.
552600af5f1SAppaRao Puli                 constexpr const size_t maxContextSize = 256;
553600af5f1SAppaRao Puli                 if (context->size() > maxContextSize)
554600af5f1SAppaRao Puli                 {
555600af5f1SAppaRao Puli                     messages::stringValueTooLong(asyncResp->res, "Context",
556600af5f1SAppaRao Puli                                                  maxContextSize);
557600af5f1SAppaRao Puli                     return;
558600af5f1SAppaRao Puli                 }
5595fe4ef35SMyung Bae                 subValue->userSub->customText = *context;
560e5aaf047SAppaRao Puli             }
561e5aaf047SAppaRao Puli 
562e5aaf047SAppaRao Puli             if (headers)
563e5aaf047SAppaRao Puli             {
564600af5f1SAppaRao Puli                 size_t cumulativeLen = 0;
565600af5f1SAppaRao Puli 
56678d4ec4fSEd Tanous                 for (const nlohmann::json::object_t& headerChunk : *headers)
567601c71aeSEd Tanous                 {
56878d4ec4fSEd Tanous                     for (const auto& item : headerChunk)
56978d4ec4fSEd Tanous                     {
57078d4ec4fSEd Tanous                         const std::string* value =
57178d4ec4fSEd Tanous                             item.second.get_ptr<const std::string*>();
57278d4ec4fSEd Tanous                         if (value == nullptr)
57378d4ec4fSEd Tanous                         {
57478d4ec4fSEd Tanous                             messages::propertyValueFormatError(
57578d4ec4fSEd Tanous                                 asyncResp->res, item.second,
57678d4ec4fSEd Tanous                                 "HttpHeaders/" + item.first);
57778d4ec4fSEd Tanous                             return;
57878d4ec4fSEd Tanous                         }
57978d4ec4fSEd Tanous                         // Adding a new json value is the size of the key, +
58078d4ec4fSEd Tanous                         // the size of the value + 2 * 2 quotes for each, +
58178d4ec4fSEd Tanous                         // the colon and space between. example:
58278d4ec4fSEd Tanous                         // "key": "value"
58378d4ec4fSEd Tanous                         cumulativeLen += item.first.size() + value->size() + 6;
584600af5f1SAppaRao Puli                         // This value is selected to mirror http_connection.hpp
585600af5f1SAppaRao Puli                         constexpr const uint16_t maxHeaderSizeED = 8096;
586600af5f1SAppaRao Puli                         if (cumulativeLen > maxHeaderSizeED)
587600af5f1SAppaRao Puli                         {
58878d4ec4fSEd Tanous                             messages::arraySizeTooLong(
58978d4ec4fSEd Tanous                                 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
590600af5f1SAppaRao Puli                             return;
591600af5f1SAppaRao Puli                         }
5925fe4ef35SMyung Bae                         subValue->userSub->httpHeaders.set(item.first, *value);
593601c71aeSEd Tanous                     }
594601c71aeSEd Tanous                 }
595e5aaf047SAppaRao Puli             }
596e5aaf047SAppaRao Puli 
597e5aaf047SAppaRao Puli             if (regPrefixes)
598e5aaf047SAppaRao Puli             {
599e5aaf047SAppaRao Puli                 for (const std::string& it : *regPrefixes)
600e5aaf047SAppaRao Puli                 {
6013544d2a7SEd Tanous                     if (std::ranges::find(supportedRegPrefixes, it) ==
6023544d2a7SEd Tanous                         supportedRegPrefixes.end())
603e5aaf047SAppaRao Puli                     {
604fffb8c1fSEd Tanous                         messages::propertyValueNotInList(asyncResp->res, it,
605fffb8c1fSEd Tanous                                                          "RegistryPrefixes");
606e5aaf047SAppaRao Puli                         return;
607e5aaf047SAppaRao Puli                     }
608e5aaf047SAppaRao Puli                 }
6095fe4ef35SMyung Bae                 subValue->userSub->registryPrefixes = *regPrefixes;
610e5aaf047SAppaRao Puli             }
611e5aaf047SAppaRao Puli 
612a14c9113SEd Tanous             if (originResources)
613a14c9113SEd Tanous             {
6145fe4ef35SMyung Bae                 subValue->userSub->originResources = *originResources;
615a14c9113SEd Tanous             }
616a14c9113SEd Tanous 
617e56f254cSSunitha Harish             if (resTypes)
618e56f254cSSunitha Harish             {
619e56f254cSSunitha Harish                 for (const std::string& it : *resTypes)
620e56f254cSSunitha Harish                 {
6213544d2a7SEd Tanous                     if (std::ranges::find(supportedResourceTypes, it) ==
6223544d2a7SEd Tanous                         supportedResourceTypes.end())
623e56f254cSSunitha Harish                     {
624e56f254cSSunitha Harish                         messages::propertyValueNotInList(asyncResp->res, it,
625e56f254cSSunitha Harish                                                          "ResourceTypes");
626e56f254cSSunitha Harish                         return;
627e56f254cSSunitha Harish                     }
628e56f254cSSunitha Harish                 }
6295fe4ef35SMyung Bae                 subValue->userSub->resourceTypes = *resTypes;
630e56f254cSSunitha Harish             }
631e56f254cSSunitha Harish 
632e5aaf047SAppaRao Puli             if (msgIds)
633e5aaf047SAppaRao Puli             {
634b304bd79SP Dheeraj Srujan Kumar                 std::vector<std::string> registryPrefix;
635b304bd79SP Dheeraj Srujan Kumar 
6367e860f15SJohn Edward Broadbent                 // If no registry prefixes are mentioned, consider all
6377e860f15SJohn Edward Broadbent                 // supported prefixes
6385fe4ef35SMyung Bae                 if (subValue->userSub->registryPrefixes.empty())
639b304bd79SP Dheeraj Srujan Kumar                 {
640b304bd79SP Dheeraj Srujan Kumar                     registryPrefix.assign(supportedRegPrefixes.begin(),
641b304bd79SP Dheeraj Srujan Kumar                                           supportedRegPrefixes.end());
642b304bd79SP Dheeraj Srujan Kumar                 }
643b304bd79SP Dheeraj Srujan Kumar                 else
644b304bd79SP Dheeraj Srujan Kumar                 {
6455fe4ef35SMyung Bae                     registryPrefix = subValue->userSub->registryPrefixes;
646b304bd79SP Dheeraj Srujan Kumar                 }
647b304bd79SP Dheeraj Srujan Kumar 
648b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& id : *msgIds)
649b304bd79SP Dheeraj Srujan Kumar                 {
650b304bd79SP Dheeraj Srujan Kumar                     bool validId = false;
651b304bd79SP Dheeraj Srujan Kumar 
652b304bd79SP Dheeraj Srujan Kumar                     // Check for Message ID in each of the selected Registry
653b304bd79SP Dheeraj Srujan Kumar                     for (const std::string& it : registryPrefix)
654b304bd79SP Dheeraj Srujan Kumar                     {
655fffb8c1fSEd Tanous                         const std::span<const redfish::registries::MessageEntry>
656fffb8c1fSEd Tanous                             registry =
657fffb8c1fSEd Tanous                                 redfish::registries::getRegistryFromPrefix(it);
658b304bd79SP Dheeraj Srujan Kumar 
6593544d2a7SEd Tanous                         if (std::ranges::any_of(
6603544d2a7SEd Tanous                                 registry,
661fffb8c1fSEd Tanous                                 [&id](const redfish::registries::MessageEntry&
662fffb8c1fSEd Tanous                                           messageEntry) {
66355f79e6fSEd Tanous                                     return id == messageEntry.first;
664b304bd79SP Dheeraj Srujan Kumar                                 }))
665b304bd79SP Dheeraj Srujan Kumar                         {
666b304bd79SP Dheeraj Srujan Kumar                             validId = true;
667b304bd79SP Dheeraj Srujan Kumar                             break;
668b304bd79SP Dheeraj Srujan Kumar                         }
669b304bd79SP Dheeraj Srujan Kumar                     }
670b304bd79SP Dheeraj Srujan Kumar 
671b304bd79SP Dheeraj Srujan Kumar                     if (!validId)
672b304bd79SP Dheeraj Srujan Kumar                     {
673b304bd79SP Dheeraj Srujan Kumar                         messages::propertyValueNotInList(asyncResp->res, id,
674b304bd79SP Dheeraj Srujan Kumar                                                          "MessageIds");
675b304bd79SP Dheeraj Srujan Kumar                         return;
676b304bd79SP Dheeraj Srujan Kumar                     }
677b304bd79SP Dheeraj Srujan Kumar                 }
678b304bd79SP Dheeraj Srujan Kumar 
6795fe4ef35SMyung Bae                 subValue->userSub->registryMsgIds = *msgIds;
680e5aaf047SAppaRao Puli             }
681e5aaf047SAppaRao Puli 
682e5aaf047SAppaRao Puli             if (retryPolicy)
683e5aaf047SAppaRao Puli             {
6843544d2a7SEd Tanous                 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
6853544d2a7SEd Tanous                     supportedRetryPolicies.end())
686e5aaf047SAppaRao Puli                 {
687bd79bce8SPatrick Williams                     messages::propertyValueNotInList(
688bd79bce8SPatrick Williams                         asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
689e5aaf047SAppaRao Puli                     return;
690e5aaf047SAppaRao Puli                 }
6915fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = *retryPolicy;
692e5aaf047SAppaRao Puli             }
693e5aaf047SAppaRao Puli             else
694e5aaf047SAppaRao Puli             {
695e5aaf047SAppaRao Puli                 // Default "TerminateAfterRetries"
6965fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = "TerminateAfterRetries";
697e5aaf047SAppaRao Puli             }
6985064a25bSMyung Bae             if (sendHeartbeat)
6995064a25bSMyung Bae             {
7005064a25bSMyung Bae                 subValue->userSub->sendHeartbeat = *sendHeartbeat;
7015064a25bSMyung Bae             }
7025064a25bSMyung Bae             if (hbIntervalMinutes)
7035064a25bSMyung Bae             {
7045064a25bSMyung Bae                 if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
7055064a25bSMyung Bae                 {
7065064a25bSMyung Bae                     messages::propertyValueOutOfRange(
7075064a25bSMyung Bae                         asyncResp->res, *hbIntervalMinutes,
7085064a25bSMyung Bae                         "HeartbeatIntervalMinutes");
7095064a25bSMyung Bae                     return;
7105064a25bSMyung Bae                 }
7115064a25bSMyung Bae                 subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
7125064a25bSMyung Bae             }
713e5aaf047SAppaRao Puli 
714144b6318SAppaRao Puli             if (mrdJsonArray)
715156d6b00SAppaRao Puli             {
71678d4ec4fSEd Tanous                 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
717144b6318SAppaRao Puli                 {
718144b6318SAppaRao Puli                     std::string mrdUri;
719ea2e6eecSWilly Tu 
72078d4ec4fSEd Tanous                     if (!json_util::readJsonObject(mrdObj, asyncResp->res,
72178d4ec4fSEd Tanous                                                    "@odata.id", mrdUri))
722ea2e6eecSWilly Tu 
723144b6318SAppaRao Puli                     {
724144b6318SAppaRao Puli                         return;
725144b6318SAppaRao Puli                     }
7265fe4ef35SMyung Bae                     subValue->userSub->metricReportDefinitions.emplace_back(
7274b712a29SEd Tanous                         mrdUri);
728144b6318SAppaRao Puli                 }
729156d6b00SAppaRao Puli             }
730156d6b00SAppaRao Puli 
731b52664e2SAppaRao Puli             std::string id =
732bd79bce8SPatrick Williams                 EventServiceManager::getInstance().addPushSubscription(
733bd79bce8SPatrick Williams                     subValue);
734b52664e2SAppaRao Puli             if (id.empty())
735e5aaf047SAppaRao Puli             {
736e5aaf047SAppaRao Puli                 messages::internalError(asyncResp->res);
737e5aaf047SAppaRao Puli                 return;
738e5aaf047SAppaRao Puli             }
739e5aaf047SAppaRao Puli 
740e5aaf047SAppaRao Puli             messages::created(asyncResp->res);
741e5aaf047SAppaRao Puli             asyncResp->res.addHeader(
742e5aaf047SAppaRao Puli                 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
743fb546105SMyung Bae 
744fb546105SMyung Bae             // schedule a heartbeat
745fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
746fb546105SMyung Bae             {
747fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
748fb546105SMyung Bae             }
7497e860f15SJohn Edward Broadbent         });
750e5aaf047SAppaRao Puli }
751e5aaf047SAppaRao Puli 
7527e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
753e5aaf047SAppaRao Puli {
7549d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
755ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
7567e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
75745ca1b86SEd Tanous             [&app](const crow::Request& req,
7587e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
7597e860f15SJohn Edward Broadbent                    const std::string& param) {
7603ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
76145ca1b86SEd Tanous                 {
76245ca1b86SEd Tanous                     return;
76345ca1b86SEd Tanous                 }
7643d30708fSChicago Duan 
7653d30708fSChicago Duan                 if (param.starts_with("snmp"))
7663d30708fSChicago Duan                 {
7673d30708fSChicago Duan                     getSnmpTrapClient(asyncResp, param);
7683d30708fSChicago Duan                     return;
7693d30708fSChicago Duan                 }
7703d30708fSChicago Duan 
771b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
7727e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
773b52664e2SAppaRao Puli                 if (subValue == nullptr)
774e5aaf047SAppaRao Puli                 {
775bd79bce8SPatrick Williams                     asyncResp->res.result(
776bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
777e5aaf047SAppaRao Puli                     return;
778e5aaf047SAppaRao Puli                 }
7797e860f15SJohn Edward Broadbent                 const std::string& id = param;
780e5aaf047SAppaRao Puli 
7814b712a29SEd Tanous                 const persistent_data::UserSubscription& userSub =
7825fe4ef35SMyung Bae                     *subValue->userSub;
783e56f254cSSunitha Harish 
7844b712a29SEd Tanous                 nlohmann::json& jVal = asyncResp->res.jsonValue;
7854b712a29SEd Tanous                 jVal["@odata.type"] =
7864b712a29SEd Tanous                     "#EventDestination.v1_14_1.EventDestination";
7874b712a29SEd Tanous                 jVal["Protocol"] =
7884b712a29SEd Tanous                     event_destination::EventDestinationProtocol::Redfish;
7894b712a29SEd Tanous                 jVal["@odata.id"] = boost::urls::format(
7904b712a29SEd Tanous                     "/redfish/v1/EventService/Subscriptions/{}", id);
7914b712a29SEd Tanous                 jVal["Id"] = id;
7924b712a29SEd Tanous                 jVal["Name"] = "Event Destination " + id;
7934b712a29SEd Tanous                 jVal["Destination"] = userSub.destinationUrl;
7944b712a29SEd Tanous                 jVal["Context"] = userSub.customText;
7954b712a29SEd Tanous                 jVal["SubscriptionType"] = userSub.subscriptionType;
7964b712a29SEd Tanous                 jVal["HttpHeaders"] = nlohmann::json::array();
7974b712a29SEd Tanous                 jVal["EventFormatType"] = userSub.eventFormatType;
7984b712a29SEd Tanous                 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
7994b712a29SEd Tanous                 jVal["ResourceTypes"] = userSub.resourceTypes;
8004b712a29SEd Tanous 
8014b712a29SEd Tanous                 jVal["MessageIds"] = userSub.registryMsgIds;
8024b712a29SEd Tanous                 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
8035064a25bSMyung Bae                 jVal["SendHeartbeat"] = userSub.sendHeartbeat;
8045064a25bSMyung Bae                 jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
8054b712a29SEd Tanous                 jVal["VerifyCertificate"] = userSub.verifyCertificate;
806144b6318SAppaRao Puli 
8071476687dSEd Tanous                 nlohmann::json::array_t mrdJsonArray;
8084b712a29SEd Tanous                 for (const auto& mdrUri : userSub.metricReportDefinitions)
809144b6318SAppaRao Puli                 {
8101476687dSEd Tanous                     nlohmann::json::object_t mdr;
8111476687dSEd Tanous                     mdr["@odata.id"] = mdrUri;
8121476687dSEd Tanous                     mrdJsonArray.emplace_back(std::move(mdr));
813144b6318SAppaRao Puli                 }
8144b712a29SEd Tanous                 jVal["MetricReportDefinitions"] = mrdJsonArray;
8157e860f15SJohn Edward Broadbent             });
8169d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
817ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
818ed398213SEd Tanous         // ConfigureSelf
8197eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
820ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
821432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
8227e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
82345ca1b86SEd Tanous             [&app](const crow::Request& req,
8247e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8257e860f15SJohn Edward Broadbent                    const std::string& param) {
8263ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
82745ca1b86SEd Tanous                 {
82845ca1b86SEd Tanous                     return;
82945ca1b86SEd Tanous                 }
830b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
8317e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
832b52664e2SAppaRao Puli                 if (subValue == nullptr)
833e5aaf047SAppaRao Puli                 {
834bd79bce8SPatrick Williams                     asyncResp->res.result(
835bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
836e5aaf047SAppaRao Puli                     return;
837e5aaf047SAppaRao Puli                 }
838e5aaf047SAppaRao Puli 
839e5aaf047SAppaRao Puli                 std::optional<std::string> context;
840e5aaf047SAppaRao Puli                 std::optional<std::string> retryPolicy;
8415064a25bSMyung Bae                 std::optional<bool> sendHeartbeat;
8425064a25bSMyung Bae                 std::optional<uint64_t> hbIntervalMinutes;
84319bb362bSEd Tanous                 std::optional<bool> verifyCertificate;
84478d4ec4fSEd Tanous                 std::optional<std::vector<nlohmann::json::object_t>> headers;
845e5aaf047SAppaRao Puli 
846afc474aeSMyung Bae                 if (!json_util::readJsonPatch(                         //
847afc474aeSMyung Bae                         req, asyncResp->res,                           //
848afc474aeSMyung Bae                         "Context", context,                            //
849afc474aeSMyung Bae                         "DeliveryRetryPolicy", retryPolicy,            //
8505064a25bSMyung Bae                         "HeartbeatIntervalMinutes", hbIntervalMinutes, //
851afc474aeSMyung Bae                         "HttpHeaders", headers,                        //
8525064a25bSMyung Bae                         "SendHeartbeat", sendHeartbeat,                //
853afc474aeSMyung Bae                         "VerifyCertificate", verifyCertificate         //
854afc474aeSMyung Bae                         ))
855e5aaf047SAppaRao Puli                 {
856e5aaf047SAppaRao Puli                     return;
857e5aaf047SAppaRao Puli                 }
858e5aaf047SAppaRao Puli 
859e5aaf047SAppaRao Puli                 if (context)
860e5aaf047SAppaRao Puli                 {
8615fe4ef35SMyung Bae                     subValue->userSub->customText = *context;
862e5aaf047SAppaRao Puli                 }
863e5aaf047SAppaRao Puli 
864e5aaf047SAppaRao Puli                 if (headers)
865e5aaf047SAppaRao Puli                 {
866601c71aeSEd Tanous                     boost::beast::http::fields fields;
86778d4ec4fSEd Tanous                     for (const nlohmann::json::object_t& headerChunk : *headers)
868601c71aeSEd Tanous                     {
86978d4ec4fSEd Tanous                         for (const auto& it : headerChunk)
870601c71aeSEd Tanous                         {
871601c71aeSEd Tanous                             const std::string* value =
87278d4ec4fSEd Tanous                                 it.second.get_ptr<const std::string*>();
873601c71aeSEd Tanous                             if (value == nullptr)
874601c71aeSEd Tanous                             {
875601c71aeSEd Tanous                                 messages::propertyValueFormatError(
87678d4ec4fSEd Tanous                                     asyncResp->res, it.second,
87778d4ec4fSEd Tanous                                     "HttpHeaders/" + it.first);
878601c71aeSEd Tanous                                 return;
879601c71aeSEd Tanous                             }
88078d4ec4fSEd Tanous                             fields.set(it.first, *value);
881601c71aeSEd Tanous                         }
882601c71aeSEd Tanous                     }
8835fe4ef35SMyung Bae                     subValue->userSub->httpHeaders = std::move(fields);
884e5aaf047SAppaRao Puli                 }
885e5aaf047SAppaRao Puli 
886e5aaf047SAppaRao Puli                 if (retryPolicy)
887e5aaf047SAppaRao Puli                 {
888bd79bce8SPatrick Williams                     if (std::ranges::find(supportedRetryPolicies,
889bd79bce8SPatrick Williams                                           *retryPolicy) ==
8903544d2a7SEd Tanous                         supportedRetryPolicies.end())
891e5aaf047SAppaRao Puli                     {
892bd79bce8SPatrick Williams                         messages::propertyValueNotInList(asyncResp->res,
893bd79bce8SPatrick Williams                                                          *retryPolicy,
894e5aaf047SAppaRao Puli                                                          "DeliveryRetryPolicy");
895e5aaf047SAppaRao Puli                         return;
896e5aaf047SAppaRao Puli                     }
8975fe4ef35SMyung Bae                     subValue->userSub->retryPolicy = *retryPolicy;
898e5aaf047SAppaRao Puli                 }
899e5aaf047SAppaRao Puli 
9005064a25bSMyung Bae                 if (sendHeartbeat)
9015064a25bSMyung Bae                 {
9025064a25bSMyung Bae                     subValue->userSub->sendHeartbeat = *sendHeartbeat;
9035064a25bSMyung Bae                 }
9045064a25bSMyung Bae                 if (hbIntervalMinutes)
9055064a25bSMyung Bae                 {
9065064a25bSMyung Bae                     if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
9075064a25bSMyung Bae                     {
9085064a25bSMyung Bae                         messages::propertyValueOutOfRange(
9095064a25bSMyung Bae                             asyncResp->res, *hbIntervalMinutes,
9105064a25bSMyung Bae                             "HeartbeatIntervalMinutes");
9115064a25bSMyung Bae                         return;
9125064a25bSMyung Bae                     }
9135064a25bSMyung Bae                     subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
9145064a25bSMyung Bae                 }
9155064a25bSMyung Bae 
916fb546105SMyung Bae                 if (hbIntervalMinutes || sendHeartbeat)
917fb546105SMyung Bae                 {
918fb546105SMyung Bae                     // if Heartbeat interval or send heart were changed, cancel
919fb546105SMyung Bae                     // the heartbeat timer if running and start a new heartbeat
920fb546105SMyung Bae                     // if needed
921fb546105SMyung Bae                     subValue->heartbeatParametersChanged();
922fb546105SMyung Bae                 }
923fb546105SMyung Bae 
92419bb362bSEd Tanous                 if (verifyCertificate)
92519bb362bSEd Tanous                 {
9265fe4ef35SMyung Bae                     subValue->userSub->verifyCertificate = *verifyCertificate;
92719bb362bSEd Tanous                 }
92819bb362bSEd Tanous 
929b52664e2SAppaRao Puli                 EventServiceManager::getInstance().updateSubscriptionData();
9307e860f15SJohn Edward Broadbent             });
9319d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
932ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
933ed398213SEd Tanous         // ConfigureSelf
9347eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
935ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
936432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
9377e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
93845ca1b86SEd Tanous             [&app](const crow::Request& req,
9397e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9407e860f15SJohn Edward Broadbent                    const std::string& param) {
9413ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
94245ca1b86SEd Tanous                 {
94345ca1b86SEd Tanous                     return;
94445ca1b86SEd Tanous                 }
9454b712a29SEd Tanous                 EventServiceManager& event = EventServiceManager::getInstance();
9463d30708fSChicago Duan                 if (param.starts_with("snmp"))
9473d30708fSChicago Duan                 {
9483d30708fSChicago Duan                     deleteSnmpTrapClient(asyncResp, param);
9494b712a29SEd Tanous                     event.deleteSubscription(param);
9503d30708fSChicago Duan                     return;
9513d30708fSChicago Duan                 }
9523d30708fSChicago Duan 
9534b712a29SEd Tanous                 if (!event.deleteSubscription(param))
954e5aaf047SAppaRao Puli                 {
9554b712a29SEd Tanous                     messages::resourceNotFound(asyncResp->res,
9564b712a29SEd Tanous                                                "EventDestination", param);
957e5aaf047SAppaRao Puli                     return;
958e5aaf047SAppaRao Puli                 }
9594b712a29SEd Tanous                 messages::success(asyncResp->res);
9607e860f15SJohn Edward Broadbent             });
961e5aaf047SAppaRao Puli }
962e5aaf047SAppaRao Puli 
963e5aaf047SAppaRao Puli } // namespace redfish
964