xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 81ee0e74bd3a575593ee2a21de560936843f526a)
1e5aaf047SAppaRao Puli /*
26be832e2SEd Tanous Copyright (c) 2020 Intel Corporation
36be832e2SEd Tanous 
46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
56be832e2SEd Tanous you may not use this file except in compliance with the License.
66be832e2SEd Tanous You may obtain a copy of the License at
76be832e2SEd Tanous 
86be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
96be832e2SEd Tanous 
106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136be832e2SEd Tanous See the License for the specific language governing permissions and
146be832e2SEd Tanous limitations under the License.
15e5aaf047SAppaRao Puli */
16e5aaf047SAppaRao Puli #pragma once
173ccb3adbSEd Tanous #include "app.hpp"
18b52664e2SAppaRao Puli #include "event_service_manager.hpp"
19539d8c6bSEd Tanous #include "generated/enums/event_service.hpp"
203ccb3adbSEd Tanous #include "http/utility.hpp"
213ccb3adbSEd Tanous #include "logging.hpp"
223ccb3adbSEd Tanous #include "query.hpp"
23d109e2b6SAlexander Hansen #include "registries.hpp"
243ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
25d109e2b6SAlexander Hansen #include "registries_selector.hpp"
263d30708fSChicago Duan #include "snmp_trap_event_clients.hpp"
27d109e2b6SAlexander Hansen #include "utils/json_utils.hpp"
28e5aaf047SAppaRao Puli 
29601c71aeSEd Tanous #include <boost/beast/http/fields.hpp>
303d30708fSChicago Duan #include <boost/system/error_code.hpp>
31a716aa74SEd Tanous #include <boost/url/parse.hpp>
323d30708fSChicago Duan #include <sdbusplus/unpack_properties.hpp>
333d30708fSChicago Duan #include <utils/dbus_utils.hpp>
34ed398213SEd Tanous 
353d30708fSChicago Duan #include <charconv>
363d30708fSChicago Duan #include <memory>
375064a25bSMyung Bae #include <optional>
383544d2a7SEd Tanous #include <ranges>
391e270c5fSPatrick Williams #include <span>
403d30708fSChicago Duan #include <string>
41a14c9113SEd Tanous #include <vector>
421e270c5fSPatrick Williams 
43e5aaf047SAppaRao Puli namespace redfish
44e5aaf047SAppaRao Puli {
45e5aaf047SAppaRao Puli 
46156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
47156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
48fb546105SMyung Bae static constexpr const std::array<const char*, 4> supportedRegPrefixes = {
49fb546105SMyung Bae     "Base", "OpenBMC", "TaskEvent", "HeartbeatEvent"};
50e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
51e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
52e5aaf047SAppaRao Puli 
53fb546105SMyung Bae static constexpr const std::array<const char*, 2> supportedResourceTypes = {
54fb546105SMyung Bae     "Task", "Heartbeat"};
55e56f254cSSunitha Harish 
567e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
57e5aaf047SAppaRao Puli {
587e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
59ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
60bd79bce8SPatrick Williams         .methods(
61bd79bce8SPatrick Williams             boost::beast::http::verb::
62bd79bce8SPatrick Williams                 get)([&app](
63bd79bce8SPatrick Williams                          const crow::Request& req,
64002d39b4SEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
653ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
6645ca1b86SEd Tanous             {
6745ca1b86SEd Tanous                 return;
6845ca1b86SEd Tanous             }
691476687dSEd Tanous 
701476687dSEd Tanous             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
711476687dSEd Tanous             asyncResp->res.jsonValue["@odata.type"] =
721476687dSEd Tanous                 "#EventService.v1_5_0.EventService";
731476687dSEd Tanous             asyncResp->res.jsonValue["Id"] = "EventService";
741476687dSEd Tanous             asyncResp->res.jsonValue["Name"] = "Event Service";
755e44e3d8SAppaRao Puli             asyncResp->res.jsonValue["ServerSentEventUri"] =
765e44e3d8SAppaRao Puli                 "/redfish/v1/EventService/SSE";
775e44e3d8SAppaRao Puli 
781476687dSEd Tanous             asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
791476687dSEd Tanous                 "/redfish/v1/EventService/Subscriptions";
80bd79bce8SPatrick Williams             asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
81bd79bce8SPatrick Williams                                     ["target"] =
821476687dSEd Tanous                 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
83e5aaf047SAppaRao Puli 
8428afb49cSJunLin Chen             const persistent_data::EventServiceConfig eventServiceConfig =
8528afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
8628afb49cSJunLin Chen                     .getEventServiceConfig();
877d1cc387SAppaRao Puli 
887d1cc387SAppaRao Puli             asyncResp->res.jsonValue["Status"]["State"] =
8928afb49cSJunLin Chen                 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
90bd79bce8SPatrick Williams             asyncResp->res.jsonValue["ServiceEnabled"] =
91bd79bce8SPatrick Williams                 eventServiceConfig.enabled;
927e860f15SJohn Edward Broadbent             asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
9328afb49cSJunLin Chen                 eventServiceConfig.retryAttempts;
94e5aaf047SAppaRao Puli             asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
9528afb49cSJunLin Chen                 eventServiceConfig.retryTimeoutInterval;
96bd79bce8SPatrick Williams             asyncResp->res.jsonValue["EventFormatTypes"] =
97bd79bce8SPatrick Williams                 supportedEvtFormatTypes;
980fda0f12SGeorge Liu             asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
990fda0f12SGeorge Liu             asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
10007941a88SAyushi Smriti 
101613dabeaSEd Tanous             nlohmann::json::object_t supportedSSEFilters;
102613dabeaSEd Tanous             supportedSSEFilters["EventFormatType"] = true;
103613dabeaSEd Tanous             supportedSSEFilters["MessageId"] = true;
104613dabeaSEd Tanous             supportedSSEFilters["MetricReportDefinition"] = true;
105613dabeaSEd Tanous             supportedSSEFilters["RegistryPrefix"] = true;
106613dabeaSEd Tanous             supportedSSEFilters["OriginResource"] = false;
107613dabeaSEd Tanous             supportedSSEFilters["ResourceType"] = false;
10807941a88SAyushi Smriti 
10907941a88SAyushi Smriti             asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
110613dabeaSEd Tanous                 std::move(supportedSSEFilters);
1117e860f15SJohn Edward Broadbent         });
112e5aaf047SAppaRao Puli 
1137e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
114ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1157e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
11645ca1b86SEd Tanous             [&app](const crow::Request& req,
11745ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1183ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
119e5aaf047SAppaRao Puli                 {
12045ca1b86SEd Tanous                     return;
12145ca1b86SEd Tanous                 }
122e5aaf047SAppaRao Puli                 std::optional<bool> serviceEnabled;
123e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryAttemps;
124e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryInterval;
125afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
126afc474aeSMyung Bae                         req, asyncResp->res, //
127afc474aeSMyung Bae                         "DeliveryRetryAttempts", retryAttemps, //
128afc474aeSMyung Bae                         "DeliveryRetryIntervalSeconds", retryInterval, //
129afc474aeSMyung Bae                         "ServiceEnabled", serviceEnabled //
130afc474aeSMyung Bae                         ))
131e5aaf047SAppaRao Puli                 {
132e5aaf047SAppaRao Puli                     return;
133e5aaf047SAppaRao Puli                 }
134e5aaf047SAppaRao Puli 
13528afb49cSJunLin Chen                 persistent_data::EventServiceConfig eventServiceConfig =
13628afb49cSJunLin Chen                     persistent_data::EventServiceStore::getInstance()
13728afb49cSJunLin Chen                         .getEventServiceConfig();
1387d1cc387SAppaRao Puli 
139e5aaf047SAppaRao Puli                 if (serviceEnabled)
140e5aaf047SAppaRao Puli                 {
14128afb49cSJunLin Chen                     eventServiceConfig.enabled = *serviceEnabled;
142e5aaf047SAppaRao Puli                 }
143e5aaf047SAppaRao Puli 
144e5aaf047SAppaRao Puli                 if (retryAttemps)
145e5aaf047SAppaRao Puli                 {
146e5aaf047SAppaRao Puli                     // Supported range [1-3]
147e5aaf047SAppaRao Puli                     if ((*retryAttemps < 1) || (*retryAttemps > 3))
148e5aaf047SAppaRao Puli                     {
149e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
150e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryAttemps),
151e5aaf047SAppaRao Puli                             "DeliveryRetryAttempts", "[1-3]");
152e5aaf047SAppaRao Puli                     }
153e5aaf047SAppaRao Puli                     else
154e5aaf047SAppaRao Puli                     {
15528afb49cSJunLin Chen                         eventServiceConfig.retryAttempts = *retryAttemps;
156e5aaf047SAppaRao Puli                     }
157e5aaf047SAppaRao Puli                 }
158e5aaf047SAppaRao Puli 
159e5aaf047SAppaRao Puli                 if (retryInterval)
160e5aaf047SAppaRao Puli                 {
16133a32b34SGunnar Mills                     // Supported range [5 - 180]
16233a32b34SGunnar Mills                     if ((*retryInterval < 5) || (*retryInterval > 180))
163e5aaf047SAppaRao Puli                     {
164e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
165e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryInterval),
16633a32b34SGunnar Mills                             "DeliveryRetryIntervalSeconds", "[5-180]");
167e5aaf047SAppaRao Puli                     }
168e5aaf047SAppaRao Puli                     else
169e5aaf047SAppaRao Puli                     {
170bd79bce8SPatrick Williams                         eventServiceConfig.retryTimeoutInterval =
171bd79bce8SPatrick Williams                             *retryInterval;
172e5aaf047SAppaRao Puli                     }
173e5aaf047SAppaRao Puli                 }
174e5aaf047SAppaRao Puli 
1757d1cc387SAppaRao Puli                 EventServiceManager::getInstance().setEventServiceConfig(
17628afb49cSJunLin Chen                     eventServiceConfig);
1777e860f15SJohn Edward Broadbent             });
1780b4bdd93SAppaRao Puli }
1790b4bdd93SAppaRao Puli 
1807e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1810b4bdd93SAppaRao Puli {
1827e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1837e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
184ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1857e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
18645ca1b86SEd Tanous             [&app](const crow::Request& req,
1877e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1883ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
18945ca1b86SEd Tanous                 {
19045ca1b86SEd Tanous                     return;
19145ca1b86SEd Tanous                 }
192*81ee0e74SChandramohan Harkude 
193*81ee0e74SChandramohan Harkude                 TestEvent testEvent;
194*81ee0e74SChandramohan Harkude                 // clang-format off
195*81ee0e74SChandramohan Harkude                 if (!json_util::readJsonAction(
196*81ee0e74SChandramohan Harkude                         req, asyncResp->res,
197*81ee0e74SChandramohan Harkude                         "EventGroupId", testEvent.eventGroupId,
198*81ee0e74SChandramohan Harkude                         "EventId", testEvent.eventId,
199*81ee0e74SChandramohan Harkude                         "EventTimestamp", testEvent.eventTimestamp,
200*81ee0e74SChandramohan Harkude                         "Message", testEvent.message,
201*81ee0e74SChandramohan Harkude                         "MessageArgs", testEvent.messageArgs,
202*81ee0e74SChandramohan Harkude                         "MessageId", testEvent.messageId,
203*81ee0e74SChandramohan Harkude                         "OriginOfCondition", testEvent.originOfCondition,
204*81ee0e74SChandramohan Harkude                         "Resolution", testEvent.resolution,
205*81ee0e74SChandramohan Harkude                         "Severity", testEvent.severity))
206*81ee0e74SChandramohan Harkude                 {
207*81ee0e74SChandramohan Harkude                     return;
208*81ee0e74SChandramohan Harkude                 }
209*81ee0e74SChandramohan Harkude                 // clang-format on
210*81ee0e74SChandramohan Harkude 
211*81ee0e74SChandramohan Harkude                 if (!EventServiceManager::getInstance().sendTestEventLog(
212*81ee0e74SChandramohan Harkude                         testEvent))
2136ba8c82eSsunharis_in                 {
2146ba8c82eSsunharis_in                     messages::serviceDisabled(asyncResp->res,
2156ba8c82eSsunharis_in                                               "/redfish/v1/EventService/");
2166ba8c82eSsunharis_in                     return;
2176ba8c82eSsunharis_in                 }
2188d1b46d7Szhanghch05                 asyncResp->res.result(boost::beast::http::status::no_content);
2197e860f15SJohn Edward Broadbent             });
220e5aaf047SAppaRao Puli }
221e5aaf047SAppaRao Puli 
2223d30708fSChicago Duan inline void doSubscriptionCollection(
223e81de512SEd Tanous     const boost::system::error_code& ec,
2243d30708fSChicago Duan     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2253d30708fSChicago Duan     const dbus::utility::ManagedObjectType& resp)
2263d30708fSChicago Duan {
2273d30708fSChicago Duan     if (ec)
2283d30708fSChicago Duan     {
2291306101eSEd Tanous         if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
2303d30708fSChicago Duan         {
2313d30708fSChicago Duan             // This is an optional process so just return if it isn't there
2323d30708fSChicago Duan             return;
2333d30708fSChicago Duan         }
2343d30708fSChicago Duan 
23562598e31SEd Tanous         BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
2363d30708fSChicago Duan         messages::internalError(asyncResp->res);
2373d30708fSChicago Duan         return;
2383d30708fSChicago Duan     }
2393d30708fSChicago Duan     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
2403d30708fSChicago Duan     for (const auto& objpath : resp)
2413d30708fSChicago Duan     {
2423d30708fSChicago Duan         sdbusplus::message::object_path path(objpath.first);
2433d30708fSChicago Duan         const std::string snmpId = path.filename();
2443d30708fSChicago Duan         if (snmpId.empty())
2453d30708fSChicago Duan         {
24662598e31SEd Tanous             BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
2473d30708fSChicago Duan             messages::internalError(asyncResp->res);
2483d30708fSChicago Duan             return;
2493d30708fSChicago Duan         }
2503d30708fSChicago Duan 
2513d30708fSChicago Duan         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
2523d30708fSChicago Duan     }
2533d30708fSChicago Duan }
2543d30708fSChicago Duan 
2557e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
256e5aaf047SAppaRao Puli {
2571ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
258ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
2597e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
26045ca1b86SEd Tanous             [&app](const crow::Request& req,
2617e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2623ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
26345ca1b86SEd Tanous                 {
26445ca1b86SEd Tanous                     return;
26545ca1b86SEd Tanous                 }
2661476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.type"] =
2671476687dSEd Tanous                     "#EventDestinationCollection.EventDestinationCollection";
2681476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.id"] =
2691476687dSEd Tanous                     "/redfish/v1/EventService/Subscriptions";
270bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Name"] =
271bd79bce8SPatrick Williams                     "Event Destination Collections";
272e5aaf047SAppaRao Puli 
273bd79bce8SPatrick Williams                 nlohmann::json& memberArray =
274bd79bce8SPatrick Williams                     asyncResp->res.jsonValue["Members"];
275e5aaf047SAppaRao Puli 
276b52664e2SAppaRao Puli                 std::vector<std::string> subscripIds =
277b52664e2SAppaRao Puli                     EventServiceManager::getInstance().getAllIDs();
278b52664e2SAppaRao Puli                 memberArray = nlohmann::json::array();
279bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Members@odata.count"] =
280bd79bce8SPatrick Williams                     subscripIds.size();
281b52664e2SAppaRao Puli 
282b52664e2SAppaRao Puli                 for (const std::string& id : subscripIds)
283e5aaf047SAppaRao Puli                 {
2841476687dSEd Tanous                     nlohmann::json::object_t member;
2853d30708fSChicago Duan                     member["@odata.id"] = boost::urls::format(
2863d30708fSChicago Duan                         "/redfish/v1/EventService/Subscriptions/{}" + id);
287b2ba3072SPatrick Williams                     memberArray.emplace_back(std::move(member));
288e5aaf047SAppaRao Puli                 }
2893d30708fSChicago Duan                 crow::connections::systemBus->async_method_call(
290e81de512SEd Tanous                     [asyncResp](const boost::system::error_code& ec,
2913d30708fSChicago Duan                                 const dbus::utility::ManagedObjectType& resp) {
2923d30708fSChicago Duan                         doSubscriptionCollection(ec, asyncResp, resp);
2933d30708fSChicago Duan                     },
2943d30708fSChicago Duan                     "xyz.openbmc_project.Network.SNMP",
2953d30708fSChicago Duan                     "/xyz/openbmc_project/network/snmp/manager",
2963d30708fSChicago Duan                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2977e860f15SJohn Edward Broadbent             });
2983d30708fSChicago Duan 
2997e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
3007eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
301bd79bce8SPatrick Williams         .methods(
302bd79bce8SPatrick Williams             boost::beast::http::verb::
303bd79bce8SPatrick Williams                 post)([&app](
304bd79bce8SPatrick Williams                           const crow::Request& req,
3057e860f15SJohn Edward Broadbent                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3063ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
30745ca1b86SEd Tanous             {
30845ca1b86SEd Tanous                 return;
30945ca1b86SEd Tanous             }
310fffb8c1fSEd Tanous             if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
311fffb8c1fSEd Tanous                 maxNoOfSubscriptions)
312e5aaf047SAppaRao Puli             {
313e5aaf047SAppaRao Puli                 messages::eventSubscriptionLimitExceeded(asyncResp->res);
314e5aaf047SAppaRao Puli                 return;
315e5aaf047SAppaRao Puli             }
316e5aaf047SAppaRao Puli             std::string destUrl;
317e5aaf047SAppaRao Puli             std::string protocol;
31819bb362bSEd Tanous             std::optional<bool> verifyCertificate;
319e5aaf047SAppaRao Puli             std::optional<std::string> context;
320e5aaf047SAppaRao Puli             std::optional<std::string> subscriptionType;
32123a21a1cSEd Tanous             std::optional<std::string> eventFormatType2;
322e5aaf047SAppaRao Puli             std::optional<std::string> retryPolicy;
3235064a25bSMyung Bae             std::optional<bool> sendHeartbeat;
3245064a25bSMyung Bae             std::optional<uint64_t> hbIntervalMinutes;
325e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> msgIds;
326e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> regPrefixes;
327a14c9113SEd Tanous             std::optional<std::vector<std::string>> originResources;
328e56f254cSSunitha Harish             std::optional<std::vector<std::string>> resTypes;
32978d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> headers;
33078d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
331e5aaf047SAppaRao Puli 
332afc474aeSMyung Bae             if (!json_util::readJsonPatch( //
333afc474aeSMyung Bae                     req, asyncResp->res, //
334afc474aeSMyung Bae                     "Context", context, //
335afc474aeSMyung Bae                     "DeliveryRetryPolicy", retryPolicy, //
336afc474aeSMyung Bae                     "Destination", destUrl, //
337afc474aeSMyung Bae                     "EventFormatType", eventFormatType2, //
3385064a25bSMyung Bae                     "HeartbeatIntervalMinutes", hbIntervalMinutes, //
339afc474aeSMyung Bae                     "HttpHeaders", headers, //
340afc474aeSMyung Bae                     "MessageIds", msgIds, //
341afc474aeSMyung Bae                     "MetricReportDefinitions", mrdJsonArray, //
342afc474aeSMyung Bae                     "OriginResources", originResources, //
343afc474aeSMyung Bae                     "Protocol", protocol, //
344afc474aeSMyung Bae                     "RegistryPrefixes", regPrefixes, //
345afc474aeSMyung Bae                     "ResourceTypes", resTypes, //
3465064a25bSMyung Bae                     "SendHeartbeat", sendHeartbeat, //
347afc474aeSMyung Bae                     "SubscriptionType", subscriptionType, //
348afc474aeSMyung Bae                     "VerifyCertificate", verifyCertificate //
3494b712a29SEd Tanous                     ))
350e5aaf047SAppaRao Puli             {
351e5aaf047SAppaRao Puli                 return;
352e5aaf047SAppaRao Puli             }
3534b712a29SEd Tanous             // clang-format on
354e5aaf047SAppaRao Puli 
355600af5f1SAppaRao Puli             // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
356600af5f1SAppaRao Puli             static constexpr const uint16_t maxDestinationSize = 2000;
357600af5f1SAppaRao Puli             if (destUrl.size() > maxDestinationSize)
358600af5f1SAppaRao Puli             {
359600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Destination",
360600af5f1SAppaRao Puli                                              maxDestinationSize);
361600af5f1SAppaRao Puli                 return;
362600af5f1SAppaRao Puli             }
363600af5f1SAppaRao Puli 
364dd28ba82SAppaRao Puli             if (regPrefixes && msgIds)
365dd28ba82SAppaRao Puli             {
36626f6976fSEd Tanous                 if (!regPrefixes->empty() && !msgIds->empty())
367dd28ba82SAppaRao Puli                 {
368bd79bce8SPatrick Williams                     messages::propertyValueConflict(
369bd79bce8SPatrick Williams                         asyncResp->res, "MessageIds", "RegistryPrefixes");
370dd28ba82SAppaRao Puli                     return;
371dd28ba82SAppaRao Puli                 }
372dd28ba82SAppaRao Puli             }
373dd28ba82SAppaRao Puli 
3746fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
375a716aa74SEd Tanous                 boost::urls::parse_absolute_uri(destUrl);
376a716aa74SEd Tanous             if (!url)
377e5aaf047SAppaRao Puli             {
378bd79bce8SPatrick Williams                 BMCWEB_LOG_WARNING(
379bd79bce8SPatrick Williams                     "Failed to validate and split destination url");
380e5aaf047SAppaRao Puli                 messages::propertyValueFormatError(asyncResp->res, destUrl,
381e5aaf047SAppaRao Puli                                                    "Destination");
382e5aaf047SAppaRao Puli                 return;
383e5aaf047SAppaRao Puli             }
384a716aa74SEd Tanous             url->normalize();
385b07942e3SGeorge Liu 
386b07942e3SGeorge Liu             // port_number returns zero if it is not a valid representable port
387b07942e3SGeorge Liu             if (url->has_port() && url->port_number() == 0)
388b07942e3SGeorge Liu             {
389b07942e3SGeorge Liu                 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
390b07942e3SGeorge Liu                                    url->port());
391b07942e3SGeorge Liu                 messages::propertyValueFormatError(asyncResp->res, destUrl,
392b07942e3SGeorge Liu                                                    "Destination");
393b07942e3SGeorge Liu                 return;
394b07942e3SGeorge Liu             }
395b07942e3SGeorge Liu 
396a716aa74SEd Tanous             crow::utility::setProtocolDefaults(*url, protocol);
397a716aa74SEd Tanous             crow::utility::setPortDefaults(*url);
398a716aa74SEd Tanous 
399a716aa74SEd Tanous             if (url->path().empty())
400a716aa74SEd Tanous             {
401a716aa74SEd Tanous                 url->set_path("/");
402a716aa74SEd Tanous             }
403a716aa74SEd Tanous 
404a716aa74SEd Tanous             if (url->has_userinfo())
405a716aa74SEd Tanous             {
406a716aa74SEd Tanous                 messages::propertyValueFormatError(asyncResp->res, destUrl,
407a716aa74SEd Tanous                                                    "Destination");
408a716aa74SEd Tanous                 return;
409a716aa74SEd Tanous             }
410b52664e2SAppaRao Puli 
4113d30708fSChicago Duan             if (protocol == "SNMPv2c")
4123d30708fSChicago Duan             {
4133d30708fSChicago Duan                 if (context)
4143d30708fSChicago Duan                 {
4153d30708fSChicago Duan                     messages::propertyValueConflict(asyncResp->res, "Context",
4163d30708fSChicago Duan                                                     "Protocol");
4173d30708fSChicago Duan                     return;
4183d30708fSChicago Duan                 }
4193d30708fSChicago Duan                 if (eventFormatType2)
4203d30708fSChicago Duan                 {
421bd79bce8SPatrick Williams                     messages::propertyValueConflict(
422bd79bce8SPatrick Williams                         asyncResp->res, "EventFormatType", "Protocol");
4233d30708fSChicago Duan                     return;
4243d30708fSChicago Duan                 }
4253d30708fSChicago Duan                 if (retryPolicy)
4263d30708fSChicago Duan                 {
427bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
428bd79bce8SPatrick Williams                                                     "RetryPolicy", "Protocol");
4293d30708fSChicago Duan                     return;
4303d30708fSChicago Duan                 }
4315064a25bSMyung Bae                 if (sendHeartbeat)
4325064a25bSMyung Bae                 {
4335064a25bSMyung Bae                     messages::propertyValueConflict(
4345064a25bSMyung Bae                         asyncResp->res, "SendHeartbeat", "Protocol");
4355064a25bSMyung Bae                     return;
4365064a25bSMyung Bae                 }
4375064a25bSMyung Bae                 if (hbIntervalMinutes)
4385064a25bSMyung Bae                 {
4395064a25bSMyung Bae                     messages::propertyValueConflict(
4405064a25bSMyung Bae                         asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
4415064a25bSMyung Bae                     return;
4425064a25bSMyung Bae                 }
4433d30708fSChicago Duan                 if (msgIds)
4443d30708fSChicago Duan                 {
445bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
446bd79bce8SPatrick Williams                                                     "MessageIds", "Protocol");
4473d30708fSChicago Duan                     return;
4483d30708fSChicago Duan                 }
4493d30708fSChicago Duan                 if (regPrefixes)
4503d30708fSChicago Duan                 {
451bd79bce8SPatrick Williams                     messages::propertyValueConflict(
452bd79bce8SPatrick Williams                         asyncResp->res, "RegistryPrefixes", "Protocol");
4533d30708fSChicago Duan                     return;
4543d30708fSChicago Duan                 }
4553d30708fSChicago Duan                 if (resTypes)
4563d30708fSChicago Duan                 {
457bd79bce8SPatrick Williams                     messages::propertyValueConflict(
458bd79bce8SPatrick Williams                         asyncResp->res, "ResourceTypes", "Protocol");
4593d30708fSChicago Duan                     return;
4603d30708fSChicago Duan                 }
4613d30708fSChicago Duan                 if (headers)
4623d30708fSChicago Duan                 {
463bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
464bd79bce8SPatrick Williams                                                     "HttpHeaders", "Protocol");
4653d30708fSChicago Duan                     return;
4663d30708fSChicago Duan                 }
4673d30708fSChicago Duan                 if (mrdJsonArray)
4683d30708fSChicago Duan                 {
4693d30708fSChicago Duan                     messages::propertyValueConflict(
4703d30708fSChicago Duan                         asyncResp->res, "MetricReportDefinitions", "Protocol");
4713d30708fSChicago Duan                     return;
4723d30708fSChicago Duan                 }
473a716aa74SEd Tanous                 if (url->scheme() != "snmp")
474a716aa74SEd Tanous                 {
475bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
476bd79bce8SPatrick Williams                                                     "Destination", "Protocol");
4773d30708fSChicago Duan                     return;
4783d30708fSChicago Duan                 }
4793d30708fSChicago Duan 
480a716aa74SEd Tanous                 addSnmpTrapClient(asyncResp, url->host_address(),
481a716aa74SEd Tanous                                   url->port_number());
482a716aa74SEd Tanous                 return;
483b52664e2SAppaRao Puli             }
4843d30708fSChicago Duan 
485a716aa74SEd Tanous             std::shared_ptr<Subscription> subValue =
48621a94d5cSMyung Bae                 std::make_shared<Subscription>(
4875fe4ef35SMyung Bae                     std::make_shared<persistent_data::UserSubscription>(), *url,
4885fe4ef35SMyung Bae                     app.ioContext());
489e5aaf047SAppaRao Puli 
490e5aaf047SAppaRao Puli             if (subscriptionType)
491e5aaf047SAppaRao Puli             {
492e5aaf047SAppaRao Puli                 if (*subscriptionType != "RedfishEvent")
493e5aaf047SAppaRao Puli                 {
494fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
495fffb8c1fSEd Tanous                         asyncResp->res, *subscriptionType, "SubscriptionType");
496e5aaf047SAppaRao Puli                     return;
497e5aaf047SAppaRao Puli                 }
4985fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = *subscriptionType;
499e5aaf047SAppaRao Puli             }
500e5aaf047SAppaRao Puli             else
501e5aaf047SAppaRao Puli             {
5024b712a29SEd Tanous                 // Default
5035fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = "RedfishEvent";
504e5aaf047SAppaRao Puli             }
505e5aaf047SAppaRao Puli 
506e5aaf047SAppaRao Puli             if (protocol != "Redfish")
507e5aaf047SAppaRao Puli             {
508e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, protocol,
509e5aaf047SAppaRao Puli                                                  "Protocol");
510e5aaf047SAppaRao Puli                 return;
511e5aaf047SAppaRao Puli             }
5125fe4ef35SMyung Bae             subValue->userSub->protocol = protocol;
513e5aaf047SAppaRao Puli 
51419bb362bSEd Tanous             if (verifyCertificate)
51519bb362bSEd Tanous             {
5165fe4ef35SMyung Bae                 subValue->userSub->verifyCertificate = *verifyCertificate;
51719bb362bSEd Tanous             }
51819bb362bSEd Tanous 
51923a21a1cSEd Tanous             if (eventFormatType2)
520e5aaf047SAppaRao Puli             {
521bd79bce8SPatrick Williams                 if (std::ranges::find(supportedEvtFormatTypes,
522bd79bce8SPatrick Williams                                       *eventFormatType2) ==
5233544d2a7SEd Tanous                     supportedEvtFormatTypes.end())
524e5aaf047SAppaRao Puli                 {
525fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
526fffb8c1fSEd Tanous                         asyncResp->res, *eventFormatType2, "EventFormatType");
527e5aaf047SAppaRao Puli                     return;
528e5aaf047SAppaRao Puli                 }
5295fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = *eventFormatType2;
530e5aaf047SAppaRao Puli             }
531e5aaf047SAppaRao Puli             else
532e5aaf047SAppaRao Puli             {
533e5aaf047SAppaRao Puli                 // If not specified, use default "Event"
5345fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = "Event";
535e5aaf047SAppaRao Puli             }
536e5aaf047SAppaRao Puli 
537e5aaf047SAppaRao Puli             if (context)
538e5aaf047SAppaRao Puli             {
5398ece0e45SEd Tanous                 // This value is selected arbitrarily.
540600af5f1SAppaRao Puli                 constexpr const size_t maxContextSize = 256;
541600af5f1SAppaRao Puli                 if (context->size() > maxContextSize)
542600af5f1SAppaRao Puli                 {
543600af5f1SAppaRao Puli                     messages::stringValueTooLong(asyncResp->res, "Context",
544600af5f1SAppaRao Puli                                                  maxContextSize);
545600af5f1SAppaRao Puli                     return;
546600af5f1SAppaRao Puli                 }
5475fe4ef35SMyung Bae                 subValue->userSub->customText = *context;
548e5aaf047SAppaRao Puli             }
549e5aaf047SAppaRao Puli 
550e5aaf047SAppaRao Puli             if (headers)
551e5aaf047SAppaRao Puli             {
552600af5f1SAppaRao Puli                 size_t cumulativeLen = 0;
553600af5f1SAppaRao Puli 
55478d4ec4fSEd Tanous                 for (const nlohmann::json::object_t& headerChunk : *headers)
555601c71aeSEd Tanous                 {
55678d4ec4fSEd Tanous                     for (const auto& item : headerChunk)
55778d4ec4fSEd Tanous                     {
55878d4ec4fSEd Tanous                         const std::string* value =
55978d4ec4fSEd Tanous                             item.second.get_ptr<const std::string*>();
56078d4ec4fSEd Tanous                         if (value == nullptr)
56178d4ec4fSEd Tanous                         {
56278d4ec4fSEd Tanous                             messages::propertyValueFormatError(
56378d4ec4fSEd Tanous                                 asyncResp->res, item.second,
56478d4ec4fSEd Tanous                                 "HttpHeaders/" + item.first);
56578d4ec4fSEd Tanous                             return;
56678d4ec4fSEd Tanous                         }
56778d4ec4fSEd Tanous                         // Adding a new json value is the size of the key, +
56878d4ec4fSEd Tanous                         // the size of the value + 2 * 2 quotes for each, +
56978d4ec4fSEd Tanous                         // the colon and space between. example:
57078d4ec4fSEd Tanous                         // "key": "value"
57178d4ec4fSEd Tanous                         cumulativeLen += item.first.size() + value->size() + 6;
572600af5f1SAppaRao Puli                         // This value is selected to mirror http_connection.hpp
573600af5f1SAppaRao Puli                         constexpr const uint16_t maxHeaderSizeED = 8096;
574600af5f1SAppaRao Puli                         if (cumulativeLen > maxHeaderSizeED)
575600af5f1SAppaRao Puli                         {
57678d4ec4fSEd Tanous                             messages::arraySizeTooLong(
57778d4ec4fSEd Tanous                                 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
578600af5f1SAppaRao Puli                             return;
579600af5f1SAppaRao Puli                         }
5805fe4ef35SMyung Bae                         subValue->userSub->httpHeaders.set(item.first, *value);
581601c71aeSEd Tanous                     }
582601c71aeSEd Tanous                 }
583e5aaf047SAppaRao Puli             }
584e5aaf047SAppaRao Puli 
585e5aaf047SAppaRao Puli             if (regPrefixes)
586e5aaf047SAppaRao Puli             {
587e5aaf047SAppaRao Puli                 for (const std::string& it : *regPrefixes)
588e5aaf047SAppaRao Puli                 {
5893544d2a7SEd Tanous                     if (std::ranges::find(supportedRegPrefixes, it) ==
5903544d2a7SEd Tanous                         supportedRegPrefixes.end())
591e5aaf047SAppaRao Puli                     {
592fffb8c1fSEd Tanous                         messages::propertyValueNotInList(asyncResp->res, it,
593fffb8c1fSEd Tanous                                                          "RegistryPrefixes");
594e5aaf047SAppaRao Puli                         return;
595e5aaf047SAppaRao Puli                     }
596e5aaf047SAppaRao Puli                 }
5975fe4ef35SMyung Bae                 subValue->userSub->registryPrefixes = *regPrefixes;
598e5aaf047SAppaRao Puli             }
599e5aaf047SAppaRao Puli 
600a14c9113SEd Tanous             if (originResources)
601a14c9113SEd Tanous             {
6025fe4ef35SMyung Bae                 subValue->userSub->originResources = *originResources;
603a14c9113SEd Tanous             }
604a14c9113SEd Tanous 
605e56f254cSSunitha Harish             if (resTypes)
606e56f254cSSunitha Harish             {
607e56f254cSSunitha Harish                 for (const std::string& it : *resTypes)
608e56f254cSSunitha Harish                 {
6093544d2a7SEd Tanous                     if (std::ranges::find(supportedResourceTypes, it) ==
6103544d2a7SEd Tanous                         supportedResourceTypes.end())
611e56f254cSSunitha Harish                     {
612e56f254cSSunitha Harish                         messages::propertyValueNotInList(asyncResp->res, it,
613e56f254cSSunitha Harish                                                          "ResourceTypes");
614e56f254cSSunitha Harish                         return;
615e56f254cSSunitha Harish                     }
616e56f254cSSunitha Harish                 }
6175fe4ef35SMyung Bae                 subValue->userSub->resourceTypes = *resTypes;
618e56f254cSSunitha Harish             }
619e56f254cSSunitha Harish 
620e5aaf047SAppaRao Puli             if (msgIds)
621e5aaf047SAppaRao Puli             {
622b304bd79SP Dheeraj Srujan Kumar                 std::vector<std::string> registryPrefix;
623b304bd79SP Dheeraj Srujan Kumar 
6247e860f15SJohn Edward Broadbent                 // If no registry prefixes are mentioned, consider all
6257e860f15SJohn Edward Broadbent                 // supported prefixes
6265fe4ef35SMyung Bae                 if (subValue->userSub->registryPrefixes.empty())
627b304bd79SP Dheeraj Srujan Kumar                 {
628b304bd79SP Dheeraj Srujan Kumar                     registryPrefix.assign(supportedRegPrefixes.begin(),
629b304bd79SP Dheeraj Srujan Kumar                                           supportedRegPrefixes.end());
630b304bd79SP Dheeraj Srujan Kumar                 }
631b304bd79SP Dheeraj Srujan Kumar                 else
632b304bd79SP Dheeraj Srujan Kumar                 {
6335fe4ef35SMyung Bae                     registryPrefix = subValue->userSub->registryPrefixes;
634b304bd79SP Dheeraj Srujan Kumar                 }
635b304bd79SP Dheeraj Srujan Kumar 
636b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& id : *msgIds)
637b304bd79SP Dheeraj Srujan Kumar                 {
638b304bd79SP Dheeraj Srujan Kumar                     bool validId = false;
639b304bd79SP Dheeraj Srujan Kumar 
640b304bd79SP Dheeraj Srujan Kumar                     // Check for Message ID in each of the selected Registry
641b304bd79SP Dheeraj Srujan Kumar                     for (const std::string& it : registryPrefix)
642b304bd79SP Dheeraj Srujan Kumar                     {
643fffb8c1fSEd Tanous                         const std::span<const redfish::registries::MessageEntry>
644fffb8c1fSEd Tanous                             registry =
645fffb8c1fSEd Tanous                                 redfish::registries::getRegistryFromPrefix(it);
646b304bd79SP Dheeraj Srujan Kumar 
6473544d2a7SEd Tanous                         if (std::ranges::any_of(
6483544d2a7SEd Tanous                                 registry,
649fffb8c1fSEd Tanous                                 [&id](const redfish::registries::MessageEntry&
650fffb8c1fSEd Tanous                                           messageEntry) {
65155f79e6fSEd Tanous                                     return id == messageEntry.first;
652b304bd79SP Dheeraj Srujan Kumar                                 }))
653b304bd79SP Dheeraj Srujan Kumar                         {
654b304bd79SP Dheeraj Srujan Kumar                             validId = true;
655b304bd79SP Dheeraj Srujan Kumar                             break;
656b304bd79SP Dheeraj Srujan Kumar                         }
657b304bd79SP Dheeraj Srujan Kumar                     }
658b304bd79SP Dheeraj Srujan Kumar 
659b304bd79SP Dheeraj Srujan Kumar                     if (!validId)
660b304bd79SP Dheeraj Srujan Kumar                     {
661b304bd79SP Dheeraj Srujan Kumar                         messages::propertyValueNotInList(asyncResp->res, id,
662b304bd79SP Dheeraj Srujan Kumar                                                          "MessageIds");
663b304bd79SP Dheeraj Srujan Kumar                         return;
664b304bd79SP Dheeraj Srujan Kumar                     }
665b304bd79SP Dheeraj Srujan Kumar                 }
666b304bd79SP Dheeraj Srujan Kumar 
6675fe4ef35SMyung Bae                 subValue->userSub->registryMsgIds = *msgIds;
668e5aaf047SAppaRao Puli             }
669e5aaf047SAppaRao Puli 
670e5aaf047SAppaRao Puli             if (retryPolicy)
671e5aaf047SAppaRao Puli             {
6723544d2a7SEd Tanous                 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
6733544d2a7SEd Tanous                     supportedRetryPolicies.end())
674e5aaf047SAppaRao Puli                 {
675bd79bce8SPatrick Williams                     messages::propertyValueNotInList(
676bd79bce8SPatrick Williams                         asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
677e5aaf047SAppaRao Puli                     return;
678e5aaf047SAppaRao Puli                 }
6795fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = *retryPolicy;
680e5aaf047SAppaRao Puli             }
681e5aaf047SAppaRao Puli             else
682e5aaf047SAppaRao Puli             {
683e5aaf047SAppaRao Puli                 // Default "TerminateAfterRetries"
6845fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = "TerminateAfterRetries";
685e5aaf047SAppaRao Puli             }
6865064a25bSMyung Bae             if (sendHeartbeat)
6875064a25bSMyung Bae             {
6885064a25bSMyung Bae                 subValue->userSub->sendHeartbeat = *sendHeartbeat;
6895064a25bSMyung Bae             }
6905064a25bSMyung Bae             if (hbIntervalMinutes)
6915064a25bSMyung Bae             {
6925064a25bSMyung Bae                 if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
6935064a25bSMyung Bae                 {
6945064a25bSMyung Bae                     messages::propertyValueOutOfRange(
6955064a25bSMyung Bae                         asyncResp->res, *hbIntervalMinutes,
6965064a25bSMyung Bae                         "HeartbeatIntervalMinutes");
6975064a25bSMyung Bae                     return;
6985064a25bSMyung Bae                 }
6995064a25bSMyung Bae                 subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
7005064a25bSMyung Bae             }
701e5aaf047SAppaRao Puli 
702144b6318SAppaRao Puli             if (mrdJsonArray)
703156d6b00SAppaRao Puli             {
70478d4ec4fSEd Tanous                 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
705144b6318SAppaRao Puli                 {
706144b6318SAppaRao Puli                     std::string mrdUri;
707ea2e6eecSWilly Tu 
70878d4ec4fSEd Tanous                     if (!json_util::readJsonObject(mrdObj, asyncResp->res,
70978d4ec4fSEd Tanous                                                    "@odata.id", mrdUri))
710ea2e6eecSWilly Tu 
711144b6318SAppaRao Puli                     {
712144b6318SAppaRao Puli                         return;
713144b6318SAppaRao Puli                     }
7145fe4ef35SMyung Bae                     subValue->userSub->metricReportDefinitions.emplace_back(
7154b712a29SEd Tanous                         mrdUri);
716144b6318SAppaRao Puli                 }
717156d6b00SAppaRao Puli             }
718156d6b00SAppaRao Puli 
719b52664e2SAppaRao Puli             std::string id =
720bd79bce8SPatrick Williams                 EventServiceManager::getInstance().addPushSubscription(
721bd79bce8SPatrick Williams                     subValue);
722b52664e2SAppaRao Puli             if (id.empty())
723e5aaf047SAppaRao Puli             {
724e5aaf047SAppaRao Puli                 messages::internalError(asyncResp->res);
725e5aaf047SAppaRao Puli                 return;
726e5aaf047SAppaRao Puli             }
727e5aaf047SAppaRao Puli 
728e5aaf047SAppaRao Puli             messages::created(asyncResp->res);
729e5aaf047SAppaRao Puli             asyncResp->res.addHeader(
730e5aaf047SAppaRao Puli                 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
731fb546105SMyung Bae 
732fb546105SMyung Bae             // schedule a heartbeat
733fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
734fb546105SMyung Bae             {
735fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
736fb546105SMyung Bae             }
7377e860f15SJohn Edward Broadbent         });
738e5aaf047SAppaRao Puli }
739e5aaf047SAppaRao Puli 
7407e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
741e5aaf047SAppaRao Puli {
7429d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
743ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
7447e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
74545ca1b86SEd Tanous             [&app](const crow::Request& req,
7467e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
7477e860f15SJohn Edward Broadbent                    const std::string& param) {
7483ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
74945ca1b86SEd Tanous                 {
75045ca1b86SEd Tanous                     return;
75145ca1b86SEd Tanous                 }
7523d30708fSChicago Duan 
7533d30708fSChicago Duan                 if (param.starts_with("snmp"))
7543d30708fSChicago Duan                 {
7553d30708fSChicago Duan                     getSnmpTrapClient(asyncResp, param);
7563d30708fSChicago Duan                     return;
7573d30708fSChicago Duan                 }
7583d30708fSChicago Duan 
759b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
7607e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
761b52664e2SAppaRao Puli                 if (subValue == nullptr)
762e5aaf047SAppaRao Puli                 {
763bd79bce8SPatrick Williams                     asyncResp->res.result(
764bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
765e5aaf047SAppaRao Puli                     return;
766e5aaf047SAppaRao Puli                 }
7677e860f15SJohn Edward Broadbent                 const std::string& id = param;
768e5aaf047SAppaRao Puli 
7694b712a29SEd Tanous                 const persistent_data::UserSubscription& userSub =
7705fe4ef35SMyung Bae                     *subValue->userSub;
771e56f254cSSunitha Harish 
7724b712a29SEd Tanous                 nlohmann::json& jVal = asyncResp->res.jsonValue;
7734b712a29SEd Tanous                 jVal["@odata.type"] =
7744b712a29SEd Tanous                     "#EventDestination.v1_14_1.EventDestination";
7754b712a29SEd Tanous                 jVal["Protocol"] =
7764b712a29SEd Tanous                     event_destination::EventDestinationProtocol::Redfish;
7774b712a29SEd Tanous                 jVal["@odata.id"] = boost::urls::format(
7784b712a29SEd Tanous                     "/redfish/v1/EventService/Subscriptions/{}", id);
7794b712a29SEd Tanous                 jVal["Id"] = id;
7804b712a29SEd Tanous                 jVal["Name"] = "Event Destination " + id;
7814b712a29SEd Tanous                 jVal["Destination"] = userSub.destinationUrl;
7824b712a29SEd Tanous                 jVal["Context"] = userSub.customText;
7834b712a29SEd Tanous                 jVal["SubscriptionType"] = userSub.subscriptionType;
7844b712a29SEd Tanous                 jVal["HttpHeaders"] = nlohmann::json::array();
7854b712a29SEd Tanous                 jVal["EventFormatType"] = userSub.eventFormatType;
7864b712a29SEd Tanous                 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
7874b712a29SEd Tanous                 jVal["ResourceTypes"] = userSub.resourceTypes;
7884b712a29SEd Tanous 
7894b712a29SEd Tanous                 jVal["MessageIds"] = userSub.registryMsgIds;
7904b712a29SEd Tanous                 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
7915064a25bSMyung Bae                 jVal["SendHeartbeat"] = userSub.sendHeartbeat;
7925064a25bSMyung Bae                 jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
7934b712a29SEd Tanous                 jVal["VerifyCertificate"] = userSub.verifyCertificate;
794144b6318SAppaRao Puli 
7951476687dSEd Tanous                 nlohmann::json::array_t mrdJsonArray;
7964b712a29SEd Tanous                 for (const auto& mdrUri : userSub.metricReportDefinitions)
797144b6318SAppaRao Puli                 {
7981476687dSEd Tanous                     nlohmann::json::object_t mdr;
7991476687dSEd Tanous                     mdr["@odata.id"] = mdrUri;
8001476687dSEd Tanous                     mrdJsonArray.emplace_back(std::move(mdr));
801144b6318SAppaRao Puli                 }
8024b712a29SEd Tanous                 jVal["MetricReportDefinitions"] = mrdJsonArray;
8037e860f15SJohn Edward Broadbent             });
8049d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
805ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
806ed398213SEd Tanous         // ConfigureSelf
8077eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
808ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
809432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
8107e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
81145ca1b86SEd Tanous             [&app](const crow::Request& req,
8127e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8137e860f15SJohn Edward Broadbent                    const std::string& param) {
8143ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
81545ca1b86SEd Tanous                 {
81645ca1b86SEd Tanous                     return;
81745ca1b86SEd Tanous                 }
818b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
8197e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
820b52664e2SAppaRao Puli                 if (subValue == nullptr)
821e5aaf047SAppaRao Puli                 {
822bd79bce8SPatrick Williams                     asyncResp->res.result(
823bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
824e5aaf047SAppaRao Puli                     return;
825e5aaf047SAppaRao Puli                 }
826e5aaf047SAppaRao Puli 
827e5aaf047SAppaRao Puli                 std::optional<std::string> context;
828e5aaf047SAppaRao Puli                 std::optional<std::string> retryPolicy;
8295064a25bSMyung Bae                 std::optional<bool> sendHeartbeat;
8305064a25bSMyung Bae                 std::optional<uint64_t> hbIntervalMinutes;
83119bb362bSEd Tanous                 std::optional<bool> verifyCertificate;
83278d4ec4fSEd Tanous                 std::optional<std::vector<nlohmann::json::object_t>> headers;
833e5aaf047SAppaRao Puli 
834afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
835afc474aeSMyung Bae                         req, asyncResp->res, //
836afc474aeSMyung Bae                         "Context", context, //
837afc474aeSMyung Bae                         "DeliveryRetryPolicy", retryPolicy, //
8385064a25bSMyung Bae                         "HeartbeatIntervalMinutes", hbIntervalMinutes, //
839afc474aeSMyung Bae                         "HttpHeaders", headers, //
8405064a25bSMyung Bae                         "SendHeartbeat", sendHeartbeat, //
841afc474aeSMyung Bae                         "VerifyCertificate", verifyCertificate //
842afc474aeSMyung Bae                         ))
843e5aaf047SAppaRao Puli                 {
844e5aaf047SAppaRao Puli                     return;
845e5aaf047SAppaRao Puli                 }
846e5aaf047SAppaRao Puli 
847e5aaf047SAppaRao Puli                 if (context)
848e5aaf047SAppaRao Puli                 {
8495fe4ef35SMyung Bae                     subValue->userSub->customText = *context;
850e5aaf047SAppaRao Puli                 }
851e5aaf047SAppaRao Puli 
852e5aaf047SAppaRao Puli                 if (headers)
853e5aaf047SAppaRao Puli                 {
854601c71aeSEd Tanous                     boost::beast::http::fields fields;
85578d4ec4fSEd Tanous                     for (const nlohmann::json::object_t& headerChunk : *headers)
856601c71aeSEd Tanous                     {
85778d4ec4fSEd Tanous                         for (const auto& it : headerChunk)
858601c71aeSEd Tanous                         {
859601c71aeSEd Tanous                             const std::string* value =
86078d4ec4fSEd Tanous                                 it.second.get_ptr<const std::string*>();
861601c71aeSEd Tanous                             if (value == nullptr)
862601c71aeSEd Tanous                             {
863601c71aeSEd Tanous                                 messages::propertyValueFormatError(
86478d4ec4fSEd Tanous                                     asyncResp->res, it.second,
86578d4ec4fSEd Tanous                                     "HttpHeaders/" + it.first);
866601c71aeSEd Tanous                                 return;
867601c71aeSEd Tanous                             }
86878d4ec4fSEd Tanous                             fields.set(it.first, *value);
869601c71aeSEd Tanous                         }
870601c71aeSEd Tanous                     }
8715fe4ef35SMyung Bae                     subValue->userSub->httpHeaders = std::move(fields);
872e5aaf047SAppaRao Puli                 }
873e5aaf047SAppaRao Puli 
874e5aaf047SAppaRao Puli                 if (retryPolicy)
875e5aaf047SAppaRao Puli                 {
876bd79bce8SPatrick Williams                     if (std::ranges::find(supportedRetryPolicies,
877bd79bce8SPatrick Williams                                           *retryPolicy) ==
8783544d2a7SEd Tanous                         supportedRetryPolicies.end())
879e5aaf047SAppaRao Puli                     {
880bd79bce8SPatrick Williams                         messages::propertyValueNotInList(asyncResp->res,
881bd79bce8SPatrick Williams                                                          *retryPolicy,
882e5aaf047SAppaRao Puli                                                          "DeliveryRetryPolicy");
883e5aaf047SAppaRao Puli                         return;
884e5aaf047SAppaRao Puli                     }
8855fe4ef35SMyung Bae                     subValue->userSub->retryPolicy = *retryPolicy;
886e5aaf047SAppaRao Puli                 }
887e5aaf047SAppaRao Puli 
8885064a25bSMyung Bae                 if (sendHeartbeat)
8895064a25bSMyung Bae                 {
8905064a25bSMyung Bae                     subValue->userSub->sendHeartbeat = *sendHeartbeat;
8915064a25bSMyung Bae                 }
8925064a25bSMyung Bae                 if (hbIntervalMinutes)
8935064a25bSMyung Bae                 {
8945064a25bSMyung Bae                     if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
8955064a25bSMyung Bae                     {
8965064a25bSMyung Bae                         messages::propertyValueOutOfRange(
8975064a25bSMyung Bae                             asyncResp->res, *hbIntervalMinutes,
8985064a25bSMyung Bae                             "HeartbeatIntervalMinutes");
8995064a25bSMyung Bae                         return;
9005064a25bSMyung Bae                     }
9015064a25bSMyung Bae                     subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
9025064a25bSMyung Bae                 }
9035064a25bSMyung Bae 
904fb546105SMyung Bae                 if (hbIntervalMinutes || sendHeartbeat)
905fb546105SMyung Bae                 {
906fb546105SMyung Bae                     // if Heartbeat interval or send heart were changed, cancel
907fb546105SMyung Bae                     // the heartbeat timer if running and start a new heartbeat
908fb546105SMyung Bae                     // if needed
909fb546105SMyung Bae                     subValue->heartbeatParametersChanged();
910fb546105SMyung Bae                 }
911fb546105SMyung Bae 
91219bb362bSEd Tanous                 if (verifyCertificate)
91319bb362bSEd Tanous                 {
9145fe4ef35SMyung Bae                     subValue->userSub->verifyCertificate = *verifyCertificate;
91519bb362bSEd Tanous                 }
91619bb362bSEd Tanous 
917b52664e2SAppaRao Puli                 EventServiceManager::getInstance().updateSubscriptionData();
9187e860f15SJohn Edward Broadbent             });
9199d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
920ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
921ed398213SEd Tanous         // ConfigureSelf
9227eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
923ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
924432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
9257e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
92645ca1b86SEd Tanous             [&app](const crow::Request& req,
9277e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9287e860f15SJohn Edward Broadbent                    const std::string& param) {
9293ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
93045ca1b86SEd Tanous                 {
93145ca1b86SEd Tanous                     return;
93245ca1b86SEd Tanous                 }
9334b712a29SEd Tanous                 EventServiceManager& event = EventServiceManager::getInstance();
9343d30708fSChicago Duan                 if (param.starts_with("snmp"))
9353d30708fSChicago Duan                 {
9363d30708fSChicago Duan                     deleteSnmpTrapClient(asyncResp, param);
9374b712a29SEd Tanous                     event.deleteSubscription(param);
9383d30708fSChicago Duan                     return;
9393d30708fSChicago Duan                 }
9403d30708fSChicago Duan 
9414b712a29SEd Tanous                 if (!event.deleteSubscription(param))
942e5aaf047SAppaRao Puli                 {
9434b712a29SEd Tanous                     messages::resourceNotFound(asyncResp->res,
9444b712a29SEd Tanous                                                "EventDestination", param);
945e5aaf047SAppaRao Puli                     return;
946e5aaf047SAppaRao Puli                 }
9474b712a29SEd Tanous                 messages::success(asyncResp->res);
9487e860f15SJohn Edward Broadbent             });
949e5aaf047SAppaRao Puli }
950e5aaf047SAppaRao Puli 
951e5aaf047SAppaRao Puli } // namespace redfish
952