xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 9838eb20341568971b9543c2187372d20daf64aa)
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_singleton.hpp"
8d7857201SEd Tanous #include "dbus_utility.hpp"
9d7857201SEd Tanous #include "error_messages.hpp"
10b52664e2SAppaRao Puli #include "event_service_manager.hpp"
11d7857201SEd Tanous #include "event_service_store.hpp"
12d7857201SEd Tanous #include "generated/enums/event_destination.hpp"
133ccb3adbSEd Tanous #include "http/utility.hpp"
14d7857201SEd Tanous #include "http_request.hpp"
15*9838eb20SEd Tanous #include "io_context_singleton.hpp"
163ccb3adbSEd Tanous #include "logging.hpp"
173ccb3adbSEd Tanous #include "query.hpp"
18d109e2b6SAlexander Hansen #include "registries.hpp"
193ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
20d109e2b6SAlexander Hansen #include "registries_selector.hpp"
213d30708fSChicago Duan #include "snmp_trap_event_clients.hpp"
22d7857201SEd Tanous #include "subscription.hpp"
23d109e2b6SAlexander Hansen #include "utils/json_utils.hpp"
24e5aaf047SAppaRao Puli 
25d7857201SEd Tanous #include <asm-generic/errno.h>
26ed398213SEd Tanous 
27d7857201SEd Tanous #include <boost/beast/http/fields.hpp>
28d7857201SEd Tanous #include <boost/beast/http/status.hpp>
29d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
30d7857201SEd Tanous #include <boost/system/error_code.hpp>
31d7857201SEd Tanous #include <boost/system/result.hpp>
32d7857201SEd Tanous #include <boost/url/format.hpp>
33d7857201SEd Tanous #include <boost/url/parse.hpp>
34d7857201SEd Tanous #include <boost/url/url.hpp>
35d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
36d7857201SEd Tanous 
37d7857201SEd Tanous #include <algorithm>
38d7857201SEd Tanous #include <array>
39d7857201SEd Tanous #include <cerrno>
40d7857201SEd Tanous #include <cstddef>
41d7857201SEd Tanous #include <cstdint>
423d30708fSChicago Duan #include <memory>
435064a25bSMyung Bae #include <optional>
443544d2a7SEd Tanous #include <ranges>
451e270c5fSPatrick Williams #include <span>
463d30708fSChicago Duan #include <string>
47d7857201SEd Tanous #include <utility>
48a14c9113SEd Tanous #include <vector>
491e270c5fSPatrick Williams 
50e5aaf047SAppaRao Puli namespace redfish
51e5aaf047SAppaRao Puli {
52e5aaf047SAppaRao Puli 
53156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
54156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
55fb546105SMyung Bae static constexpr const std::array<const char*, 4> supportedRegPrefixes = {
56fb546105SMyung Bae     "Base", "OpenBMC", "TaskEvent", "HeartbeatEvent"};
57e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
58e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
59e5aaf047SAppaRao Puli 
60fb546105SMyung Bae static constexpr const std::array<const char*, 2> supportedResourceTypes = {
61fb546105SMyung Bae     "Task", "Heartbeat"};
62e56f254cSSunitha Harish 
637e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
64e5aaf047SAppaRao Puli {
657e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
66ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
67bd79bce8SPatrick Williams         .methods(
68bd79bce8SPatrick Williams             boost::beast::http::verb::
69bd79bce8SPatrick Williams                 get)([&app](
70bd79bce8SPatrick Williams                          const crow::Request& req,
71002d39b4SEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
723ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
7345ca1b86SEd Tanous             {
7445ca1b86SEd Tanous                 return;
7545ca1b86SEd Tanous             }
761476687dSEd Tanous 
771476687dSEd Tanous             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
781476687dSEd Tanous             asyncResp->res.jsonValue["@odata.type"] =
791476687dSEd Tanous                 "#EventService.v1_5_0.EventService";
801476687dSEd Tanous             asyncResp->res.jsonValue["Id"] = "EventService";
811476687dSEd Tanous             asyncResp->res.jsonValue["Name"] = "Event Service";
825e44e3d8SAppaRao Puli             asyncResp->res.jsonValue["ServerSentEventUri"] =
835e44e3d8SAppaRao Puli                 "/redfish/v1/EventService/SSE";
845e44e3d8SAppaRao Puli 
851476687dSEd Tanous             asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
861476687dSEd Tanous                 "/redfish/v1/EventService/Subscriptions";
87bd79bce8SPatrick Williams             asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
88bd79bce8SPatrick Williams                                     ["target"] =
891476687dSEd Tanous                 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
90e5aaf047SAppaRao Puli 
9128afb49cSJunLin Chen             const persistent_data::EventServiceConfig eventServiceConfig =
9228afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
9328afb49cSJunLin Chen                     .getEventServiceConfig();
947d1cc387SAppaRao Puli 
957d1cc387SAppaRao Puli             asyncResp->res.jsonValue["Status"]["State"] =
9628afb49cSJunLin Chen                 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
97bd79bce8SPatrick Williams             asyncResp->res.jsonValue["ServiceEnabled"] =
98bd79bce8SPatrick Williams                 eventServiceConfig.enabled;
997e860f15SJohn Edward Broadbent             asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
10028afb49cSJunLin Chen                 eventServiceConfig.retryAttempts;
101e5aaf047SAppaRao Puli             asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
10228afb49cSJunLin Chen                 eventServiceConfig.retryTimeoutInterval;
103bd79bce8SPatrick Williams             asyncResp->res.jsonValue["EventFormatTypes"] =
104bd79bce8SPatrick Williams                 supportedEvtFormatTypes;
1050fda0f12SGeorge Liu             asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
1060fda0f12SGeorge Liu             asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
10707941a88SAyushi Smriti 
108613dabeaSEd Tanous             nlohmann::json::object_t supportedSSEFilters;
109613dabeaSEd Tanous             supportedSSEFilters["EventFormatType"] = true;
110613dabeaSEd Tanous             supportedSSEFilters["MessageId"] = true;
111613dabeaSEd Tanous             supportedSSEFilters["MetricReportDefinition"] = true;
112613dabeaSEd Tanous             supportedSSEFilters["RegistryPrefix"] = true;
113613dabeaSEd Tanous             supportedSSEFilters["OriginResource"] = false;
114613dabeaSEd Tanous             supportedSSEFilters["ResourceType"] = false;
11507941a88SAyushi Smriti 
11607941a88SAyushi Smriti             asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
117613dabeaSEd Tanous                 std::move(supportedSSEFilters);
1187e860f15SJohn Edward Broadbent         });
119e5aaf047SAppaRao Puli 
1207e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
121ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1227e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
12345ca1b86SEd Tanous             [&app](const crow::Request& req,
12445ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1253ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
126e5aaf047SAppaRao Puli                 {
12745ca1b86SEd Tanous                     return;
12845ca1b86SEd Tanous                 }
129e5aaf047SAppaRao Puli                 std::optional<bool> serviceEnabled;
130e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryAttemps;
131e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryInterval;
132afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
133afc474aeSMyung Bae                         req, asyncResp->res, //
134afc474aeSMyung Bae                         "DeliveryRetryAttempts", retryAttemps, //
135afc474aeSMyung Bae                         "DeliveryRetryIntervalSeconds", retryInterval, //
136afc474aeSMyung Bae                         "ServiceEnabled", serviceEnabled //
137afc474aeSMyung Bae                         ))
138e5aaf047SAppaRao Puli                 {
139e5aaf047SAppaRao Puli                     return;
140e5aaf047SAppaRao Puli                 }
141e5aaf047SAppaRao Puli 
14228afb49cSJunLin Chen                 persistent_data::EventServiceConfig eventServiceConfig =
14328afb49cSJunLin Chen                     persistent_data::EventServiceStore::getInstance()
14428afb49cSJunLin Chen                         .getEventServiceConfig();
1457d1cc387SAppaRao Puli 
146e5aaf047SAppaRao Puli                 if (serviceEnabled)
147e5aaf047SAppaRao Puli                 {
14828afb49cSJunLin Chen                     eventServiceConfig.enabled = *serviceEnabled;
149e5aaf047SAppaRao Puli                 }
150e5aaf047SAppaRao Puli 
151e5aaf047SAppaRao Puli                 if (retryAttemps)
152e5aaf047SAppaRao Puli                 {
153e5aaf047SAppaRao Puli                     // Supported range [1-3]
154e5aaf047SAppaRao Puli                     if ((*retryAttemps < 1) || (*retryAttemps > 3))
155e5aaf047SAppaRao Puli                     {
156e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
157e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryAttemps),
158e5aaf047SAppaRao Puli                             "DeliveryRetryAttempts", "[1-3]");
159e5aaf047SAppaRao Puli                     }
160e5aaf047SAppaRao Puli                     else
161e5aaf047SAppaRao Puli                     {
16228afb49cSJunLin Chen                         eventServiceConfig.retryAttempts = *retryAttemps;
163e5aaf047SAppaRao Puli                     }
164e5aaf047SAppaRao Puli                 }
165e5aaf047SAppaRao Puli 
166e5aaf047SAppaRao Puli                 if (retryInterval)
167e5aaf047SAppaRao Puli                 {
16833a32b34SGunnar Mills                     // Supported range [5 - 180]
16933a32b34SGunnar Mills                     if ((*retryInterval < 5) || (*retryInterval > 180))
170e5aaf047SAppaRao Puli                     {
171e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
172e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryInterval),
17333a32b34SGunnar Mills                             "DeliveryRetryIntervalSeconds", "[5-180]");
174e5aaf047SAppaRao Puli                     }
175e5aaf047SAppaRao Puli                     else
176e5aaf047SAppaRao Puli                     {
177bd79bce8SPatrick Williams                         eventServiceConfig.retryTimeoutInterval =
178bd79bce8SPatrick Williams                             *retryInterval;
179e5aaf047SAppaRao Puli                     }
180e5aaf047SAppaRao Puli                 }
181e5aaf047SAppaRao Puli 
1827d1cc387SAppaRao Puli                 EventServiceManager::getInstance().setEventServiceConfig(
18328afb49cSJunLin Chen                     eventServiceConfig);
1847e860f15SJohn Edward Broadbent             });
1850b4bdd93SAppaRao Puli }
1860b4bdd93SAppaRao Puli 
1877e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1880b4bdd93SAppaRao Puli {
1897e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1907e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
191ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1927e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
19345ca1b86SEd Tanous             [&app](const crow::Request& req,
1947e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1953ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
19645ca1b86SEd Tanous                 {
19745ca1b86SEd Tanous                     return;
19845ca1b86SEd Tanous                 }
19981ee0e74SChandramohan Harkude 
20081ee0e74SChandramohan Harkude                 TestEvent testEvent;
20181ee0e74SChandramohan Harkude                 // clang-format off
20281ee0e74SChandramohan Harkude                 if (!json_util::readJsonAction(
20381ee0e74SChandramohan Harkude                         req, asyncResp->res,
20481ee0e74SChandramohan Harkude                         "EventGroupId", testEvent.eventGroupId,
20581ee0e74SChandramohan Harkude                         "EventId", testEvent.eventId,
20681ee0e74SChandramohan Harkude                         "EventTimestamp", testEvent.eventTimestamp,
20781ee0e74SChandramohan Harkude                         "Message", testEvent.message,
20881ee0e74SChandramohan Harkude                         "MessageArgs", testEvent.messageArgs,
20981ee0e74SChandramohan Harkude                         "MessageId", testEvent.messageId,
21081ee0e74SChandramohan Harkude                         "OriginOfCondition", testEvent.originOfCondition,
21181ee0e74SChandramohan Harkude                         "Resolution", testEvent.resolution,
21281ee0e74SChandramohan Harkude                         "Severity", testEvent.severity))
21381ee0e74SChandramohan Harkude                 {
21481ee0e74SChandramohan Harkude                     return;
21581ee0e74SChandramohan Harkude                 }
21681ee0e74SChandramohan Harkude                 // clang-format on
21781ee0e74SChandramohan Harkude 
21881ee0e74SChandramohan Harkude                 if (!EventServiceManager::getInstance().sendTestEventLog(
21981ee0e74SChandramohan Harkude                         testEvent))
2206ba8c82eSsunharis_in                 {
2216ba8c82eSsunharis_in                     messages::serviceDisabled(asyncResp->res,
2226ba8c82eSsunharis_in                                               "/redfish/v1/EventService/");
2236ba8c82eSsunharis_in                     return;
2246ba8c82eSsunharis_in                 }
2258d1b46d7Szhanghch05                 asyncResp->res.result(boost::beast::http::status::no_content);
2267e860f15SJohn Edward Broadbent             });
227e5aaf047SAppaRao Puli }
228e5aaf047SAppaRao Puli 
2293d30708fSChicago Duan inline void doSubscriptionCollection(
230e81de512SEd Tanous     const boost::system::error_code& ec,
2313d30708fSChicago Duan     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2323d30708fSChicago Duan     const dbus::utility::ManagedObjectType& resp)
2333d30708fSChicago Duan {
2343d30708fSChicago Duan     if (ec)
2353d30708fSChicago Duan     {
2361306101eSEd Tanous         if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
2373d30708fSChicago Duan         {
2383d30708fSChicago Duan             // This is an optional process so just return if it isn't there
2393d30708fSChicago Duan             return;
2403d30708fSChicago Duan         }
2413d30708fSChicago Duan 
24262598e31SEd Tanous         BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
2433d30708fSChicago Duan         messages::internalError(asyncResp->res);
2443d30708fSChicago Duan         return;
2453d30708fSChicago Duan     }
2463d30708fSChicago Duan     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
2473d30708fSChicago Duan     for (const auto& objpath : resp)
2483d30708fSChicago Duan     {
2493d30708fSChicago Duan         sdbusplus::message::object_path path(objpath.first);
2503d30708fSChicago Duan         const std::string snmpId = path.filename();
2513d30708fSChicago Duan         if (snmpId.empty())
2523d30708fSChicago Duan         {
25362598e31SEd Tanous             BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
2543d30708fSChicago Duan             messages::internalError(asyncResp->res);
2553d30708fSChicago Duan             return;
2563d30708fSChicago Duan         }
2573d30708fSChicago Duan 
2583d30708fSChicago Duan         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
2593d30708fSChicago Duan     }
2603d30708fSChicago Duan }
2613d30708fSChicago Duan 
2627e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
263e5aaf047SAppaRao Puli {
2641ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
265ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
2667e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
26745ca1b86SEd Tanous             [&app](const crow::Request& req,
2687e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2693ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
27045ca1b86SEd Tanous                 {
27145ca1b86SEd Tanous                     return;
27245ca1b86SEd Tanous                 }
2731476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.type"] =
2741476687dSEd Tanous                     "#EventDestinationCollection.EventDestinationCollection";
2751476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.id"] =
2761476687dSEd Tanous                     "/redfish/v1/EventService/Subscriptions";
277bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Name"] =
278bd79bce8SPatrick Williams                     "Event Destination Collections";
279e5aaf047SAppaRao Puli 
280bd79bce8SPatrick Williams                 nlohmann::json& memberArray =
281bd79bce8SPatrick Williams                     asyncResp->res.jsonValue["Members"];
282e5aaf047SAppaRao Puli 
283b52664e2SAppaRao Puli                 std::vector<std::string> subscripIds =
284b52664e2SAppaRao Puli                     EventServiceManager::getInstance().getAllIDs();
285b52664e2SAppaRao Puli                 memberArray = nlohmann::json::array();
286bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Members@odata.count"] =
287bd79bce8SPatrick Williams                     subscripIds.size();
288b52664e2SAppaRao Puli 
289b52664e2SAppaRao Puli                 for (const std::string& id : subscripIds)
290e5aaf047SAppaRao Puli                 {
2911476687dSEd Tanous                     nlohmann::json::object_t member;
2923d30708fSChicago Duan                     member["@odata.id"] = boost::urls::format(
2933d30708fSChicago Duan                         "/redfish/v1/EventService/Subscriptions/{}" + id);
294b2ba3072SPatrick Williams                     memberArray.emplace_back(std::move(member));
295e5aaf047SAppaRao Puli                 }
2963d30708fSChicago Duan                 crow::connections::systemBus->async_method_call(
297e81de512SEd Tanous                     [asyncResp](const boost::system::error_code& ec,
2983d30708fSChicago Duan                                 const dbus::utility::ManagedObjectType& resp) {
2993d30708fSChicago Duan                         doSubscriptionCollection(ec, asyncResp, resp);
3003d30708fSChicago Duan                     },
3013d30708fSChicago Duan                     "xyz.openbmc_project.Network.SNMP",
3023d30708fSChicago Duan                     "/xyz/openbmc_project/network/snmp/manager",
3033d30708fSChicago Duan                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
3047e860f15SJohn Edward Broadbent             });
3053d30708fSChicago Duan 
3067e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
3077eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
308bd79bce8SPatrick Williams         .methods(
309bd79bce8SPatrick Williams             boost::beast::http::verb::
310bd79bce8SPatrick Williams                 post)([&app](
311bd79bce8SPatrick Williams                           const crow::Request& req,
3127e860f15SJohn Edward Broadbent                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3133ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
31445ca1b86SEd Tanous             {
31545ca1b86SEd Tanous                 return;
31645ca1b86SEd Tanous             }
317fffb8c1fSEd Tanous             if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
318fffb8c1fSEd Tanous                 maxNoOfSubscriptions)
319e5aaf047SAppaRao Puli             {
320e5aaf047SAppaRao Puli                 messages::eventSubscriptionLimitExceeded(asyncResp->res);
321e5aaf047SAppaRao Puli                 return;
322e5aaf047SAppaRao Puli             }
323e5aaf047SAppaRao Puli             std::string destUrl;
324e5aaf047SAppaRao Puli             std::string protocol;
32519bb362bSEd Tanous             std::optional<bool> verifyCertificate;
326e5aaf047SAppaRao Puli             std::optional<std::string> context;
327e5aaf047SAppaRao Puli             std::optional<std::string> subscriptionType;
32823a21a1cSEd Tanous             std::optional<std::string> eventFormatType2;
329e5aaf047SAppaRao Puli             std::optional<std::string> retryPolicy;
3305064a25bSMyung Bae             std::optional<bool> sendHeartbeat;
3315064a25bSMyung Bae             std::optional<uint64_t> hbIntervalMinutes;
332e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> msgIds;
333e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> regPrefixes;
334a14c9113SEd Tanous             std::optional<std::vector<std::string>> originResources;
335e56f254cSSunitha Harish             std::optional<std::vector<std::string>> resTypes;
33678d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> headers;
33778d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
338e5aaf047SAppaRao Puli 
339afc474aeSMyung Bae             if (!json_util::readJsonPatch( //
340afc474aeSMyung Bae                     req, asyncResp->res, //
341afc474aeSMyung Bae                     "Context", context, //
342afc474aeSMyung Bae                     "DeliveryRetryPolicy", retryPolicy, //
343afc474aeSMyung Bae                     "Destination", destUrl, //
344afc474aeSMyung Bae                     "EventFormatType", eventFormatType2, //
3455064a25bSMyung Bae                     "HeartbeatIntervalMinutes", hbIntervalMinutes, //
346afc474aeSMyung Bae                     "HttpHeaders", headers, //
347afc474aeSMyung Bae                     "MessageIds", msgIds, //
348afc474aeSMyung Bae                     "MetricReportDefinitions", mrdJsonArray, //
349afc474aeSMyung Bae                     "OriginResources", originResources, //
350afc474aeSMyung Bae                     "Protocol", protocol, //
351afc474aeSMyung Bae                     "RegistryPrefixes", regPrefixes, //
352afc474aeSMyung Bae                     "ResourceTypes", resTypes, //
3535064a25bSMyung Bae                     "SendHeartbeat", sendHeartbeat, //
354afc474aeSMyung Bae                     "SubscriptionType", subscriptionType, //
355afc474aeSMyung Bae                     "VerifyCertificate", verifyCertificate //
3564b712a29SEd Tanous                     ))
357e5aaf047SAppaRao Puli             {
358e5aaf047SAppaRao Puli                 return;
359e5aaf047SAppaRao Puli             }
3604b712a29SEd Tanous             // clang-format on
361e5aaf047SAppaRao Puli 
362600af5f1SAppaRao Puli             // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
363600af5f1SAppaRao Puli             static constexpr const uint16_t maxDestinationSize = 2000;
364600af5f1SAppaRao Puli             if (destUrl.size() > maxDestinationSize)
365600af5f1SAppaRao Puli             {
366600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Destination",
367600af5f1SAppaRao Puli                                              maxDestinationSize);
368600af5f1SAppaRao Puli                 return;
369600af5f1SAppaRao Puli             }
370600af5f1SAppaRao Puli 
371dd28ba82SAppaRao Puli             if (regPrefixes && msgIds)
372dd28ba82SAppaRao Puli             {
37326f6976fSEd Tanous                 if (!regPrefixes->empty() && !msgIds->empty())
374dd28ba82SAppaRao Puli                 {
375bd79bce8SPatrick Williams                     messages::propertyValueConflict(
376bd79bce8SPatrick Williams                         asyncResp->res, "MessageIds", "RegistryPrefixes");
377dd28ba82SAppaRao Puli                     return;
378dd28ba82SAppaRao Puli                 }
379dd28ba82SAppaRao Puli             }
380dd28ba82SAppaRao Puli 
3816fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
382a716aa74SEd Tanous                 boost::urls::parse_absolute_uri(destUrl);
383a716aa74SEd Tanous             if (!url)
384e5aaf047SAppaRao Puli             {
385bd79bce8SPatrick Williams                 BMCWEB_LOG_WARNING(
386bd79bce8SPatrick Williams                     "Failed to validate and split destination url");
387e5aaf047SAppaRao Puli                 messages::propertyValueFormatError(asyncResp->res, destUrl,
388e5aaf047SAppaRao Puli                                                    "Destination");
389e5aaf047SAppaRao Puli                 return;
390e5aaf047SAppaRao Puli             }
391a716aa74SEd Tanous             url->normalize();
392b07942e3SGeorge Liu 
393b07942e3SGeorge Liu             // port_number returns zero if it is not a valid representable port
394b07942e3SGeorge Liu             if (url->has_port() && url->port_number() == 0)
395b07942e3SGeorge Liu             {
396b07942e3SGeorge Liu                 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
397b07942e3SGeorge Liu                                    url->port());
398b07942e3SGeorge Liu                 messages::propertyValueFormatError(asyncResp->res, destUrl,
399b07942e3SGeorge Liu                                                    "Destination");
400b07942e3SGeorge Liu                 return;
401b07942e3SGeorge Liu             }
402b07942e3SGeorge Liu 
403a716aa74SEd Tanous             crow::utility::setProtocolDefaults(*url, protocol);
404a716aa74SEd Tanous             crow::utility::setPortDefaults(*url);
405a716aa74SEd Tanous 
406a716aa74SEd Tanous             if (url->path().empty())
407a716aa74SEd Tanous             {
408a716aa74SEd Tanous                 url->set_path("/");
409a716aa74SEd Tanous             }
410a716aa74SEd Tanous 
411a716aa74SEd Tanous             if (url->has_userinfo())
412a716aa74SEd Tanous             {
413a716aa74SEd Tanous                 messages::propertyValueFormatError(asyncResp->res, destUrl,
414a716aa74SEd Tanous                                                    "Destination");
415a716aa74SEd Tanous                 return;
416a716aa74SEd Tanous             }
417b52664e2SAppaRao Puli 
4183d30708fSChicago Duan             if (protocol == "SNMPv2c")
4193d30708fSChicago Duan             {
4203d30708fSChicago Duan                 if (context)
4213d30708fSChicago Duan                 {
4223d30708fSChicago Duan                     messages::propertyValueConflict(asyncResp->res, "Context",
4233d30708fSChicago Duan                                                     "Protocol");
4243d30708fSChicago Duan                     return;
4253d30708fSChicago Duan                 }
4263d30708fSChicago Duan                 if (eventFormatType2)
4273d30708fSChicago Duan                 {
428bd79bce8SPatrick Williams                     messages::propertyValueConflict(
429bd79bce8SPatrick Williams                         asyncResp->res, "EventFormatType", "Protocol");
4303d30708fSChicago Duan                     return;
4313d30708fSChicago Duan                 }
4323d30708fSChicago Duan                 if (retryPolicy)
4333d30708fSChicago Duan                 {
434bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
435bd79bce8SPatrick Williams                                                     "RetryPolicy", "Protocol");
4363d30708fSChicago Duan                     return;
4373d30708fSChicago Duan                 }
4385064a25bSMyung Bae                 if (sendHeartbeat)
4395064a25bSMyung Bae                 {
4405064a25bSMyung Bae                     messages::propertyValueConflict(
4415064a25bSMyung Bae                         asyncResp->res, "SendHeartbeat", "Protocol");
4425064a25bSMyung Bae                     return;
4435064a25bSMyung Bae                 }
4445064a25bSMyung Bae                 if (hbIntervalMinutes)
4455064a25bSMyung Bae                 {
4465064a25bSMyung Bae                     messages::propertyValueConflict(
4475064a25bSMyung Bae                         asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
4485064a25bSMyung Bae                     return;
4495064a25bSMyung Bae                 }
4503d30708fSChicago Duan                 if (msgIds)
4513d30708fSChicago Duan                 {
452bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
453bd79bce8SPatrick Williams                                                     "MessageIds", "Protocol");
4543d30708fSChicago Duan                     return;
4553d30708fSChicago Duan                 }
4563d30708fSChicago Duan                 if (regPrefixes)
4573d30708fSChicago Duan                 {
458bd79bce8SPatrick Williams                     messages::propertyValueConflict(
459bd79bce8SPatrick Williams                         asyncResp->res, "RegistryPrefixes", "Protocol");
4603d30708fSChicago Duan                     return;
4613d30708fSChicago Duan                 }
4623d30708fSChicago Duan                 if (resTypes)
4633d30708fSChicago Duan                 {
464bd79bce8SPatrick Williams                     messages::propertyValueConflict(
465bd79bce8SPatrick Williams                         asyncResp->res, "ResourceTypes", "Protocol");
4663d30708fSChicago Duan                     return;
4673d30708fSChicago Duan                 }
4683d30708fSChicago Duan                 if (headers)
4693d30708fSChicago Duan                 {
470bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
471bd79bce8SPatrick Williams                                                     "HttpHeaders", "Protocol");
4723d30708fSChicago Duan                     return;
4733d30708fSChicago Duan                 }
4743d30708fSChicago Duan                 if (mrdJsonArray)
4753d30708fSChicago Duan                 {
4763d30708fSChicago Duan                     messages::propertyValueConflict(
4773d30708fSChicago Duan                         asyncResp->res, "MetricReportDefinitions", "Protocol");
4783d30708fSChicago Duan                     return;
4793d30708fSChicago Duan                 }
480a716aa74SEd Tanous                 if (url->scheme() != "snmp")
481a716aa74SEd Tanous                 {
482bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
483bd79bce8SPatrick Williams                                                     "Destination", "Protocol");
4843d30708fSChicago Duan                     return;
4853d30708fSChicago Duan                 }
4863d30708fSChicago Duan 
487a716aa74SEd Tanous                 addSnmpTrapClient(asyncResp, url->host_address(),
488a716aa74SEd Tanous                                   url->port_number());
489a716aa74SEd Tanous                 return;
490b52664e2SAppaRao Puli             }
4913d30708fSChicago Duan 
492a716aa74SEd Tanous             std::shared_ptr<Subscription> subValue =
49321a94d5cSMyung Bae                 std::make_shared<Subscription>(
4945fe4ef35SMyung Bae                     std::make_shared<persistent_data::UserSubscription>(), *url,
495*9838eb20SEd Tanous                     getIoContext());
496e5aaf047SAppaRao Puli 
497e5aaf047SAppaRao Puli             if (subscriptionType)
498e5aaf047SAppaRao Puli             {
499e5aaf047SAppaRao Puli                 if (*subscriptionType != "RedfishEvent")
500e5aaf047SAppaRao Puli                 {
501fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
502fffb8c1fSEd Tanous                         asyncResp->res, *subscriptionType, "SubscriptionType");
503e5aaf047SAppaRao Puli                     return;
504e5aaf047SAppaRao Puli                 }
5055fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = *subscriptionType;
506e5aaf047SAppaRao Puli             }
507e5aaf047SAppaRao Puli             else
508e5aaf047SAppaRao Puli             {
5094b712a29SEd Tanous                 // Default
5105fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = "RedfishEvent";
511e5aaf047SAppaRao Puli             }
512e5aaf047SAppaRao Puli 
513e5aaf047SAppaRao Puli             if (protocol != "Redfish")
514e5aaf047SAppaRao Puli             {
515e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, protocol,
516e5aaf047SAppaRao Puli                                                  "Protocol");
517e5aaf047SAppaRao Puli                 return;
518e5aaf047SAppaRao Puli             }
5195fe4ef35SMyung Bae             subValue->userSub->protocol = protocol;
520e5aaf047SAppaRao Puli 
52119bb362bSEd Tanous             if (verifyCertificate)
52219bb362bSEd Tanous             {
5235fe4ef35SMyung Bae                 subValue->userSub->verifyCertificate = *verifyCertificate;
52419bb362bSEd Tanous             }
52519bb362bSEd Tanous 
52623a21a1cSEd Tanous             if (eventFormatType2)
527e5aaf047SAppaRao Puli             {
528bd79bce8SPatrick Williams                 if (std::ranges::find(supportedEvtFormatTypes,
529bd79bce8SPatrick Williams                                       *eventFormatType2) ==
5303544d2a7SEd Tanous                     supportedEvtFormatTypes.end())
531e5aaf047SAppaRao Puli                 {
532fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
533fffb8c1fSEd Tanous                         asyncResp->res, *eventFormatType2, "EventFormatType");
534e5aaf047SAppaRao Puli                     return;
535e5aaf047SAppaRao Puli                 }
5365fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = *eventFormatType2;
537e5aaf047SAppaRao Puli             }
538e5aaf047SAppaRao Puli             else
539e5aaf047SAppaRao Puli             {
540e5aaf047SAppaRao Puli                 // If not specified, use default "Event"
5415fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = "Event";
542e5aaf047SAppaRao Puli             }
543e5aaf047SAppaRao Puli 
544e5aaf047SAppaRao Puli             if (context)
545e5aaf047SAppaRao Puli             {
5468ece0e45SEd Tanous                 // This value is selected arbitrarily.
547600af5f1SAppaRao Puli                 constexpr const size_t maxContextSize = 256;
548600af5f1SAppaRao Puli                 if (context->size() > maxContextSize)
549600af5f1SAppaRao Puli                 {
550600af5f1SAppaRao Puli                     messages::stringValueTooLong(asyncResp->res, "Context",
551600af5f1SAppaRao Puli                                                  maxContextSize);
552600af5f1SAppaRao Puli                     return;
553600af5f1SAppaRao Puli                 }
5545fe4ef35SMyung Bae                 subValue->userSub->customText = *context;
555e5aaf047SAppaRao Puli             }
556e5aaf047SAppaRao Puli 
557e5aaf047SAppaRao Puli             if (headers)
558e5aaf047SAppaRao Puli             {
559600af5f1SAppaRao Puli                 size_t cumulativeLen = 0;
560600af5f1SAppaRao Puli 
56178d4ec4fSEd Tanous                 for (const nlohmann::json::object_t& headerChunk : *headers)
562601c71aeSEd Tanous                 {
56378d4ec4fSEd Tanous                     for (const auto& item : headerChunk)
56478d4ec4fSEd Tanous                     {
56578d4ec4fSEd Tanous                         const std::string* value =
56678d4ec4fSEd Tanous                             item.second.get_ptr<const std::string*>();
56778d4ec4fSEd Tanous                         if (value == nullptr)
56878d4ec4fSEd Tanous                         {
56978d4ec4fSEd Tanous                             messages::propertyValueFormatError(
57078d4ec4fSEd Tanous                                 asyncResp->res, item.second,
57178d4ec4fSEd Tanous                                 "HttpHeaders/" + item.first);
57278d4ec4fSEd Tanous                             return;
57378d4ec4fSEd Tanous                         }
57478d4ec4fSEd Tanous                         // Adding a new json value is the size of the key, +
57578d4ec4fSEd Tanous                         // the size of the value + 2 * 2 quotes for each, +
57678d4ec4fSEd Tanous                         // the colon and space between. example:
57778d4ec4fSEd Tanous                         // "key": "value"
57878d4ec4fSEd Tanous                         cumulativeLen += item.first.size() + value->size() + 6;
579600af5f1SAppaRao Puli                         // This value is selected to mirror http_connection.hpp
580600af5f1SAppaRao Puli                         constexpr const uint16_t maxHeaderSizeED = 8096;
581600af5f1SAppaRao Puli                         if (cumulativeLen > maxHeaderSizeED)
582600af5f1SAppaRao Puli                         {
58378d4ec4fSEd Tanous                             messages::arraySizeTooLong(
58478d4ec4fSEd Tanous                                 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
585600af5f1SAppaRao Puli                             return;
586600af5f1SAppaRao Puli                         }
5875fe4ef35SMyung Bae                         subValue->userSub->httpHeaders.set(item.first, *value);
588601c71aeSEd Tanous                     }
589601c71aeSEd Tanous                 }
590e5aaf047SAppaRao Puli             }
591e5aaf047SAppaRao Puli 
592e5aaf047SAppaRao Puli             if (regPrefixes)
593e5aaf047SAppaRao Puli             {
594e5aaf047SAppaRao Puli                 for (const std::string& it : *regPrefixes)
595e5aaf047SAppaRao Puli                 {
5963544d2a7SEd Tanous                     if (std::ranges::find(supportedRegPrefixes, it) ==
5973544d2a7SEd Tanous                         supportedRegPrefixes.end())
598e5aaf047SAppaRao Puli                     {
599fffb8c1fSEd Tanous                         messages::propertyValueNotInList(asyncResp->res, it,
600fffb8c1fSEd Tanous                                                          "RegistryPrefixes");
601e5aaf047SAppaRao Puli                         return;
602e5aaf047SAppaRao Puli                     }
603e5aaf047SAppaRao Puli                 }
6045fe4ef35SMyung Bae                 subValue->userSub->registryPrefixes = *regPrefixes;
605e5aaf047SAppaRao Puli             }
606e5aaf047SAppaRao Puli 
607a14c9113SEd Tanous             if (originResources)
608a14c9113SEd Tanous             {
6095fe4ef35SMyung Bae                 subValue->userSub->originResources = *originResources;
610a14c9113SEd Tanous             }
611a14c9113SEd Tanous 
612e56f254cSSunitha Harish             if (resTypes)
613e56f254cSSunitha Harish             {
614e56f254cSSunitha Harish                 for (const std::string& it : *resTypes)
615e56f254cSSunitha Harish                 {
6163544d2a7SEd Tanous                     if (std::ranges::find(supportedResourceTypes, it) ==
6173544d2a7SEd Tanous                         supportedResourceTypes.end())
618e56f254cSSunitha Harish                     {
619e56f254cSSunitha Harish                         messages::propertyValueNotInList(asyncResp->res, it,
620e56f254cSSunitha Harish                                                          "ResourceTypes");
621e56f254cSSunitha Harish                         return;
622e56f254cSSunitha Harish                     }
623e56f254cSSunitha Harish                 }
6245fe4ef35SMyung Bae                 subValue->userSub->resourceTypes = *resTypes;
625e56f254cSSunitha Harish             }
626e56f254cSSunitha Harish 
627e5aaf047SAppaRao Puli             if (msgIds)
628e5aaf047SAppaRao Puli             {
629b304bd79SP Dheeraj Srujan Kumar                 std::vector<std::string> registryPrefix;
630b304bd79SP Dheeraj Srujan Kumar 
6317e860f15SJohn Edward Broadbent                 // If no registry prefixes are mentioned, consider all
6327e860f15SJohn Edward Broadbent                 // supported prefixes
6335fe4ef35SMyung Bae                 if (subValue->userSub->registryPrefixes.empty())
634b304bd79SP Dheeraj Srujan Kumar                 {
635b304bd79SP Dheeraj Srujan Kumar                     registryPrefix.assign(supportedRegPrefixes.begin(),
636b304bd79SP Dheeraj Srujan Kumar                                           supportedRegPrefixes.end());
637b304bd79SP Dheeraj Srujan Kumar                 }
638b304bd79SP Dheeraj Srujan Kumar                 else
639b304bd79SP Dheeraj Srujan Kumar                 {
6405fe4ef35SMyung Bae                     registryPrefix = subValue->userSub->registryPrefixes;
641b304bd79SP Dheeraj Srujan Kumar                 }
642b304bd79SP Dheeraj Srujan Kumar 
643b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& id : *msgIds)
644b304bd79SP Dheeraj Srujan Kumar                 {
645b304bd79SP Dheeraj Srujan Kumar                     bool validId = false;
646b304bd79SP Dheeraj Srujan Kumar 
647b304bd79SP Dheeraj Srujan Kumar                     // Check for Message ID in each of the selected Registry
648b304bd79SP Dheeraj Srujan Kumar                     for (const std::string& it : registryPrefix)
649b304bd79SP Dheeraj Srujan Kumar                     {
650fffb8c1fSEd Tanous                         const std::span<const redfish::registries::MessageEntry>
651fffb8c1fSEd Tanous                             registry =
652fffb8c1fSEd Tanous                                 redfish::registries::getRegistryFromPrefix(it);
653b304bd79SP Dheeraj Srujan Kumar 
6543544d2a7SEd Tanous                         if (std::ranges::any_of(
6553544d2a7SEd Tanous                                 registry,
656fffb8c1fSEd Tanous                                 [&id](const redfish::registries::MessageEntry&
657fffb8c1fSEd Tanous                                           messageEntry) {
65855f79e6fSEd Tanous                                     return id == messageEntry.first;
659b304bd79SP Dheeraj Srujan Kumar                                 }))
660b304bd79SP Dheeraj Srujan Kumar                         {
661b304bd79SP Dheeraj Srujan Kumar                             validId = true;
662b304bd79SP Dheeraj Srujan Kumar                             break;
663b304bd79SP Dheeraj Srujan Kumar                         }
664b304bd79SP Dheeraj Srujan Kumar                     }
665b304bd79SP Dheeraj Srujan Kumar 
666b304bd79SP Dheeraj Srujan Kumar                     if (!validId)
667b304bd79SP Dheeraj Srujan Kumar                     {
668b304bd79SP Dheeraj Srujan Kumar                         messages::propertyValueNotInList(asyncResp->res, id,
669b304bd79SP Dheeraj Srujan Kumar                                                          "MessageIds");
670b304bd79SP Dheeraj Srujan Kumar                         return;
671b304bd79SP Dheeraj Srujan Kumar                     }
672b304bd79SP Dheeraj Srujan Kumar                 }
673b304bd79SP Dheeraj Srujan Kumar 
6745fe4ef35SMyung Bae                 subValue->userSub->registryMsgIds = *msgIds;
675e5aaf047SAppaRao Puli             }
676e5aaf047SAppaRao Puli 
677e5aaf047SAppaRao Puli             if (retryPolicy)
678e5aaf047SAppaRao Puli             {
6793544d2a7SEd Tanous                 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
6803544d2a7SEd Tanous                     supportedRetryPolicies.end())
681e5aaf047SAppaRao Puli                 {
682bd79bce8SPatrick Williams                     messages::propertyValueNotInList(
683bd79bce8SPatrick Williams                         asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
684e5aaf047SAppaRao Puli                     return;
685e5aaf047SAppaRao Puli                 }
6865fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = *retryPolicy;
687e5aaf047SAppaRao Puli             }
688e5aaf047SAppaRao Puli             else
689e5aaf047SAppaRao Puli             {
690e5aaf047SAppaRao Puli                 // Default "TerminateAfterRetries"
6915fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = "TerminateAfterRetries";
692e5aaf047SAppaRao Puli             }
6935064a25bSMyung Bae             if (sendHeartbeat)
6945064a25bSMyung Bae             {
6955064a25bSMyung Bae                 subValue->userSub->sendHeartbeat = *sendHeartbeat;
6965064a25bSMyung Bae             }
6975064a25bSMyung Bae             if (hbIntervalMinutes)
6985064a25bSMyung Bae             {
6995064a25bSMyung Bae                 if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
7005064a25bSMyung Bae                 {
7015064a25bSMyung Bae                     messages::propertyValueOutOfRange(
7025064a25bSMyung Bae                         asyncResp->res, *hbIntervalMinutes,
7035064a25bSMyung Bae                         "HeartbeatIntervalMinutes");
7045064a25bSMyung Bae                     return;
7055064a25bSMyung Bae                 }
7065064a25bSMyung Bae                 subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
7075064a25bSMyung Bae             }
708e5aaf047SAppaRao Puli 
709144b6318SAppaRao Puli             if (mrdJsonArray)
710156d6b00SAppaRao Puli             {
71178d4ec4fSEd Tanous                 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
712144b6318SAppaRao Puli                 {
713144b6318SAppaRao Puli                     std::string mrdUri;
714ea2e6eecSWilly Tu 
71578d4ec4fSEd Tanous                     if (!json_util::readJsonObject(mrdObj, asyncResp->res,
71678d4ec4fSEd Tanous                                                    "@odata.id", mrdUri))
717ea2e6eecSWilly Tu 
718144b6318SAppaRao Puli                     {
719144b6318SAppaRao Puli                         return;
720144b6318SAppaRao Puli                     }
7215fe4ef35SMyung Bae                     subValue->userSub->metricReportDefinitions.emplace_back(
7224b712a29SEd Tanous                         mrdUri);
723144b6318SAppaRao Puli                 }
724156d6b00SAppaRao Puli             }
725156d6b00SAppaRao Puli 
726b52664e2SAppaRao Puli             std::string id =
727bd79bce8SPatrick Williams                 EventServiceManager::getInstance().addPushSubscription(
728bd79bce8SPatrick Williams                     subValue);
729b52664e2SAppaRao Puli             if (id.empty())
730e5aaf047SAppaRao Puli             {
731e5aaf047SAppaRao Puli                 messages::internalError(asyncResp->res);
732e5aaf047SAppaRao Puli                 return;
733e5aaf047SAppaRao Puli             }
734e5aaf047SAppaRao Puli 
735e5aaf047SAppaRao Puli             messages::created(asyncResp->res);
736e5aaf047SAppaRao Puli             asyncResp->res.addHeader(
737e5aaf047SAppaRao Puli                 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
738fb546105SMyung Bae 
739fb546105SMyung Bae             // schedule a heartbeat
740fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
741fb546105SMyung Bae             {
742fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
743fb546105SMyung Bae             }
7447e860f15SJohn Edward Broadbent         });
745e5aaf047SAppaRao Puli }
746e5aaf047SAppaRao Puli 
7477e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
748e5aaf047SAppaRao Puli {
7499d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
750ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
7517e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
75245ca1b86SEd Tanous             [&app](const crow::Request& req,
7537e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
7547e860f15SJohn Edward Broadbent                    const std::string& param) {
7553ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
75645ca1b86SEd Tanous                 {
75745ca1b86SEd Tanous                     return;
75845ca1b86SEd Tanous                 }
7593d30708fSChicago Duan 
7603d30708fSChicago Duan                 if (param.starts_with("snmp"))
7613d30708fSChicago Duan                 {
7623d30708fSChicago Duan                     getSnmpTrapClient(asyncResp, param);
7633d30708fSChicago Duan                     return;
7643d30708fSChicago Duan                 }
7653d30708fSChicago Duan 
766b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
7677e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
768b52664e2SAppaRao Puli                 if (subValue == nullptr)
769e5aaf047SAppaRao Puli                 {
770bd79bce8SPatrick Williams                     asyncResp->res.result(
771bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
772e5aaf047SAppaRao Puli                     return;
773e5aaf047SAppaRao Puli                 }
7747e860f15SJohn Edward Broadbent                 const std::string& id = param;
775e5aaf047SAppaRao Puli 
7764b712a29SEd Tanous                 const persistent_data::UserSubscription& userSub =
7775fe4ef35SMyung Bae                     *subValue->userSub;
778e56f254cSSunitha Harish 
7794b712a29SEd Tanous                 nlohmann::json& jVal = asyncResp->res.jsonValue;
7804b712a29SEd Tanous                 jVal["@odata.type"] =
7814b712a29SEd Tanous                     "#EventDestination.v1_14_1.EventDestination";
7824b712a29SEd Tanous                 jVal["Protocol"] =
7834b712a29SEd Tanous                     event_destination::EventDestinationProtocol::Redfish;
7844b712a29SEd Tanous                 jVal["@odata.id"] = boost::urls::format(
7854b712a29SEd Tanous                     "/redfish/v1/EventService/Subscriptions/{}", id);
7864b712a29SEd Tanous                 jVal["Id"] = id;
7874b712a29SEd Tanous                 jVal["Name"] = "Event Destination " + id;
7884b712a29SEd Tanous                 jVal["Destination"] = userSub.destinationUrl;
7894b712a29SEd Tanous                 jVal["Context"] = userSub.customText;
7904b712a29SEd Tanous                 jVal["SubscriptionType"] = userSub.subscriptionType;
7914b712a29SEd Tanous                 jVal["HttpHeaders"] = nlohmann::json::array();
7924b712a29SEd Tanous                 jVal["EventFormatType"] = userSub.eventFormatType;
7934b712a29SEd Tanous                 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
7944b712a29SEd Tanous                 jVal["ResourceTypes"] = userSub.resourceTypes;
7954b712a29SEd Tanous 
7964b712a29SEd Tanous                 jVal["MessageIds"] = userSub.registryMsgIds;
7974b712a29SEd Tanous                 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
7985064a25bSMyung Bae                 jVal["SendHeartbeat"] = userSub.sendHeartbeat;
7995064a25bSMyung Bae                 jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
8004b712a29SEd Tanous                 jVal["VerifyCertificate"] = userSub.verifyCertificate;
801144b6318SAppaRao Puli 
8021476687dSEd Tanous                 nlohmann::json::array_t mrdJsonArray;
8034b712a29SEd Tanous                 for (const auto& mdrUri : userSub.metricReportDefinitions)
804144b6318SAppaRao Puli                 {
8051476687dSEd Tanous                     nlohmann::json::object_t mdr;
8061476687dSEd Tanous                     mdr["@odata.id"] = mdrUri;
8071476687dSEd Tanous                     mrdJsonArray.emplace_back(std::move(mdr));
808144b6318SAppaRao Puli                 }
8094b712a29SEd Tanous                 jVal["MetricReportDefinitions"] = mrdJsonArray;
8107e860f15SJohn Edward Broadbent             });
8119d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
812ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
813ed398213SEd Tanous         // ConfigureSelf
8147eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
815ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
816432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
8177e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
81845ca1b86SEd Tanous             [&app](const crow::Request& req,
8197e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8207e860f15SJohn Edward Broadbent                    const std::string& param) {
8213ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
82245ca1b86SEd Tanous                 {
82345ca1b86SEd Tanous                     return;
82445ca1b86SEd Tanous                 }
825b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
8267e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
827b52664e2SAppaRao Puli                 if (subValue == nullptr)
828e5aaf047SAppaRao Puli                 {
829bd79bce8SPatrick Williams                     asyncResp->res.result(
830bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
831e5aaf047SAppaRao Puli                     return;
832e5aaf047SAppaRao Puli                 }
833e5aaf047SAppaRao Puli 
834e5aaf047SAppaRao Puli                 std::optional<std::string> context;
835e5aaf047SAppaRao Puli                 std::optional<std::string> retryPolicy;
8365064a25bSMyung Bae                 std::optional<bool> sendHeartbeat;
8375064a25bSMyung Bae                 std::optional<uint64_t> hbIntervalMinutes;
83819bb362bSEd Tanous                 std::optional<bool> verifyCertificate;
83978d4ec4fSEd Tanous                 std::optional<std::vector<nlohmann::json::object_t>> headers;
840e5aaf047SAppaRao Puli 
841afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
842afc474aeSMyung Bae                         req, asyncResp->res, //
843afc474aeSMyung Bae                         "Context", context, //
844afc474aeSMyung Bae                         "DeliveryRetryPolicy", retryPolicy, //
8455064a25bSMyung Bae                         "HeartbeatIntervalMinutes", hbIntervalMinutes, //
846afc474aeSMyung Bae                         "HttpHeaders", headers, //
8475064a25bSMyung Bae                         "SendHeartbeat", sendHeartbeat, //
848afc474aeSMyung Bae                         "VerifyCertificate", verifyCertificate //
849afc474aeSMyung Bae                         ))
850e5aaf047SAppaRao Puli                 {
851e5aaf047SAppaRao Puli                     return;
852e5aaf047SAppaRao Puli                 }
853e5aaf047SAppaRao Puli 
854e5aaf047SAppaRao Puli                 if (context)
855e5aaf047SAppaRao Puli                 {
8565fe4ef35SMyung Bae                     subValue->userSub->customText = *context;
857e5aaf047SAppaRao Puli                 }
858e5aaf047SAppaRao Puli 
859e5aaf047SAppaRao Puli                 if (headers)
860e5aaf047SAppaRao Puli                 {
861601c71aeSEd Tanous                     boost::beast::http::fields fields;
86278d4ec4fSEd Tanous                     for (const nlohmann::json::object_t& headerChunk : *headers)
863601c71aeSEd Tanous                     {
86478d4ec4fSEd Tanous                         for (const auto& it : headerChunk)
865601c71aeSEd Tanous                         {
866601c71aeSEd Tanous                             const std::string* value =
86778d4ec4fSEd Tanous                                 it.second.get_ptr<const std::string*>();
868601c71aeSEd Tanous                             if (value == nullptr)
869601c71aeSEd Tanous                             {
870601c71aeSEd Tanous                                 messages::propertyValueFormatError(
87178d4ec4fSEd Tanous                                     asyncResp->res, it.second,
87278d4ec4fSEd Tanous                                     "HttpHeaders/" + it.first);
873601c71aeSEd Tanous                                 return;
874601c71aeSEd Tanous                             }
87578d4ec4fSEd Tanous                             fields.set(it.first, *value);
876601c71aeSEd Tanous                         }
877601c71aeSEd Tanous                     }
8785fe4ef35SMyung Bae                     subValue->userSub->httpHeaders = std::move(fields);
879e5aaf047SAppaRao Puli                 }
880e5aaf047SAppaRao Puli 
881e5aaf047SAppaRao Puli                 if (retryPolicy)
882e5aaf047SAppaRao Puli                 {
883bd79bce8SPatrick Williams                     if (std::ranges::find(supportedRetryPolicies,
884bd79bce8SPatrick Williams                                           *retryPolicy) ==
8853544d2a7SEd Tanous                         supportedRetryPolicies.end())
886e5aaf047SAppaRao Puli                     {
887bd79bce8SPatrick Williams                         messages::propertyValueNotInList(asyncResp->res,
888bd79bce8SPatrick Williams                                                          *retryPolicy,
889e5aaf047SAppaRao Puli                                                          "DeliveryRetryPolicy");
890e5aaf047SAppaRao Puli                         return;
891e5aaf047SAppaRao Puli                     }
8925fe4ef35SMyung Bae                     subValue->userSub->retryPolicy = *retryPolicy;
893e5aaf047SAppaRao Puli                 }
894e5aaf047SAppaRao Puli 
8955064a25bSMyung Bae                 if (sendHeartbeat)
8965064a25bSMyung Bae                 {
8975064a25bSMyung Bae                     subValue->userSub->sendHeartbeat = *sendHeartbeat;
8985064a25bSMyung Bae                 }
8995064a25bSMyung Bae                 if (hbIntervalMinutes)
9005064a25bSMyung Bae                 {
9015064a25bSMyung Bae                     if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
9025064a25bSMyung Bae                     {
9035064a25bSMyung Bae                         messages::propertyValueOutOfRange(
9045064a25bSMyung Bae                             asyncResp->res, *hbIntervalMinutes,
9055064a25bSMyung Bae                             "HeartbeatIntervalMinutes");
9065064a25bSMyung Bae                         return;
9075064a25bSMyung Bae                     }
9085064a25bSMyung Bae                     subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
9095064a25bSMyung Bae                 }
9105064a25bSMyung Bae 
911fb546105SMyung Bae                 if (hbIntervalMinutes || sendHeartbeat)
912fb546105SMyung Bae                 {
913fb546105SMyung Bae                     // if Heartbeat interval or send heart were changed, cancel
914fb546105SMyung Bae                     // the heartbeat timer if running and start a new heartbeat
915fb546105SMyung Bae                     // if needed
916fb546105SMyung Bae                     subValue->heartbeatParametersChanged();
917fb546105SMyung Bae                 }
918fb546105SMyung Bae 
91919bb362bSEd Tanous                 if (verifyCertificate)
92019bb362bSEd Tanous                 {
9215fe4ef35SMyung Bae                     subValue->userSub->verifyCertificate = *verifyCertificate;
92219bb362bSEd Tanous                 }
92319bb362bSEd Tanous 
924b52664e2SAppaRao Puli                 EventServiceManager::getInstance().updateSubscriptionData();
9257e860f15SJohn Edward Broadbent             });
9269d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
927ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
928ed398213SEd Tanous         // ConfigureSelf
9297eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
930ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
931432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
9327e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
93345ca1b86SEd Tanous             [&app](const crow::Request& req,
9347e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9357e860f15SJohn Edward Broadbent                    const std::string& param) {
9363ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
93745ca1b86SEd Tanous                 {
93845ca1b86SEd Tanous                     return;
93945ca1b86SEd Tanous                 }
9404b712a29SEd Tanous                 EventServiceManager& event = EventServiceManager::getInstance();
9413d30708fSChicago Duan                 if (param.starts_with("snmp"))
9423d30708fSChicago Duan                 {
9433d30708fSChicago Duan                     deleteSnmpTrapClient(asyncResp, param);
9444b712a29SEd Tanous                     event.deleteSubscription(param);
9453d30708fSChicago Duan                     return;
9463d30708fSChicago Duan                 }
9473d30708fSChicago Duan 
9484b712a29SEd Tanous                 if (!event.deleteSubscription(param))
949e5aaf047SAppaRao Puli                 {
9504b712a29SEd Tanous                     messages::resourceNotFound(asyncResp->res,
9514b712a29SEd Tanous                                                "EventDestination", param);
952e5aaf047SAppaRao Puli                     return;
953e5aaf047SAppaRao Puli                 }
9544b712a29SEd Tanous                 messages::success(asyncResp->res);
9557e860f15SJohn Edward Broadbent             });
956e5aaf047SAppaRao Puli }
957e5aaf047SAppaRao Puli 
958e5aaf047SAppaRao Puli } // namespace redfish
959