xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation
4e5aaf047SAppaRao Puli #pragma once
53ccb3adbSEd Tanous #include "app.hpp"
6*d7857201SEd Tanous #include "async_resp.hpp"
7*d7857201SEd Tanous #include "dbus_singleton.hpp"
8*d7857201SEd Tanous #include "dbus_utility.hpp"
9*d7857201SEd Tanous #include "error_messages.hpp"
10b52664e2SAppaRao Puli #include "event_service_manager.hpp"
11*d7857201SEd Tanous #include "event_service_store.hpp"
12*d7857201SEd Tanous #include "generated/enums/event_destination.hpp"
133ccb3adbSEd Tanous #include "http/utility.hpp"
14*d7857201SEd Tanous #include "http_request.hpp"
153ccb3adbSEd Tanous #include "logging.hpp"
163ccb3adbSEd Tanous #include "query.hpp"
17d109e2b6SAlexander Hansen #include "registries.hpp"
183ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
19d109e2b6SAlexander Hansen #include "registries_selector.hpp"
203d30708fSChicago Duan #include "snmp_trap_event_clients.hpp"
21*d7857201SEd Tanous #include "subscription.hpp"
22d109e2b6SAlexander Hansen #include "utils/json_utils.hpp"
23e5aaf047SAppaRao Puli 
24*d7857201SEd Tanous #include <asm-generic/errno.h>
25ed398213SEd Tanous 
26*d7857201SEd Tanous #include <boost/beast/http/fields.hpp>
27*d7857201SEd Tanous #include <boost/beast/http/status.hpp>
28*d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
29*d7857201SEd Tanous #include <boost/system/error_code.hpp>
30*d7857201SEd Tanous #include <boost/system/result.hpp>
31*d7857201SEd Tanous #include <boost/url/format.hpp>
32*d7857201SEd Tanous #include <boost/url/parse.hpp>
33*d7857201SEd Tanous #include <boost/url/url.hpp>
34*d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
35*d7857201SEd Tanous 
36*d7857201SEd Tanous #include <algorithm>
37*d7857201SEd Tanous #include <array>
38*d7857201SEd Tanous #include <cerrno>
39*d7857201SEd Tanous #include <cstddef>
40*d7857201SEd Tanous #include <cstdint>
413d30708fSChicago Duan #include <memory>
425064a25bSMyung Bae #include <optional>
433544d2a7SEd Tanous #include <ranges>
441e270c5fSPatrick Williams #include <span>
453d30708fSChicago Duan #include <string>
46*d7857201SEd Tanous #include <utility>
47a14c9113SEd Tanous #include <vector>
481e270c5fSPatrick Williams 
49e5aaf047SAppaRao Puli namespace redfish
50e5aaf047SAppaRao Puli {
51e5aaf047SAppaRao Puli 
52156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
53156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
54fb546105SMyung Bae static constexpr const std::array<const char*, 4> supportedRegPrefixes = {
55fb546105SMyung Bae     "Base", "OpenBMC", "TaskEvent", "HeartbeatEvent"};
56e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
57e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
58e5aaf047SAppaRao Puli 
59fb546105SMyung Bae static constexpr const std::array<const char*, 2> supportedResourceTypes = {
60fb546105SMyung Bae     "Task", "Heartbeat"};
61e56f254cSSunitha Harish 
627e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
63e5aaf047SAppaRao Puli {
647e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
65ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
66bd79bce8SPatrick Williams         .methods(
67bd79bce8SPatrick Williams             boost::beast::http::verb::
68bd79bce8SPatrick Williams                 get)([&app](
69bd79bce8SPatrick Williams                          const crow::Request& req,
70002d39b4SEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
713ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
7245ca1b86SEd Tanous             {
7345ca1b86SEd Tanous                 return;
7445ca1b86SEd Tanous             }
751476687dSEd Tanous 
761476687dSEd Tanous             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
771476687dSEd Tanous             asyncResp->res.jsonValue["@odata.type"] =
781476687dSEd Tanous                 "#EventService.v1_5_0.EventService";
791476687dSEd Tanous             asyncResp->res.jsonValue["Id"] = "EventService";
801476687dSEd Tanous             asyncResp->res.jsonValue["Name"] = "Event Service";
815e44e3d8SAppaRao Puli             asyncResp->res.jsonValue["ServerSentEventUri"] =
825e44e3d8SAppaRao Puli                 "/redfish/v1/EventService/SSE";
835e44e3d8SAppaRao Puli 
841476687dSEd Tanous             asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
851476687dSEd Tanous                 "/redfish/v1/EventService/Subscriptions";
86bd79bce8SPatrick Williams             asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
87bd79bce8SPatrick Williams                                     ["target"] =
881476687dSEd Tanous                 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
89e5aaf047SAppaRao Puli 
9028afb49cSJunLin Chen             const persistent_data::EventServiceConfig eventServiceConfig =
9128afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
9228afb49cSJunLin Chen                     .getEventServiceConfig();
937d1cc387SAppaRao Puli 
947d1cc387SAppaRao Puli             asyncResp->res.jsonValue["Status"]["State"] =
9528afb49cSJunLin Chen                 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
96bd79bce8SPatrick Williams             asyncResp->res.jsonValue["ServiceEnabled"] =
97bd79bce8SPatrick Williams                 eventServiceConfig.enabled;
987e860f15SJohn Edward Broadbent             asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
9928afb49cSJunLin Chen                 eventServiceConfig.retryAttempts;
100e5aaf047SAppaRao Puli             asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
10128afb49cSJunLin Chen                 eventServiceConfig.retryTimeoutInterval;
102bd79bce8SPatrick Williams             asyncResp->res.jsonValue["EventFormatTypes"] =
103bd79bce8SPatrick Williams                 supportedEvtFormatTypes;
1040fda0f12SGeorge Liu             asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
1050fda0f12SGeorge Liu             asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
10607941a88SAyushi Smriti 
107613dabeaSEd Tanous             nlohmann::json::object_t supportedSSEFilters;
108613dabeaSEd Tanous             supportedSSEFilters["EventFormatType"] = true;
109613dabeaSEd Tanous             supportedSSEFilters["MessageId"] = true;
110613dabeaSEd Tanous             supportedSSEFilters["MetricReportDefinition"] = true;
111613dabeaSEd Tanous             supportedSSEFilters["RegistryPrefix"] = true;
112613dabeaSEd Tanous             supportedSSEFilters["OriginResource"] = false;
113613dabeaSEd Tanous             supportedSSEFilters["ResourceType"] = false;
11407941a88SAyushi Smriti 
11507941a88SAyushi Smriti             asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
116613dabeaSEd Tanous                 std::move(supportedSSEFilters);
1177e860f15SJohn Edward Broadbent         });
118e5aaf047SAppaRao Puli 
1197e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
120ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1217e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
12245ca1b86SEd Tanous             [&app](const crow::Request& req,
12345ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1243ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
125e5aaf047SAppaRao Puli                 {
12645ca1b86SEd Tanous                     return;
12745ca1b86SEd Tanous                 }
128e5aaf047SAppaRao Puli                 std::optional<bool> serviceEnabled;
129e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryAttemps;
130e5aaf047SAppaRao Puli                 std::optional<uint32_t> retryInterval;
131afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
132afc474aeSMyung Bae                         req, asyncResp->res, //
133afc474aeSMyung Bae                         "DeliveryRetryAttempts", retryAttemps, //
134afc474aeSMyung Bae                         "DeliveryRetryIntervalSeconds", retryInterval, //
135afc474aeSMyung Bae                         "ServiceEnabled", serviceEnabled //
136afc474aeSMyung Bae                         ))
137e5aaf047SAppaRao Puli                 {
138e5aaf047SAppaRao Puli                     return;
139e5aaf047SAppaRao Puli                 }
140e5aaf047SAppaRao Puli 
14128afb49cSJunLin Chen                 persistent_data::EventServiceConfig eventServiceConfig =
14228afb49cSJunLin Chen                     persistent_data::EventServiceStore::getInstance()
14328afb49cSJunLin Chen                         .getEventServiceConfig();
1447d1cc387SAppaRao Puli 
145e5aaf047SAppaRao Puli                 if (serviceEnabled)
146e5aaf047SAppaRao Puli                 {
14728afb49cSJunLin Chen                     eventServiceConfig.enabled = *serviceEnabled;
148e5aaf047SAppaRao Puli                 }
149e5aaf047SAppaRao Puli 
150e5aaf047SAppaRao Puli                 if (retryAttemps)
151e5aaf047SAppaRao Puli                 {
152e5aaf047SAppaRao Puli                     // Supported range [1-3]
153e5aaf047SAppaRao Puli                     if ((*retryAttemps < 1) || (*retryAttemps > 3))
154e5aaf047SAppaRao Puli                     {
155e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
156e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryAttemps),
157e5aaf047SAppaRao Puli                             "DeliveryRetryAttempts", "[1-3]");
158e5aaf047SAppaRao Puli                     }
159e5aaf047SAppaRao Puli                     else
160e5aaf047SAppaRao Puli                     {
16128afb49cSJunLin Chen                         eventServiceConfig.retryAttempts = *retryAttemps;
162e5aaf047SAppaRao Puli                     }
163e5aaf047SAppaRao Puli                 }
164e5aaf047SAppaRao Puli 
165e5aaf047SAppaRao Puli                 if (retryInterval)
166e5aaf047SAppaRao Puli                 {
16733a32b34SGunnar Mills                     // Supported range [5 - 180]
16833a32b34SGunnar Mills                     if ((*retryInterval < 5) || (*retryInterval > 180))
169e5aaf047SAppaRao Puli                     {
170e5aaf047SAppaRao Puli                         messages::queryParameterOutOfRange(
171e5aaf047SAppaRao Puli                             asyncResp->res, std::to_string(*retryInterval),
17233a32b34SGunnar Mills                             "DeliveryRetryIntervalSeconds", "[5-180]");
173e5aaf047SAppaRao Puli                     }
174e5aaf047SAppaRao Puli                     else
175e5aaf047SAppaRao Puli                     {
176bd79bce8SPatrick Williams                         eventServiceConfig.retryTimeoutInterval =
177bd79bce8SPatrick Williams                             *retryInterval;
178e5aaf047SAppaRao Puli                     }
179e5aaf047SAppaRao Puli                 }
180e5aaf047SAppaRao Puli 
1817d1cc387SAppaRao Puli                 EventServiceManager::getInstance().setEventServiceConfig(
18228afb49cSJunLin Chen                     eventServiceConfig);
1837e860f15SJohn Edward Broadbent             });
1840b4bdd93SAppaRao Puli }
1850b4bdd93SAppaRao Puli 
1867e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1870b4bdd93SAppaRao Puli {
1887e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1897e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
190ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1917e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
19245ca1b86SEd Tanous             [&app](const crow::Request& req,
1937e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1943ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
19545ca1b86SEd Tanous                 {
19645ca1b86SEd Tanous                     return;
19745ca1b86SEd Tanous                 }
19881ee0e74SChandramohan Harkude 
19981ee0e74SChandramohan Harkude                 TestEvent testEvent;
20081ee0e74SChandramohan Harkude                 // clang-format off
20181ee0e74SChandramohan Harkude                 if (!json_util::readJsonAction(
20281ee0e74SChandramohan Harkude                         req, asyncResp->res,
20381ee0e74SChandramohan Harkude                         "EventGroupId", testEvent.eventGroupId,
20481ee0e74SChandramohan Harkude                         "EventId", testEvent.eventId,
20581ee0e74SChandramohan Harkude                         "EventTimestamp", testEvent.eventTimestamp,
20681ee0e74SChandramohan Harkude                         "Message", testEvent.message,
20781ee0e74SChandramohan Harkude                         "MessageArgs", testEvent.messageArgs,
20881ee0e74SChandramohan Harkude                         "MessageId", testEvent.messageId,
20981ee0e74SChandramohan Harkude                         "OriginOfCondition", testEvent.originOfCondition,
21081ee0e74SChandramohan Harkude                         "Resolution", testEvent.resolution,
21181ee0e74SChandramohan Harkude                         "Severity", testEvent.severity))
21281ee0e74SChandramohan Harkude                 {
21381ee0e74SChandramohan Harkude                     return;
21481ee0e74SChandramohan Harkude                 }
21581ee0e74SChandramohan Harkude                 // clang-format on
21681ee0e74SChandramohan Harkude 
21781ee0e74SChandramohan Harkude                 if (!EventServiceManager::getInstance().sendTestEventLog(
21881ee0e74SChandramohan Harkude                         testEvent))
2196ba8c82eSsunharis_in                 {
2206ba8c82eSsunharis_in                     messages::serviceDisabled(asyncResp->res,
2216ba8c82eSsunharis_in                                               "/redfish/v1/EventService/");
2226ba8c82eSsunharis_in                     return;
2236ba8c82eSsunharis_in                 }
2248d1b46d7Szhanghch05                 asyncResp->res.result(boost::beast::http::status::no_content);
2257e860f15SJohn Edward Broadbent             });
226e5aaf047SAppaRao Puli }
227e5aaf047SAppaRao Puli 
2283d30708fSChicago Duan inline void doSubscriptionCollection(
229e81de512SEd Tanous     const boost::system::error_code& ec,
2303d30708fSChicago Duan     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2313d30708fSChicago Duan     const dbus::utility::ManagedObjectType& resp)
2323d30708fSChicago Duan {
2333d30708fSChicago Duan     if (ec)
2343d30708fSChicago Duan     {
2351306101eSEd Tanous         if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
2363d30708fSChicago Duan         {
2373d30708fSChicago Duan             // This is an optional process so just return if it isn't there
2383d30708fSChicago Duan             return;
2393d30708fSChicago Duan         }
2403d30708fSChicago Duan 
24162598e31SEd Tanous         BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
2423d30708fSChicago Duan         messages::internalError(asyncResp->res);
2433d30708fSChicago Duan         return;
2443d30708fSChicago Duan     }
2453d30708fSChicago Duan     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
2463d30708fSChicago Duan     for (const auto& objpath : resp)
2473d30708fSChicago Duan     {
2483d30708fSChicago Duan         sdbusplus::message::object_path path(objpath.first);
2493d30708fSChicago Duan         const std::string snmpId = path.filename();
2503d30708fSChicago Duan         if (snmpId.empty())
2513d30708fSChicago Duan         {
25262598e31SEd Tanous             BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
2533d30708fSChicago Duan             messages::internalError(asyncResp->res);
2543d30708fSChicago Duan             return;
2553d30708fSChicago Duan         }
2563d30708fSChicago Duan 
2573d30708fSChicago Duan         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
2583d30708fSChicago Duan     }
2593d30708fSChicago Duan }
2603d30708fSChicago Duan 
2617e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
262e5aaf047SAppaRao Puli {
2631ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
264ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
2657e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
26645ca1b86SEd Tanous             [&app](const crow::Request& req,
2677e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2683ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
26945ca1b86SEd Tanous                 {
27045ca1b86SEd Tanous                     return;
27145ca1b86SEd Tanous                 }
2721476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.type"] =
2731476687dSEd Tanous                     "#EventDestinationCollection.EventDestinationCollection";
2741476687dSEd Tanous                 asyncResp->res.jsonValue["@odata.id"] =
2751476687dSEd Tanous                     "/redfish/v1/EventService/Subscriptions";
276bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Name"] =
277bd79bce8SPatrick Williams                     "Event Destination Collections";
278e5aaf047SAppaRao Puli 
279bd79bce8SPatrick Williams                 nlohmann::json& memberArray =
280bd79bce8SPatrick Williams                     asyncResp->res.jsonValue["Members"];
281e5aaf047SAppaRao Puli 
282b52664e2SAppaRao Puli                 std::vector<std::string> subscripIds =
283b52664e2SAppaRao Puli                     EventServiceManager::getInstance().getAllIDs();
284b52664e2SAppaRao Puli                 memberArray = nlohmann::json::array();
285bd79bce8SPatrick Williams                 asyncResp->res.jsonValue["Members@odata.count"] =
286bd79bce8SPatrick Williams                     subscripIds.size();
287b52664e2SAppaRao Puli 
288b52664e2SAppaRao Puli                 for (const std::string& id : subscripIds)
289e5aaf047SAppaRao Puli                 {
2901476687dSEd Tanous                     nlohmann::json::object_t member;
2913d30708fSChicago Duan                     member["@odata.id"] = boost::urls::format(
2923d30708fSChicago Duan                         "/redfish/v1/EventService/Subscriptions/{}" + id);
293b2ba3072SPatrick Williams                     memberArray.emplace_back(std::move(member));
294e5aaf047SAppaRao Puli                 }
2953d30708fSChicago Duan                 crow::connections::systemBus->async_method_call(
296e81de512SEd Tanous                     [asyncResp](const boost::system::error_code& ec,
2973d30708fSChicago Duan                                 const dbus::utility::ManagedObjectType& resp) {
2983d30708fSChicago Duan                         doSubscriptionCollection(ec, asyncResp, resp);
2993d30708fSChicago Duan                     },
3003d30708fSChicago Duan                     "xyz.openbmc_project.Network.SNMP",
3013d30708fSChicago Duan                     "/xyz/openbmc_project/network/snmp/manager",
3023d30708fSChicago Duan                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
3037e860f15SJohn Edward Broadbent             });
3043d30708fSChicago Duan 
3057e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
3067eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
307bd79bce8SPatrick Williams         .methods(
308bd79bce8SPatrick Williams             boost::beast::http::verb::
309bd79bce8SPatrick Williams                 post)([&app](
310bd79bce8SPatrick Williams                           const crow::Request& req,
3117e860f15SJohn Edward Broadbent                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3123ba00073SCarson Labrado             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
31345ca1b86SEd Tanous             {
31445ca1b86SEd Tanous                 return;
31545ca1b86SEd Tanous             }
316fffb8c1fSEd Tanous             if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
317fffb8c1fSEd Tanous                 maxNoOfSubscriptions)
318e5aaf047SAppaRao Puli             {
319e5aaf047SAppaRao Puli                 messages::eventSubscriptionLimitExceeded(asyncResp->res);
320e5aaf047SAppaRao Puli                 return;
321e5aaf047SAppaRao Puli             }
322e5aaf047SAppaRao Puli             std::string destUrl;
323e5aaf047SAppaRao Puli             std::string protocol;
32419bb362bSEd Tanous             std::optional<bool> verifyCertificate;
325e5aaf047SAppaRao Puli             std::optional<std::string> context;
326e5aaf047SAppaRao Puli             std::optional<std::string> subscriptionType;
32723a21a1cSEd Tanous             std::optional<std::string> eventFormatType2;
328e5aaf047SAppaRao Puli             std::optional<std::string> retryPolicy;
3295064a25bSMyung Bae             std::optional<bool> sendHeartbeat;
3305064a25bSMyung Bae             std::optional<uint64_t> hbIntervalMinutes;
331e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> msgIds;
332e5aaf047SAppaRao Puli             std::optional<std::vector<std::string>> regPrefixes;
333a14c9113SEd Tanous             std::optional<std::vector<std::string>> originResources;
334e56f254cSSunitha Harish             std::optional<std::vector<std::string>> resTypes;
33578d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> headers;
33678d4ec4fSEd Tanous             std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
337e5aaf047SAppaRao Puli 
338afc474aeSMyung Bae             if (!json_util::readJsonPatch( //
339afc474aeSMyung Bae                     req, asyncResp->res, //
340afc474aeSMyung Bae                     "Context", context, //
341afc474aeSMyung Bae                     "DeliveryRetryPolicy", retryPolicy, //
342afc474aeSMyung Bae                     "Destination", destUrl, //
343afc474aeSMyung Bae                     "EventFormatType", eventFormatType2, //
3445064a25bSMyung Bae                     "HeartbeatIntervalMinutes", hbIntervalMinutes, //
345afc474aeSMyung Bae                     "HttpHeaders", headers, //
346afc474aeSMyung Bae                     "MessageIds", msgIds, //
347afc474aeSMyung Bae                     "MetricReportDefinitions", mrdJsonArray, //
348afc474aeSMyung Bae                     "OriginResources", originResources, //
349afc474aeSMyung Bae                     "Protocol", protocol, //
350afc474aeSMyung Bae                     "RegistryPrefixes", regPrefixes, //
351afc474aeSMyung Bae                     "ResourceTypes", resTypes, //
3525064a25bSMyung Bae                     "SendHeartbeat", sendHeartbeat, //
353afc474aeSMyung Bae                     "SubscriptionType", subscriptionType, //
354afc474aeSMyung Bae                     "VerifyCertificate", verifyCertificate //
3554b712a29SEd Tanous                     ))
356e5aaf047SAppaRao Puli             {
357e5aaf047SAppaRao Puli                 return;
358e5aaf047SAppaRao Puli             }
3594b712a29SEd Tanous             // clang-format on
360e5aaf047SAppaRao Puli 
361600af5f1SAppaRao Puli             // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
362600af5f1SAppaRao Puli             static constexpr const uint16_t maxDestinationSize = 2000;
363600af5f1SAppaRao Puli             if (destUrl.size() > maxDestinationSize)
364600af5f1SAppaRao Puli             {
365600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Destination",
366600af5f1SAppaRao Puli                                              maxDestinationSize);
367600af5f1SAppaRao Puli                 return;
368600af5f1SAppaRao Puli             }
369600af5f1SAppaRao Puli 
370dd28ba82SAppaRao Puli             if (regPrefixes && msgIds)
371dd28ba82SAppaRao Puli             {
37226f6976fSEd Tanous                 if (!regPrefixes->empty() && !msgIds->empty())
373dd28ba82SAppaRao Puli                 {
374bd79bce8SPatrick Williams                     messages::propertyValueConflict(
375bd79bce8SPatrick Williams                         asyncResp->res, "MessageIds", "RegistryPrefixes");
376dd28ba82SAppaRao Puli                     return;
377dd28ba82SAppaRao Puli                 }
378dd28ba82SAppaRao Puli             }
379dd28ba82SAppaRao Puli 
3806fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
381a716aa74SEd Tanous                 boost::urls::parse_absolute_uri(destUrl);
382a716aa74SEd Tanous             if (!url)
383e5aaf047SAppaRao Puli             {
384bd79bce8SPatrick Williams                 BMCWEB_LOG_WARNING(
385bd79bce8SPatrick Williams                     "Failed to validate and split destination url");
386e5aaf047SAppaRao Puli                 messages::propertyValueFormatError(asyncResp->res, destUrl,
387e5aaf047SAppaRao Puli                                                    "Destination");
388e5aaf047SAppaRao Puli                 return;
389e5aaf047SAppaRao Puli             }
390a716aa74SEd Tanous             url->normalize();
391b07942e3SGeorge Liu 
392b07942e3SGeorge Liu             // port_number returns zero if it is not a valid representable port
393b07942e3SGeorge Liu             if (url->has_port() && url->port_number() == 0)
394b07942e3SGeorge Liu             {
395b07942e3SGeorge Liu                 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
396b07942e3SGeorge Liu                                    url->port());
397b07942e3SGeorge Liu                 messages::propertyValueFormatError(asyncResp->res, destUrl,
398b07942e3SGeorge Liu                                                    "Destination");
399b07942e3SGeorge Liu                 return;
400b07942e3SGeorge Liu             }
401b07942e3SGeorge Liu 
402a716aa74SEd Tanous             crow::utility::setProtocolDefaults(*url, protocol);
403a716aa74SEd Tanous             crow::utility::setPortDefaults(*url);
404a716aa74SEd Tanous 
405a716aa74SEd Tanous             if (url->path().empty())
406a716aa74SEd Tanous             {
407a716aa74SEd Tanous                 url->set_path("/");
408a716aa74SEd Tanous             }
409a716aa74SEd Tanous 
410a716aa74SEd Tanous             if (url->has_userinfo())
411a716aa74SEd Tanous             {
412a716aa74SEd Tanous                 messages::propertyValueFormatError(asyncResp->res, destUrl,
413a716aa74SEd Tanous                                                    "Destination");
414a716aa74SEd Tanous                 return;
415a716aa74SEd Tanous             }
416b52664e2SAppaRao Puli 
4173d30708fSChicago Duan             if (protocol == "SNMPv2c")
4183d30708fSChicago Duan             {
4193d30708fSChicago Duan                 if (context)
4203d30708fSChicago Duan                 {
4213d30708fSChicago Duan                     messages::propertyValueConflict(asyncResp->res, "Context",
4223d30708fSChicago Duan                                                     "Protocol");
4233d30708fSChicago Duan                     return;
4243d30708fSChicago Duan                 }
4253d30708fSChicago Duan                 if (eventFormatType2)
4263d30708fSChicago Duan                 {
427bd79bce8SPatrick Williams                     messages::propertyValueConflict(
428bd79bce8SPatrick Williams                         asyncResp->res, "EventFormatType", "Protocol");
4293d30708fSChicago Duan                     return;
4303d30708fSChicago Duan                 }
4313d30708fSChicago Duan                 if (retryPolicy)
4323d30708fSChicago Duan                 {
433bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
434bd79bce8SPatrick Williams                                                     "RetryPolicy", "Protocol");
4353d30708fSChicago Duan                     return;
4363d30708fSChicago Duan                 }
4375064a25bSMyung Bae                 if (sendHeartbeat)
4385064a25bSMyung Bae                 {
4395064a25bSMyung Bae                     messages::propertyValueConflict(
4405064a25bSMyung Bae                         asyncResp->res, "SendHeartbeat", "Protocol");
4415064a25bSMyung Bae                     return;
4425064a25bSMyung Bae                 }
4435064a25bSMyung Bae                 if (hbIntervalMinutes)
4445064a25bSMyung Bae                 {
4455064a25bSMyung Bae                     messages::propertyValueConflict(
4465064a25bSMyung Bae                         asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
4475064a25bSMyung Bae                     return;
4485064a25bSMyung Bae                 }
4493d30708fSChicago Duan                 if (msgIds)
4503d30708fSChicago Duan                 {
451bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
452bd79bce8SPatrick Williams                                                     "MessageIds", "Protocol");
4533d30708fSChicago Duan                     return;
4543d30708fSChicago Duan                 }
4553d30708fSChicago Duan                 if (regPrefixes)
4563d30708fSChicago Duan                 {
457bd79bce8SPatrick Williams                     messages::propertyValueConflict(
458bd79bce8SPatrick Williams                         asyncResp->res, "RegistryPrefixes", "Protocol");
4593d30708fSChicago Duan                     return;
4603d30708fSChicago Duan                 }
4613d30708fSChicago Duan                 if (resTypes)
4623d30708fSChicago Duan                 {
463bd79bce8SPatrick Williams                     messages::propertyValueConflict(
464bd79bce8SPatrick Williams                         asyncResp->res, "ResourceTypes", "Protocol");
4653d30708fSChicago Duan                     return;
4663d30708fSChicago Duan                 }
4673d30708fSChicago Duan                 if (headers)
4683d30708fSChicago Duan                 {
469bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
470bd79bce8SPatrick Williams                                                     "HttpHeaders", "Protocol");
4713d30708fSChicago Duan                     return;
4723d30708fSChicago Duan                 }
4733d30708fSChicago Duan                 if (mrdJsonArray)
4743d30708fSChicago Duan                 {
4753d30708fSChicago Duan                     messages::propertyValueConflict(
4763d30708fSChicago Duan                         asyncResp->res, "MetricReportDefinitions", "Protocol");
4773d30708fSChicago Duan                     return;
4783d30708fSChicago Duan                 }
479a716aa74SEd Tanous                 if (url->scheme() != "snmp")
480a716aa74SEd Tanous                 {
481bd79bce8SPatrick Williams                     messages::propertyValueConflict(asyncResp->res,
482bd79bce8SPatrick Williams                                                     "Destination", "Protocol");
4833d30708fSChicago Duan                     return;
4843d30708fSChicago Duan                 }
4853d30708fSChicago Duan 
486a716aa74SEd Tanous                 addSnmpTrapClient(asyncResp, url->host_address(),
487a716aa74SEd Tanous                                   url->port_number());
488a716aa74SEd Tanous                 return;
489b52664e2SAppaRao Puli             }
4903d30708fSChicago Duan 
491a716aa74SEd Tanous             std::shared_ptr<Subscription> subValue =
49221a94d5cSMyung Bae                 std::make_shared<Subscription>(
4935fe4ef35SMyung Bae                     std::make_shared<persistent_data::UserSubscription>(), *url,
4945fe4ef35SMyung Bae                     app.ioContext());
495e5aaf047SAppaRao Puli 
496e5aaf047SAppaRao Puli             if (subscriptionType)
497e5aaf047SAppaRao Puli             {
498e5aaf047SAppaRao Puli                 if (*subscriptionType != "RedfishEvent")
499e5aaf047SAppaRao Puli                 {
500fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
501fffb8c1fSEd Tanous                         asyncResp->res, *subscriptionType, "SubscriptionType");
502e5aaf047SAppaRao Puli                     return;
503e5aaf047SAppaRao Puli                 }
5045fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = *subscriptionType;
505e5aaf047SAppaRao Puli             }
506e5aaf047SAppaRao Puli             else
507e5aaf047SAppaRao Puli             {
5084b712a29SEd Tanous                 // Default
5095fe4ef35SMyung Bae                 subValue->userSub->subscriptionType = "RedfishEvent";
510e5aaf047SAppaRao Puli             }
511e5aaf047SAppaRao Puli 
512e5aaf047SAppaRao Puli             if (protocol != "Redfish")
513e5aaf047SAppaRao Puli             {
514e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, protocol,
515e5aaf047SAppaRao Puli                                                  "Protocol");
516e5aaf047SAppaRao Puli                 return;
517e5aaf047SAppaRao Puli             }
5185fe4ef35SMyung Bae             subValue->userSub->protocol = protocol;
519e5aaf047SAppaRao Puli 
52019bb362bSEd Tanous             if (verifyCertificate)
52119bb362bSEd Tanous             {
5225fe4ef35SMyung Bae                 subValue->userSub->verifyCertificate = *verifyCertificate;
52319bb362bSEd Tanous             }
52419bb362bSEd Tanous 
52523a21a1cSEd Tanous             if (eventFormatType2)
526e5aaf047SAppaRao Puli             {
527bd79bce8SPatrick Williams                 if (std::ranges::find(supportedEvtFormatTypes,
528bd79bce8SPatrick Williams                                       *eventFormatType2) ==
5293544d2a7SEd Tanous                     supportedEvtFormatTypes.end())
530e5aaf047SAppaRao Puli                 {
531fffb8c1fSEd Tanous                     messages::propertyValueNotInList(
532fffb8c1fSEd Tanous                         asyncResp->res, *eventFormatType2, "EventFormatType");
533e5aaf047SAppaRao Puli                     return;
534e5aaf047SAppaRao Puli                 }
5355fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = *eventFormatType2;
536e5aaf047SAppaRao Puli             }
537e5aaf047SAppaRao Puli             else
538e5aaf047SAppaRao Puli             {
539e5aaf047SAppaRao Puli                 // If not specified, use default "Event"
5405fe4ef35SMyung Bae                 subValue->userSub->eventFormatType = "Event";
541e5aaf047SAppaRao Puli             }
542e5aaf047SAppaRao Puli 
543e5aaf047SAppaRao Puli             if (context)
544e5aaf047SAppaRao Puli             {
5458ece0e45SEd Tanous                 // This value is selected arbitrarily.
546600af5f1SAppaRao Puli                 constexpr const size_t maxContextSize = 256;
547600af5f1SAppaRao Puli                 if (context->size() > maxContextSize)
548600af5f1SAppaRao Puli                 {
549600af5f1SAppaRao Puli                     messages::stringValueTooLong(asyncResp->res, "Context",
550600af5f1SAppaRao Puli                                                  maxContextSize);
551600af5f1SAppaRao Puli                     return;
552600af5f1SAppaRao Puli                 }
5535fe4ef35SMyung Bae                 subValue->userSub->customText = *context;
554e5aaf047SAppaRao Puli             }
555e5aaf047SAppaRao Puli 
556e5aaf047SAppaRao Puli             if (headers)
557e5aaf047SAppaRao Puli             {
558600af5f1SAppaRao Puli                 size_t cumulativeLen = 0;
559600af5f1SAppaRao Puli 
56078d4ec4fSEd Tanous                 for (const nlohmann::json::object_t& headerChunk : *headers)
561601c71aeSEd Tanous                 {
56278d4ec4fSEd Tanous                     for (const auto& item : headerChunk)
56378d4ec4fSEd Tanous                     {
56478d4ec4fSEd Tanous                         const std::string* value =
56578d4ec4fSEd Tanous                             item.second.get_ptr<const std::string*>();
56678d4ec4fSEd Tanous                         if (value == nullptr)
56778d4ec4fSEd Tanous                         {
56878d4ec4fSEd Tanous                             messages::propertyValueFormatError(
56978d4ec4fSEd Tanous                                 asyncResp->res, item.second,
57078d4ec4fSEd Tanous                                 "HttpHeaders/" + item.first);
57178d4ec4fSEd Tanous                             return;
57278d4ec4fSEd Tanous                         }
57378d4ec4fSEd Tanous                         // Adding a new json value is the size of the key, +
57478d4ec4fSEd Tanous                         // the size of the value + 2 * 2 quotes for each, +
57578d4ec4fSEd Tanous                         // the colon and space between. example:
57678d4ec4fSEd Tanous                         // "key": "value"
57778d4ec4fSEd Tanous                         cumulativeLen += item.first.size() + value->size() + 6;
578600af5f1SAppaRao Puli                         // This value is selected to mirror http_connection.hpp
579600af5f1SAppaRao Puli                         constexpr const uint16_t maxHeaderSizeED = 8096;
580600af5f1SAppaRao Puli                         if (cumulativeLen > maxHeaderSizeED)
581600af5f1SAppaRao Puli                         {
58278d4ec4fSEd Tanous                             messages::arraySizeTooLong(
58378d4ec4fSEd Tanous                                 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
584600af5f1SAppaRao Puli                             return;
585600af5f1SAppaRao Puli                         }
5865fe4ef35SMyung Bae                         subValue->userSub->httpHeaders.set(item.first, *value);
587601c71aeSEd Tanous                     }
588601c71aeSEd Tanous                 }
589e5aaf047SAppaRao Puli             }
590e5aaf047SAppaRao Puli 
591e5aaf047SAppaRao Puli             if (regPrefixes)
592e5aaf047SAppaRao Puli             {
593e5aaf047SAppaRao Puli                 for (const std::string& it : *regPrefixes)
594e5aaf047SAppaRao Puli                 {
5953544d2a7SEd Tanous                     if (std::ranges::find(supportedRegPrefixes, it) ==
5963544d2a7SEd Tanous                         supportedRegPrefixes.end())
597e5aaf047SAppaRao Puli                     {
598fffb8c1fSEd Tanous                         messages::propertyValueNotInList(asyncResp->res, it,
599fffb8c1fSEd Tanous                                                          "RegistryPrefixes");
600e5aaf047SAppaRao Puli                         return;
601e5aaf047SAppaRao Puli                     }
602e5aaf047SAppaRao Puli                 }
6035fe4ef35SMyung Bae                 subValue->userSub->registryPrefixes = *regPrefixes;
604e5aaf047SAppaRao Puli             }
605e5aaf047SAppaRao Puli 
606a14c9113SEd Tanous             if (originResources)
607a14c9113SEd Tanous             {
6085fe4ef35SMyung Bae                 subValue->userSub->originResources = *originResources;
609a14c9113SEd Tanous             }
610a14c9113SEd Tanous 
611e56f254cSSunitha Harish             if (resTypes)
612e56f254cSSunitha Harish             {
613e56f254cSSunitha Harish                 for (const std::string& it : *resTypes)
614e56f254cSSunitha Harish                 {
6153544d2a7SEd Tanous                     if (std::ranges::find(supportedResourceTypes, it) ==
6163544d2a7SEd Tanous                         supportedResourceTypes.end())
617e56f254cSSunitha Harish                     {
618e56f254cSSunitha Harish                         messages::propertyValueNotInList(asyncResp->res, it,
619e56f254cSSunitha Harish                                                          "ResourceTypes");
620e56f254cSSunitha Harish                         return;
621e56f254cSSunitha Harish                     }
622e56f254cSSunitha Harish                 }
6235fe4ef35SMyung Bae                 subValue->userSub->resourceTypes = *resTypes;
624e56f254cSSunitha Harish             }
625e56f254cSSunitha Harish 
626e5aaf047SAppaRao Puli             if (msgIds)
627e5aaf047SAppaRao Puli             {
628b304bd79SP Dheeraj Srujan Kumar                 std::vector<std::string> registryPrefix;
629b304bd79SP Dheeraj Srujan Kumar 
6307e860f15SJohn Edward Broadbent                 // If no registry prefixes are mentioned, consider all
6317e860f15SJohn Edward Broadbent                 // supported prefixes
6325fe4ef35SMyung Bae                 if (subValue->userSub->registryPrefixes.empty())
633b304bd79SP Dheeraj Srujan Kumar                 {
634b304bd79SP Dheeraj Srujan Kumar                     registryPrefix.assign(supportedRegPrefixes.begin(),
635b304bd79SP Dheeraj Srujan Kumar                                           supportedRegPrefixes.end());
636b304bd79SP Dheeraj Srujan Kumar                 }
637b304bd79SP Dheeraj Srujan Kumar                 else
638b304bd79SP Dheeraj Srujan Kumar                 {
6395fe4ef35SMyung Bae                     registryPrefix = subValue->userSub->registryPrefixes;
640b304bd79SP Dheeraj Srujan Kumar                 }
641b304bd79SP Dheeraj Srujan Kumar 
642b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& id : *msgIds)
643b304bd79SP Dheeraj Srujan Kumar                 {
644b304bd79SP Dheeraj Srujan Kumar                     bool validId = false;
645b304bd79SP Dheeraj Srujan Kumar 
646b304bd79SP Dheeraj Srujan Kumar                     // Check for Message ID in each of the selected Registry
647b304bd79SP Dheeraj Srujan Kumar                     for (const std::string& it : registryPrefix)
648b304bd79SP Dheeraj Srujan Kumar                     {
649fffb8c1fSEd Tanous                         const std::span<const redfish::registries::MessageEntry>
650fffb8c1fSEd Tanous                             registry =
651fffb8c1fSEd Tanous                                 redfish::registries::getRegistryFromPrefix(it);
652b304bd79SP Dheeraj Srujan Kumar 
6533544d2a7SEd Tanous                         if (std::ranges::any_of(
6543544d2a7SEd Tanous                                 registry,
655fffb8c1fSEd Tanous                                 [&id](const redfish::registries::MessageEntry&
656fffb8c1fSEd Tanous                                           messageEntry) {
65755f79e6fSEd Tanous                                     return id == messageEntry.first;
658b304bd79SP Dheeraj Srujan Kumar                                 }))
659b304bd79SP Dheeraj Srujan Kumar                         {
660b304bd79SP Dheeraj Srujan Kumar                             validId = true;
661b304bd79SP Dheeraj Srujan Kumar                             break;
662b304bd79SP Dheeraj Srujan Kumar                         }
663b304bd79SP Dheeraj Srujan Kumar                     }
664b304bd79SP Dheeraj Srujan Kumar 
665b304bd79SP Dheeraj Srujan Kumar                     if (!validId)
666b304bd79SP Dheeraj Srujan Kumar                     {
667b304bd79SP Dheeraj Srujan Kumar                         messages::propertyValueNotInList(asyncResp->res, id,
668b304bd79SP Dheeraj Srujan Kumar                                                          "MessageIds");
669b304bd79SP Dheeraj Srujan Kumar                         return;
670b304bd79SP Dheeraj Srujan Kumar                     }
671b304bd79SP Dheeraj Srujan Kumar                 }
672b304bd79SP Dheeraj Srujan Kumar 
6735fe4ef35SMyung Bae                 subValue->userSub->registryMsgIds = *msgIds;
674e5aaf047SAppaRao Puli             }
675e5aaf047SAppaRao Puli 
676e5aaf047SAppaRao Puli             if (retryPolicy)
677e5aaf047SAppaRao Puli             {
6783544d2a7SEd Tanous                 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
6793544d2a7SEd Tanous                     supportedRetryPolicies.end())
680e5aaf047SAppaRao Puli                 {
681bd79bce8SPatrick Williams                     messages::propertyValueNotInList(
682bd79bce8SPatrick Williams                         asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
683e5aaf047SAppaRao Puli                     return;
684e5aaf047SAppaRao Puli                 }
6855fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = *retryPolicy;
686e5aaf047SAppaRao Puli             }
687e5aaf047SAppaRao Puli             else
688e5aaf047SAppaRao Puli             {
689e5aaf047SAppaRao Puli                 // Default "TerminateAfterRetries"
6905fe4ef35SMyung Bae                 subValue->userSub->retryPolicy = "TerminateAfterRetries";
691e5aaf047SAppaRao Puli             }
6925064a25bSMyung Bae             if (sendHeartbeat)
6935064a25bSMyung Bae             {
6945064a25bSMyung Bae                 subValue->userSub->sendHeartbeat = *sendHeartbeat;
6955064a25bSMyung Bae             }
6965064a25bSMyung Bae             if (hbIntervalMinutes)
6975064a25bSMyung Bae             {
6985064a25bSMyung Bae                 if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
6995064a25bSMyung Bae                 {
7005064a25bSMyung Bae                     messages::propertyValueOutOfRange(
7015064a25bSMyung Bae                         asyncResp->res, *hbIntervalMinutes,
7025064a25bSMyung Bae                         "HeartbeatIntervalMinutes");
7035064a25bSMyung Bae                     return;
7045064a25bSMyung Bae                 }
7055064a25bSMyung Bae                 subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
7065064a25bSMyung Bae             }
707e5aaf047SAppaRao Puli 
708144b6318SAppaRao Puli             if (mrdJsonArray)
709156d6b00SAppaRao Puli             {
71078d4ec4fSEd Tanous                 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
711144b6318SAppaRao Puli                 {
712144b6318SAppaRao Puli                     std::string mrdUri;
713ea2e6eecSWilly Tu 
71478d4ec4fSEd Tanous                     if (!json_util::readJsonObject(mrdObj, asyncResp->res,
71578d4ec4fSEd Tanous                                                    "@odata.id", mrdUri))
716ea2e6eecSWilly Tu 
717144b6318SAppaRao Puli                     {
718144b6318SAppaRao Puli                         return;
719144b6318SAppaRao Puli                     }
7205fe4ef35SMyung Bae                     subValue->userSub->metricReportDefinitions.emplace_back(
7214b712a29SEd Tanous                         mrdUri);
722144b6318SAppaRao Puli                 }
723156d6b00SAppaRao Puli             }
724156d6b00SAppaRao Puli 
725b52664e2SAppaRao Puli             std::string id =
726bd79bce8SPatrick Williams                 EventServiceManager::getInstance().addPushSubscription(
727bd79bce8SPatrick Williams                     subValue);
728b52664e2SAppaRao Puli             if (id.empty())
729e5aaf047SAppaRao Puli             {
730e5aaf047SAppaRao Puli                 messages::internalError(asyncResp->res);
731e5aaf047SAppaRao Puli                 return;
732e5aaf047SAppaRao Puli             }
733e5aaf047SAppaRao Puli 
734e5aaf047SAppaRao Puli             messages::created(asyncResp->res);
735e5aaf047SAppaRao Puli             asyncResp->res.addHeader(
736e5aaf047SAppaRao Puli                 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
737fb546105SMyung Bae 
738fb546105SMyung Bae             // schedule a heartbeat
739fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
740fb546105SMyung Bae             {
741fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
742fb546105SMyung Bae             }
7437e860f15SJohn Edward Broadbent         });
744e5aaf047SAppaRao Puli }
745e5aaf047SAppaRao Puli 
7467e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
747e5aaf047SAppaRao Puli {
7489d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
749ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
7507e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
75145ca1b86SEd Tanous             [&app](const crow::Request& req,
7527e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
7537e860f15SJohn Edward Broadbent                    const std::string& param) {
7543ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
75545ca1b86SEd Tanous                 {
75645ca1b86SEd Tanous                     return;
75745ca1b86SEd Tanous                 }
7583d30708fSChicago Duan 
7593d30708fSChicago Duan                 if (param.starts_with("snmp"))
7603d30708fSChicago Duan                 {
7613d30708fSChicago Duan                     getSnmpTrapClient(asyncResp, param);
7623d30708fSChicago Duan                     return;
7633d30708fSChicago Duan                 }
7643d30708fSChicago Duan 
765b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
7667e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
767b52664e2SAppaRao Puli                 if (subValue == nullptr)
768e5aaf047SAppaRao Puli                 {
769bd79bce8SPatrick Williams                     asyncResp->res.result(
770bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
771e5aaf047SAppaRao Puli                     return;
772e5aaf047SAppaRao Puli                 }
7737e860f15SJohn Edward Broadbent                 const std::string& id = param;
774e5aaf047SAppaRao Puli 
7754b712a29SEd Tanous                 const persistent_data::UserSubscription& userSub =
7765fe4ef35SMyung Bae                     *subValue->userSub;
777e56f254cSSunitha Harish 
7784b712a29SEd Tanous                 nlohmann::json& jVal = asyncResp->res.jsonValue;
7794b712a29SEd Tanous                 jVal["@odata.type"] =
7804b712a29SEd Tanous                     "#EventDestination.v1_14_1.EventDestination";
7814b712a29SEd Tanous                 jVal["Protocol"] =
7824b712a29SEd Tanous                     event_destination::EventDestinationProtocol::Redfish;
7834b712a29SEd Tanous                 jVal["@odata.id"] = boost::urls::format(
7844b712a29SEd Tanous                     "/redfish/v1/EventService/Subscriptions/{}", id);
7854b712a29SEd Tanous                 jVal["Id"] = id;
7864b712a29SEd Tanous                 jVal["Name"] = "Event Destination " + id;
7874b712a29SEd Tanous                 jVal["Destination"] = userSub.destinationUrl;
7884b712a29SEd Tanous                 jVal["Context"] = userSub.customText;
7894b712a29SEd Tanous                 jVal["SubscriptionType"] = userSub.subscriptionType;
7904b712a29SEd Tanous                 jVal["HttpHeaders"] = nlohmann::json::array();
7914b712a29SEd Tanous                 jVal["EventFormatType"] = userSub.eventFormatType;
7924b712a29SEd Tanous                 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
7934b712a29SEd Tanous                 jVal["ResourceTypes"] = userSub.resourceTypes;
7944b712a29SEd Tanous 
7954b712a29SEd Tanous                 jVal["MessageIds"] = userSub.registryMsgIds;
7964b712a29SEd Tanous                 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
7975064a25bSMyung Bae                 jVal["SendHeartbeat"] = userSub.sendHeartbeat;
7985064a25bSMyung Bae                 jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
7994b712a29SEd Tanous                 jVal["VerifyCertificate"] = userSub.verifyCertificate;
800144b6318SAppaRao Puli 
8011476687dSEd Tanous                 nlohmann::json::array_t mrdJsonArray;
8024b712a29SEd Tanous                 for (const auto& mdrUri : userSub.metricReportDefinitions)
803144b6318SAppaRao Puli                 {
8041476687dSEd Tanous                     nlohmann::json::object_t mdr;
8051476687dSEd Tanous                     mdr["@odata.id"] = mdrUri;
8061476687dSEd Tanous                     mrdJsonArray.emplace_back(std::move(mdr));
807144b6318SAppaRao Puli                 }
8084b712a29SEd Tanous                 jVal["MetricReportDefinitions"] = mrdJsonArray;
8097e860f15SJohn Edward Broadbent             });
8109d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
811ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
812ed398213SEd Tanous         // ConfigureSelf
8137eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
814ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
815432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
8167e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
81745ca1b86SEd Tanous             [&app](const crow::Request& req,
8187e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8197e860f15SJohn Edward Broadbent                    const std::string& param) {
8203ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
82145ca1b86SEd Tanous                 {
82245ca1b86SEd Tanous                     return;
82345ca1b86SEd Tanous                 }
824b52664e2SAppaRao Puli                 std::shared_ptr<Subscription> subValue =
8257e860f15SJohn Edward Broadbent                     EventServiceManager::getInstance().getSubscription(param);
826b52664e2SAppaRao Puli                 if (subValue == nullptr)
827e5aaf047SAppaRao Puli                 {
828bd79bce8SPatrick Williams                     asyncResp->res.result(
829bd79bce8SPatrick Williams                         boost::beast::http::status::not_found);
830e5aaf047SAppaRao Puli                     return;
831e5aaf047SAppaRao Puli                 }
832e5aaf047SAppaRao Puli 
833e5aaf047SAppaRao Puli                 std::optional<std::string> context;
834e5aaf047SAppaRao Puli                 std::optional<std::string> retryPolicy;
8355064a25bSMyung Bae                 std::optional<bool> sendHeartbeat;
8365064a25bSMyung Bae                 std::optional<uint64_t> hbIntervalMinutes;
83719bb362bSEd Tanous                 std::optional<bool> verifyCertificate;
83878d4ec4fSEd Tanous                 std::optional<std::vector<nlohmann::json::object_t>> headers;
839e5aaf047SAppaRao Puli 
840afc474aeSMyung Bae                 if (!json_util::readJsonPatch( //
841afc474aeSMyung Bae                         req, asyncResp->res, //
842afc474aeSMyung Bae                         "Context", context, //
843afc474aeSMyung Bae                         "DeliveryRetryPolicy", retryPolicy, //
8445064a25bSMyung Bae                         "HeartbeatIntervalMinutes", hbIntervalMinutes, //
845afc474aeSMyung Bae                         "HttpHeaders", headers, //
8465064a25bSMyung Bae                         "SendHeartbeat", sendHeartbeat, //
847afc474aeSMyung Bae                         "VerifyCertificate", verifyCertificate //
848afc474aeSMyung Bae                         ))
849e5aaf047SAppaRao Puli                 {
850e5aaf047SAppaRao Puli                     return;
851e5aaf047SAppaRao Puli                 }
852e5aaf047SAppaRao Puli 
853e5aaf047SAppaRao Puli                 if (context)
854e5aaf047SAppaRao Puli                 {
8555fe4ef35SMyung Bae                     subValue->userSub->customText = *context;
856e5aaf047SAppaRao Puli                 }
857e5aaf047SAppaRao Puli 
858e5aaf047SAppaRao Puli                 if (headers)
859e5aaf047SAppaRao Puli                 {
860601c71aeSEd Tanous                     boost::beast::http::fields fields;
86178d4ec4fSEd Tanous                     for (const nlohmann::json::object_t& headerChunk : *headers)
862601c71aeSEd Tanous                     {
86378d4ec4fSEd Tanous                         for (const auto& it : headerChunk)
864601c71aeSEd Tanous                         {
865601c71aeSEd Tanous                             const std::string* value =
86678d4ec4fSEd Tanous                                 it.second.get_ptr<const std::string*>();
867601c71aeSEd Tanous                             if (value == nullptr)
868601c71aeSEd Tanous                             {
869601c71aeSEd Tanous                                 messages::propertyValueFormatError(
87078d4ec4fSEd Tanous                                     asyncResp->res, it.second,
87178d4ec4fSEd Tanous                                     "HttpHeaders/" + it.first);
872601c71aeSEd Tanous                                 return;
873601c71aeSEd Tanous                             }
87478d4ec4fSEd Tanous                             fields.set(it.first, *value);
875601c71aeSEd Tanous                         }
876601c71aeSEd Tanous                     }
8775fe4ef35SMyung Bae                     subValue->userSub->httpHeaders = std::move(fields);
878e5aaf047SAppaRao Puli                 }
879e5aaf047SAppaRao Puli 
880e5aaf047SAppaRao Puli                 if (retryPolicy)
881e5aaf047SAppaRao Puli                 {
882bd79bce8SPatrick Williams                     if (std::ranges::find(supportedRetryPolicies,
883bd79bce8SPatrick Williams                                           *retryPolicy) ==
8843544d2a7SEd Tanous                         supportedRetryPolicies.end())
885e5aaf047SAppaRao Puli                     {
886bd79bce8SPatrick Williams                         messages::propertyValueNotInList(asyncResp->res,
887bd79bce8SPatrick Williams                                                          *retryPolicy,
888e5aaf047SAppaRao Puli                                                          "DeliveryRetryPolicy");
889e5aaf047SAppaRao Puli                         return;
890e5aaf047SAppaRao Puli                     }
8915fe4ef35SMyung Bae                     subValue->userSub->retryPolicy = *retryPolicy;
892e5aaf047SAppaRao Puli                 }
893e5aaf047SAppaRao Puli 
8945064a25bSMyung Bae                 if (sendHeartbeat)
8955064a25bSMyung Bae                 {
8965064a25bSMyung Bae                     subValue->userSub->sendHeartbeat = *sendHeartbeat;
8975064a25bSMyung Bae                 }
8985064a25bSMyung Bae                 if (hbIntervalMinutes)
8995064a25bSMyung Bae                 {
9005064a25bSMyung Bae                     if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
9015064a25bSMyung Bae                     {
9025064a25bSMyung Bae                         messages::propertyValueOutOfRange(
9035064a25bSMyung Bae                             asyncResp->res, *hbIntervalMinutes,
9045064a25bSMyung Bae                             "HeartbeatIntervalMinutes");
9055064a25bSMyung Bae                         return;
9065064a25bSMyung Bae                     }
9075064a25bSMyung Bae                     subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
9085064a25bSMyung Bae                 }
9095064a25bSMyung Bae 
910fb546105SMyung Bae                 if (hbIntervalMinutes || sendHeartbeat)
911fb546105SMyung Bae                 {
912fb546105SMyung Bae                     // if Heartbeat interval or send heart were changed, cancel
913fb546105SMyung Bae                     // the heartbeat timer if running and start a new heartbeat
914fb546105SMyung Bae                     // if needed
915fb546105SMyung Bae                     subValue->heartbeatParametersChanged();
916fb546105SMyung Bae                 }
917fb546105SMyung Bae 
91819bb362bSEd Tanous                 if (verifyCertificate)
91919bb362bSEd Tanous                 {
9205fe4ef35SMyung Bae                     subValue->userSub->verifyCertificate = *verifyCertificate;
92119bb362bSEd Tanous                 }
92219bb362bSEd Tanous 
923b52664e2SAppaRao Puli                 EventServiceManager::getInstance().updateSubscriptionData();
9247e860f15SJohn Edward Broadbent             });
9259d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
926ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
927ed398213SEd Tanous         // ConfigureSelf
9287eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
929ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
930432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
9317e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
93245ca1b86SEd Tanous             [&app](const crow::Request& req,
9337e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9347e860f15SJohn Edward Broadbent                    const std::string& param) {
9353ba00073SCarson Labrado                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
93645ca1b86SEd Tanous                 {
93745ca1b86SEd Tanous                     return;
93845ca1b86SEd Tanous                 }
9394b712a29SEd Tanous                 EventServiceManager& event = EventServiceManager::getInstance();
9403d30708fSChicago Duan                 if (param.starts_with("snmp"))
9413d30708fSChicago Duan                 {
9423d30708fSChicago Duan                     deleteSnmpTrapClient(asyncResp, param);
9434b712a29SEd Tanous                     event.deleteSubscription(param);
9443d30708fSChicago Duan                     return;
9453d30708fSChicago Duan                 }
9463d30708fSChicago Duan 
9474b712a29SEd Tanous                 if (!event.deleteSubscription(param))
948e5aaf047SAppaRao Puli                 {
9494b712a29SEd Tanous                     messages::resourceNotFound(asyncResp->res,
9504b712a29SEd Tanous                                                "EventDestination", param);
951e5aaf047SAppaRao Puli                     return;
952e5aaf047SAppaRao Puli                 }
9534b712a29SEd Tanous                 messages::success(asyncResp->res);
9547e860f15SJohn Edward Broadbent             });
955e5aaf047SAppaRao Puli }
956e5aaf047SAppaRao Puli 
957e5aaf047SAppaRao Puli } // namespace redfish
958