blob: bef13ebc00a910b6d5b3be804e4852ea10c3b4b3 [file] [log] [blame]
AppaRao Pulie5aaf042020-03-20 01:05:52 +05301/*
Ed Tanous6be832e2024-09-10 11:44:48 -07002Copyright (c) 2020 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
AppaRao Pulie5aaf042020-03-20 01:05:52 +053015*/
16#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080017#include "app.hpp"
AppaRao Pulib52664e2020-04-09 21:36:51 +053018#include "event_service_manager.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -070019#include "generated/enums/event_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "http/utility.hpp"
21#include "logging.hpp"
22#include "query.hpp"
23#include "registries/privilege_registry.hpp"
Chicago Duan3d307082020-11-26 14:12:12 +080024#include "snmp_trap_event_clients.hpp"
AppaRao Pulie5aaf042020-03-20 01:05:52 +053025
Ed Tanous601c71a2021-09-08 16:40:12 -070026#include <boost/beast/http/fields.hpp>
Chicago Duan3d307082020-11-26 14:12:12 +080027#include <boost/system/error_code.hpp>
Ed Tanousa716aa72023-08-01 11:35:53 -070028#include <boost/url/parse.hpp>
Chicago Duan3d307082020-11-26 14:12:12 +080029#include <sdbusplus/unpack_properties.hpp>
30#include <utils/dbus_utils.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070031
Chicago Duan3d307082020-11-26 14:12:12 +080032#include <charconv>
33#include <memory>
Ed Tanous3544d2a2023-08-06 18:12:20 -070034#include <ranges>
Patrick Williams1e270c52021-12-04 06:06:56 -060035#include <span>
Chicago Duan3d307082020-11-26 14:12:12 +080036#include <string>
Ed Tanousa14c9112024-09-04 10:46:47 -070037#include <vector>
Patrick Williams1e270c52021-12-04 06:06:56 -060038
AppaRao Pulie5aaf042020-03-20 01:05:52 +053039namespace redfish
40{
41
AppaRao Puli156d6b02020-04-25 06:04:05 +053042static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
43 eventFormatType, metricReportFormatType};
AppaRao Pulie5aaf042020-03-20 01:05:52 +053044static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +053045 "Base", "OpenBMC", "TaskEvent"};
AppaRao Pulie5aaf042020-03-20 01:05:52 +053046static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
47 "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
48
Sunitha Harishe56f2542020-07-22 02:38:59 -050049static constexpr const std::array<const char*, 1> supportedResourceTypes = {
50 "Task"};
Sunitha Harishe56f2542020-07-22 02:38:59 -050051
John Edward Broadbent7e860f12021-04-08 15:57:16 -070052inline void requestRoutesEventService(App& app)
AppaRao Pulie5aaf042020-03-20 01:05:52 +053053{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070054 BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
Ed Tanoused398212021-06-09 17:05:54 -070055 .privileges(redfish::privileges::getEventService)
Patrick Williamsbd79bce2024-08-16 15:22:20 -040056 .methods(
57 boost::beast::http::verb::
58 get)([&app](
59 const crow::Request& req,
60 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
61 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
62 {
63 return;
64 }
Ed Tanous14766872022-03-15 10:44:42 -070065
Patrick Williamsbd79bce2024-08-16 15:22:20 -040066 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
67 asyncResp->res.jsonValue["@odata.type"] =
68 "#EventService.v1_5_0.EventService";
69 asyncResp->res.jsonValue["Id"] = "EventService";
70 asyncResp->res.jsonValue["Name"] = "Event Service";
71 asyncResp->res.jsonValue["ServerSentEventUri"] =
72 "/redfish/v1/EventService/SSE";
AppaRao Puli5e44e3d2021-03-16 15:37:24 +000073
Patrick Williamsbd79bce2024-08-16 15:22:20 -040074 asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
75 "/redfish/v1/EventService/Subscriptions";
76 asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
77 ["target"] =
78 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
AppaRao Pulie5aaf042020-03-20 01:05:52 +053079
Patrick Williamsbd79bce2024-08-16 15:22:20 -040080 const persistent_data::EventServiceConfig eventServiceConfig =
81 persistent_data::EventServiceStore::getInstance()
82 .getEventServiceConfig();
zhanghch058d1b46d2021-04-01 11:18:24 +080083
Patrick Williamsbd79bce2024-08-16 15:22:20 -040084 asyncResp->res.jsonValue["Status"]["State"] =
85 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
86 asyncResp->res.jsonValue["ServiceEnabled"] =
87 eventServiceConfig.enabled;
88 asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
89 eventServiceConfig.retryAttempts;
90 asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
91 eventServiceConfig.retryTimeoutInterval;
92 asyncResp->res.jsonValue["EventFormatTypes"] =
93 supportedEvtFormatTypes;
94 asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
95 asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053096
Patrick Williamsbd79bce2024-08-16 15:22:20 -040097 nlohmann::json::object_t supportedSSEFilters;
98 supportedSSEFilters["EventFormatType"] = true;
99 supportedSSEFilters["MessageId"] = true;
100 supportedSSEFilters["MetricReportDefinition"] = true;
101 supportedSSEFilters["RegistryPrefix"] = true;
102 supportedSSEFilters["OriginResource"] = false;
103 supportedSSEFilters["ResourceType"] = false;
AppaRao Puli7d1cc382020-05-16 02:42:22 +0530104
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400105 asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
106 std::move(supportedSSEFilters);
107 });
Ayushi Smriti07941a82020-05-21 15:55:34 +0530108
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700109 BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
Ed Tanoused398212021-06-09 17:05:54 -0700110 .privileges(redfish::privileges::patchEventService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700111 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700112 [&app](const crow::Request& req,
113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400114 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
115 {
116 return;
117 }
118 std::optional<bool> serviceEnabled;
119 std::optional<uint32_t> retryAttemps;
120 std::optional<uint32_t> retryInterval;
Myung Baeafc474a2024-10-09 00:53:29 -0700121 if (!json_util::readJsonPatch( //
122 req, asyncResp->res, //
123 "DeliveryRetryAttempts", retryAttemps, //
124 "DeliveryRetryIntervalSeconds", retryInterval, //
125 "ServiceEnabled", serviceEnabled //
126 ))
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400127 {
128 return;
129 }
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530130
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400131 persistent_data::EventServiceConfig eventServiceConfig =
132 persistent_data::EventServiceStore::getInstance()
133 .getEventServiceConfig();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700134
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400135 if (serviceEnabled)
136 {
137 eventServiceConfig.enabled = *serviceEnabled;
138 }
Sunitha Harishe56f2542020-07-22 02:38:59 -0500139
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400140 if (retryAttemps)
141 {
142 // Supported range [1-3]
143 if ((*retryAttemps < 1) || (*retryAttemps > 3))
144 {
145 messages::queryParameterOutOfRange(
146 asyncResp->res, std::to_string(*retryAttemps),
147 "DeliveryRetryAttempts", "[1-3]");
148 }
149 else
150 {
151 eventServiceConfig.retryAttempts = *retryAttemps;
152 }
153 }
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +0530154
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400155 if (retryInterval)
156 {
157 // Supported range [5 - 180]
158 if ((*retryInterval < 5) || (*retryInterval > 180))
159 {
160 messages::queryParameterOutOfRange(
161 asyncResp->res, std::to_string(*retryInterval),
162 "DeliveryRetryIntervalSeconds", "[5-180]");
163 }
164 else
165 {
166 eventServiceConfig.retryTimeoutInterval =
167 *retryInterval;
168 }
169 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700170
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400171 EventServiceManager::getInstance().setEventServiceConfig(
172 eventServiceConfig);
173 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700174}
175
176inline void requestRoutesSubmitTestEvent(App& app)
177{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700178 BMCWEB_ROUTE(
179 app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
Ed Tanoused398212021-06-09 17:05:54 -0700180 .privileges(redfish::privileges::postEventService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700181 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700182 [&app](const crow::Request& req,
183 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400184 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
185 {
186 return;
187 }
188 if (!EventServiceManager::getInstance().sendTestEventLog())
189 {
190 messages::serviceDisabled(asyncResp->res,
191 "/redfish/v1/EventService/");
192 return;
193 }
194 asyncResp->res.result(boost::beast::http::status::no_content);
195 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700196}
197
Chicago Duan3d307082020-11-26 14:12:12 +0800198inline void doSubscriptionCollection(
Ed Tanouse81de512023-06-27 17:07:00 -0700199 const boost::system::error_code& ec,
Chicago Duan3d307082020-11-26 14:12:12 +0800200 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
201 const dbus::utility::ManagedObjectType& resp)
202{
203 if (ec)
204 {
Ed Tanous13061012023-07-25 11:12:19 -0700205 if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
Chicago Duan3d307082020-11-26 14:12:12 +0800206 {
207 // This is an optional process so just return if it isn't there
208 return;
209 }
210
Ed Tanous62598e32023-07-17 17:06:25 -0700211 BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
Chicago Duan3d307082020-11-26 14:12:12 +0800212 messages::internalError(asyncResp->res);
213 return;
214 }
215 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
216 for (const auto& objpath : resp)
217 {
218 sdbusplus::message::object_path path(objpath.first);
219 const std::string snmpId = path.filename();
220 if (snmpId.empty())
221 {
Ed Tanous62598e32023-07-17 17:06:25 -0700222 BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
Chicago Duan3d307082020-11-26 14:12:12 +0800223 messages::internalError(asyncResp->res);
224 return;
225 }
226
227 getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
228 }
229}
230
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700231inline void requestRoutesEventDestinationCollection(App& app)
232{
Gayathri Leburu1ebe3e42022-02-09 10:45:19 +0000233 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
Ed Tanoused398212021-06-09 17:05:54 -0700234 .privileges(redfish::privileges::getEventDestinationCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700235 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700236 [&app](const crow::Request& req,
237 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400238 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
239 {
240 return;
241 }
242 asyncResp->res.jsonValue["@odata.type"] =
243 "#EventDestinationCollection.EventDestinationCollection";
244 asyncResp->res.jsonValue["@odata.id"] =
245 "/redfish/v1/EventService/Subscriptions";
246 asyncResp->res.jsonValue["Name"] =
247 "Event Destination Collections";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700248
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400249 nlohmann::json& memberArray =
250 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700251
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400252 std::vector<std::string> subscripIds =
253 EventServiceManager::getInstance().getAllIDs();
254 memberArray = nlohmann::json::array();
255 asyncResp->res.jsonValue["Members@odata.count"] =
256 subscripIds.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700257
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400258 for (const std::string& id : subscripIds)
259 {
260 nlohmann::json::object_t member;
261 member["@odata.id"] = boost::urls::format(
262 "/redfish/v1/EventService/Subscriptions/{}" + id);
263 memberArray.emplace_back(std::move(member));
264 }
265 crow::connections::systemBus->async_method_call(
266 [asyncResp](const boost::system::error_code& ec,
267 const dbus::utility::ManagedObjectType& resp) {
268 doSubscriptionCollection(ec, asyncResp, resp);
269 },
270 "xyz.openbmc_project.Network.SNMP",
271 "/xyz/openbmc_project/network/snmp/manager",
272 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
273 });
Chicago Duan3d307082020-11-26 14:12:12 +0800274
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700275 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
Abhishek Patel7eeafa72021-07-28 10:59:16 -0500276 .privileges(redfish::privileges::postEventDestinationCollection)
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400277 .methods(
278 boost::beast::http::verb::
279 post)([&app](
280 const crow::Request& req,
281 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
282 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
283 {
284 return;
285 }
286 if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
287 maxNoOfSubscriptions)
288 {
289 messages::eventSubscriptionLimitExceeded(asyncResp->res);
290 return;
291 }
292 std::string destUrl;
293 std::string protocol;
294 std::optional<bool> verifyCertificate;
295 std::optional<std::string> context;
296 std::optional<std::string> subscriptionType;
297 std::optional<std::string> eventFormatType2;
298 std::optional<std::string> retryPolicy;
299 std::optional<std::vector<std::string>> msgIds;
300 std::optional<std::vector<std::string>> regPrefixes;
Ed Tanousa14c9112024-09-04 10:46:47 -0700301 std::optional<std::vector<std::string>> originResources;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400302 std::optional<std::vector<std::string>> resTypes;
303 std::optional<std::vector<nlohmann::json::object_t>> headers;
304 std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
Ed Tanousfffb8c12022-02-07 23:53:03 -0800305
Myung Baeafc474a2024-10-09 00:53:29 -0700306 if (!json_util::readJsonPatch( //
307 req, asyncResp->res, //
308 "Context", context, //
309 "DeliveryRetryPolicy", retryPolicy, //
310 "Destination", destUrl, //
311 "EventFormatType", eventFormatType2, //
312 "HttpHeaders", headers, //
313 "MessageIds", msgIds, //
314 "MetricReportDefinitions", mrdJsonArray, //
315 "OriginResources", originResources, //
316 "Protocol", protocol, //
317 "RegistryPrefixes", regPrefixes, //
318 "ResourceTypes", resTypes, //
319 "SubscriptionType", subscriptionType, //
320 "VerifyCertificate", verifyCertificate //
321 ))
Ed Tanousfffb8c12022-02-07 23:53:03 -0800322 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700323 return;
324 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700325 // clang-format on
Chicago Duan3d307082020-11-26 14:12:12 +0800326
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400327 // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
328 static constexpr const uint16_t maxDestinationSize = 2000;
329 if (destUrl.size() > maxDestinationSize)
Ed Tanousfffb8c12022-02-07 23:53:03 -0800330 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400331 messages::stringValueTooLong(asyncResp->res, "Destination",
332 maxDestinationSize);
Ed Tanous002d39b2022-05-31 08:59:27 -0700333 return;
334 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700335
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400336 if (regPrefixes && msgIds)
Ed Tanous002d39b2022-05-31 08:59:27 -0700337 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400338 if (!regPrefixes->empty() && !msgIds->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700339 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400340 messages::propertyValueConflict(
341 asyncResp->res, "MessageIds", "RegistryPrefixes");
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +0530342 return;
343 }
Ed Tanousfffb8c12022-02-07 23:53:03 -0800344 }
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +0530345
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400346 boost::system::result<boost::urls::url> url =
347 boost::urls::parse_absolute_uri(destUrl);
348 if (!url)
Ed Tanousfffb8c12022-02-07 23:53:03 -0800349 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400350 BMCWEB_LOG_WARNING(
351 "Failed to validate and split destination url");
352 messages::propertyValueFormatError(asyncResp->res, destUrl,
353 "Destination");
354 return;
355 }
356 url->normalize();
357 crow::utility::setProtocolDefaults(*url, protocol);
358 crow::utility::setPortDefaults(*url);
359
360 if (url->path().empty())
361 {
362 url->set_path("/");
363 }
364
365 if (url->has_userinfo())
366 {
367 messages::propertyValueFormatError(asyncResp->res, destUrl,
368 "Destination");
369 return;
370 }
371
372 if (protocol == "SNMPv2c")
373 {
374 if (context)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700375 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400376 messages::propertyValueConflict(asyncResp->res, "Context",
377 "Protocol");
AppaRao Puli144b6312020-08-03 22:23:12 +0530378 return;
379 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400380 if (eventFormatType2)
381 {
382 messages::propertyValueConflict(
383 asyncResp->res, "EventFormatType", "Protocol");
384 return;
385 }
386 if (retryPolicy)
387 {
388 messages::propertyValueConflict(asyncResp->res,
389 "RetryPolicy", "Protocol");
390 return;
391 }
392 if (msgIds)
393 {
394 messages::propertyValueConflict(asyncResp->res,
395 "MessageIds", "Protocol");
396 return;
397 }
398 if (regPrefixes)
399 {
400 messages::propertyValueConflict(
401 asyncResp->res, "RegistryPrefixes", "Protocol");
402 return;
403 }
404 if (resTypes)
405 {
406 messages::propertyValueConflict(
407 asyncResp->res, "ResourceTypes", "Protocol");
408 return;
409 }
410 if (headers)
411 {
412 messages::propertyValueConflict(asyncResp->res,
413 "HttpHeaders", "Protocol");
414 return;
415 }
416 if (mrdJsonArray)
417 {
418 messages::propertyValueConflict(
419 asyncResp->res, "MetricReportDefinitions", "Protocol");
420 return;
421 }
422 if (url->scheme() != "snmp")
423 {
424 messages::propertyValueConflict(asyncResp->res,
425 "Destination", "Protocol");
426 return;
427 }
428
429 addSnmpTrapClient(asyncResp, url->host_address(),
430 url->port_number());
431 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700432 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700433
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400434 std::shared_ptr<Subscription> subValue =
Myung Bae21a94d52024-10-14 15:02:57 -0700435 std::make_shared<Subscription>(
436 persistent_data::UserSubscription{}, *url, app.ioContext());
Ed Tanous002d39b2022-05-31 08:59:27 -0700437
Ed Tanous4b712a22023-08-02 12:56:52 -0700438 subValue->userSub.destinationUrl = std::move(*url);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400439
440 if (subscriptionType)
Ed Tanous002d39b2022-05-31 08:59:27 -0700441 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400442 if (*subscriptionType != "RedfishEvent")
443 {
444 messages::propertyValueNotInList(
445 asyncResp->res, *subscriptionType, "SubscriptionType");
446 return;
447 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700448 subValue->userSub.subscriptionType = *subscriptionType;
Ed Tanousfffb8c12022-02-07 23:53:03 -0800449 }
450 else
451 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700452 // Default
453 subValue->userSub.subscriptionType = "RedfishEvent";
Ed Tanousfffb8c12022-02-07 23:53:03 -0800454 }
AppaRao Puli156d6b02020-04-25 06:04:05 +0530455
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400456 if (protocol != "Redfish")
Ed Tanousfffb8c12022-02-07 23:53:03 -0800457 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400458 messages::propertyValueNotInList(asyncResp->res, protocol,
459 "Protocol");
460 return;
461 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700462 subValue->userSub.protocol = protocol;
Ed Tanousfffb8c12022-02-07 23:53:03 -0800463
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400464 if (verifyCertificate)
465 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700466 subValue->userSub.verifyCertificate = *verifyCertificate;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400467 }
468
469 if (eventFormatType2)
470 {
471 if (std::ranges::find(supportedEvtFormatTypes,
472 *eventFormatType2) ==
473 supportedEvtFormatTypes.end())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700474 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400475 messages::propertyValueNotInList(
476 asyncResp->res, *eventFormatType2, "EventFormatType");
477 return;
478 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700479 subValue->userSub.eventFormatType = *eventFormatType2;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400480 }
481 else
482 {
483 // If not specified, use default "Event"
Ed Tanous4b712a22023-08-02 12:56:52 -0700484 subValue->userSub.eventFormatType = "Event";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400485 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700486
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400487 if (context)
488 {
489 // This value is selected arbitrarily.
490 constexpr const size_t maxContextSize = 256;
491 if (context->size() > maxContextSize)
492 {
493 messages::stringValueTooLong(asyncResp->res, "Context",
494 maxContextSize);
495 return;
496 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700497 subValue->userSub.customText = *context;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400498 }
499
500 if (headers)
501 {
502 size_t cumulativeLen = 0;
503
504 for (const nlohmann::json::object_t& headerChunk : *headers)
505 {
506 for (const auto& item : headerChunk)
Ed Tanous002d39b2022-05-31 08:59:27 -0700507 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400508 const std::string* value =
509 item.second.get_ptr<const std::string*>();
510 if (value == nullptr)
511 {
512 messages::propertyValueFormatError(
513 asyncResp->res, item.second,
514 "HttpHeaders/" + item.first);
515 return;
516 }
517 // Adding a new json value is the size of the key, +
518 // the size of the value + 2 * 2 quotes for each, +
519 // the colon and space between. example:
520 // "key": "value"
521 cumulativeLen += item.first.size() + value->size() + 6;
522 // This value is selected to mirror http_connection.hpp
523 constexpr const uint16_t maxHeaderSizeED = 8096;
524 if (cumulativeLen > maxHeaderSizeED)
525 {
526 messages::arraySizeTooLong(
527 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
528 return;
529 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700530 subValue->userSub.httpHeaders.set(item.first, *value);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400531 }
532 }
533 }
534
535 if (regPrefixes)
536 {
537 for (const std::string& it : *regPrefixes)
538 {
539 if (std::ranges::find(supportedRegPrefixes, it) ==
540 supportedRegPrefixes.end())
541 {
542 messages::propertyValueNotInList(asyncResp->res, it,
543 "RegistryPrefixes");
544 return;
545 }
546 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700547 subValue->userSub.registryPrefixes = *regPrefixes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400548 }
549
Ed Tanousa14c9112024-09-04 10:46:47 -0700550 if (originResources)
551 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700552 subValue->userSub.originResources = *originResources;
Ed Tanousa14c9112024-09-04 10:46:47 -0700553 }
554
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400555 if (resTypes)
556 {
557 for (const std::string& it : *resTypes)
558 {
559 if (std::ranges::find(supportedResourceTypes, it) ==
560 supportedResourceTypes.end())
561 {
562 messages::propertyValueNotInList(asyncResp->res, it,
563 "ResourceTypes");
564 return;
565 }
566 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700567 subValue->userSub.resourceTypes = *resTypes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400568 }
569
570 if (msgIds)
571 {
572 std::vector<std::string> registryPrefix;
573
574 // If no registry prefixes are mentioned, consider all
575 // supported prefixes
Ed Tanous4b712a22023-08-02 12:56:52 -0700576 if (subValue->userSub.registryPrefixes.empty())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400577 {
578 registryPrefix.assign(supportedRegPrefixes.begin(),
579 supportedRegPrefixes.end());
580 }
581 else
582 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700583 registryPrefix = subValue->userSub.registryPrefixes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400584 }
585
586 for (const std::string& id : *msgIds)
587 {
588 bool validId = false;
589
590 // Check for Message ID in each of the selected Registry
591 for (const std::string& it : registryPrefix)
592 {
593 const std::span<const redfish::registries::MessageEntry>
594 registry =
595 redfish::registries::getRegistryFromPrefix(it);
596
597 if (std::ranges::any_of(
598 registry,
599 [&id](const redfish::registries::MessageEntry&
600 messageEntry) {
601 return id == messageEntry.first;
602 }))
603 {
604 validId = true;
605 break;
606 }
607 }
608
609 if (!validId)
610 {
611 messages::propertyValueNotInList(asyncResp->res, id,
612 "MessageIds");
613 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700614 }
615 }
616
Ed Tanous4b712a22023-08-02 12:56:52 -0700617 subValue->userSub.registryMsgIds = *msgIds;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400618 }
619
620 if (retryPolicy)
621 {
622 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
623 supportedRetryPolicies.end())
Ed Tanous002d39b2022-05-31 08:59:27 -0700624 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400625 messages::propertyValueNotInList(
626 asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
Ed Tanousfffb8c12022-02-07 23:53:03 -0800627 return;
628 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700629 subValue->userSub.retryPolicy = *retryPolicy;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400630 }
631 else
632 {
633 // Default "TerminateAfterRetries"
Ed Tanous4b712a22023-08-02 12:56:52 -0700634 subValue->userSub.retryPolicy = "TerminateAfterRetries";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400635 }
636
637 if (mrdJsonArray)
638 {
639 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
640 {
641 std::string mrdUri;
642
643 if (!json_util::readJsonObject(mrdObj, asyncResp->res,
644 "@odata.id", mrdUri))
645
646 {
647 return;
648 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700649 subValue->userSub.metricReportDefinitions.emplace_back(
650 mrdUri);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400651 }
Ed Tanousfffb8c12022-02-07 23:53:03 -0800652 }
653
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400654 std::string id =
655 EventServiceManager::getInstance().addPushSubscription(
656 subValue);
657 if (id.empty())
Ed Tanousfffb8c12022-02-07 23:53:03 -0800658 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400659 messages::internalError(asyncResp->res);
Ed Tanousfffb8c12022-02-07 23:53:03 -0800660 return;
661 }
662
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400663 messages::created(asyncResp->res);
664 asyncResp->res.addHeader(
665 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
666 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700667}
668
669inline void requestRoutesEventDestination(App& app)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530670{
Ravi Teja9d41aec2021-07-23 01:57:01 -0500671 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700672 .privileges(redfish::privileges::getEventDestination)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700673 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700674 [&app](const crow::Request& req,
675 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
676 const std::string& param) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400677 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
678 {
679 return;
680 }
Chicago Duan3d307082020-11-26 14:12:12 +0800681
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400682 if (param.starts_with("snmp"))
683 {
684 getSnmpTrapClient(asyncResp, param);
685 return;
686 }
Chicago Duan3d307082020-11-26 14:12:12 +0800687
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400688 std::shared_ptr<Subscription> subValue =
689 EventServiceManager::getInstance().getSubscription(param);
690 if (subValue == nullptr)
691 {
692 asyncResp->res.result(
693 boost::beast::http::status::not_found);
694 return;
695 }
696 const std::string& id = param;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530697
Ed Tanous4b712a22023-08-02 12:56:52 -0700698 const persistent_data::UserSubscription& userSub =
699 subValue->userSub;
zhanghch058d1b46d2021-04-01 11:18:24 +0800700
Ed Tanous4b712a22023-08-02 12:56:52 -0700701 nlohmann::json& jVal = asyncResp->res.jsonValue;
702 jVal["@odata.type"] =
703 "#EventDestination.v1_14_1.EventDestination";
704 jVal["Protocol"] =
705 event_destination::EventDestinationProtocol::Redfish;
706 jVal["@odata.id"] = boost::urls::format(
707 "/redfish/v1/EventService/Subscriptions/{}", id);
708 jVal["Id"] = id;
709 jVal["Name"] = "Event Destination " + id;
710 jVal["Destination"] = userSub.destinationUrl;
711 jVal["Context"] = userSub.customText;
712 jVal["SubscriptionType"] = userSub.subscriptionType;
713 jVal["HttpHeaders"] = nlohmann::json::array();
714 jVal["EventFormatType"] = userSub.eventFormatType;
715 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
716 jVal["ResourceTypes"] = userSub.resourceTypes;
717
718 jVal["MessageIds"] = userSub.registryMsgIds;
719 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
720 jVal["VerifyCertificate"] = userSub.verifyCertificate;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530721
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400722 nlohmann::json::array_t mrdJsonArray;
Ed Tanous4b712a22023-08-02 12:56:52 -0700723 for (const auto& mdrUri : userSub.metricReportDefinitions)
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400724 {
725 nlohmann::json::object_t mdr;
726 mdr["@odata.id"] = mdrUri;
727 mrdJsonArray.emplace_back(std::move(mdr));
728 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700729 jVal["MetricReportDefinitions"] = mrdJsonArray;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400730 });
Ravi Teja9d41aec2021-07-23 01:57:01 -0500731 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700732 // The below privilege is wrong, it should be ConfigureManager OR
733 // ConfigureSelf
Abhishek Patel7eeafa72021-07-28 10:59:16 -0500734 // https://github.com/openbmc/bmcweb/issues/220
Ed Tanoused398212021-06-09 17:05:54 -0700735 //.privileges(redfish::privileges::patchEventDestination)
Ed Tanous432a8902021-06-14 15:28:56 -0700736 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700737 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700738 [&app](const crow::Request& req,
739 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
740 const std::string& param) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400741 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700742 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400743 return;
744 }
745 std::shared_ptr<Subscription> subValue =
746 EventServiceManager::getInstance().getSubscription(param);
747 if (subValue == nullptr)
748 {
749 asyncResp->res.result(
750 boost::beast::http::status::not_found);
751 return;
752 }
753
754 std::optional<std::string> context;
755 std::optional<std::string> retryPolicy;
756 std::optional<bool> verifyCertificate;
757 std::optional<std::vector<nlohmann::json::object_t>> headers;
758
Myung Baeafc474a2024-10-09 00:53:29 -0700759 if (!json_util::readJsonPatch( //
760 req, asyncResp->res, //
761 "Context", context, //
762 "DeliveryRetryPolicy", retryPolicy, //
763 "HttpHeaders", headers, //
764 "VerifyCertificate", verifyCertificate //
765 ))
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400766 {
767 return;
768 }
769
770 if (context)
771 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700772 subValue->userSub.customText = *context;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400773 }
774
775 if (headers)
776 {
777 boost::beast::http::fields fields;
778 for (const nlohmann::json::object_t& headerChunk : *headers)
Ed Tanous601c71a2021-09-08 16:40:12 -0700779 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400780 for (const auto& it : headerChunk)
781 {
782 const std::string* value =
783 it.second.get_ptr<const std::string*>();
784 if (value == nullptr)
785 {
786 messages::propertyValueFormatError(
787 asyncResp->res, it.second,
788 "HttpHeaders/" + it.first);
789 return;
790 }
791 fields.set(it.first, *value);
792 }
793 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700794 subValue->userSub.httpHeaders = std::move(fields);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400795 }
796
797 if (retryPolicy)
798 {
799 if (std::ranges::find(supportedRetryPolicies,
800 *retryPolicy) ==
801 supportedRetryPolicies.end())
802 {
803 messages::propertyValueNotInList(asyncResp->res,
804 *retryPolicy,
805 "DeliveryRetryPolicy");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700806 return;
807 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700808 subValue->userSub.retryPolicy = *retryPolicy;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700809 }
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530810
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400811 if (verifyCertificate)
812 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700813 subValue->userSub.verifyCertificate = *verifyCertificate;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400814 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700815
Myung Bae56ba3862024-10-10 17:09:37 -0700816 // Sync Subscription to UserSubscriptionConfig
817 persistent_data::EventServiceStore::getInstance()
818 .updateUserSubscriptionConfig(subValue->userSub);
819
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400820 EventServiceManager::getInstance().updateSubscriptionData();
821 });
Ravi Teja9d41aec2021-07-23 01:57:01 -0500822 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700823 // The below privilege is wrong, it should be ConfigureManager OR
824 // ConfigureSelf
Abhishek Patel7eeafa72021-07-28 10:59:16 -0500825 // https://github.com/openbmc/bmcweb/issues/220
Ed Tanoused398212021-06-09 17:05:54 -0700826 //.privileges(redfish::privileges::deleteEventDestination)
Ed Tanous432a8902021-06-14 15:28:56 -0700827 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700828 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700829 [&app](const crow::Request& req,
830 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
831 const std::string& param) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400832 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
833 {
834 return;
835 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700836 EventServiceManager& event = EventServiceManager::getInstance();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400837 if (param.starts_with("snmp"))
838 {
839 deleteSnmpTrapClient(asyncResp, param);
Ed Tanous4b712a22023-08-02 12:56:52 -0700840 event.deleteSubscription(param);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400841 return;
842 }
Chicago Duan3d307082020-11-26 14:12:12 +0800843
Ed Tanous4b712a22023-08-02 12:56:52 -0700844 if (!event.deleteSubscription(param))
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400845 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700846 messages::resourceNotFound(asyncResp->res,
847 "EventDestination", param);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400848 return;
849 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700850 messages::success(asyncResp->res);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400851 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700852}
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530853
854} // namespace redfish