xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation
4e5aaf047SAppaRao Puli #pragma once
53ccb3adbSEd Tanous #include "app.hpp"
6b52664e2SAppaRao Puli #include "event_service_manager.hpp"
7539d8c6bSEd Tanous #include "generated/enums/event_service.hpp"
83ccb3adbSEd Tanous #include "http/utility.hpp"
93ccb3adbSEd Tanous #include "logging.hpp"
103ccb3adbSEd Tanous #include "query.hpp"
11d109e2b6SAlexander Hansen #include "registries.hpp"
123ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
13d109e2b6SAlexander Hansen #include "registries_selector.hpp"
143d30708fSChicago Duan #include "snmp_trap_event_clients.hpp"
15d109e2b6SAlexander Hansen #include "utils/json_utils.hpp"
16e5aaf047SAppaRao Puli 
17601c71aeSEd Tanous #include <boost/beast/http/fields.hpp>
183d30708fSChicago Duan #include <boost/system/error_code.hpp>
19a716aa74SEd Tanous #include <boost/url/parse.hpp>
203d30708fSChicago Duan #include <sdbusplus/unpack_properties.hpp>
213d30708fSChicago Duan #include <utils/dbus_utils.hpp>
22ed398213SEd Tanous 
233d30708fSChicago Duan #include <charconv>
243d30708fSChicago Duan #include <memory>
255064a25bSMyung Bae #include <optional>
263544d2a7SEd Tanous #include <ranges>
271e270c5fSPatrick Williams #include <span>
283d30708fSChicago Duan #include <string>
29a14c9113SEd Tanous #include <vector>
301e270c5fSPatrick Williams 
31e5aaf047SAppaRao Puli namespace redfish
32e5aaf047SAppaRao Puli {
33e5aaf047SAppaRao Puli 
34156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
35156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
36fb546105SMyung Bae static constexpr const std::array<const char*, 4> supportedRegPrefixes = {
37fb546105SMyung Bae     "Base", "OpenBMC", "TaskEvent", "HeartbeatEvent"};
38e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
39e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
40e5aaf047SAppaRao Puli 
41fb546105SMyung Bae static constexpr const std::array<const char*, 2> supportedResourceTypes = {
42fb546105SMyung Bae     "Task", "Heartbeat"};
43e56f254cSSunitha Harish 
447e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
45e5aaf047SAppaRao Puli {
467e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
47ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
48bd79bce8SPatrick Williams         .methods(
49bd79bce8SPatrick Williams             boost::beast::http::verb::
50bd79bce8SPatrick Williams                 get)([&app](
51bd79bce8SPatrick Williams                          const crow::Request& req,
52002d39b4SEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
533ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
5445ca1b86SEd Tanous             {
5545ca1b86SEd Tanous                 return;
5645ca1b86SEd Tanous             }
571476687dSEd Tanous 
581476687dSEd Tanous             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
591476687dSEd Tanous             asyncResp->res.jsonValue["@odata.type"] =
601476687dSEd Tanous                 "#EventService.v1_5_0.EventService";
611476687dSEd Tanous             asyncResp->res.jsonValue["Id"] = "EventService";
621476687dSEd Tanous             asyncResp->res.jsonValue["Name"] = "Event Service";
635e44e3d8SAppaRao Puli             asyncResp->res.jsonValue["ServerSentEventUri"] =
645e44e3d8SAppaRao Puli                 "/redfish/v1/EventService/SSE";
655e44e3d8SAppaRao Puli 
661476687dSEd Tanous             asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
671476687dSEd Tanous                 "/redfish/v1/EventService/Subscriptions";
68bd79bce8SPatrick Williams             asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
69bd79bce8SPatrick Williams                                     ["target"] =
701476687dSEd Tanous                 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
71e5aaf047SAppaRao Puli 
7228afb49cSJunLin Chen             const persistent_data::EventServiceConfig eventServiceConfig =
7328afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
7428afb49cSJunLin Chen                     .getEventServiceConfig();
757d1cc387SAppaRao Puli 
767d1cc387SAppaRao Puli             asyncResp->res.jsonValue["Status"]["State"] =
7728afb49cSJunLin Chen                 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
78bd79bce8SPatrick Williams             asyncResp->res.jsonValue["ServiceEnabled"] =
79bd79bce8SPatrick Williams                 eventServiceConfig.enabled;
807e860f15SJohn Edward Broadbent             asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
8128afb49cSJunLin Chen                 eventServiceConfig.retryAttempts;
82e5aaf047SAppaRao Puli             asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
8328afb49cSJunLin Chen                 eventServiceConfig.retryTimeoutInterval;
84bd79bce8SPatrick Williams             asyncResp->res.jsonValue["EventFormatTypes"] =
85bd79bce8SPatrick Williams                 supportedEvtFormatTypes;
860fda0f12SGeorge Liu             asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
870fda0f12SGeorge Liu             asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
8807941a88SAyushi Smriti 
89613dabeaSEd Tanous             nlohmann::json::object_t supportedSSEFilters;
90613dabeaSEd Tanous             supportedSSEFilters["EventFormatType"] = true;
91613dabeaSEd Tanous             supportedSSEFilters["MessageId"] = true;
92613dabeaSEd Tanous             supportedSSEFilters["MetricReportDefinition"] = true;
93613dabeaSEd Tanous             supportedSSEFilters["RegistryPrefix"] = true;
94613dabeaSEd Tanous             supportedSSEFilters["OriginResource"] = false;
95613dabeaSEd Tanous             supportedSSEFilters["ResourceType"] = false;
9607941a88SAyushi Smriti 
9707941a88SAyushi Smriti             asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
98613dabeaSEd Tanous                 std::move(supportedSSEFilters);
997e860f15SJohn Edward Broadbent         });
100e5aaf047SAppaRao Puli 
1017e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
102ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1037e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
10445ca1b86SEd Tanous             [&app](const crow::Request& req,
10545ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1063ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
107e5aaf047SAppaRao Puli                 {
10845ca1b86SEd Tanous                     return;
10945ca1b86SEd Tanous                 }
110e5aaf047SAppaRao Puli                 std::optional<bool> serviceEnabled;
111e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryAttemps;
112e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryInterval;
113afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
114afc474aeSMyung Bae                         req, asyncResp->res, //
115afc474aeSMyung Bae                         "DeliveryRetryAttempts", retryAttemps, //
116afc474aeSMyung Bae                         "DeliveryRetryIntervalSeconds", retryInterval, //
117afc474aeSMyung Bae                         "ServiceEnabled", serviceEnabled //
118afc474aeSMyung Bae                         ))
119e5aaf047SAppaRao Puli                 {
120e5aaf047SAppaRao Puli                     return;
121e5aaf047SAppaRao Puli                 }
122e5aaf047SAppaRao Puli 
12328afb49cSJunLin Chen                 persistent_data::EventServiceConfig eventServiceConfig =
12428afb49cSJunLin Chen                     persistent_data::EventServiceStore::getInstance()
12528afb49cSJunLin Chen                         .getEventServiceConfig();
1267d1cc387SAppaRao Puli 
127e5aaf047SAppaRao Puli                 if (serviceEnabled)
128e5aaf047SAppaRao Puli                 {
12928afb49cSJunLin Chen                     eventServiceConfig.enabled = *serviceEnabled;
130e5aaf047SAppaRao Puli                 }
131e5aaf047SAppaRao Puli 
132e5aaf047SAppaRao Puli                 if (retryAttemps)
133e5aaf047SAppaRao Puli                 {
134e5aaf047SAppaRao Puli                     // Supported range [1-3]
135e5aaf047SAppaRao Puli                     if ((*retryAttemps < 1) || (*retryAttemps > 3))
136e5aaf047SAppaRao Puli                     {
137e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
138e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryAttemps),
139e5aaf047SAppaRao Puli                             "DeliveryRetryAttempts", "[1-3]");
140e5aaf047SAppaRao Puli                     }
141e5aaf047SAppaRao Puli                     else
142e5aaf047SAppaRao Puli                     {
14328afb49cSJunLin Chen                         eventServiceConfig.retryAttempts = *retryAttemps;
144e5aaf047SAppaRao Puli                     }
145e5aaf047SAppaRao Puli                 }
146e5aaf047SAppaRao Puli 
147e5aaf047SAppaRao Puli                 if (retryInterval)
148e5aaf047SAppaRao Puli                 {
14933a32b34SGunnar Mills                     // Supported range [5 - 180]
15033a32b34SGunnar Mills                     if ((*retryInterval < 5) || (*retryInterval > 180))
151e5aaf047SAppaRao Puli                     {
152e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
153e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryInterval),
15433a32b34SGunnar Mills                             "DeliveryRetryIntervalSeconds", "[5-180]");
155e5aaf047SAppaRao Puli                     }
156e5aaf047SAppaRao Puli                     else
157e5aaf047SAppaRao Puli                     {
158bd79bce8SPatrick Williams                         eventServiceConfig.retryTimeoutInterval =
159bd79bce8SPatrick Williams                             *retryInterval;
160e5aaf047SAppaRao Puli                     }
161e5aaf047SAppaRao Puli                 }
162e5aaf047SAppaRao Puli 
1637d1cc387SAppaRao Puli                 EventServiceManager::getInstance().setEventServiceConfig(
16428afb49cSJunLin Chen                     eventServiceConfig);
1657e860f15SJohn Edward Broadbent             });
1660b4bdd93SAppaRao Puli }
1670b4bdd93SAppaRao Puli 
1687e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1690b4bdd93SAppaRao Puli {
1707e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1717e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
172ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1737e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
17445ca1b86SEd Tanous             [&app](const crow::Request& req,
1757e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1763ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
17745ca1b86SEd Tanous                 {
17845ca1b86SEd Tanous                     return;
17945ca1b86SEd Tanous                 }
18081ee0e74SChandramohan Harkude 
18181ee0e74SChandramohan Harkude                 TestEvent testEvent;
18281ee0e74SChandramohan Harkude                 // clang-format off
18381ee0e74SChandramohan Harkude                 if (!json_util::readJsonAction(
18481ee0e74SChandramohan Harkude                         req, asyncResp->res,
18581ee0e74SChandramohan Harkude                         "EventGroupId", testEvent.eventGroupId,
18681ee0e74SChandramohan Harkude                         "EventId", testEvent.eventId,
18781ee0e74SChandramohan Harkude                         "EventTimestamp", testEvent.eventTimestamp,
18881ee0e74SChandramohan Harkude                         "Message", testEvent.message,
18981ee0e74SChandramohan Harkude                         "MessageArgs", testEvent.messageArgs,
19081ee0e74SChandramohan Harkude                         "MessageId", testEvent.messageId,
19181ee0e74SChandramohan Harkude                         "OriginOfCondition", testEvent.originOfCondition,
19281ee0e74SChandramohan Harkude                         "Resolution", testEvent.resolution,
19381ee0e74SChandramohan Harkude                         "Severity", testEvent.severity))
19481ee0e74SChandramohan Harkude                 {
19581ee0e74SChandramohan Harkude                     return;
19681ee0e74SChandramohan Harkude                 }
19781ee0e74SChandramohan Harkude                 // clang-format on
19881ee0e74SChandramohan Harkude 
19981ee0e74SChandramohan Harkude                 if (!EventServiceManager::getInstance().sendTestEventLog(
20081ee0e74SChandramohan Harkude                         testEvent))
2016ba8c82eSsunharis_in                 {
2026ba8c82eSsunharis_in                     messages::serviceDisabled(asyncResp->res,
2036ba8c82eSsunharis_in                                               "/redfish/v1/EventService/");
2046ba8c82eSsunharis_in                     return;
2056ba8c82eSsunharis_in                 }
2068d1b46d7Szhanghch05                 asyncResp->res.result(boost::beast::http::status::no_content);
2077e860f15SJohn Edward Broadbent             });
208e5aaf047SAppaRao Puli }
209e5aaf047SAppaRao Puli 
2103d30708fSChicago Duan inline void doSubscriptionCollection(
211e81de512SEd Tanous     const boost::system::error_code& ec,
2123d30708fSChicago Duan     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2133d30708fSChicago Duan     const dbus::utility::ManagedObjectType& resp)
2143d30708fSChicago Duan {
2153d30708fSChicago Duan     if (ec)
2163d30708fSChicago Duan     {
2171306101eSEd Tanous         if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
2183d30708fSChicago Duan         {
2193d30708fSChicago Duan             // This is an optional process so just return if it isn't there
2203d30708fSChicago Duan             return;
2213d30708fSChicago Duan         }
2223d30708fSChicago Duan 
22362598e31SEd Tanous         BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
2243d30708fSChicago Duan         messages::internalError(asyncResp->res);
2253d30708fSChicago Duan         return;
2263d30708fSChicago Duan     }
2273d30708fSChicago Duan     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
2283d30708fSChicago Duan     for (const auto& objpath : resp)
2293d30708fSChicago Duan     {
2303d30708fSChicago Duan         sdbusplus::message::object_path path(objpath.first);
2313d30708fSChicago Duan         const std::string snmpId = path.filename();
2323d30708fSChicago Duan         if (snmpId.empty())
2333d30708fSChicago Duan         {
23462598e31SEd Tanous             BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
2353d30708fSChicago Duan             messages::internalError(asyncResp->res);
2363d30708fSChicago Duan             return;
2373d30708fSChicago Duan         }
2383d30708fSChicago Duan 
2393d30708fSChicago Duan         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
2403d30708fSChicago Duan     }
2413d30708fSChicago Duan }
2423d30708fSChicago Duan 
2437e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
244e5aaf047SAppaRao Puli {
2451ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
246ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
2477e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
24845ca1b86SEd Tanous             [&app](const crow::Request& req,
2497e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2503ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
25145ca1b86SEd Tanous                 {
25245ca1b86SEd Tanous                     return;
25345ca1b86SEd Tanous                 }
2541476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.type"] =
2551476687dSEd Tanous                     "#EventDestinationCollection.EventDestinationCollection";
2561476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.id"] =
2571476687dSEd Tanous                     "/redfish/v1/EventService/Subscriptions";
258bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Name"] =
259bd79bce8SPatrick Williams                     "Event Destination Collections";
260e5aaf047SAppaRao Puli 
261bd79bce8SPatrick Williams                 nlohmann::json& memberArray =
262bd79bce8SPatrick Williams                     asyncResp->res.jsonValue["Members"];
263e5aaf047SAppaRao Puli 
264b52664e2SAppaRao Puli                 std::vector<std::string> subscripIds =
265b52664e2SAppaRao Puli                     EventServiceManager::getInstance().getAllIDs();
266b52664e2SAppaRao Puli                 memberArray = nlohmann::json::array();
267bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Members@odata.count"] =
268bd79bce8SPatrick Williams                     subscripIds.size();
269b52664e2SAppaRao Puli 
270b52664e2SAppaRao Puli                 for (const std::string& id : subscripIds)
271e5aaf047SAppaRao Puli                 {
2721476687dSEd Tanous                     nlohmann::json::object_t member;
2733d30708fSChicago Duan                     member["@odata.id"] = boost::urls::format(
2743d30708fSChicago Duan                         "/redfish/v1/EventService/Subscriptions/{}" + id);
275b2ba3072SPatrick Williams                     memberArray.emplace_back(std::move(member));
276e5aaf047SAppaRao Puli                 }
2773d30708fSChicago Duan                 crow::connections::systemBus->async_method_call(
278e81de512SEd Tanous                     [asyncResp](const boost::system::error_code& ec,
2793d30708fSChicago Duan                                 const dbus::utility::ManagedObjectType& resp) {
2803d30708fSChicago Duan                         doSubscriptionCollection(ec, asyncResp, resp);
2813d30708fSChicago Duan                     },
2823d30708fSChicago Duan                     "xyz.openbmc_project.Network.SNMP",
2833d30708fSChicago Duan                     "/xyz/openbmc_project/network/snmp/manager",
2843d30708fSChicago Duan                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2857e860f15SJohn Edward Broadbent             });
2863d30708fSChicago Duan 
2877e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
2887eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
289bd79bce8SPatrick Williams         .methods(
290bd79bce8SPatrick Williams             boost::beast::http::verb::
291bd79bce8SPatrick Williams                 post)([&app](
292bd79bce8SPatrick Williams                           const crow::Request& req,
2937e860f15SJohn Edward Broadbent                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2943ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
29545ca1b86SEd Tanous             {
29645ca1b86SEd Tanous                 return;
29745ca1b86SEd Tanous             }
298fffb8c1fSEd Tanous             if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
299fffb8c1fSEd Tanous                 maxNoOfSubscriptions)
300e5aaf047SAppaRao Puli             {
301e5aaf047SAppaRao Puli                 messages::eventSubscriptionLimitExceeded(asyncResp->res);
302e5aaf047SAppaRao Puli                 return;
303e5aaf047SAppaRao Puli             }
304e5aaf047SAppaRao Puli             std::string destUrl;
305e5aaf047SAppaRao Puli             std::string protocol;
30619bb362bSEd Tanous             std::optional<bool> verifyCertificate;
307e5aaf047SAppaRao Puli             std::optional<std::string> context;
308e5aaf047SAppaRao Puli             std::optional<std::string> subscriptionType;
30923a21a1cSEd Tanous             std::optional<std::string> eventFormatType2;
310e5aaf047SAppaRao Puli             std::optional<std::string> retryPolicy;
3115064a25bSMyung Bae             std::optional<bool> sendHeartbeat;
3125064a25bSMyung Bae             std::optional<uint64_t> hbIntervalMinutes;
313e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> msgIds;
314e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> regPrefixes;
315a14c9113SEd Tanous             std::optional<std::vector<std::string>> originResources;
316e56f254cSSunitha Harish             std::optional<std::vector<std::string>> resTypes;
31778d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> headers;
31878d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
319e5aaf047SAppaRao Puli 
320afc474aeSMyung Bae             if (!json_util::readJsonPatch( //
321afc474aeSMyung Bae                     req, asyncResp->res, //
322afc474aeSMyung Bae                     "Context", context, //
323afc474aeSMyung Bae                     "DeliveryRetryPolicy", retryPolicy, //
324afc474aeSMyung Bae                     "Destination", destUrl, //
325afc474aeSMyung Bae                     "EventFormatType", eventFormatType2, //
3265064a25bSMyung Bae                     "HeartbeatIntervalMinutes", hbIntervalMinutes, //
327afc474aeSMyung Bae                     "HttpHeaders", headers, //
328afc474aeSMyung Bae                     "MessageIds", msgIds, //
329afc474aeSMyung Bae                     "MetricReportDefinitions", mrdJsonArray, //
330afc474aeSMyung Bae                     "OriginResources", originResources, //
331afc474aeSMyung Bae                     "Protocol", protocol, //
332afc474aeSMyung Bae                     "RegistryPrefixes", regPrefixes, //
333afc474aeSMyung Bae                     "ResourceTypes", resTypes, //
3345064a25bSMyung Bae                     "SendHeartbeat", sendHeartbeat, //
335afc474aeSMyung Bae                     "SubscriptionType", subscriptionType, //
336afc474aeSMyung Bae                     "VerifyCertificate", verifyCertificate //
3374b712a29SEd Tanous                     ))
338e5aaf047SAppaRao Puli             {
339e5aaf047SAppaRao Puli                 return;
340e5aaf047SAppaRao Puli             }
3414b712a29SEd Tanous             // clang-format on
342e5aaf047SAppaRao Puli 
343600af5f1SAppaRao Puli             // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
344600af5f1SAppaRao Puli             static constexpr const uint16_t maxDestinationSize = 2000;
345600af5f1SAppaRao Puli             if (destUrl.size() > maxDestinationSize)
346600af5f1SAppaRao Puli             {
347600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Destination",
348600af5f1SAppaRao Puli                                              maxDestinationSize);
349600af5f1SAppaRao Puli                 return;
350600af5f1SAppaRao Puli             }
351600af5f1SAppaRao Puli 
352dd28ba82SAppaRao Puli             if (regPrefixes && msgIds)
353dd28ba82SAppaRao Puli             {
35426f6976fSEd Tanous                 if (!regPrefixes->empty() && !msgIds->empty())
355dd28ba82SAppaRao Puli                 {
356bd79bce8SPatrick Williams                     messages::propertyValueConflict(
357bd79bce8SPatrick Williams                         asyncResp->res, "MessageIds", "RegistryPrefixes");
358dd28ba82SAppaRao Puli                     return;
359dd28ba82SAppaRao Puli                 }
360dd28ba82SAppaRao Puli             }
361dd28ba82SAppaRao Puli 
3626fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
363a716aa74SEd Tanous                 boost::urls::parse_absolute_uri(destUrl);
364a716aa74SEd Tanous             if (!url)
365e5aaf047SAppaRao Puli             {
366bd79bce8SPatrick Williams                 BMCWEB_LOG_WARNING(
367bd79bce8SPatrick Williams                     "Failed to validate and split destination url");
368e5aaf047SAppaRao Puli                 messages::propertyValueFormatError(asyncResp->res, destUrl,
369e5aaf047SAppaRao Puli                                                    "Destination");
370e5aaf047SAppaRao Puli                 return;
371e5aaf047SAppaRao Puli             }
372a716aa74SEd Tanous             url->normalize();
373b07942e3SGeorge Liu 
374b07942e3SGeorge Liu             // port_number returns zero if it is not a valid representable port
375b07942e3SGeorge Liu             if (url->has_port() && url->port_number() == 0)
376b07942e3SGeorge Liu             {
377b07942e3SGeorge Liu                 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
378b07942e3SGeorge Liu                                    url->port());
379b07942e3SGeorge Liu                 messages::propertyValueFormatError(asyncResp->res, destUrl,
380b07942e3SGeorge Liu                                                    "Destination");
381b07942e3SGeorge Liu                 return;
382b07942e3SGeorge Liu             }
383b07942e3SGeorge Liu 
384a716aa74SEd Tanous             crow::utility::setProtocolDefaults(*url, protocol);
385a716aa74SEd Tanous             crow::utility::setPortDefaults(*url);
386a716aa74SEd Tanous 
387a716aa74SEd Tanous             if (url->path().empty())
388a716aa74SEd Tanous             {
389a716aa74SEd Tanous                 url->set_path("/");
390a716aa74SEd Tanous             }
391a716aa74SEd Tanous 
392a716aa74SEd Tanous             if (url->has_userinfo())
393a716aa74SEd Tanous             {
394a716aa74SEd Tanous                 messages::propertyValueFormatError(asyncResp->res, destUrl,
395a716aa74SEd Tanous                                                    "Destination");
396a716aa74SEd Tanous                 return;
397a716aa74SEd Tanous             }
398b52664e2SAppaRao Puli 
3993d30708fSChicago Duan             if (protocol == "SNMPv2c")
4003d30708fSChicago Duan             {
4013d30708fSChicago Duan                 if (context)
4023d30708fSChicago Duan                 {
4033d30708fSChicago Duan                     messages::propertyValueConflict(asyncResp->res, "Context",
4043d30708fSChicago Duan                                                     "Protocol");
4053d30708fSChicago Duan                     return;
4063d30708fSChicago Duan                 }
4073d30708fSChicago Duan                 if (eventFormatType2)
4083d30708fSChicago Duan                 {
409bd79bce8SPatrick Williams                     messages::propertyValueConflict(
410bd79bce8SPatrick Williams                         asyncResp->res, "EventFormatType", "Protocol");
4113d30708fSChicago Duan                     return;
4123d30708fSChicago Duan                 }
4133d30708fSChicago Duan                 if (retryPolicy)
4143d30708fSChicago Duan                 {
415bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
416bd79bce8SPatrick Williams                                                     "RetryPolicy", "Protocol");
4173d30708fSChicago Duan                     return;
4183d30708fSChicago Duan                 }
4195064a25bSMyung Bae                 if (sendHeartbeat)
4205064a25bSMyung Bae                 {
4215064a25bSMyung Bae                     messages::propertyValueConflict(
4225064a25bSMyung Bae                         asyncResp->res, "SendHeartbeat", "Protocol");
4235064a25bSMyung Bae                     return;
4245064a25bSMyung Bae                 }
4255064a25bSMyung Bae                 if (hbIntervalMinutes)
4265064a25bSMyung Bae                 {
4275064a25bSMyung Bae                     messages::propertyValueConflict(
4285064a25bSMyung Bae                         asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
4295064a25bSMyung Bae                     return;
4305064a25bSMyung Bae                 }
4313d30708fSChicago Duan                 if (msgIds)
4323d30708fSChicago Duan                 {
433bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
434bd79bce8SPatrick Williams                                                     "MessageIds", "Protocol");
4353d30708fSChicago Duan                     return;
4363d30708fSChicago Duan                 }
4373d30708fSChicago Duan                 if (regPrefixes)
4383d30708fSChicago Duan                 {
439bd79bce8SPatrick Williams                     messages::propertyValueConflict(
440bd79bce8SPatrick Williams                         asyncResp->res, "RegistryPrefixes", "Protocol");
4413d30708fSChicago Duan                     return;
4423d30708fSChicago Duan                 }
4433d30708fSChicago Duan                 if (resTypes)
4443d30708fSChicago Duan                 {
445bd79bce8SPatrick Williams                     messages::propertyValueConflict(
446bd79bce8SPatrick Williams                         asyncResp->res, "ResourceTypes", "Protocol");
4473d30708fSChicago Duan                     return;
4483d30708fSChicago Duan                 }
4493d30708fSChicago Duan                 if (headers)
4503d30708fSChicago Duan                 {
451bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
452bd79bce8SPatrick Williams                                                     "HttpHeaders", "Protocol");
4533d30708fSChicago Duan                     return;
4543d30708fSChicago Duan                 }
4553d30708fSChicago Duan                 if (mrdJsonArray)
4563d30708fSChicago Duan                 {
4573d30708fSChicago Duan                     messages::propertyValueConflict(
4583d30708fSChicago Duan                         asyncResp->res, "MetricReportDefinitions", "Protocol");
4593d30708fSChicago Duan                     return;
4603d30708fSChicago Duan                 }
461a716aa74SEd Tanous                 if (url->scheme() != "snmp")
462a716aa74SEd Tanous                 {
463bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
464bd79bce8SPatrick Williams                                                     "Destination", "Protocol");
4653d30708fSChicago Duan                     return;
4663d30708fSChicago Duan                 }
4673d30708fSChicago Duan 
468a716aa74SEd Tanous                 addSnmpTrapClient(asyncResp, url->host_address(),
469a716aa74SEd Tanous                                   url->port_number());
470a716aa74SEd Tanous                 return;
471b52664e2SAppaRao Puli             }
4723d30708fSChicago Duan 
473a716aa74SEd Tanous             std::shared_ptr<Subscription> subValue =
47421a94d5cSMyung Bae                 std::make_shared<Subscription>(
4755fe4ef35SMyung Bae                     std::make_shared<persistent_data::UserSubscription>(), *url,
4765fe4ef35SMyung Bae                     app.ioContext());
477e5aaf047SAppaRao Puli 
478e5aaf047SAppaRao Puli             if (subscriptionType)
479e5aaf047SAppaRao Puli             {
480e5aaf047SAppaRao Puli                 if (*subscriptionType != "RedfishEvent")
481e5aaf047SAppaRao Puli                 {
482fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
483fffb8c1fSEd Tanous                         asyncResp->res, *subscriptionType, "SubscriptionType");
484e5aaf047SAppaRao Puli                     return;
485e5aaf047SAppaRao Puli                 }
4865fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = *subscriptionType;
487e5aaf047SAppaRao Puli             }
488e5aaf047SAppaRao Puli             else
489e5aaf047SAppaRao Puli             {
4904b712a29SEd Tanous                 // Default
4915fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = "RedfishEvent";
492e5aaf047SAppaRao Puli             }
493e5aaf047SAppaRao Puli 
494e5aaf047SAppaRao Puli             if (protocol != "Redfish")
495e5aaf047SAppaRao Puli             {
496e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, protocol,
497e5aaf047SAppaRao Puli                                                  "Protocol");
498e5aaf047SAppaRao Puli                 return;
499e5aaf047SAppaRao Puli             }
5005fe4ef35SMyung Bae             subValue->userSub->protocol = protocol;
501e5aaf047SAppaRao Puli 
50219bb362bSEd Tanous             if (verifyCertificate)
50319bb362bSEd Tanous             {
5045fe4ef35SMyung Bae                 subValue->userSub->verifyCertificate = *verifyCertificate;
50519bb362bSEd Tanous             }
50619bb362bSEd Tanous 
50723a21a1cSEd Tanous             if (eventFormatType2)
508e5aaf047SAppaRao Puli             {
509bd79bce8SPatrick Williams                 if (std::ranges::find(supportedEvtFormatTypes,
510bd79bce8SPatrick Williams                                       *eventFormatType2) ==
5113544d2a7SEd Tanous                     supportedEvtFormatTypes.end())
512e5aaf047SAppaRao Puli                 {
513fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
514fffb8c1fSEd Tanous                         asyncResp->res, *eventFormatType2, "EventFormatType");
515e5aaf047SAppaRao Puli                     return;
516e5aaf047SAppaRao Puli                 }
5175fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = *eventFormatType2;
518e5aaf047SAppaRao Puli             }
519e5aaf047SAppaRao Puli             else
520e5aaf047SAppaRao Puli             {
521e5aaf047SAppaRao Puli                 // If not specified, use default "Event"
5225fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = "Event";
523e5aaf047SAppaRao Puli             }
524e5aaf047SAppaRao Puli 
525e5aaf047SAppaRao Puli             if (context)
526e5aaf047SAppaRao Puli             {
5278ece0e45SEd Tanous                 // This value is selected arbitrarily.
528600af5f1SAppaRao Puli                 constexpr const size_t maxContextSize = 256;
529600af5f1SAppaRao Puli                 if (context->size() > maxContextSize)
530600af5f1SAppaRao Puli                 {
531600af5f1SAppaRao Puli                     messages::stringValueTooLong(asyncResp->res, "Context",
532600af5f1SAppaRao Puli                                                  maxContextSize);
533600af5f1SAppaRao Puli                     return;
534600af5f1SAppaRao Puli                 }
5355fe4ef35SMyung Bae                 subValue->userSub->customText = *context;
536e5aaf047SAppaRao Puli             }
537e5aaf047SAppaRao Puli 
538e5aaf047SAppaRao Puli             if (headers)
539e5aaf047SAppaRao Puli             {
540600af5f1SAppaRao Puli                 size_t cumulativeLen = 0;
541600af5f1SAppaRao Puli 
54278d4ec4fSEd Tanous                 for (const nlohmann::json::object_t& headerChunk : *headers)
543601c71aeSEd Tanous                 {
54478d4ec4fSEd Tanous                     for (const auto& item : headerChunk)
54578d4ec4fSEd Tanous                     {
54678d4ec4fSEd Tanous                         const std::string* value =
54778d4ec4fSEd Tanous                             item.second.get_ptr<const std::string*>();
54878d4ec4fSEd Tanous                         if (value == nullptr)
54978d4ec4fSEd Tanous                         {
55078d4ec4fSEd Tanous                             messages::propertyValueFormatError(
55178d4ec4fSEd Tanous                                 asyncResp->res, item.second,
55278d4ec4fSEd Tanous                                 "HttpHeaders/" + item.first);
55378d4ec4fSEd Tanous                             return;
55478d4ec4fSEd Tanous                         }
55578d4ec4fSEd Tanous                         // Adding a new json value is the size of the key, +
55678d4ec4fSEd Tanous                         // the size of the value + 2 * 2 quotes for each, +
55778d4ec4fSEd Tanous                         // the colon and space between. example:
55878d4ec4fSEd Tanous                         // "key": "value"
55978d4ec4fSEd Tanous                         cumulativeLen += item.first.size() + value->size() + 6;
560600af5f1SAppaRao Puli                         // This value is selected to mirror http_connection.hpp
561600af5f1SAppaRao Puli                         constexpr const uint16_t maxHeaderSizeED = 8096;
562600af5f1SAppaRao Puli                         if (cumulativeLen > maxHeaderSizeED)
563600af5f1SAppaRao Puli                         {
56478d4ec4fSEd Tanous                             messages::arraySizeTooLong(
56578d4ec4fSEd Tanous                                 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
566600af5f1SAppaRao Puli                             return;
567600af5f1SAppaRao Puli                         }
5685fe4ef35SMyung Bae                         subValue->userSub->httpHeaders.set(item.first, *value);
569601c71aeSEd Tanous                     }
570601c71aeSEd Tanous                 }
571e5aaf047SAppaRao Puli             }
572e5aaf047SAppaRao Puli 
573e5aaf047SAppaRao Puli             if (regPrefixes)
574e5aaf047SAppaRao Puli             {
575e5aaf047SAppaRao Puli                 for (const std::string& it : *regPrefixes)
576e5aaf047SAppaRao Puli                 {
5773544d2a7SEd Tanous                     if (std::ranges::find(supportedRegPrefixes, it) ==
5783544d2a7SEd Tanous                         supportedRegPrefixes.end())
579e5aaf047SAppaRao Puli                     {
580fffb8c1fSEd Tanous                         messages::propertyValueNotInList(asyncResp->res, it,
581fffb8c1fSEd Tanous                                                          "RegistryPrefixes");
582e5aaf047SAppaRao Puli                         return;
583e5aaf047SAppaRao Puli                     }
584e5aaf047SAppaRao Puli                 }
5855fe4ef35SMyung Bae                 subValue->userSub->registryPrefixes = *regPrefixes;
586e5aaf047SAppaRao Puli             }
587e5aaf047SAppaRao Puli 
588a14c9113SEd Tanous             if (originResources)
589a14c9113SEd Tanous             {
5905fe4ef35SMyung Bae                 subValue->userSub->originResources = *originResources;
591a14c9113SEd Tanous             }
592a14c9113SEd Tanous 
593e56f254cSSunitha Harish             if (resTypes)
594e56f254cSSunitha Harish             {
595e56f254cSSunitha Harish                 for (const std::string& it : *resTypes)
596e56f254cSSunitha Harish                 {
5973544d2a7SEd Tanous                     if (std::ranges::find(supportedResourceTypes, it) ==
5983544d2a7SEd Tanous                         supportedResourceTypes.end())
599e56f254cSSunitha Harish                     {
600e56f254cSSunitha Harish                         messages::propertyValueNotInList(asyncResp->res, it,
601e56f254cSSunitha Harish                                                          "ResourceTypes");
602e56f254cSSunitha Harish                         return;
603e56f254cSSunitha Harish                     }
604e56f254cSSunitha Harish                 }
6055fe4ef35SMyung Bae                 subValue->userSub->resourceTypes = *resTypes;
606e56f254cSSunitha Harish             }
607e56f254cSSunitha Harish 
608e5aaf047SAppaRao Puli             if (msgIds)
609e5aaf047SAppaRao Puli             {
610b304bd79SP Dheeraj Srujan Kumar                 std::vector<std::string> registryPrefix;
611b304bd79SP Dheeraj Srujan Kumar 
6127e860f15SJohn Edward Broadbent                 // If no registry prefixes are mentioned, consider all
6137e860f15SJohn Edward Broadbent                 // supported prefixes
6145fe4ef35SMyung Bae                 if (subValue->userSub->registryPrefixes.empty())
615b304bd79SP Dheeraj Srujan Kumar                 {
616b304bd79SP Dheeraj Srujan Kumar                     registryPrefix.assign(supportedRegPrefixes.begin(),
617b304bd79SP Dheeraj Srujan Kumar                                           supportedRegPrefixes.end());
618b304bd79SP Dheeraj Srujan Kumar                 }
619b304bd79SP Dheeraj Srujan Kumar                 else
620b304bd79SP Dheeraj Srujan Kumar                 {
6215fe4ef35SMyung Bae                     registryPrefix = subValue->userSub->registryPrefixes;
622b304bd79SP Dheeraj Srujan Kumar                 }
623b304bd79SP Dheeraj Srujan Kumar 
624b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& id : *msgIds)
625b304bd79SP Dheeraj Srujan Kumar                 {
626b304bd79SP Dheeraj Srujan Kumar                     bool validId = false;
627b304bd79SP Dheeraj Srujan Kumar 
628b304bd79SP Dheeraj Srujan Kumar                     // Check for Message ID in each of the selected Registry
629b304bd79SP Dheeraj Srujan Kumar                     for (const std::string& it : registryPrefix)
630b304bd79SP Dheeraj Srujan Kumar                     {
631fffb8c1fSEd Tanous                         const std::span<const redfish::registries::MessageEntry>
632fffb8c1fSEd Tanous                             registry =
633fffb8c1fSEd Tanous                                 redfish::registries::getRegistryFromPrefix(it);
634b304bd79SP Dheeraj Srujan Kumar 
6353544d2a7SEd Tanous                         if (std::ranges::any_of(
6363544d2a7SEd Tanous                                 registry,
637fffb8c1fSEd Tanous                                 [&id](const redfish::registries::MessageEntry&
638fffb8c1fSEd Tanous                                           messageEntry) {
63955f79e6fSEd Tanous                                     return id == messageEntry.first;
640b304bd79SP Dheeraj Srujan Kumar                                 }))
641b304bd79SP Dheeraj Srujan Kumar                         {
642b304bd79SP Dheeraj Srujan Kumar                             validId = true;
643b304bd79SP Dheeraj Srujan Kumar                             break;
644b304bd79SP Dheeraj Srujan Kumar                         }
645b304bd79SP Dheeraj Srujan Kumar                     }
646b304bd79SP Dheeraj Srujan Kumar 
647b304bd79SP Dheeraj Srujan Kumar                     if (!validId)
648b304bd79SP Dheeraj Srujan Kumar                     {
649b304bd79SP Dheeraj Srujan Kumar                         messages::propertyValueNotInList(asyncResp->res, id,
650b304bd79SP Dheeraj Srujan Kumar                                                          "MessageIds");
651b304bd79SP Dheeraj Srujan Kumar                         return;
652b304bd79SP Dheeraj Srujan Kumar                     }
653b304bd79SP Dheeraj Srujan Kumar                 }
654b304bd79SP Dheeraj Srujan Kumar 
6555fe4ef35SMyung Bae                 subValue->userSub->registryMsgIds = *msgIds;
656e5aaf047SAppaRao Puli             }
657e5aaf047SAppaRao Puli 
658e5aaf047SAppaRao Puli             if (retryPolicy)
659e5aaf047SAppaRao Puli             {
6603544d2a7SEd Tanous                 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
6613544d2a7SEd Tanous                     supportedRetryPolicies.end())
662e5aaf047SAppaRao Puli                 {
663bd79bce8SPatrick Williams                     messages::propertyValueNotInList(
664bd79bce8SPatrick Williams                         asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
665e5aaf047SAppaRao Puli                     return;
666e5aaf047SAppaRao Puli                 }
6675fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = *retryPolicy;
668e5aaf047SAppaRao Puli             }
669e5aaf047SAppaRao Puli             else
670e5aaf047SAppaRao Puli             {
671e5aaf047SAppaRao Puli                 // Default "TerminateAfterRetries"
6725fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = "TerminateAfterRetries";
673e5aaf047SAppaRao Puli             }
6745064a25bSMyung Bae             if (sendHeartbeat)
6755064a25bSMyung Bae             {
6765064a25bSMyung Bae                 subValue->userSub->sendHeartbeat = *sendHeartbeat;
6775064a25bSMyung Bae             }
6785064a25bSMyung Bae             if (hbIntervalMinutes)
6795064a25bSMyung Bae             {
6805064a25bSMyung Bae                 if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
6815064a25bSMyung Bae                 {
6825064a25bSMyung Bae                     messages::propertyValueOutOfRange(
6835064a25bSMyung Bae                         asyncResp->res, *hbIntervalMinutes,
6845064a25bSMyung Bae                         "HeartbeatIntervalMinutes");
6855064a25bSMyung Bae                     return;
6865064a25bSMyung Bae                 }
6875064a25bSMyung Bae                 subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
6885064a25bSMyung Bae             }
689e5aaf047SAppaRao Puli 
690144b6318SAppaRao Puli             if (mrdJsonArray)
691156d6b00SAppaRao Puli             {
69278d4ec4fSEd Tanous                 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
693144b6318SAppaRao Puli                 {
694144b6318SAppaRao Puli                     std::string mrdUri;
695ea2e6eecSWilly Tu 
69678d4ec4fSEd Tanous                     if (!json_util::readJsonObject(mrdObj, asyncResp->res,
69778d4ec4fSEd Tanous                                                    "@odata.id", mrdUri))
698ea2e6eecSWilly Tu 
699144b6318SAppaRao Puli                     {
700144b6318SAppaRao Puli                         return;
701144b6318SAppaRao Puli                     }
7025fe4ef35SMyung Bae                     subValue->userSub->metricReportDefinitions.emplace_back(
7034b712a29SEd Tanous                         mrdUri);
704144b6318SAppaRao Puli                 }
705156d6b00SAppaRao Puli             }
706156d6b00SAppaRao Puli 
707b52664e2SAppaRao Puli             std::string id =
708bd79bce8SPatrick Williams                 EventServiceManager::getInstance().addPushSubscription(
709bd79bce8SPatrick Williams                     subValue);
710b52664e2SAppaRao Puli             if (id.empty())
711e5aaf047SAppaRao Puli             {
712e5aaf047SAppaRao Puli                 messages::internalError(asyncResp->res);
713e5aaf047SAppaRao Puli                 return;
714e5aaf047SAppaRao Puli             }
715e5aaf047SAppaRao Puli 
716e5aaf047SAppaRao Puli             messages::created(asyncResp->res);
717e5aaf047SAppaRao Puli             asyncResp->res.addHeader(
718e5aaf047SAppaRao Puli                 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
719fb546105SMyung Bae 
720fb546105SMyung Bae             // schedule a heartbeat
721fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
722fb546105SMyung Bae             {
723fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
724fb546105SMyung Bae             }
7257e860f15SJohn Edward Broadbent         });
726e5aaf047SAppaRao Puli }
727e5aaf047SAppaRao Puli 
7287e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
729e5aaf047SAppaRao Puli {
7309d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
731ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
7327e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
73345ca1b86SEd Tanous             [&app](const crow::Request& req,
7347e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
7357e860f15SJohn Edward Broadbent                    const std::string& param) {
7363ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
73745ca1b86SEd Tanous                 {
73845ca1b86SEd Tanous                     return;
73945ca1b86SEd Tanous                 }
7403d30708fSChicago Duan 
7413d30708fSChicago Duan                 if (param.starts_with("snmp"))
7423d30708fSChicago Duan                 {
7433d30708fSChicago Duan                     getSnmpTrapClient(asyncResp, param);
7443d30708fSChicago Duan                     return;
7453d30708fSChicago Duan                 }
7463d30708fSChicago Duan 
747b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
7487e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
749b52664e2SAppaRao Puli                 if (subValue == nullptr)
750e5aaf047SAppaRao Puli                 {
751bd79bce8SPatrick Williams                     asyncResp->res.result(
752bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
753e5aaf047SAppaRao Puli                     return;
754e5aaf047SAppaRao Puli                 }
7557e860f15SJohn Edward Broadbent                 const std::string& id = param;
756e5aaf047SAppaRao Puli 
7574b712a29SEd Tanous                 const persistent_data::UserSubscription& userSub =
7585fe4ef35SMyung Bae                     *subValue->userSub;
759e56f254cSSunitha Harish 
7604b712a29SEd Tanous                 nlohmann::json& jVal = asyncResp->res.jsonValue;
7614b712a29SEd Tanous                 jVal["@odata.type"] =
7624b712a29SEd Tanous                     "#EventDestination.v1_14_1.EventDestination";
7634b712a29SEd Tanous                 jVal["Protocol"] =
7644b712a29SEd Tanous                     event_destination::EventDestinationProtocol::Redfish;
7654b712a29SEd Tanous                 jVal["@odata.id"] = boost::urls::format(
7664b712a29SEd Tanous                     "/redfish/v1/EventService/Subscriptions/{}", id);
7674b712a29SEd Tanous                 jVal["Id"] = id;
7684b712a29SEd Tanous                 jVal["Name"] = "Event Destination " + id;
7694b712a29SEd Tanous                 jVal["Destination"] = userSub.destinationUrl;
7704b712a29SEd Tanous                 jVal["Context"] = userSub.customText;
7714b712a29SEd Tanous                 jVal["SubscriptionType"] = userSub.subscriptionType;
7724b712a29SEd Tanous                 jVal["HttpHeaders"] = nlohmann::json::array();
7734b712a29SEd Tanous                 jVal["EventFormatType"] = userSub.eventFormatType;
7744b712a29SEd Tanous                 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
7754b712a29SEd Tanous                 jVal["ResourceTypes"] = userSub.resourceTypes;
7764b712a29SEd Tanous 
7774b712a29SEd Tanous                 jVal["MessageIds"] = userSub.registryMsgIds;
7784b712a29SEd Tanous                 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
7795064a25bSMyung Bae                 jVal["SendHeartbeat"] = userSub.sendHeartbeat;
7805064a25bSMyung Bae                 jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
7814b712a29SEd Tanous                 jVal["VerifyCertificate"] = userSub.verifyCertificate;
782144b6318SAppaRao Puli 
7831476687dSEd Tanous                 nlohmann::json::array_t mrdJsonArray;
7844b712a29SEd Tanous                 for (const auto& mdrUri : userSub.metricReportDefinitions)
785144b6318SAppaRao Puli                 {
7861476687dSEd Tanous                     nlohmann::json::object_t mdr;
7871476687dSEd Tanous                     mdr["@odata.id"] = mdrUri;
7881476687dSEd Tanous                     mrdJsonArray.emplace_back(std::move(mdr));
789144b6318SAppaRao Puli                 }
7904b712a29SEd Tanous                 jVal["MetricReportDefinitions"] = mrdJsonArray;
7917e860f15SJohn Edward Broadbent             });
7929d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
793ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
794ed398213SEd Tanous         // ConfigureSelf
7957eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
796ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
797432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
7987e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
79945ca1b86SEd Tanous             [&app](const crow::Request& req,
8007e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8017e860f15SJohn Edward Broadbent                    const std::string& param) {
8023ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
80345ca1b86SEd Tanous                 {
80445ca1b86SEd Tanous                     return;
80545ca1b86SEd Tanous                 }
806b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
8077e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
808b52664e2SAppaRao Puli                 if (subValue == nullptr)
809e5aaf047SAppaRao Puli                 {
810bd79bce8SPatrick Williams                     asyncResp->res.result(
811bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
812e5aaf047SAppaRao Puli                     return;
813e5aaf047SAppaRao Puli                 }
814e5aaf047SAppaRao Puli 
815e5aaf047SAppaRao Puli                 std::optional<std::string> context;
816e5aaf047SAppaRao Puli                 std::optional<std::string> retryPolicy;
8175064a25bSMyung Bae                 std::optional<bool> sendHeartbeat;
8185064a25bSMyung Bae                 std::optional<uint64_t> hbIntervalMinutes;
81919bb362bSEd Tanous                 std::optional<bool> verifyCertificate;
82078d4ec4fSEd Tanous                 std::optional<std::vector<nlohmann::json::object_t>> headers;
821e5aaf047SAppaRao Puli 
822afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
823afc474aeSMyung Bae                         req, asyncResp->res, //
824afc474aeSMyung Bae                         "Context", context, //
825afc474aeSMyung Bae                         "DeliveryRetryPolicy", retryPolicy, //
8265064a25bSMyung Bae                         "HeartbeatIntervalMinutes", hbIntervalMinutes, //
827afc474aeSMyung Bae                         "HttpHeaders", headers, //
8285064a25bSMyung Bae                         "SendHeartbeat", sendHeartbeat, //
829afc474aeSMyung Bae                         "VerifyCertificate", verifyCertificate //
830afc474aeSMyung Bae                         ))
831e5aaf047SAppaRao Puli                 {
832e5aaf047SAppaRao Puli                     return;
833e5aaf047SAppaRao Puli                 }
834e5aaf047SAppaRao Puli 
835e5aaf047SAppaRao Puli                 if (context)
836e5aaf047SAppaRao Puli                 {
8375fe4ef35SMyung Bae                     subValue->userSub->customText = *context;
838e5aaf047SAppaRao Puli                 }
839e5aaf047SAppaRao Puli 
840e5aaf047SAppaRao Puli                 if (headers)
841e5aaf047SAppaRao Puli                 {
842601c71aeSEd Tanous                     boost::beast::http::fields fields;
84378d4ec4fSEd Tanous                     for (const nlohmann::json::object_t& headerChunk : *headers)
844601c71aeSEd Tanous                     {
84578d4ec4fSEd Tanous                         for (const auto& it : headerChunk)
846601c71aeSEd Tanous                         {
847601c71aeSEd Tanous                             const std::string* value =
84878d4ec4fSEd Tanous                                 it.second.get_ptr<const std::string*>();
849601c71aeSEd Tanous                             if (value == nullptr)
850601c71aeSEd Tanous                             {
851601c71aeSEd Tanous                                 messages::propertyValueFormatError(
85278d4ec4fSEd Tanous                                     asyncResp->res, it.second,
85378d4ec4fSEd Tanous                                     "HttpHeaders/" + it.first);
854601c71aeSEd Tanous                                 return;
855601c71aeSEd Tanous                             }
85678d4ec4fSEd Tanous                             fields.set(it.first, *value);
857601c71aeSEd Tanous                         }
858601c71aeSEd Tanous                     }
8595fe4ef35SMyung Bae                     subValue->userSub->httpHeaders = std::move(fields);
860e5aaf047SAppaRao Puli                 }
861e5aaf047SAppaRao Puli 
862e5aaf047SAppaRao Puli                 if (retryPolicy)
863e5aaf047SAppaRao Puli                 {
864bd79bce8SPatrick Williams                     if (std::ranges::find(supportedRetryPolicies,
865bd79bce8SPatrick Williams                                           *retryPolicy) ==
8663544d2a7SEd Tanous                         supportedRetryPolicies.end())
867e5aaf047SAppaRao Puli                     {
868bd79bce8SPatrick Williams                         messages::propertyValueNotInList(asyncResp->res,
869bd79bce8SPatrick Williams                                                          *retryPolicy,
870e5aaf047SAppaRao Puli                                                          "DeliveryRetryPolicy");
871e5aaf047SAppaRao Puli                         return;
872e5aaf047SAppaRao Puli                     }
8735fe4ef35SMyung Bae                     subValue->userSub->retryPolicy = *retryPolicy;
874e5aaf047SAppaRao Puli                 }
875e5aaf047SAppaRao Puli 
8765064a25bSMyung Bae                 if (sendHeartbeat)
8775064a25bSMyung Bae                 {
8785064a25bSMyung Bae                     subValue->userSub->sendHeartbeat = *sendHeartbeat;
8795064a25bSMyung Bae                 }
8805064a25bSMyung Bae                 if (hbIntervalMinutes)
8815064a25bSMyung Bae                 {
8825064a25bSMyung Bae                     if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
8835064a25bSMyung Bae                     {
8845064a25bSMyung Bae                         messages::propertyValueOutOfRange(
8855064a25bSMyung Bae                             asyncResp->res, *hbIntervalMinutes,
8865064a25bSMyung Bae                             "HeartbeatIntervalMinutes");
8875064a25bSMyung Bae                         return;
8885064a25bSMyung Bae                     }
8895064a25bSMyung Bae                     subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
8905064a25bSMyung Bae                 }
8915064a25bSMyung Bae 
892fb546105SMyung Bae                 if (hbIntervalMinutes || sendHeartbeat)
893fb546105SMyung Bae                 {
894fb546105SMyung Bae                     // if Heartbeat interval or send heart were changed, cancel
895fb546105SMyung Bae                     // the heartbeat timer if running and start a new heartbeat
896fb546105SMyung Bae                     // if needed
897fb546105SMyung Bae                     subValue->heartbeatParametersChanged();
898fb546105SMyung Bae                 }
899fb546105SMyung Bae 
90019bb362bSEd Tanous                 if (verifyCertificate)
90119bb362bSEd Tanous                 {
9025fe4ef35SMyung Bae                     subValue->userSub->verifyCertificate = *verifyCertificate;
90319bb362bSEd Tanous                 }
90419bb362bSEd Tanous 
905b52664e2SAppaRao Puli                 EventServiceManager::getInstance().updateSubscriptionData();
9067e860f15SJohn Edward Broadbent             });
9079d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
908ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
909ed398213SEd Tanous         // ConfigureSelf
9107eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
911ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
912432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
9137e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
91445ca1b86SEd Tanous             [&app](const crow::Request& req,
9157e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9167e860f15SJohn Edward Broadbent                    const std::string& param) {
9173ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
91845ca1b86SEd Tanous                 {
91945ca1b86SEd Tanous                     return;
92045ca1b86SEd Tanous                 }
9214b712a29SEd Tanous                 EventServiceManager& event = EventServiceManager::getInstance();
9223d30708fSChicago Duan                 if (param.starts_with("snmp"))
9233d30708fSChicago Duan                 {
9243d30708fSChicago Duan                     deleteSnmpTrapClient(asyncResp, param);
9254b712a29SEd Tanous                     event.deleteSubscription(param);
9263d30708fSChicago Duan                     return;
9273d30708fSChicago Duan                 }
9283d30708fSChicago Duan 
9294b712a29SEd Tanous                 if (!event.deleteSubscription(param))
930e5aaf047SAppaRao Puli                 {
9314b712a29SEd Tanous                     messages::resourceNotFound(asyncResp->res,
9324b712a29SEd Tanous                                                "EventDestination", param);
933e5aaf047SAppaRao Puli                     return;
934e5aaf047SAppaRao Puli                 }
9354b712a29SEd Tanous                 messages::success(asyncResp->res);
9367e860f15SJohn Edward Broadbent             });
937e5aaf047SAppaRao Puli }
938e5aaf047SAppaRao Puli 
939e5aaf047SAppaRao Puli } // namespace redfish
940