xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 3577e44683a5ade8ad02a6418984b56f4ca2bcac)
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"
193d30708fSChicago Duan #include "snmp_trap_event_clients.hpp"
20d7857201SEd Tanous #include "subscription.hpp"
21d109e2b6SAlexander Hansen #include "utils/json_utils.hpp"
22e5aaf047SAppaRao Puli 
23d7857201SEd Tanous #include <asm-generic/errno.h>
24ed398213SEd Tanous 
25d7857201SEd Tanous #include <boost/beast/http/status.hpp>
26d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
27d7857201SEd Tanous #include <boost/system/error_code.hpp>
28d7857201SEd Tanous #include <boost/system/result.hpp>
29d7857201SEd Tanous #include <boost/url/format.hpp>
30d7857201SEd Tanous #include <boost/url/parse.hpp>
31d7857201SEd Tanous #include <boost/url/url.hpp>
32d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
33d7857201SEd Tanous 
34d7857201SEd Tanous #include <algorithm>
35d7857201SEd Tanous #include <array>
36d7857201SEd Tanous #include <cerrno>
37d7857201SEd Tanous #include <cstddef>
38d7857201SEd Tanous #include <cstdint>
393d30708fSChicago Duan #include <memory>
405064a25bSMyung Bae #include <optional>
413544d2a7SEd Tanous #include <ranges>
423d30708fSChicago Duan #include <string>
43d7857201SEd Tanous #include <utility>
44a14c9113SEd Tanous #include <vector>
451e270c5fSPatrick Williams 
46e5aaf047SAppaRao Puli namespace redfish
47e5aaf047SAppaRao Puli {
48e5aaf047SAppaRao Puli 
49156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
50156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
51fb546105SMyung Bae static constexpr const std::array<const char*, 4> supportedRegPrefixes = {
52fb546105SMyung Bae     "Base", "OpenBMC", "TaskEvent", "HeartbeatEvent"};
53e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
54e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
55e5aaf047SAppaRao Puli 
56fb546105SMyung Bae static constexpr const std::array<const char*, 2> supportedResourceTypes = {
57fb546105SMyung Bae     "Task", "Heartbeat"};
58e56f254cSSunitha Harish 
597e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
60e5aaf047SAppaRao Puli {
617e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
62ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
63bd79bce8SPatrick Williams         .methods(
64bd79bce8SPatrick Williams             boost::beast::http::verb::
65bd79bce8SPatrick Williams                 get)([&app](
66bd79bce8SPatrick Williams                          const crow::Request& req,
67002d39b4SEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
683ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
6945ca1b86SEd Tanous             {
7045ca1b86SEd Tanous                 return;
7145ca1b86SEd Tanous             }
721476687dSEd Tanous 
731476687dSEd Tanous             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
741476687dSEd Tanous             asyncResp->res.jsonValue["@odata.type"] =
751476687dSEd Tanous                 "#EventService.v1_5_0.EventService";
761476687dSEd Tanous             asyncResp->res.jsonValue["Id"] = "EventService";
771476687dSEd Tanous             asyncResp->res.jsonValue["Name"] = "Event Service";
785e44e3d8SAppaRao Puli             asyncResp->res.jsonValue["ServerSentEventUri"] =
795e44e3d8SAppaRao Puli                 "/redfish/v1/EventService/SSE";
805e44e3d8SAppaRao Puli 
811476687dSEd Tanous             asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
821476687dSEd Tanous                 "/redfish/v1/EventService/Subscriptions";
83bd79bce8SPatrick Williams             asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
84bd79bce8SPatrick Williams                                     ["target"] =
851476687dSEd Tanous                 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
86e5aaf047SAppaRao Puli 
8728afb49cSJunLin Chen             const persistent_data::EventServiceConfig eventServiceConfig =
8828afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
8928afb49cSJunLin Chen                     .getEventServiceConfig();
907d1cc387SAppaRao Puli 
917d1cc387SAppaRao Puli             asyncResp->res.jsonValue["Status"]["State"] =
9228afb49cSJunLin Chen                 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
93bd79bce8SPatrick Williams             asyncResp->res.jsonValue["ServiceEnabled"] =
94bd79bce8SPatrick Williams                 eventServiceConfig.enabled;
957e860f15SJohn Edward Broadbent             asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
9628afb49cSJunLin Chen                 eventServiceConfig.retryAttempts;
97e5aaf047SAppaRao Puli             asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
9828afb49cSJunLin Chen                 eventServiceConfig.retryTimeoutInterval;
99bd79bce8SPatrick Williams             asyncResp->res.jsonValue["EventFormatTypes"] =
100bd79bce8SPatrick Williams                 supportedEvtFormatTypes;
1010fda0f12SGeorge Liu             asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
1020fda0f12SGeorge Liu             asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
10307941a88SAyushi Smriti 
104613dabeaSEd Tanous             nlohmann::json::object_t supportedSSEFilters;
105613dabeaSEd Tanous             supportedSSEFilters["EventFormatType"] = true;
106613dabeaSEd Tanous             supportedSSEFilters["MessageId"] = true;
107613dabeaSEd Tanous             supportedSSEFilters["MetricReportDefinition"] = true;
108613dabeaSEd Tanous             supportedSSEFilters["RegistryPrefix"] = true;
109613dabeaSEd Tanous             supportedSSEFilters["OriginResource"] = false;
110613dabeaSEd Tanous             supportedSSEFilters["ResourceType"] = false;
11107941a88SAyushi Smriti 
11207941a88SAyushi Smriti             asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
113613dabeaSEd Tanous                 std::move(supportedSSEFilters);
1147e860f15SJohn Edward Broadbent         });
115e5aaf047SAppaRao Puli 
1167e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
117ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1187e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
11945ca1b86SEd Tanous             [&app](const crow::Request& req,
12045ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1213ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
122e5aaf047SAppaRao Puli                 {
12345ca1b86SEd Tanous                     return;
12445ca1b86SEd Tanous                 }
125e5aaf047SAppaRao Puli                 std::optional<bool> serviceEnabled;
126e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryAttemps;
127e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryInterval;
128afc474aeSMyung Bae                 if (!json_util::readJsonPatch(                         //
129afc474aeSMyung Bae                         req, asyncResp->res,                           //
130afc474aeSMyung Bae                         "DeliveryRetryAttempts", retryAttemps,         //
131afc474aeSMyung Bae                         "DeliveryRetryIntervalSeconds", retryInterval, //
132afc474aeSMyung Bae                         "ServiceEnabled", serviceEnabled               //
133afc474aeSMyung Bae                         ))
134e5aaf047SAppaRao Puli                 {
135e5aaf047SAppaRao Puli                     return;
136e5aaf047SAppaRao Puli                 }
137e5aaf047SAppaRao Puli 
13828afb49cSJunLin Chen                 persistent_data::EventServiceConfig eventServiceConfig =
13928afb49cSJunLin Chen                     persistent_data::EventServiceStore::getInstance()
14028afb49cSJunLin Chen                         .getEventServiceConfig();
1417d1cc387SAppaRao Puli 
142e5aaf047SAppaRao Puli                 if (serviceEnabled)
143e5aaf047SAppaRao Puli                 {
14428afb49cSJunLin Chen                     eventServiceConfig.enabled = *serviceEnabled;
145e5aaf047SAppaRao Puli                 }
146e5aaf047SAppaRao Puli 
147e5aaf047SAppaRao Puli                 if (retryAttemps)
148e5aaf047SAppaRao Puli                 {
149e5aaf047SAppaRao Puli                     // Supported range [1-3]
150e5aaf047SAppaRao Puli                     if ((*retryAttemps < 1) || (*retryAttemps > 3))
151e5aaf047SAppaRao Puli                     {
152e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
153e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryAttemps),
154e5aaf047SAppaRao Puli                             "DeliveryRetryAttempts", "[1-3]");
155e5aaf047SAppaRao Puli                     }
156e5aaf047SAppaRao Puli                     else
157e5aaf047SAppaRao Puli                     {
15828afb49cSJunLin Chen                         eventServiceConfig.retryAttempts = *retryAttemps;
159e5aaf047SAppaRao Puli                     }
160e5aaf047SAppaRao Puli                 }
161e5aaf047SAppaRao Puli 
162e5aaf047SAppaRao Puli                 if (retryInterval)
163e5aaf047SAppaRao Puli                 {
16433a32b34SGunnar Mills                     // Supported range [5 - 180]
16533a32b34SGunnar Mills                     if ((*retryInterval < 5) || (*retryInterval > 180))
166e5aaf047SAppaRao Puli                     {
167e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
168e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryInterval),
16933a32b34SGunnar Mills                             "DeliveryRetryIntervalSeconds", "[5-180]");
170e5aaf047SAppaRao Puli                     }
171e5aaf047SAppaRao Puli                     else
172e5aaf047SAppaRao Puli                     {
173bd79bce8SPatrick Williams                         eventServiceConfig.retryTimeoutInterval =
174bd79bce8SPatrick Williams                             *retryInterval;
175e5aaf047SAppaRao Puli                     }
176e5aaf047SAppaRao Puli                 }
177e5aaf047SAppaRao Puli 
1787d1cc387SAppaRao Puli                 EventServiceManager::getInstance().setEventServiceConfig(
17928afb49cSJunLin Chen                     eventServiceConfig);
1807e860f15SJohn Edward Broadbent             });
1810b4bdd93SAppaRao Puli }
1820b4bdd93SAppaRao Puli 
1837e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1840b4bdd93SAppaRao Puli {
1857e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1867e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
187ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1887e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
18945ca1b86SEd Tanous             [&app](const crow::Request& req,
1907e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1913ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
19245ca1b86SEd Tanous                 {
19345ca1b86SEd Tanous                     return;
19445ca1b86SEd Tanous                 }
19581ee0e74SChandramohan Harkude 
1964a19a7b5SEd Tanous                 // From the Redfish spec on EventId
1974a19a7b5SEd Tanous                 // A service can ignore this value and replace it with its own.
1984a19a7b5SEd Tanous                 // note that this parameter is intentionally ignored
1994a19a7b5SEd Tanous 
2004a19a7b5SEd Tanous                 std::optional<std::string> eventId;
20181ee0e74SChandramohan Harkude                 TestEvent testEvent;
20281ee0e74SChandramohan Harkude                 // clang-format off
20381ee0e74SChandramohan Harkude                 if (!json_util::readJsonAction(
20481ee0e74SChandramohan Harkude                         req, asyncResp->res,
20581ee0e74SChandramohan Harkude                         "EventGroupId", testEvent.eventGroupId,
2064a19a7b5SEd Tanous                         "EventId", eventId,
20781ee0e74SChandramohan Harkude                         "EventTimestamp", testEvent.eventTimestamp,
20881ee0e74SChandramohan Harkude                         "Message", testEvent.message,
20981ee0e74SChandramohan Harkude                         "MessageArgs", testEvent.messageArgs,
21081ee0e74SChandramohan Harkude                         "MessageId", testEvent.messageId,
21181ee0e74SChandramohan Harkude                         "OriginOfCondition", testEvent.originOfCondition,
21281ee0e74SChandramohan Harkude                         "Resolution", testEvent.resolution,
21381ee0e74SChandramohan Harkude                         "Severity", testEvent.severity))
21481ee0e74SChandramohan Harkude                 {
21581ee0e74SChandramohan Harkude                     return;
21681ee0e74SChandramohan Harkude                 }
21781ee0e74SChandramohan Harkude                 // clang-format on
21881ee0e74SChandramohan Harkude 
21981ee0e74SChandramohan Harkude                 if (!EventServiceManager::getInstance().sendTestEventLog(
22081ee0e74SChandramohan Harkude                         testEvent))
2216ba8c82eSsunharis_in                 {
2226ba8c82eSsunharis_in                     messages::serviceDisabled(asyncResp->res,
2236ba8c82eSsunharis_in                                               "/redfish/v1/EventService/");
2246ba8c82eSsunharis_in                     return;
2256ba8c82eSsunharis_in                 }
2268d1b46d7Szhanghch05                 asyncResp->res.result(boost::beast::http::status::no_content);
2277e860f15SJohn Edward Broadbent             });
228e5aaf047SAppaRao Puli }
229e5aaf047SAppaRao Puli 
2303d30708fSChicago Duan inline void doSubscriptionCollection(
231e81de512SEd Tanous     const boost::system::error_code& ec,
2323d30708fSChicago Duan     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2333d30708fSChicago Duan     const dbus::utility::ManagedObjectType& resp)
2343d30708fSChicago Duan {
2353d30708fSChicago Duan     if (ec)
2363d30708fSChicago Duan     {
2371306101eSEd Tanous         if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
2383d30708fSChicago Duan         {
2393d30708fSChicago Duan             // This is an optional process so just return if it isn't there
2403d30708fSChicago Duan             return;
2413d30708fSChicago Duan         }
2423d30708fSChicago Duan 
24362598e31SEd Tanous         BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
2443d30708fSChicago Duan         messages::internalError(asyncResp->res);
2453d30708fSChicago Duan         return;
2463d30708fSChicago Duan     }
2473d30708fSChicago Duan     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
2483d30708fSChicago Duan     for (const auto& objpath : resp)
2493d30708fSChicago Duan     {
2503d30708fSChicago Duan         sdbusplus::message::object_path path(objpath.first);
2513d30708fSChicago Duan         const std::string snmpId = path.filename();
2523d30708fSChicago Duan         if (snmpId.empty())
2533d30708fSChicago Duan         {
25462598e31SEd Tanous             BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
2553d30708fSChicago Duan             messages::internalError(asyncResp->res);
2563d30708fSChicago Duan             return;
2573d30708fSChicago Duan         }
2583d30708fSChicago Duan 
2593d30708fSChicago Duan         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
2603d30708fSChicago Duan     }
2613d30708fSChicago Duan }
2623d30708fSChicago Duan 
2637e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
264e5aaf047SAppaRao Puli {
2651ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
266ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
2677e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
26845ca1b86SEd Tanous             [&app](const crow::Request& req,
2697e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2703ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
27145ca1b86SEd Tanous                 {
27245ca1b86SEd Tanous                     return;
27345ca1b86SEd Tanous                 }
2741476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.type"] =
2751476687dSEd Tanous                     "#EventDestinationCollection.EventDestinationCollection";
2761476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.id"] =
2771476687dSEd Tanous                     "/redfish/v1/EventService/Subscriptions";
278bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Name"] =
279bd79bce8SPatrick Williams                     "Event Destination Collections";
280e5aaf047SAppaRao Puli 
281bd79bce8SPatrick Williams                 nlohmann::json& memberArray =
282bd79bce8SPatrick Williams                     asyncResp->res.jsonValue["Members"];
283e5aaf047SAppaRao Puli 
284b52664e2SAppaRao Puli                 std::vector<std::string> subscripIds =
285b52664e2SAppaRao Puli                     EventServiceManager::getInstance().getAllIDs();
286b52664e2SAppaRao Puli                 memberArray = nlohmann::json::array();
287bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Members@odata.count"] =
288bd79bce8SPatrick Williams                     subscripIds.size();
289b52664e2SAppaRao Puli 
290b52664e2SAppaRao Puli                 for (const std::string& id : subscripIds)
291e5aaf047SAppaRao Puli                 {
2921476687dSEd Tanous                     nlohmann::json::object_t member;
2933d30708fSChicago Duan                     member["@odata.id"] = boost::urls::format(
2943d30708fSChicago Duan                         "/redfish/v1/EventService/Subscriptions/{}" + id);
295b2ba3072SPatrick Williams                     memberArray.emplace_back(std::move(member));
296e5aaf047SAppaRao Puli                 }
297177612aaSEd Tanous                 dbus::utility::async_method_call(
298177612aaSEd Tanous                     asyncResp,
299e81de512SEd Tanous                     [asyncResp](const boost::system::error_code& ec,
3003d30708fSChicago Duan                                 const dbus::utility::ManagedObjectType& resp) {
3013d30708fSChicago Duan                         doSubscriptionCollection(ec, asyncResp, resp);
3023d30708fSChicago Duan                     },
3033d30708fSChicago Duan                     "xyz.openbmc_project.Network.SNMP",
3043d30708fSChicago Duan                     "/xyz/openbmc_project/network/snmp/manager",
3053d30708fSChicago Duan                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
3067e860f15SJohn Edward Broadbent             });
3073d30708fSChicago Duan 
3087e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
3097eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
310bd79bce8SPatrick Williams         .methods(
311bd79bce8SPatrick Williams             boost::beast::http::verb::
312bd79bce8SPatrick Williams                 post)([&app](
313bd79bce8SPatrick Williams                           const crow::Request& req,
3147e860f15SJohn Edward Broadbent                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3153ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
31645ca1b86SEd Tanous             {
31745ca1b86SEd Tanous                 return;
31845ca1b86SEd Tanous             }
319fffb8c1fSEd Tanous             if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
320fffb8c1fSEd Tanous                 maxNoOfSubscriptions)
321e5aaf047SAppaRao Puli             {
322e5aaf047SAppaRao Puli                 messages::eventSubscriptionLimitExceeded(asyncResp->res);
323e5aaf047SAppaRao Puli                 return;
324e5aaf047SAppaRao Puli             }
325e5aaf047SAppaRao Puli             std::string destUrl;
326e5aaf047SAppaRao Puli             std::string protocol;
32719bb362bSEd Tanous             std::optional<bool> verifyCertificate;
328e5aaf047SAppaRao Puli             std::optional<std::string> context;
329e5aaf047SAppaRao Puli             std::optional<std::string> subscriptionType;
33023a21a1cSEd Tanous             std::optional<std::string> eventFormatType2;
331e5aaf047SAppaRao Puli             std::optional<std::string> retryPolicy;
3325064a25bSMyung Bae             std::optional<bool> sendHeartbeat;
3335064a25bSMyung Bae             std::optional<uint64_t> hbIntervalMinutes;
334e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> msgIds;
335e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> regPrefixes;
336a14c9113SEd Tanous             std::optional<std::vector<std::string>> originResources;
337e56f254cSSunitha Harish             std::optional<std::vector<std::string>> resTypes;
33878d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> headers;
33978d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
340e5aaf047SAppaRao Puli 
341afc474aeSMyung Bae             if (!json_util::readJsonPatch(                         //
342afc474aeSMyung Bae                     req, asyncResp->res,                           //
343afc474aeSMyung Bae                     "Context", context,                            //
344afc474aeSMyung Bae                     "DeliveryRetryPolicy", retryPolicy,            //
345afc474aeSMyung Bae                     "Destination", destUrl,                        //
346afc474aeSMyung Bae                     "EventFormatType", eventFormatType2,           //
3475064a25bSMyung Bae                     "HeartbeatIntervalMinutes", hbIntervalMinutes, //
348afc474aeSMyung Bae                     "HttpHeaders", headers,                        //
349afc474aeSMyung Bae                     "MessageIds", msgIds,                          //
350afc474aeSMyung Bae                     "MetricReportDefinitions", mrdJsonArray,       //
351afc474aeSMyung Bae                     "OriginResources", originResources,            //
352afc474aeSMyung Bae                     "Protocol", protocol,                          //
353afc474aeSMyung Bae                     "RegistryPrefixes", regPrefixes,               //
354afc474aeSMyung Bae                     "ResourceTypes", resTypes,                     //
3555064a25bSMyung Bae                     "SendHeartbeat", sendHeartbeat,                //
356afc474aeSMyung Bae                     "SubscriptionType", subscriptionType,          //
357afc474aeSMyung Bae                     "VerifyCertificate", verifyCertificate         //
3584b712a29SEd Tanous                     ))
359e5aaf047SAppaRao Puli             {
360e5aaf047SAppaRao Puli                 return;
361e5aaf047SAppaRao Puli             }
3624b712a29SEd Tanous             // clang-format on
363e5aaf047SAppaRao Puli 
364600af5f1SAppaRao Puli             // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
365600af5f1SAppaRao Puli             static constexpr const uint16_t maxDestinationSize = 2000;
366600af5f1SAppaRao Puli             if (destUrl.size() > maxDestinationSize)
367600af5f1SAppaRao Puli             {
368600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Destination",
369600af5f1SAppaRao Puli                                              maxDestinationSize);
370600af5f1SAppaRao Puli                 return;
371600af5f1SAppaRao Puli             }
372600af5f1SAppaRao Puli 
373dd28ba82SAppaRao Puli             if (regPrefixes && msgIds)
374dd28ba82SAppaRao Puli             {
37526f6976fSEd Tanous                 if (!regPrefixes->empty() && !msgIds->empty())
376dd28ba82SAppaRao Puli                 {
377bd79bce8SPatrick Williams                     messages::propertyValueConflict(
378bd79bce8SPatrick Williams                         asyncResp->res, "MessageIds", "RegistryPrefixes");
379dd28ba82SAppaRao Puli                     return;
380dd28ba82SAppaRao Puli                 }
381dd28ba82SAppaRao Puli             }
382dd28ba82SAppaRao Puli 
3836fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
384a716aa74SEd Tanous                 boost::urls::parse_absolute_uri(destUrl);
385a716aa74SEd Tanous             if (!url)
386e5aaf047SAppaRao Puli             {
387bd79bce8SPatrick Williams                 BMCWEB_LOG_WARNING(
388bd79bce8SPatrick Williams                     "Failed to validate and split destination url");
389e5aaf047SAppaRao Puli                 messages::propertyValueFormatError(asyncResp->res, destUrl,
390e5aaf047SAppaRao Puli                                                    "Destination");
391e5aaf047SAppaRao Puli                 return;
392e5aaf047SAppaRao Puli             }
393a716aa74SEd Tanous             url->normalize();
394b07942e3SGeorge Liu 
395b07942e3SGeorge Liu             // port_number returns zero if it is not a valid representable port
396b07942e3SGeorge Liu             if (url->has_port() && url->port_number() == 0)
397b07942e3SGeorge Liu             {
398b07942e3SGeorge Liu                 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
399b07942e3SGeorge Liu                                    url->port());
400b07942e3SGeorge Liu                 messages::propertyValueFormatError(asyncResp->res, destUrl,
401b07942e3SGeorge Liu                                                    "Destination");
402b07942e3SGeorge Liu                 return;
403b07942e3SGeorge Liu             }
404b07942e3SGeorge Liu 
405a716aa74SEd Tanous             crow::utility::setProtocolDefaults(*url, protocol);
406a716aa74SEd Tanous             crow::utility::setPortDefaults(*url);
407a716aa74SEd Tanous 
408a716aa74SEd Tanous             if (url->path().empty())
409a716aa74SEd Tanous             {
410a716aa74SEd Tanous                 url->set_path("/");
411a716aa74SEd Tanous             }
412a716aa74SEd Tanous 
413a716aa74SEd Tanous             if (url->has_userinfo())
414a716aa74SEd Tanous             {
415a716aa74SEd Tanous                 messages::propertyValueFormatError(asyncResp->res, destUrl,
416a716aa74SEd Tanous                                                    "Destination");
417a716aa74SEd Tanous                 return;
418a716aa74SEd Tanous             }
419b52664e2SAppaRao Puli 
4203d30708fSChicago Duan             if (protocol == "SNMPv2c")
4213d30708fSChicago Duan             {
4223d30708fSChicago Duan                 if (context)
4233d30708fSChicago Duan                 {
4243d30708fSChicago Duan                     messages::propertyValueConflict(asyncResp->res, "Context",
4253d30708fSChicago Duan                                                     "Protocol");
4263d30708fSChicago Duan                     return;
4273d30708fSChicago Duan                 }
4283d30708fSChicago Duan                 if (eventFormatType2)
4293d30708fSChicago Duan                 {
430bd79bce8SPatrick Williams                     messages::propertyValueConflict(
431bd79bce8SPatrick Williams                         asyncResp->res, "EventFormatType", "Protocol");
4323d30708fSChicago Duan                     return;
4333d30708fSChicago Duan                 }
4343d30708fSChicago Duan                 if (retryPolicy)
4353d30708fSChicago Duan                 {
436bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
437bd79bce8SPatrick Williams                                                     "RetryPolicy", "Protocol");
4383d30708fSChicago Duan                     return;
4393d30708fSChicago Duan                 }
4405064a25bSMyung Bae                 if (sendHeartbeat)
4415064a25bSMyung Bae                 {
4425064a25bSMyung Bae                     messages::propertyValueConflict(
4435064a25bSMyung Bae                         asyncResp->res, "SendHeartbeat", "Protocol");
4445064a25bSMyung Bae                     return;
4455064a25bSMyung Bae                 }
4465064a25bSMyung Bae                 if (hbIntervalMinutes)
4475064a25bSMyung Bae                 {
4485064a25bSMyung Bae                     messages::propertyValueConflict(
4495064a25bSMyung Bae                         asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
4505064a25bSMyung Bae                     return;
4515064a25bSMyung Bae                 }
4523d30708fSChicago Duan                 if (msgIds)
4533d30708fSChicago Duan                 {
454bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
455bd79bce8SPatrick Williams                                                     "MessageIds", "Protocol");
4563d30708fSChicago Duan                     return;
4573d30708fSChicago Duan                 }
4583d30708fSChicago Duan                 if (regPrefixes)
4593d30708fSChicago Duan                 {
460bd79bce8SPatrick Williams                     messages::propertyValueConflict(
461bd79bce8SPatrick Williams                         asyncResp->res, "RegistryPrefixes", "Protocol");
4623d30708fSChicago Duan                     return;
4633d30708fSChicago Duan                 }
4643d30708fSChicago Duan                 if (resTypes)
4653d30708fSChicago Duan                 {
466bd79bce8SPatrick Williams                     messages::propertyValueConflict(
467bd79bce8SPatrick Williams                         asyncResp->res, "ResourceTypes", "Protocol");
4683d30708fSChicago Duan                     return;
4693d30708fSChicago Duan                 }
4703d30708fSChicago Duan                 if (headers)
4713d30708fSChicago Duan                 {
472bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
473bd79bce8SPatrick Williams                                                     "HttpHeaders", "Protocol");
4743d30708fSChicago Duan                     return;
4753d30708fSChicago Duan                 }
4763d30708fSChicago Duan                 if (mrdJsonArray)
4773d30708fSChicago Duan                 {
4783d30708fSChicago Duan                     messages::propertyValueConflict(
4793d30708fSChicago Duan                         asyncResp->res, "MetricReportDefinitions", "Protocol");
4803d30708fSChicago Duan                     return;
4813d30708fSChicago Duan                 }
482a716aa74SEd Tanous                 if (url->scheme() != "snmp")
483a716aa74SEd Tanous                 {
484bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
485bd79bce8SPatrick Williams                                                     "Destination", "Protocol");
4863d30708fSChicago Duan                     return;
4873d30708fSChicago Duan                 }
4883d30708fSChicago Duan 
489a716aa74SEd Tanous                 addSnmpTrapClient(asyncResp, url->host_address(),
490a716aa74SEd Tanous                                   url->port_number());
491a716aa74SEd Tanous                 return;
492b52664e2SAppaRao Puli             }
4933d30708fSChicago Duan 
494a716aa74SEd Tanous             std::shared_ptr<Subscription> subValue =
49521a94d5cSMyung Bae                 std::make_shared<Subscription>(
4965fe4ef35SMyung Bae                     std::make_shared<persistent_data::UserSubscription>(), *url,
4979838eb20SEd Tanous                     getIoContext());
498e5aaf047SAppaRao Puli 
499e5aaf047SAppaRao Puli             if (subscriptionType)
500e5aaf047SAppaRao Puli             {
501e5aaf047SAppaRao Puli                 if (*subscriptionType != "RedfishEvent")
502e5aaf047SAppaRao Puli                 {
503fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
504fffb8c1fSEd Tanous                         asyncResp->res, *subscriptionType, "SubscriptionType");
505e5aaf047SAppaRao Puli                     return;
506e5aaf047SAppaRao Puli                 }
5075fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = *subscriptionType;
508e5aaf047SAppaRao Puli             }
509e5aaf047SAppaRao Puli             else
510e5aaf047SAppaRao Puli             {
5114b712a29SEd Tanous                 // Default
5125fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = "RedfishEvent";
513e5aaf047SAppaRao Puli             }
514e5aaf047SAppaRao Puli 
515e5aaf047SAppaRao Puli             if (protocol != "Redfish")
516e5aaf047SAppaRao Puli             {
517e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, protocol,
518e5aaf047SAppaRao Puli                                                  "Protocol");
519e5aaf047SAppaRao Puli                 return;
520e5aaf047SAppaRao Puli             }
5215fe4ef35SMyung Bae             subValue->userSub->protocol = protocol;
522e5aaf047SAppaRao Puli 
52319bb362bSEd Tanous             if (verifyCertificate)
52419bb362bSEd Tanous             {
5255fe4ef35SMyung Bae                 subValue->userSub->verifyCertificate = *verifyCertificate;
52619bb362bSEd Tanous             }
52719bb362bSEd Tanous 
52823a21a1cSEd Tanous             if (eventFormatType2)
529e5aaf047SAppaRao Puli             {
530bd79bce8SPatrick Williams                 if (std::ranges::find(supportedEvtFormatTypes,
531bd79bce8SPatrick Williams                                       *eventFormatType2) ==
5323544d2a7SEd Tanous                     supportedEvtFormatTypes.end())
533e5aaf047SAppaRao Puli                 {
534fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
535fffb8c1fSEd Tanous                         asyncResp->res, *eventFormatType2, "EventFormatType");
536e5aaf047SAppaRao Puli                     return;
537e5aaf047SAppaRao Puli                 }
5385fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = *eventFormatType2;
539e5aaf047SAppaRao Puli             }
540e5aaf047SAppaRao Puli             else
541e5aaf047SAppaRao Puli             {
542e5aaf047SAppaRao Puli                 // If not specified, use default "Event"
5435fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = "Event";
544e5aaf047SAppaRao Puli             }
545e5aaf047SAppaRao Puli 
546e5aaf047SAppaRao Puli             if (context)
547e5aaf047SAppaRao Puli             {
5488ece0e45SEd Tanous                 // This value is selected arbitrarily.
549600af5f1SAppaRao Puli                 constexpr const size_t maxContextSize = 256;
550600af5f1SAppaRao Puli                 if (context->size() > maxContextSize)
551600af5f1SAppaRao Puli                 {
552600af5f1SAppaRao Puli                     messages::stringValueTooLong(asyncResp->res, "Context",
553600af5f1SAppaRao Puli                                                  maxContextSize);
554600af5f1SAppaRao Puli                     return;
555600af5f1SAppaRao Puli                 }
5565fe4ef35SMyung Bae                 subValue->userSub->customText = *context;
557e5aaf047SAppaRao Puli             }
558e5aaf047SAppaRao Puli 
559e5aaf047SAppaRao Puli             if (headers)
560e5aaf047SAppaRao Puli             {
561600af5f1SAppaRao Puli                 size_t cumulativeLen = 0;
562600af5f1SAppaRao Puli 
56378d4ec4fSEd Tanous                 for (const nlohmann::json::object_t& headerChunk : *headers)
564601c71aeSEd Tanous                 {
56578d4ec4fSEd Tanous                     for (const auto& item : headerChunk)
56678d4ec4fSEd Tanous                     {
56778d4ec4fSEd Tanous                         const std::string* value =
56878d4ec4fSEd Tanous                             item.second.get_ptr<const std::string*>();
56978d4ec4fSEd Tanous                         if (value == nullptr)
57078d4ec4fSEd Tanous                         {
57178d4ec4fSEd Tanous                             messages::propertyValueFormatError(
57278d4ec4fSEd Tanous                                 asyncResp->res, item.second,
57378d4ec4fSEd Tanous                                 "HttpHeaders/" + item.first);
57478d4ec4fSEd Tanous                             return;
57578d4ec4fSEd Tanous                         }
57678d4ec4fSEd Tanous                         // Adding a new json value is the size of the key, +
57778d4ec4fSEd Tanous                         // the size of the value + 2 * 2 quotes for each, +
57878d4ec4fSEd Tanous                         // the colon and space between. example:
57978d4ec4fSEd Tanous                         // "key": "value"
58078d4ec4fSEd Tanous                         cumulativeLen += item.first.size() + value->size() + 6;
581600af5f1SAppaRao Puli                         // This value is selected to mirror http_connection.hpp
582600af5f1SAppaRao Puli                         constexpr const uint16_t maxHeaderSizeED = 8096;
583600af5f1SAppaRao Puli                         if (cumulativeLen > maxHeaderSizeED)
584600af5f1SAppaRao Puli                         {
58578d4ec4fSEd Tanous                             messages::arraySizeTooLong(
58678d4ec4fSEd Tanous                                 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
587600af5f1SAppaRao Puli                             return;
588600af5f1SAppaRao Puli                         }
5895fe4ef35SMyung Bae                         subValue->userSub->httpHeaders.set(item.first, *value);
590601c71aeSEd Tanous                     }
591601c71aeSEd Tanous                 }
592e5aaf047SAppaRao Puli             }
593e5aaf047SAppaRao Puli 
594e5aaf047SAppaRao Puli             if (regPrefixes)
595e5aaf047SAppaRao Puli             {
596e5aaf047SAppaRao Puli                 for (const std::string& it : *regPrefixes)
597e5aaf047SAppaRao Puli                 {
5983544d2a7SEd Tanous                     if (std::ranges::find(supportedRegPrefixes, it) ==
5993544d2a7SEd Tanous                         supportedRegPrefixes.end())
600e5aaf047SAppaRao Puli                     {
601fffb8c1fSEd Tanous                         messages::propertyValueNotInList(asyncResp->res, it,
602fffb8c1fSEd Tanous                                                          "RegistryPrefixes");
603e5aaf047SAppaRao Puli                         return;
604e5aaf047SAppaRao Puli                     }
605e5aaf047SAppaRao Puli                 }
6065fe4ef35SMyung Bae                 subValue->userSub->registryPrefixes = *regPrefixes;
607e5aaf047SAppaRao Puli             }
608e5aaf047SAppaRao Puli 
609a14c9113SEd Tanous             if (originResources)
610a14c9113SEd Tanous             {
6115fe4ef35SMyung Bae                 subValue->userSub->originResources = *originResources;
612a14c9113SEd Tanous             }
613a14c9113SEd Tanous 
614e56f254cSSunitha Harish             if (resTypes)
615e56f254cSSunitha Harish             {
616e56f254cSSunitha Harish                 for (const std::string& it : *resTypes)
617e56f254cSSunitha Harish                 {
6183544d2a7SEd Tanous                     if (std::ranges::find(supportedResourceTypes, it) ==
6193544d2a7SEd Tanous                         supportedResourceTypes.end())
620e56f254cSSunitha Harish                     {
621e56f254cSSunitha Harish                         messages::propertyValueNotInList(asyncResp->res, it,
622e56f254cSSunitha Harish                                                          "ResourceTypes");
623e56f254cSSunitha Harish                         return;
624e56f254cSSunitha Harish                     }
625e56f254cSSunitha Harish                 }
6265fe4ef35SMyung Bae                 subValue->userSub->resourceTypes = *resTypes;
627e56f254cSSunitha Harish             }
628e56f254cSSunitha Harish 
629e5aaf047SAppaRao Puli             if (msgIds)
630e5aaf047SAppaRao Puli             {
631b304bd79SP Dheeraj Srujan Kumar                 std::vector<std::string> registryPrefix;
632b304bd79SP Dheeraj Srujan Kumar 
6337e860f15SJohn Edward Broadbent                 // If no registry prefixes are mentioned, consider all
6347e860f15SJohn Edward Broadbent                 // supported prefixes
6355fe4ef35SMyung Bae                 if (subValue->userSub->registryPrefixes.empty())
636b304bd79SP Dheeraj Srujan Kumar                 {
637b304bd79SP Dheeraj Srujan Kumar                     registryPrefix.assign(supportedRegPrefixes.begin(),
638b304bd79SP Dheeraj Srujan Kumar                                           supportedRegPrefixes.end());
639b304bd79SP Dheeraj Srujan Kumar                 }
640b304bd79SP Dheeraj Srujan Kumar                 else
641b304bd79SP Dheeraj Srujan Kumar                 {
6425fe4ef35SMyung Bae                     registryPrefix = subValue->userSub->registryPrefixes;
643b304bd79SP Dheeraj Srujan Kumar                 }
644b304bd79SP Dheeraj Srujan Kumar 
645b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& id : *msgIds)
646b304bd79SP Dheeraj Srujan Kumar                 {
647b304bd79SP Dheeraj Srujan Kumar                     bool validId = false;
648b304bd79SP Dheeraj Srujan Kumar 
649b304bd79SP Dheeraj Srujan Kumar                     // Check for Message ID in each of the selected Registry
650b304bd79SP Dheeraj Srujan Kumar                     for (const std::string& it : registryPrefix)
651b304bd79SP Dheeraj Srujan Kumar                     {
652*4a102cd4SPatrick Williams                         const registries::MessageEntries registry =
653*4a102cd4SPatrick Williams                             redfish::registries::getRegistryMessagesFromPrefix(
654*4a102cd4SPatrick Williams                                 it);
655b304bd79SP Dheeraj Srujan Kumar 
6563544d2a7SEd Tanous                         if (std::ranges::any_of(
6573544d2a7SEd Tanous                                 registry,
658fffb8c1fSEd Tanous                                 [&id](const redfish::registries::MessageEntry&
659fffb8c1fSEd Tanous                                           messageEntry) {
66055f79e6fSEd Tanous                                     return id == messageEntry.first;
661b304bd79SP Dheeraj Srujan Kumar                                 }))
662b304bd79SP Dheeraj Srujan Kumar                         {
663b304bd79SP Dheeraj Srujan Kumar                             validId = true;
664b304bd79SP Dheeraj Srujan Kumar                             break;
665b304bd79SP Dheeraj Srujan Kumar                         }
666b304bd79SP Dheeraj Srujan Kumar                     }
667b304bd79SP Dheeraj Srujan Kumar 
668b304bd79SP Dheeraj Srujan Kumar                     if (!validId)
669b304bd79SP Dheeraj Srujan Kumar                     {
670b304bd79SP Dheeraj Srujan Kumar                         messages::propertyValueNotInList(asyncResp->res, id,
671b304bd79SP Dheeraj Srujan Kumar                                                          "MessageIds");
672b304bd79SP Dheeraj Srujan Kumar                         return;
673b304bd79SP Dheeraj Srujan Kumar                     }
674b304bd79SP Dheeraj Srujan Kumar                 }
675b304bd79SP Dheeraj Srujan Kumar 
6765fe4ef35SMyung Bae                 subValue->userSub->registryMsgIds = *msgIds;
677e5aaf047SAppaRao Puli             }
678e5aaf047SAppaRao Puli 
679e5aaf047SAppaRao Puli             if (retryPolicy)
680e5aaf047SAppaRao Puli             {
6813544d2a7SEd Tanous                 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
6823544d2a7SEd Tanous                     supportedRetryPolicies.end())
683e5aaf047SAppaRao Puli                 {
684bd79bce8SPatrick Williams                     messages::propertyValueNotInList(
685bd79bce8SPatrick Williams                         asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
686e5aaf047SAppaRao Puli                     return;
687e5aaf047SAppaRao Puli                 }
6885fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = *retryPolicy;
689e5aaf047SAppaRao Puli             }
690e5aaf047SAppaRao Puli             else
691e5aaf047SAppaRao Puli             {
692e5aaf047SAppaRao Puli                 // Default "TerminateAfterRetries"
6935fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = "TerminateAfterRetries";
694e5aaf047SAppaRao Puli             }
6955064a25bSMyung Bae             if (sendHeartbeat)
6965064a25bSMyung Bae             {
6975064a25bSMyung Bae                 subValue->userSub->sendHeartbeat = *sendHeartbeat;
6985064a25bSMyung Bae             }
6995064a25bSMyung Bae             if (hbIntervalMinutes)
7005064a25bSMyung Bae             {
7015064a25bSMyung Bae                 if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
7025064a25bSMyung Bae                 {
7035064a25bSMyung Bae                     messages::propertyValueOutOfRange(
7045064a25bSMyung Bae                         asyncResp->res, *hbIntervalMinutes,
7055064a25bSMyung Bae                         "HeartbeatIntervalMinutes");
7065064a25bSMyung Bae                     return;
7075064a25bSMyung Bae                 }
7085064a25bSMyung Bae                 subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
7095064a25bSMyung Bae             }
710e5aaf047SAppaRao Puli 
711144b6318SAppaRao Puli             if (mrdJsonArray)
712156d6b00SAppaRao Puli             {
71378d4ec4fSEd Tanous                 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
714144b6318SAppaRao Puli                 {
715144b6318SAppaRao Puli                     std::string mrdUri;
716ea2e6eecSWilly Tu 
71778d4ec4fSEd Tanous                     if (!json_util::readJsonObject(mrdObj, asyncResp->res,
71878d4ec4fSEd Tanous                                                    "@odata.id", mrdUri))
719ea2e6eecSWilly Tu 
720144b6318SAppaRao Puli                     {
721144b6318SAppaRao Puli                         return;
722144b6318SAppaRao Puli                     }
7235fe4ef35SMyung Bae                     subValue->userSub->metricReportDefinitions.emplace_back(
7244b712a29SEd Tanous                         mrdUri);
725144b6318SAppaRao Puli                 }
726156d6b00SAppaRao Puli             }
727156d6b00SAppaRao Puli 
728b52664e2SAppaRao Puli             std::string id =
729bd79bce8SPatrick Williams                 EventServiceManager::getInstance().addPushSubscription(
730bd79bce8SPatrick Williams                     subValue);
731b52664e2SAppaRao Puli             if (id.empty())
732e5aaf047SAppaRao Puli             {
733e5aaf047SAppaRao Puli                 messages::internalError(asyncResp->res);
734e5aaf047SAppaRao Puli                 return;
735e5aaf047SAppaRao Puli             }
736e5aaf047SAppaRao Puli 
737e5aaf047SAppaRao Puli             messages::created(asyncResp->res);
738e5aaf047SAppaRao Puli             asyncResp->res.addHeader(
739e5aaf047SAppaRao Puli                 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
740fb546105SMyung Bae 
741fb546105SMyung Bae             // schedule a heartbeat
742fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
743fb546105SMyung Bae             {
744fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
745fb546105SMyung Bae             }
7467e860f15SJohn Edward Broadbent         });
747e5aaf047SAppaRao Puli }
748e5aaf047SAppaRao Puli 
7497e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
750e5aaf047SAppaRao Puli {
7519d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
752ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
7537e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
75445ca1b86SEd Tanous             [&app](const crow::Request& req,
7557e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
7567e860f15SJohn Edward Broadbent                    const std::string& param) {
7573ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
75845ca1b86SEd Tanous                 {
75945ca1b86SEd Tanous                     return;
76045ca1b86SEd Tanous                 }
7613d30708fSChicago Duan 
7623d30708fSChicago Duan                 if (param.starts_with("snmp"))
7633d30708fSChicago Duan                 {
7643d30708fSChicago Duan                     getSnmpTrapClient(asyncResp, param);
7653d30708fSChicago Duan                     return;
7663d30708fSChicago Duan                 }
7673d30708fSChicago Duan 
768b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
7697e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
770b52664e2SAppaRao Puli                 if (subValue == nullptr)
771e5aaf047SAppaRao Puli                 {
772bd79bce8SPatrick Williams                     asyncResp->res.result(
773bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
774e5aaf047SAppaRao Puli                     return;
775e5aaf047SAppaRao Puli                 }
7767e860f15SJohn Edward Broadbent                 const std::string& id = param;
777e5aaf047SAppaRao Puli 
7784b712a29SEd Tanous                 const persistent_data::UserSubscription& userSub =
7795fe4ef35SMyung Bae                     *subValue->userSub;
780e56f254cSSunitha Harish 
7814b712a29SEd Tanous                 nlohmann::json& jVal = asyncResp->res.jsonValue;
7824b712a29SEd Tanous                 jVal["@odata.type"] =
7834b712a29SEd Tanous                     "#EventDestination.v1_14_1.EventDestination";
7844b712a29SEd Tanous                 jVal["Protocol"] =
7854b712a29SEd Tanous                     event_destination::EventDestinationProtocol::Redfish;
7864b712a29SEd Tanous                 jVal["@odata.id"] = boost::urls::format(
7874b712a29SEd Tanous                     "/redfish/v1/EventService/Subscriptions/{}", id);
7884b712a29SEd Tanous                 jVal["Id"] = id;
7894b712a29SEd Tanous                 jVal["Name"] = "Event Destination " + id;
7904b712a29SEd Tanous                 jVal["Destination"] = userSub.destinationUrl;
7914b712a29SEd Tanous                 jVal["Context"] = userSub.customText;
7924b712a29SEd Tanous                 jVal["SubscriptionType"] = userSub.subscriptionType;
7934b712a29SEd Tanous                 jVal["HttpHeaders"] = nlohmann::json::array();
7944b712a29SEd Tanous                 jVal["EventFormatType"] = userSub.eventFormatType;
7954b712a29SEd Tanous                 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
7964b712a29SEd Tanous                 jVal["ResourceTypes"] = userSub.resourceTypes;
7974b712a29SEd Tanous 
7984b712a29SEd Tanous                 jVal["MessageIds"] = userSub.registryMsgIds;
7994b712a29SEd Tanous                 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
8005064a25bSMyung Bae                 jVal["SendHeartbeat"] = userSub.sendHeartbeat;
8015064a25bSMyung Bae                 jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
8024b712a29SEd Tanous                 jVal["VerifyCertificate"] = userSub.verifyCertificate;
803144b6318SAppaRao Puli 
8041476687dSEd Tanous                 nlohmann::json::array_t mrdJsonArray;
8054b712a29SEd Tanous                 for (const auto& mdrUri : userSub.metricReportDefinitions)
806144b6318SAppaRao Puli                 {
8071476687dSEd Tanous                     nlohmann::json::object_t mdr;
8081476687dSEd Tanous                     mdr["@odata.id"] = mdrUri;
8091476687dSEd Tanous                     mrdJsonArray.emplace_back(std::move(mdr));
810144b6318SAppaRao Puli                 }
8114b712a29SEd Tanous                 jVal["MetricReportDefinitions"] = mrdJsonArray;
8127e860f15SJohn Edward Broadbent             });
8139d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
814ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
815ed398213SEd Tanous         // ConfigureSelf
8167eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
817ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
818432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
8197e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
82045ca1b86SEd Tanous             [&app](const crow::Request& req,
8217e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8227e860f15SJohn Edward Broadbent                    const std::string& param) {
8233ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
82445ca1b86SEd Tanous                 {
82545ca1b86SEd Tanous                     return;
82645ca1b86SEd Tanous                 }
827b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
8287e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
829b52664e2SAppaRao Puli                 if (subValue == nullptr)
830e5aaf047SAppaRao Puli                 {
831bd79bce8SPatrick Williams                     asyncResp->res.result(
832bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
833e5aaf047SAppaRao Puli                     return;
834e5aaf047SAppaRao Puli                 }
835e5aaf047SAppaRao Puli 
836e5aaf047SAppaRao Puli                 std::optional<std::string> context;
837e5aaf047SAppaRao Puli                 std::optional<std::string> retryPolicy;
8385064a25bSMyung Bae                 std::optional<bool> sendHeartbeat;
8395064a25bSMyung Bae                 std::optional<uint64_t> hbIntervalMinutes;
84019bb362bSEd Tanous                 std::optional<bool> verifyCertificate;
84178d4ec4fSEd Tanous                 std::optional<std::vector<nlohmann::json::object_t>> headers;
842e5aaf047SAppaRao Puli 
843afc474aeSMyung Bae                 if (!json_util::readJsonPatch(                         //
844afc474aeSMyung Bae                         req, asyncResp->res,                           //
845afc474aeSMyung Bae                         "Context", context,                            //
846afc474aeSMyung Bae                         "DeliveryRetryPolicy", retryPolicy,            //
8475064a25bSMyung Bae                         "HeartbeatIntervalMinutes", hbIntervalMinutes, //
848afc474aeSMyung Bae                         "HttpHeaders", headers,                        //
8495064a25bSMyung Bae                         "SendHeartbeat", sendHeartbeat,                //
850afc474aeSMyung Bae                         "VerifyCertificate", verifyCertificate         //
851afc474aeSMyung Bae                         ))
852e5aaf047SAppaRao Puli                 {
853e5aaf047SAppaRao Puli                     return;
854e5aaf047SAppaRao Puli                 }
855e5aaf047SAppaRao Puli 
856e5aaf047SAppaRao Puli                 if (context)
857e5aaf047SAppaRao Puli                 {
8585fe4ef35SMyung Bae                     subValue->userSub->customText = *context;
859e5aaf047SAppaRao Puli                 }
860e5aaf047SAppaRao Puli 
861e5aaf047SAppaRao Puli                 if (headers)
862e5aaf047SAppaRao Puli                 {
863601c71aeSEd Tanous                     boost::beast::http::fields fields;
86478d4ec4fSEd Tanous                     for (const nlohmann::json::object_t& headerChunk : *headers)
865601c71aeSEd Tanous                     {
86678d4ec4fSEd Tanous                         for (const auto& it : headerChunk)
867601c71aeSEd Tanous                         {
868601c71aeSEd Tanous                             const std::string* value =
86978d4ec4fSEd Tanous                                 it.second.get_ptr<const std::string*>();
870601c71aeSEd Tanous                             if (value == nullptr)
871601c71aeSEd Tanous                             {
872601c71aeSEd Tanous                                 messages::propertyValueFormatError(
87378d4ec4fSEd Tanous                                     asyncResp->res, it.second,
87478d4ec4fSEd Tanous                                     "HttpHeaders/" + it.first);
875601c71aeSEd Tanous                                 return;
876601c71aeSEd Tanous                             }
87778d4ec4fSEd Tanous                             fields.set(it.first, *value);
878601c71aeSEd Tanous                         }
879601c71aeSEd Tanous                     }
8805fe4ef35SMyung Bae                     subValue->userSub->httpHeaders = std::move(fields);
881e5aaf047SAppaRao Puli                 }
882e5aaf047SAppaRao Puli 
883e5aaf047SAppaRao Puli                 if (retryPolicy)
884e5aaf047SAppaRao Puli                 {
885bd79bce8SPatrick Williams                     if (std::ranges::find(supportedRetryPolicies,
886bd79bce8SPatrick Williams                                           *retryPolicy) ==
8873544d2a7SEd Tanous                         supportedRetryPolicies.end())
888e5aaf047SAppaRao Puli                     {
889bd79bce8SPatrick Williams                         messages::propertyValueNotInList(asyncResp->res,
890bd79bce8SPatrick Williams                                                          *retryPolicy,
891e5aaf047SAppaRao Puli                                                          "DeliveryRetryPolicy");
892e5aaf047SAppaRao Puli                         return;
893e5aaf047SAppaRao Puli                     }
8945fe4ef35SMyung Bae                     subValue->userSub->retryPolicy = *retryPolicy;
895e5aaf047SAppaRao Puli                 }
896e5aaf047SAppaRao Puli 
8975064a25bSMyung Bae                 if (sendHeartbeat)
8985064a25bSMyung Bae                 {
8995064a25bSMyung Bae                     subValue->userSub->sendHeartbeat = *sendHeartbeat;
9005064a25bSMyung Bae                 }
9015064a25bSMyung Bae                 if (hbIntervalMinutes)
9025064a25bSMyung Bae                 {
9035064a25bSMyung Bae                     if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
9045064a25bSMyung Bae                     {
9055064a25bSMyung Bae                         messages::propertyValueOutOfRange(
9065064a25bSMyung Bae                             asyncResp->res, *hbIntervalMinutes,
9075064a25bSMyung Bae                             "HeartbeatIntervalMinutes");
9085064a25bSMyung Bae                         return;
9095064a25bSMyung Bae                     }
9105064a25bSMyung Bae                     subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
9115064a25bSMyung Bae                 }
9125064a25bSMyung Bae 
913fb546105SMyung Bae                 if (hbIntervalMinutes || sendHeartbeat)
914fb546105SMyung Bae                 {
915fb546105SMyung Bae                     // if Heartbeat interval or send heart were changed, cancel
916fb546105SMyung Bae                     // the heartbeat timer if running and start a new heartbeat
917fb546105SMyung Bae                     // if needed
918fb546105SMyung Bae                     subValue->heartbeatParametersChanged();
919fb546105SMyung Bae                 }
920fb546105SMyung Bae 
92119bb362bSEd Tanous                 if (verifyCertificate)
92219bb362bSEd Tanous                 {
9235fe4ef35SMyung Bae                     subValue->userSub->verifyCertificate = *verifyCertificate;
92419bb362bSEd Tanous                 }
92519bb362bSEd Tanous 
926b52664e2SAppaRao Puli                 EventServiceManager::getInstance().updateSubscriptionData();
9277e860f15SJohn Edward Broadbent             });
9289d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
929ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
930ed398213SEd Tanous         // ConfigureSelf
9317eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
932ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
933432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
9347e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
93545ca1b86SEd Tanous             [&app](const crow::Request& req,
9367e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9377e860f15SJohn Edward Broadbent                    const std::string& param) {
9383ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
93945ca1b86SEd Tanous                 {
94045ca1b86SEd Tanous                     return;
94145ca1b86SEd Tanous                 }
9424b712a29SEd Tanous                 EventServiceManager& event = EventServiceManager::getInstance();
9433d30708fSChicago Duan                 if (param.starts_with("snmp"))
9443d30708fSChicago Duan                 {
9453d30708fSChicago Duan                     deleteSnmpTrapClient(asyncResp, param);
9464b712a29SEd Tanous                     event.deleteSubscription(param);
9473d30708fSChicago Duan                     return;
9483d30708fSChicago Duan                 }
9493d30708fSChicago Duan 
9504b712a29SEd Tanous                 if (!event.deleteSubscription(param))
951e5aaf047SAppaRao Puli                 {
9524b712a29SEd Tanous                     messages::resourceNotFound(asyncResp->res,
9534b712a29SEd Tanous                                                "EventDestination", param);
954e5aaf047SAppaRao Puli                     return;
955e5aaf047SAppaRao Puli                 }
9564b712a29SEd Tanous                 messages::success(asyncResp->res);
9577e860f15SJohn Edward Broadbent             });
958e5aaf047SAppaRao Puli }
959e5aaf047SAppaRao Puli 
960e5aaf047SAppaRao Puli } // namespace redfish
961