xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 600af5f1ee6f3a589d5c1a7be31579ca705eef71)
1e5aaf047SAppaRao Puli /*
2e5aaf047SAppaRao Puli // Copyright (c) 2020 Intel Corporation
3e5aaf047SAppaRao Puli //
4e5aaf047SAppaRao Puli // Licensed under the Apache License, Version 2.0 (the "License");
5e5aaf047SAppaRao Puli // you may not use this file except in compliance with the License.
6e5aaf047SAppaRao Puli // You may obtain a copy of the License at
7e5aaf047SAppaRao Puli //
8e5aaf047SAppaRao Puli //      http://www.apache.org/licenses/LICENSE-2.0
9e5aaf047SAppaRao Puli //
10e5aaf047SAppaRao Puli // Unless required by applicable law or agreed to in writing, software
11e5aaf047SAppaRao Puli // distributed under the License is distributed on an "AS IS" BASIS,
12e5aaf047SAppaRao Puli // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e5aaf047SAppaRao Puli // See the License for the specific language governing permissions and
14e5aaf047SAppaRao Puli // limitations under the License.
15e5aaf047SAppaRao Puli */
16e5aaf047SAppaRao Puli #pragma once
173ccb3adbSEd Tanous #include "app.hpp"
18b52664e2SAppaRao Puli #include "event_service_manager.hpp"
193ccb3adbSEd Tanous #include "http/utility.hpp"
203ccb3adbSEd Tanous #include "logging.hpp"
213ccb3adbSEd Tanous #include "query.hpp"
223ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
23e5aaf047SAppaRao Puli 
24601c71aeSEd Tanous #include <boost/beast/http/fields.hpp>
25ed398213SEd Tanous 
261e270c5fSPatrick Williams #include <span>
271e270c5fSPatrick Williams 
28e5aaf047SAppaRao Puli namespace redfish
29e5aaf047SAppaRao Puli {
30e5aaf047SAppaRao Puli 
31156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
32156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
33e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
34b304bd79SP Dheeraj Srujan Kumar     "Base", "OpenBMC", "TaskEvent"};
35e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
36e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
37e5aaf047SAppaRao Puli 
38e56f254cSSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
39e56f254cSSunitha Harish static constexpr const std::array<const char*, 2> supportedResourceTypes = {
40e56f254cSSunitha Harish     "IBMConfigFile", "Task"};
41e56f254cSSunitha Harish #else
42e56f254cSSunitha Harish static constexpr const std::array<const char*, 1> supportedResourceTypes = {
43e56f254cSSunitha Harish     "Task"};
44e56f254cSSunitha Harish #endif
45e56f254cSSunitha Harish 
46e5aaf047SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20;
47e5aaf047SAppaRao Puli 
487e860f15SJohn Edward Broadbent inline void requestRoutesEventService(App& app)
49e5aaf047SAppaRao Puli {
507e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
51ed398213SEd Tanous         .privileges(redfish::privileges::getEventService)
52002d39b4SEd Tanous         .methods(boost::beast::http::verb::get)(
53002d39b4SEd Tanous             [&app](const crow::Request& req,
54002d39b4SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
553ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
5645ca1b86SEd Tanous         {
5745ca1b86SEd Tanous             return;
5845ca1b86SEd Tanous         }
591476687dSEd Tanous 
601476687dSEd Tanous         asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
611476687dSEd Tanous         asyncResp->res.jsonValue["@odata.type"] =
621476687dSEd Tanous             "#EventService.v1_5_0.EventService";
631476687dSEd Tanous         asyncResp->res.jsonValue["Id"] = "EventService";
641476687dSEd Tanous         asyncResp->res.jsonValue["Name"] = "Event Service";
651476687dSEd Tanous         asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
661476687dSEd Tanous             "/redfish/v1/EventService/Subscriptions";
67002d39b4SEd Tanous         asyncResp->res
68002d39b4SEd Tanous             .jsonValue["Actions"]["#EventService.SubmitTestEvent"]["target"] =
691476687dSEd Tanous             "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
70e5aaf047SAppaRao Puli 
7128afb49cSJunLin Chen         const persistent_data::EventServiceConfig eventServiceConfig =
7228afb49cSJunLin Chen             persistent_data::EventServiceStore::getInstance()
7328afb49cSJunLin Chen                 .getEventServiceConfig();
747d1cc387SAppaRao Puli 
757d1cc387SAppaRao Puli         asyncResp->res.jsonValue["Status"]["State"] =
7628afb49cSJunLin Chen             (eventServiceConfig.enabled ? "Enabled" : "Disabled");
77002d39b4SEd Tanous         asyncResp->res.jsonValue["ServiceEnabled"] = eventServiceConfig.enabled;
787e860f15SJohn Edward Broadbent         asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
7928afb49cSJunLin Chen             eventServiceConfig.retryAttempts;
80e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
8128afb49cSJunLin Chen             eventServiceConfig.retryTimeoutInterval;
82002d39b4SEd Tanous         asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
830fda0f12SGeorge Liu         asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
840fda0f12SGeorge Liu         asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
8507941a88SAyushi Smriti 
86613dabeaSEd Tanous         nlohmann::json::object_t supportedSSEFilters;
87613dabeaSEd Tanous         supportedSSEFilters["EventFormatType"] = true;
88613dabeaSEd Tanous         supportedSSEFilters["MessageId"] = true;
89613dabeaSEd Tanous         supportedSSEFilters["MetricReportDefinition"] = true;
90613dabeaSEd Tanous         supportedSSEFilters["RegistryPrefix"] = true;
91613dabeaSEd Tanous         supportedSSEFilters["OriginResource"] = false;
92613dabeaSEd Tanous         supportedSSEFilters["ResourceType"] = false;
9307941a88SAyushi Smriti 
9407941a88SAyushi Smriti         asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
95613dabeaSEd Tanous             std::move(supportedSSEFilters);
967e860f15SJohn Edward Broadbent         });
97e5aaf047SAppaRao Puli 
987e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
99ed398213SEd Tanous         .privileges(redfish::privileges::patchEventService)
1007e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
10145ca1b86SEd Tanous             [&app](const crow::Request& req,
10245ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1033ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
104e5aaf047SAppaRao Puli         {
10545ca1b86SEd Tanous             return;
10645ca1b86SEd Tanous         }
107e5aaf047SAppaRao Puli         std::optional<bool> serviceEnabled;
108e5aaf047SAppaRao Puli         std::optional<uint32_t> retryAttemps;
109e5aaf047SAppaRao Puli         std::optional<uint32_t> retryInterval;
110e5aaf047SAppaRao Puli 
11115ed6780SWilly Tu         if (!json_util::readJsonPatch(
1127e860f15SJohn Edward Broadbent                 req, asyncResp->res, "ServiceEnabled", serviceEnabled,
1137e860f15SJohn Edward Broadbent                 "DeliveryRetryAttempts", retryAttemps,
1147e860f15SJohn Edward Broadbent                 "DeliveryRetryIntervalSeconds", retryInterval))
115e5aaf047SAppaRao Puli         {
116e5aaf047SAppaRao Puli             return;
117e5aaf047SAppaRao Puli         }
118e5aaf047SAppaRao Puli 
11928afb49cSJunLin Chen         persistent_data::EventServiceConfig eventServiceConfig =
12028afb49cSJunLin Chen             persistent_data::EventServiceStore::getInstance()
12128afb49cSJunLin Chen                 .getEventServiceConfig();
1227d1cc387SAppaRao Puli 
123e5aaf047SAppaRao Puli         if (serviceEnabled)
124e5aaf047SAppaRao Puli         {
12528afb49cSJunLin Chen             eventServiceConfig.enabled = *serviceEnabled;
126e5aaf047SAppaRao Puli         }
127e5aaf047SAppaRao Puli 
128e5aaf047SAppaRao Puli         if (retryAttemps)
129e5aaf047SAppaRao Puli         {
130e5aaf047SAppaRao Puli             // Supported range [1-3]
131e5aaf047SAppaRao Puli             if ((*retryAttemps < 1) || (*retryAttemps > 3))
132e5aaf047SAppaRao Puli             {
133e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
134e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryAttemps),
135e5aaf047SAppaRao Puli                     "DeliveryRetryAttempts", "[1-3]");
136e5aaf047SAppaRao Puli             }
137e5aaf047SAppaRao Puli             else
138e5aaf047SAppaRao Puli             {
13928afb49cSJunLin Chen                 eventServiceConfig.retryAttempts = *retryAttemps;
140e5aaf047SAppaRao Puli             }
141e5aaf047SAppaRao Puli         }
142e5aaf047SAppaRao Puli 
143e5aaf047SAppaRao Puli         if (retryInterval)
144e5aaf047SAppaRao Puli         {
14533a32b34SGunnar Mills             // Supported range [5 - 180]
14633a32b34SGunnar Mills             if ((*retryInterval < 5) || (*retryInterval > 180))
147e5aaf047SAppaRao Puli             {
148e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
149e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryInterval),
15033a32b34SGunnar Mills                     "DeliveryRetryIntervalSeconds", "[5-180]");
151e5aaf047SAppaRao Puli             }
152e5aaf047SAppaRao Puli             else
153e5aaf047SAppaRao Puli             {
154002d39b4SEd Tanous                 eventServiceConfig.retryTimeoutInterval = *retryInterval;
155e5aaf047SAppaRao Puli             }
156e5aaf047SAppaRao Puli         }
157e5aaf047SAppaRao Puli 
1587d1cc387SAppaRao Puli         EventServiceManager::getInstance().setEventServiceConfig(
15928afb49cSJunLin Chen             eventServiceConfig);
1607e860f15SJohn Edward Broadbent         });
1610b4bdd93SAppaRao Puli }
1620b4bdd93SAppaRao Puli 
1637e860f15SJohn Edward Broadbent inline void requestRoutesSubmitTestEvent(App& app)
1640b4bdd93SAppaRao Puli {
1657e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
1667e860f15SJohn Edward Broadbent         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
167ed398213SEd Tanous         .privileges(redfish::privileges::postEventService)
1687e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
16945ca1b86SEd Tanous             [&app](const crow::Request& req,
1707e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1713ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
17245ca1b86SEd Tanous         {
17345ca1b86SEd Tanous             return;
17445ca1b86SEd Tanous         }
1756ba8c82eSsunharis_in         if (!EventServiceManager::getInstance().sendTestEventLog())
1766ba8c82eSsunharis_in         {
1776ba8c82eSsunharis_in             messages::serviceDisabled(asyncResp->res,
1786ba8c82eSsunharis_in                                       "/redfish/v1/EventService/");
1796ba8c82eSsunharis_in             return;
1806ba8c82eSsunharis_in         }
1818d1b46d7Szhanghch05         asyncResp->res.result(boost::beast::http::status::no_content);
1827e860f15SJohn Edward Broadbent         });
183e5aaf047SAppaRao Puli }
184e5aaf047SAppaRao Puli 
1857e860f15SJohn Edward Broadbent inline void requestRoutesEventDestinationCollection(App& app)
186e5aaf047SAppaRao Puli {
1871ebe3e41SGayathri Leburu     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
188ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestinationCollection)
1897e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
19045ca1b86SEd Tanous             [&app](const crow::Request& req,
1917e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1923ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
19345ca1b86SEd Tanous         {
19445ca1b86SEd Tanous             return;
19545ca1b86SEd Tanous         }
1961476687dSEd Tanous         asyncResp->res.jsonValue["@odata.type"] =
1971476687dSEd Tanous             "#EventDestinationCollection.EventDestinationCollection";
1981476687dSEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1991476687dSEd Tanous             "/redfish/v1/EventService/Subscriptions";
200002d39b4SEd Tanous         asyncResp->res.jsonValue["Name"] = "Event Destination Collections";
201e5aaf047SAppaRao Puli 
202002d39b4SEd Tanous         nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
203e5aaf047SAppaRao Puli 
204b52664e2SAppaRao Puli         std::vector<std::string> subscripIds =
205b52664e2SAppaRao Puli             EventServiceManager::getInstance().getAllIDs();
206b52664e2SAppaRao Puli         memberArray = nlohmann::json::array();
207002d39b4SEd Tanous         asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
208b52664e2SAppaRao Puli 
209b52664e2SAppaRao Puli         for (const std::string& id : subscripIds)
210e5aaf047SAppaRao Puli         {
2111476687dSEd Tanous             nlohmann::json::object_t member;
21289492a15SPatrick Williams             member["@odata.id"] = "/redfish/v1/EventService/Subscriptions/" +
21389492a15SPatrick Williams                                   id;
214b2ba3072SPatrick Williams             memberArray.emplace_back(std::move(member));
215e5aaf047SAppaRao Puli         }
2167e860f15SJohn Edward Broadbent         });
2177e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
2187eeafa76SAbhishek Patel         .privileges(redfish::privileges::postEventDestinationCollection)
219002d39b4SEd Tanous         .methods(boost::beast::http::verb::post)(
220002d39b4SEd Tanous             [&app](const crow::Request& req,
2217e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2223ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
22345ca1b86SEd Tanous         {
22445ca1b86SEd Tanous             return;
22545ca1b86SEd Tanous         }
226fffb8c1fSEd Tanous         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
227fffb8c1fSEd Tanous             maxNoOfSubscriptions)
228e5aaf047SAppaRao Puli         {
229e5aaf047SAppaRao Puli             messages::eventSubscriptionLimitExceeded(asyncResp->res);
230e5aaf047SAppaRao Puli             return;
231e5aaf047SAppaRao Puli         }
232e5aaf047SAppaRao Puli         std::string destUrl;
233e5aaf047SAppaRao Puli         std::string protocol;
234e5aaf047SAppaRao Puli         std::optional<std::string> context;
235e5aaf047SAppaRao Puli         std::optional<std::string> subscriptionType;
23623a21a1cSEd Tanous         std::optional<std::string> eventFormatType2;
237e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
238e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> msgIds;
239e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> regPrefixes;
240e56f254cSSunitha Harish         std::optional<std::vector<std::string>> resTypes;
241e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
242144b6318SAppaRao Puli         std::optional<std::vector<nlohmann::json>> mrdJsonArray;
243e5aaf047SAppaRao Puli 
24415ed6780SWilly Tu         if (!json_util::readJsonPatch(
245002d39b4SEd Tanous                 req, asyncResp->res, "Destination", destUrl, "Context", context,
246002d39b4SEd Tanous                 "Protocol", protocol, "SubscriptionType", subscriptionType,
247002d39b4SEd Tanous                 "EventFormatType", eventFormatType2, "HttpHeaders", headers,
248002d39b4SEd Tanous                 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
249002d39b4SEd Tanous                 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
250002d39b4SEd Tanous                 mrdJsonArray, "ResourceTypes", resTypes))
251e5aaf047SAppaRao Puli         {
252e5aaf047SAppaRao Puli             return;
253e5aaf047SAppaRao Puli         }
254e5aaf047SAppaRao Puli 
255*600af5f1SAppaRao Puli         // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
256*600af5f1SAppaRao Puli         static constexpr const uint16_t maxDestinationSize = 2000;
257*600af5f1SAppaRao Puli         if (destUrl.size() > maxDestinationSize)
258*600af5f1SAppaRao Puli         {
259*600af5f1SAppaRao Puli             messages::stringValueTooLong(asyncResp->res, "Destination",
260*600af5f1SAppaRao Puli                                          maxDestinationSize);
261*600af5f1SAppaRao Puli             return;
262*600af5f1SAppaRao Puli         }
263*600af5f1SAppaRao Puli 
264dd28ba82SAppaRao Puli         if (regPrefixes && msgIds)
265dd28ba82SAppaRao Puli         {
26626f6976fSEd Tanous             if (!regPrefixes->empty() && !msgIds->empty())
267dd28ba82SAppaRao Puli             {
268002d39b4SEd Tanous                 messages::propertyValueConflict(asyncResp->res, "MessageIds",
269002d39b4SEd Tanous                                                 "RegistryPrefixes");
270dd28ba82SAppaRao Puli                 return;
271dd28ba82SAppaRao Puli             }
272dd28ba82SAppaRao Puli         }
273dd28ba82SAppaRao Puli 
274eb1c47d3SEd Tanous         std::string host;
275eb1c47d3SEd Tanous         std::string urlProto;
276eb1c47d3SEd Tanous         uint16_t port = 0;
277eb1c47d3SEd Tanous         std::string path;
278eb1c47d3SEd Tanous 
279002d39b4SEd Tanous         if (!crow::utility::validateAndSplitUrl(destUrl, urlProto, host, port,
280002d39b4SEd Tanous                                                 path))
281e5aaf047SAppaRao Puli         {
282eb1c47d3SEd Tanous             BMCWEB_LOG_WARNING
283eb1c47d3SEd Tanous                 << "Failed to validate and split destination url";
284e5aaf047SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
285e5aaf047SAppaRao Puli                                                "Destination");
286e5aaf047SAppaRao Puli             return;
287e5aaf047SAppaRao Puli         }
288b52664e2SAppaRao Puli 
289b52664e2SAppaRao Puli         if (path.empty())
290b52664e2SAppaRao Puli         {
291b52664e2SAppaRao Puli             path = "/";
292b52664e2SAppaRao Puli         }
293f8ca6d79SEd Tanous         std::shared_ptr<Subscription> subValue = std::make_shared<Subscription>(
294f8ca6d79SEd Tanous             host, port, path, urlProto, app.ioContext());
295b52664e2SAppaRao Puli 
296b52664e2SAppaRao Puli         subValue->destinationUrl = destUrl;
297e5aaf047SAppaRao Puli 
298e5aaf047SAppaRao Puli         if (subscriptionType)
299e5aaf047SAppaRao Puli         {
300e5aaf047SAppaRao Puli             if (*subscriptionType != "RedfishEvent")
301e5aaf047SAppaRao Puli             {
302fffb8c1fSEd Tanous                 messages::propertyValueNotInList(
303fffb8c1fSEd Tanous                     asyncResp->res, *subscriptionType, "SubscriptionType");
304e5aaf047SAppaRao Puli                 return;
305e5aaf047SAppaRao Puli             }
306b52664e2SAppaRao Puli             subValue->subscriptionType = *subscriptionType;
307e5aaf047SAppaRao Puli         }
308e5aaf047SAppaRao Puli         else
309e5aaf047SAppaRao Puli         {
310b52664e2SAppaRao Puli             subValue->subscriptionType = "RedfishEvent"; // Default
311e5aaf047SAppaRao Puli         }
312e5aaf047SAppaRao Puli 
313e5aaf047SAppaRao Puli         if (protocol != "Redfish")
314e5aaf047SAppaRao Puli         {
315e5aaf047SAppaRao Puli             messages::propertyValueNotInList(asyncResp->res, protocol,
316e5aaf047SAppaRao Puli                                              "Protocol");
317e5aaf047SAppaRao Puli             return;
318e5aaf047SAppaRao Puli         }
319b52664e2SAppaRao Puli         subValue->protocol = protocol;
320e5aaf047SAppaRao Puli 
32123a21a1cSEd Tanous         if (eventFormatType2)
322e5aaf047SAppaRao Puli         {
323e5aaf047SAppaRao Puli             if (std::find(supportedEvtFormatTypes.begin(),
324e5aaf047SAppaRao Puli                           supportedEvtFormatTypes.end(),
325002d39b4SEd Tanous                           *eventFormatType2) == supportedEvtFormatTypes.end())
326e5aaf047SAppaRao Puli             {
327fffb8c1fSEd Tanous                 messages::propertyValueNotInList(
328fffb8c1fSEd Tanous                     asyncResp->res, *eventFormatType2, "EventFormatType");
329e5aaf047SAppaRao Puli                 return;
330e5aaf047SAppaRao Puli             }
33123a21a1cSEd Tanous             subValue->eventFormatType = *eventFormatType2;
332e5aaf047SAppaRao Puli         }
333e5aaf047SAppaRao Puli         else
334e5aaf047SAppaRao Puli         {
335e5aaf047SAppaRao Puli             // If not specified, use default "Event"
33623a21a1cSEd Tanous             subValue->eventFormatType = "Event";
337e5aaf047SAppaRao Puli         }
338e5aaf047SAppaRao Puli 
339e5aaf047SAppaRao Puli         if (context)
340e5aaf047SAppaRao Puli         {
341*600af5f1SAppaRao Puli             // This value is selected aribitrarily.
342*600af5f1SAppaRao Puli             constexpr const size_t maxContextSize = 256;
343*600af5f1SAppaRao Puli             if (context->size() > maxContextSize)
344*600af5f1SAppaRao Puli             {
345*600af5f1SAppaRao Puli                 messages::stringValueTooLong(asyncResp->res, "Context",
346*600af5f1SAppaRao Puli                                              maxContextSize);
347*600af5f1SAppaRao Puli                 return;
348*600af5f1SAppaRao Puli             }
349b52664e2SAppaRao Puli             subValue->customText = *context;
350e5aaf047SAppaRao Puli         }
351e5aaf047SAppaRao Puli 
352e5aaf047SAppaRao Puli         if (headers)
353e5aaf047SAppaRao Puli         {
354*600af5f1SAppaRao Puli             size_t cumulativeLen = 0;
355*600af5f1SAppaRao Puli 
356601c71aeSEd Tanous             for (const nlohmann::json& headerChunk : *headers)
357601c71aeSEd Tanous             {
358*600af5f1SAppaRao Puli                 std::string hdr{headerChunk.dump(
359*600af5f1SAppaRao Puli                     -1, ' ', true, nlohmann::json::error_handler_t::replace)};
360*600af5f1SAppaRao Puli                 cumulativeLen += hdr.length();
361*600af5f1SAppaRao Puli 
362*600af5f1SAppaRao Puli                 // This value is selected to mirror http_connection.hpp
363*600af5f1SAppaRao Puli                 constexpr const uint16_t maxHeaderSizeED = 8096;
364*600af5f1SAppaRao Puli                 if (cumulativeLen > maxHeaderSizeED)
365*600af5f1SAppaRao Puli                 {
366*600af5f1SAppaRao Puli                     messages::arraySizeTooLong(asyncResp->res, "HttpHeaders",
367*600af5f1SAppaRao Puli                                                maxHeaderSizeED);
368*600af5f1SAppaRao Puli                     return;
369*600af5f1SAppaRao Puli                 }
370601c71aeSEd Tanous                 for (const auto& item : headerChunk.items())
371601c71aeSEd Tanous                 {
372601c71aeSEd Tanous                     const std::string* value =
373601c71aeSEd Tanous                         item.value().get_ptr<const std::string*>();
374601c71aeSEd Tanous                     if (value == nullptr)
375601c71aeSEd Tanous                     {
376601c71aeSEd Tanous                         messages::propertyValueFormatError(
377e662eae8SEd Tanous                             asyncResp->res, item.value().dump(2, 1),
378601c71aeSEd Tanous                             "HttpHeaders/" + item.key());
379601c71aeSEd Tanous                         return;
380601c71aeSEd Tanous                     }
381601c71aeSEd Tanous                     subValue->httpHeaders.set(item.key(), *value);
382601c71aeSEd Tanous                 }
383601c71aeSEd Tanous             }
384e5aaf047SAppaRao Puli         }
385e5aaf047SAppaRao Puli 
386e5aaf047SAppaRao Puli         if (regPrefixes)
387e5aaf047SAppaRao Puli         {
388e5aaf047SAppaRao Puli             for (const std::string& it : *regPrefixes)
389e5aaf047SAppaRao Puli             {
390e5aaf047SAppaRao Puli                 if (std::find(supportedRegPrefixes.begin(),
391e5aaf047SAppaRao Puli                               supportedRegPrefixes.end(),
392e5aaf047SAppaRao Puli                               it) == supportedRegPrefixes.end())
393e5aaf047SAppaRao Puli                 {
394fffb8c1fSEd Tanous                     messages::propertyValueNotInList(asyncResp->res, it,
395fffb8c1fSEd Tanous                                                      "RegistryPrefixes");
396e5aaf047SAppaRao Puli                     return;
397e5aaf047SAppaRao Puli                 }
398e5aaf047SAppaRao Puli             }
399b52664e2SAppaRao Puli             subValue->registryPrefixes = *regPrefixes;
400e5aaf047SAppaRao Puli         }
401e5aaf047SAppaRao Puli 
402e56f254cSSunitha Harish         if (resTypes)
403e56f254cSSunitha Harish         {
404e56f254cSSunitha Harish             for (const std::string& it : *resTypes)
405e56f254cSSunitha Harish             {
406e56f254cSSunitha Harish                 if (std::find(supportedResourceTypes.begin(),
407e56f254cSSunitha Harish                               supportedResourceTypes.end(),
408e56f254cSSunitha Harish                               it) == supportedResourceTypes.end())
409e56f254cSSunitha Harish                 {
410e56f254cSSunitha Harish                     messages::propertyValueNotInList(asyncResp->res, it,
411e56f254cSSunitha Harish                                                      "ResourceTypes");
412e56f254cSSunitha Harish                     return;
413e56f254cSSunitha Harish                 }
414e56f254cSSunitha Harish             }
415e56f254cSSunitha Harish             subValue->resourceTypes = *resTypes;
416e56f254cSSunitha Harish         }
417e56f254cSSunitha Harish 
418e5aaf047SAppaRao Puli         if (msgIds)
419e5aaf047SAppaRao Puli         {
420b304bd79SP Dheeraj Srujan Kumar             std::vector<std::string> registryPrefix;
421b304bd79SP Dheeraj Srujan Kumar 
4227e860f15SJohn Edward Broadbent             // If no registry prefixes are mentioned, consider all
4237e860f15SJohn Edward Broadbent             // supported prefixes
424b304bd79SP Dheeraj Srujan Kumar             if (subValue->registryPrefixes.empty())
425b304bd79SP Dheeraj Srujan Kumar             {
426b304bd79SP Dheeraj Srujan Kumar                 registryPrefix.assign(supportedRegPrefixes.begin(),
427b304bd79SP Dheeraj Srujan Kumar                                       supportedRegPrefixes.end());
428b304bd79SP Dheeraj Srujan Kumar             }
429b304bd79SP Dheeraj Srujan Kumar             else
430b304bd79SP Dheeraj Srujan Kumar             {
431b304bd79SP Dheeraj Srujan Kumar                 registryPrefix = subValue->registryPrefixes;
432b304bd79SP Dheeraj Srujan Kumar             }
433b304bd79SP Dheeraj Srujan Kumar 
434b304bd79SP Dheeraj Srujan Kumar             for (const std::string& id : *msgIds)
435b304bd79SP Dheeraj Srujan Kumar             {
436b304bd79SP Dheeraj Srujan Kumar                 bool validId = false;
437b304bd79SP Dheeraj Srujan Kumar 
438b304bd79SP Dheeraj Srujan Kumar                 // Check for Message ID in each of the selected Registry
439b304bd79SP Dheeraj Srujan Kumar                 for (const std::string& it : registryPrefix)
440b304bd79SP Dheeraj Srujan Kumar                 {
441fffb8c1fSEd Tanous                     const std::span<const redfish::registries::MessageEntry>
442fffb8c1fSEd Tanous                         registry =
443fffb8c1fSEd Tanous                             redfish::registries::getRegistryFromPrefix(it);
444b304bd79SP Dheeraj Srujan Kumar 
445b304bd79SP Dheeraj Srujan Kumar                     if (std::any_of(
44626702d01SEd Tanous                             registry.begin(), registry.end(),
447fffb8c1fSEd Tanous                             [&id](const redfish::registries::MessageEntry&
448fffb8c1fSEd Tanous                                       messageEntry) {
44955f79e6fSEd Tanous                         return id == messageEntry.first;
450b304bd79SP Dheeraj Srujan Kumar                             }))
451b304bd79SP Dheeraj Srujan Kumar                     {
452b304bd79SP Dheeraj Srujan Kumar                         validId = true;
453b304bd79SP Dheeraj Srujan Kumar                         break;
454b304bd79SP Dheeraj Srujan Kumar                     }
455b304bd79SP Dheeraj Srujan Kumar                 }
456b304bd79SP Dheeraj Srujan Kumar 
457b304bd79SP Dheeraj Srujan Kumar                 if (!validId)
458b304bd79SP Dheeraj Srujan Kumar                 {
459b304bd79SP Dheeraj Srujan Kumar                     messages::propertyValueNotInList(asyncResp->res, id,
460b304bd79SP Dheeraj Srujan Kumar                                                      "MessageIds");
461b304bd79SP Dheeraj Srujan Kumar                     return;
462b304bd79SP Dheeraj Srujan Kumar                 }
463b304bd79SP Dheeraj Srujan Kumar             }
464b304bd79SP Dheeraj Srujan Kumar 
465b52664e2SAppaRao Puli             subValue->registryMsgIds = *msgIds;
466e5aaf047SAppaRao Puli         }
467e5aaf047SAppaRao Puli 
468e5aaf047SAppaRao Puli         if (retryPolicy)
469e5aaf047SAppaRao Puli         {
470e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
471e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
472e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
473e5aaf047SAppaRao Puli             {
474002d39b4SEd Tanous                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
475002d39b4SEd Tanous                                                  "DeliveryRetryPolicy");
476e5aaf047SAppaRao Puli                 return;
477e5aaf047SAppaRao Puli             }
478b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
479e5aaf047SAppaRao Puli         }
480e5aaf047SAppaRao Puli         else
481e5aaf047SAppaRao Puli         {
482e5aaf047SAppaRao Puli             // Default "TerminateAfterRetries"
483b52664e2SAppaRao Puli             subValue->retryPolicy = "TerminateAfterRetries";
484e5aaf047SAppaRao Puli         }
485e5aaf047SAppaRao Puli 
486144b6318SAppaRao Puli         if (mrdJsonArray)
487156d6b00SAppaRao Puli         {
488144b6318SAppaRao Puli             for (nlohmann::json& mrdObj : *mrdJsonArray)
489144b6318SAppaRao Puli             {
490144b6318SAppaRao Puli                 std::string mrdUri;
491ea2e6eecSWilly Tu 
492002d39b4SEd Tanous                 if (!json_util::readJson(mrdObj, asyncResp->res, "@odata.id",
493002d39b4SEd Tanous                                          mrdUri))
494ea2e6eecSWilly Tu 
495144b6318SAppaRao Puli                 {
496144b6318SAppaRao Puli                     return;
497144b6318SAppaRao Puli                 }
498ea2e6eecSWilly Tu                 subValue->metricReportDefinitions.emplace_back(mrdUri);
499144b6318SAppaRao Puli             }
500156d6b00SAppaRao Puli         }
501156d6b00SAppaRao Puli 
502b52664e2SAppaRao Puli         std::string id =
503fffb8c1fSEd Tanous             EventServiceManager::getInstance().addSubscription(subValue);
504b52664e2SAppaRao Puli         if (id.empty())
505e5aaf047SAppaRao Puli         {
506e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
507e5aaf047SAppaRao Puli             return;
508e5aaf047SAppaRao Puli         }
509e5aaf047SAppaRao Puli 
510e5aaf047SAppaRao Puli         messages::created(asyncResp->res);
511e5aaf047SAppaRao Puli         asyncResp->res.addHeader(
512e5aaf047SAppaRao Puli             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
5137e860f15SJohn Edward Broadbent         });
514e5aaf047SAppaRao Puli }
515e5aaf047SAppaRao Puli 
5167e860f15SJohn Edward Broadbent inline void requestRoutesEventDestination(App& app)
517e5aaf047SAppaRao Puli {
5189d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
519ed398213SEd Tanous         .privileges(redfish::privileges::getEventDestination)
5207e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
52145ca1b86SEd Tanous             [&app](const crow::Request& req,
5227e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5237e860f15SJohn Edward Broadbent                    const std::string& param) {
5243ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
52545ca1b86SEd Tanous         {
52645ca1b86SEd Tanous             return;
52745ca1b86SEd Tanous         }
528b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
5297e860f15SJohn Edward Broadbent             EventServiceManager::getInstance().getSubscription(param);
530b52664e2SAppaRao Puli         if (subValue == nullptr)
531e5aaf047SAppaRao Puli         {
532002d39b4SEd Tanous             asyncResp->res.result(boost::beast::http::status::not_found);
533e5aaf047SAppaRao Puli             return;
534e5aaf047SAppaRao Puli         }
5357e860f15SJohn Edward Broadbent         const std::string& id = param;
536e5aaf047SAppaRao Puli 
5371476687dSEd Tanous         asyncResp->res.jsonValue["@odata.type"] =
5381476687dSEd Tanous             "#EventDestination.v1_7_0.EventDestination";
5391476687dSEd Tanous         asyncResp->res.jsonValue["Protocol"] = "Redfish";
540e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["@odata.id"] =
541e5aaf047SAppaRao Puli             "/redfish/v1/EventService/Subscriptions/" + id;
542e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Id"] = id;
543e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
544002d39b4SEd Tanous         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
545b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Context"] = subValue->customText;
546e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["SubscriptionType"] =
547b52664e2SAppaRao Puli             subValue->subscriptionType;
548002d39b4SEd Tanous         asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array();
549002d39b4SEd Tanous         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
550e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] =
551b52664e2SAppaRao Puli             subValue->registryPrefixes;
552002d39b4SEd Tanous         asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes;
553e56f254cSSunitha Harish 
554002d39b4SEd Tanous         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
555002d39b4SEd Tanous         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
556144b6318SAppaRao Puli 
5571476687dSEd Tanous         nlohmann::json::array_t mrdJsonArray;
558144b6318SAppaRao Puli         for (const auto& mdrUri : subValue->metricReportDefinitions)
559144b6318SAppaRao Puli         {
5601476687dSEd Tanous             nlohmann::json::object_t mdr;
5611476687dSEd Tanous             mdr["@odata.id"] = mdrUri;
5621476687dSEd Tanous             mrdJsonArray.emplace_back(std::move(mdr));
563144b6318SAppaRao Puli         }
564002d39b4SEd Tanous         asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray;
5657e860f15SJohn Edward Broadbent         });
5669d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
567ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
568ed398213SEd Tanous         // ConfigureSelf
5697eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
570ed398213SEd Tanous         //.privileges(redfish::privileges::patchEventDestination)
571432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
5727e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::patch)(
57345ca1b86SEd Tanous             [&app](const crow::Request& req,
5747e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5757e860f15SJohn Edward Broadbent                    const std::string& param) {
5763ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
57745ca1b86SEd Tanous         {
57845ca1b86SEd Tanous             return;
57945ca1b86SEd Tanous         }
580b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
5817e860f15SJohn Edward Broadbent             EventServiceManager::getInstance().getSubscription(param);
582b52664e2SAppaRao Puli         if (subValue == nullptr)
583e5aaf047SAppaRao Puli         {
584002d39b4SEd Tanous             asyncResp->res.result(boost::beast::http::status::not_found);
585e5aaf047SAppaRao Puli             return;
586e5aaf047SAppaRao Puli         }
587e5aaf047SAppaRao Puli 
588e5aaf047SAppaRao Puli         std::optional<std::string> context;
589e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
590e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
591e5aaf047SAppaRao Puli 
592002d39b4SEd Tanous         if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context,
593002d39b4SEd Tanous                                       "DeliveryRetryPolicy", retryPolicy,
594002d39b4SEd Tanous                                       "HttpHeaders", headers))
595e5aaf047SAppaRao Puli         {
596e5aaf047SAppaRao Puli             return;
597e5aaf047SAppaRao Puli         }
598e5aaf047SAppaRao Puli 
599e5aaf047SAppaRao Puli         if (context)
600e5aaf047SAppaRao Puli         {
601b52664e2SAppaRao Puli             subValue->customText = *context;
602e5aaf047SAppaRao Puli         }
603e5aaf047SAppaRao Puli 
604e5aaf047SAppaRao Puli         if (headers)
605e5aaf047SAppaRao Puli         {
606601c71aeSEd Tanous             boost::beast::http::fields fields;
607601c71aeSEd Tanous             for (const nlohmann::json& headerChunk : *headers)
608601c71aeSEd Tanous             {
60962bafc01SPatrick Williams                 for (const auto& it : headerChunk.items())
610601c71aeSEd Tanous                 {
611601c71aeSEd Tanous                     const std::string* value =
612601c71aeSEd Tanous                         it.value().get_ptr<const std::string*>();
613601c71aeSEd Tanous                     if (value == nullptr)
614601c71aeSEd Tanous                     {
615601c71aeSEd Tanous                         messages::propertyValueFormatError(
616002d39b4SEd Tanous                             asyncResp->res, it.value().dump(2, ' ', true),
617601c71aeSEd Tanous                             "HttpHeaders/" + it.key());
618601c71aeSEd Tanous                         return;
619601c71aeSEd Tanous                     }
620601c71aeSEd Tanous                     fields.set(it.key(), *value);
621601c71aeSEd Tanous                 }
622601c71aeSEd Tanous             }
623601c71aeSEd Tanous             subValue->httpHeaders = fields;
624e5aaf047SAppaRao Puli         }
625e5aaf047SAppaRao Puli 
626e5aaf047SAppaRao Puli         if (retryPolicy)
627e5aaf047SAppaRao Puli         {
628e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
629e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
630e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
631e5aaf047SAppaRao Puli             {
632002d39b4SEd Tanous                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
633e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
634e5aaf047SAppaRao Puli                 return;
635e5aaf047SAppaRao Puli             }
636b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
637e5aaf047SAppaRao Puli         }
638e5aaf047SAppaRao Puli 
639b52664e2SAppaRao Puli         EventServiceManager::getInstance().updateSubscriptionData();
6407e860f15SJohn Edward Broadbent         });
6419d41aec6SRavi Teja     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
642ed398213SEd Tanous         // The below privilege is wrong, it should be ConfigureManager OR
643ed398213SEd Tanous         // ConfigureSelf
6447eeafa76SAbhishek Patel         // https://github.com/openbmc/bmcweb/issues/220
645ed398213SEd Tanous         //.privileges(redfish::privileges::deleteEventDestination)
646432a890cSEd Tanous         .privileges({{"ConfigureManager"}})
6477e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::delete_)(
64845ca1b86SEd Tanous             [&app](const crow::Request& req,
6497e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6507e860f15SJohn Edward Broadbent                    const std::string& param) {
6513ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
65245ca1b86SEd Tanous         {
65345ca1b86SEd Tanous             return;
65445ca1b86SEd Tanous         }
655002d39b4SEd Tanous         if (!EventServiceManager::getInstance().isSubscriptionExist(param))
656e5aaf047SAppaRao Puli         {
657002d39b4SEd Tanous             asyncResp->res.result(boost::beast::http::status::not_found);
658e5aaf047SAppaRao Puli             return;
659e5aaf047SAppaRao Puli         }
6607e860f15SJohn Edward Broadbent         EventServiceManager::getInstance().deleteSubscription(param);
6617e860f15SJohn Edward Broadbent         });
662e5aaf047SAppaRao Puli }
663e5aaf047SAppaRao Puli 
664e5aaf047SAppaRao Puli } // namespace redfish
665