blob: d415496c8e66999292caf1c20618c278a7c54482 [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"
Alexander Hansend109e2b2024-11-18 14:38:06 +010023#include "registries.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080024#include "registries/privilege_registry.hpp"
Alexander Hansend109e2b2024-11-18 14:38:06 +010025#include "registries_selector.hpp"
Chicago Duan3d307082020-11-26 14:12:12 +080026#include "snmp_trap_event_clients.hpp"
Alexander Hansend109e2b2024-11-18 14:38:06 +010027#include "utils/json_utils.hpp"
AppaRao Pulie5aaf042020-03-20 01:05:52 +053028
Ed Tanous601c71a2021-09-08 16:40:12 -070029#include <boost/beast/http/fields.hpp>
Chicago Duan3d307082020-11-26 14:12:12 +080030#include <boost/system/error_code.hpp>
Ed Tanousa716aa72023-08-01 11:35:53 -070031#include <boost/url/parse.hpp>
Chicago Duan3d307082020-11-26 14:12:12 +080032#include <sdbusplus/unpack_properties.hpp>
33#include <utils/dbus_utils.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070034
Chicago Duan3d307082020-11-26 14:12:12 +080035#include <charconv>
36#include <memory>
Ed Tanous3544d2a2023-08-06 18:12:20 -070037#include <ranges>
Patrick Williams1e270c52021-12-04 06:06:56 -060038#include <span>
Chicago Duan3d307082020-11-26 14:12:12 +080039#include <string>
Ed Tanousa14c9112024-09-04 10:46:47 -070040#include <vector>
Patrick Williams1e270c52021-12-04 06:06:56 -060041
AppaRao Pulie5aaf042020-03-20 01:05:52 +053042namespace redfish
43{
44
AppaRao Puli156d6b02020-04-25 06:04:05 +053045static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
46 eventFormatType, metricReportFormatType};
AppaRao Pulie5aaf042020-03-20 01:05:52 +053047static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +053048 "Base", "OpenBMC", "TaskEvent"};
AppaRao Pulie5aaf042020-03-20 01:05:52 +053049static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
50 "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
51
Sunitha Harishe56f2542020-07-22 02:38:59 -050052static constexpr const std::array<const char*, 1> supportedResourceTypes = {
53 "Task"};
Sunitha Harishe56f2542020-07-22 02:38:59 -050054
John Edward Broadbent7e860f12021-04-08 15:57:16 -070055inline void requestRoutesEventService(App& app)
AppaRao Pulie5aaf042020-03-20 01:05:52 +053056{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070057 BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
Ed Tanoused398212021-06-09 17:05:54 -070058 .privileges(redfish::privileges::getEventService)
Patrick Williamsbd79bce2024-08-16 15:22:20 -040059 .methods(
60 boost::beast::http::verb::
61 get)([&app](
62 const crow::Request& req,
63 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
64 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
65 {
66 return;
67 }
Ed Tanous14766872022-03-15 10:44:42 -070068
Patrick Williamsbd79bce2024-08-16 15:22:20 -040069 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
70 asyncResp->res.jsonValue["@odata.type"] =
71 "#EventService.v1_5_0.EventService";
72 asyncResp->res.jsonValue["Id"] = "EventService";
73 asyncResp->res.jsonValue["Name"] = "Event Service";
74 asyncResp->res.jsonValue["ServerSentEventUri"] =
75 "/redfish/v1/EventService/SSE";
AppaRao Puli5e44e3d2021-03-16 15:37:24 +000076
Patrick Williamsbd79bce2024-08-16 15:22:20 -040077 asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
78 "/redfish/v1/EventService/Subscriptions";
79 asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"]
80 ["target"] =
81 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
AppaRao Pulie5aaf042020-03-20 01:05:52 +053082
Patrick Williamsbd79bce2024-08-16 15:22:20 -040083 const persistent_data::EventServiceConfig eventServiceConfig =
84 persistent_data::EventServiceStore::getInstance()
85 .getEventServiceConfig();
zhanghch058d1b46d2021-04-01 11:18:24 +080086
Patrick Williamsbd79bce2024-08-16 15:22:20 -040087 asyncResp->res.jsonValue["Status"]["State"] =
88 (eventServiceConfig.enabled ? "Enabled" : "Disabled");
89 asyncResp->res.jsonValue["ServiceEnabled"] =
90 eventServiceConfig.enabled;
91 asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
92 eventServiceConfig.retryAttempts;
93 asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
94 eventServiceConfig.retryTimeoutInterval;
95 asyncResp->res.jsonValue["EventFormatTypes"] =
96 supportedEvtFormatTypes;
97 asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
98 asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053099
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400100 nlohmann::json::object_t supportedSSEFilters;
101 supportedSSEFilters["EventFormatType"] = true;
102 supportedSSEFilters["MessageId"] = true;
103 supportedSSEFilters["MetricReportDefinition"] = true;
104 supportedSSEFilters["RegistryPrefix"] = true;
105 supportedSSEFilters["OriginResource"] = false;
106 supportedSSEFilters["ResourceType"] = false;
AppaRao Puli7d1cc382020-05-16 02:42:22 +0530107
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400108 asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
109 std::move(supportedSSEFilters);
110 });
Ayushi Smriti07941a82020-05-21 15:55:34 +0530111
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700112 BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
Ed Tanoused398212021-06-09 17:05:54 -0700113 .privileges(redfish::privileges::patchEventService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700114 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700115 [&app](const crow::Request& req,
116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400117 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
118 {
119 return;
120 }
121 std::optional<bool> serviceEnabled;
122 std::optional<uint32_t> retryAttemps;
123 std::optional<uint32_t> retryInterval;
Myung Baeafc474a2024-10-09 00:53:29 -0700124 if (!json_util::readJsonPatch( //
125 req, asyncResp->res, //
126 "DeliveryRetryAttempts", retryAttemps, //
127 "DeliveryRetryIntervalSeconds", retryInterval, //
128 "ServiceEnabled", serviceEnabled //
129 ))
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400130 {
131 return;
132 }
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530133
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400134 persistent_data::EventServiceConfig eventServiceConfig =
135 persistent_data::EventServiceStore::getInstance()
136 .getEventServiceConfig();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700137
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400138 if (serviceEnabled)
139 {
140 eventServiceConfig.enabled = *serviceEnabled;
141 }
Sunitha Harishe56f2542020-07-22 02:38:59 -0500142
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400143 if (retryAttemps)
144 {
145 // Supported range [1-3]
146 if ((*retryAttemps < 1) || (*retryAttemps > 3))
147 {
148 messages::queryParameterOutOfRange(
149 asyncResp->res, std::to_string(*retryAttemps),
150 "DeliveryRetryAttempts", "[1-3]");
151 }
152 else
153 {
154 eventServiceConfig.retryAttempts = *retryAttemps;
155 }
156 }
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +0530157
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400158 if (retryInterval)
159 {
160 // Supported range [5 - 180]
161 if ((*retryInterval < 5) || (*retryInterval > 180))
162 {
163 messages::queryParameterOutOfRange(
164 asyncResp->res, std::to_string(*retryInterval),
165 "DeliveryRetryIntervalSeconds", "[5-180]");
166 }
167 else
168 {
169 eventServiceConfig.retryTimeoutInterval =
170 *retryInterval;
171 }
172 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700173
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400174 EventServiceManager::getInstance().setEventServiceConfig(
175 eventServiceConfig);
176 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700177}
178
179inline void requestRoutesSubmitTestEvent(App& app)
180{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700181 BMCWEB_ROUTE(
182 app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
Ed Tanoused398212021-06-09 17:05:54 -0700183 .privileges(redfish::privileges::postEventService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700184 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700185 [&app](const crow::Request& req,
186 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400187 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
188 {
189 return;
190 }
191 if (!EventServiceManager::getInstance().sendTestEventLog())
192 {
193 messages::serviceDisabled(asyncResp->res,
194 "/redfish/v1/EventService/");
195 return;
196 }
197 asyncResp->res.result(boost::beast::http::status::no_content);
198 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700199}
200
Chicago Duan3d307082020-11-26 14:12:12 +0800201inline void doSubscriptionCollection(
Ed Tanouse81de512023-06-27 17:07:00 -0700202 const boost::system::error_code& ec,
Chicago Duan3d307082020-11-26 14:12:12 +0800203 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
204 const dbus::utility::ManagedObjectType& resp)
205{
206 if (ec)
207 {
Ed Tanous13061012023-07-25 11:12:19 -0700208 if (ec.value() == EBADR || ec.value() == EHOSTUNREACH)
Chicago Duan3d307082020-11-26 14:12:12 +0800209 {
210 // This is an optional process so just return if it isn't there
211 return;
212 }
213
Ed Tanous62598e32023-07-17 17:06:25 -0700214 BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec);
Chicago Duan3d307082020-11-26 14:12:12 +0800215 messages::internalError(asyncResp->res);
216 return;
217 }
218 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
219 for (const auto& objpath : resp)
220 {
221 sdbusplus::message::object_path path(objpath.first);
222 const std::string snmpId = path.filename();
223 if (snmpId.empty())
224 {
Ed Tanous62598e32023-07-17 17:06:25 -0700225 BMCWEB_LOG_ERROR("The SNMP client ID is wrong");
Chicago Duan3d307082020-11-26 14:12:12 +0800226 messages::internalError(asyncResp->res);
227 return;
228 }
229
230 getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
231 }
232}
233
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700234inline void requestRoutesEventDestinationCollection(App& app)
235{
Gayathri Leburu1ebe3e42022-02-09 10:45:19 +0000236 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
Ed Tanoused398212021-06-09 17:05:54 -0700237 .privileges(redfish::privileges::getEventDestinationCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700238 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700239 [&app](const crow::Request& req,
240 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400241 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
242 {
243 return;
244 }
245 asyncResp->res.jsonValue["@odata.type"] =
246 "#EventDestinationCollection.EventDestinationCollection";
247 asyncResp->res.jsonValue["@odata.id"] =
248 "/redfish/v1/EventService/Subscriptions";
249 asyncResp->res.jsonValue["Name"] =
250 "Event Destination Collections";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700251
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400252 nlohmann::json& memberArray =
253 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700254
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400255 std::vector<std::string> subscripIds =
256 EventServiceManager::getInstance().getAllIDs();
257 memberArray = nlohmann::json::array();
258 asyncResp->res.jsonValue["Members@odata.count"] =
259 subscripIds.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700260
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400261 for (const std::string& id : subscripIds)
262 {
263 nlohmann::json::object_t member;
264 member["@odata.id"] = boost::urls::format(
265 "/redfish/v1/EventService/Subscriptions/{}" + id);
266 memberArray.emplace_back(std::move(member));
267 }
268 crow::connections::systemBus->async_method_call(
269 [asyncResp](const boost::system::error_code& ec,
270 const dbus::utility::ManagedObjectType& resp) {
271 doSubscriptionCollection(ec, asyncResp, resp);
272 },
273 "xyz.openbmc_project.Network.SNMP",
274 "/xyz/openbmc_project/network/snmp/manager",
275 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
276 });
Chicago Duan3d307082020-11-26 14:12:12 +0800277
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700278 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
Abhishek Patel7eeafa72021-07-28 10:59:16 -0500279 .privileges(redfish::privileges::postEventDestinationCollection)
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400280 .methods(
281 boost::beast::http::verb::
282 post)([&app](
283 const crow::Request& req,
284 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
285 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
286 {
287 return;
288 }
289 if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
290 maxNoOfSubscriptions)
291 {
292 messages::eventSubscriptionLimitExceeded(asyncResp->res);
293 return;
294 }
295 std::string destUrl;
296 std::string protocol;
297 std::optional<bool> verifyCertificate;
298 std::optional<std::string> context;
299 std::optional<std::string> subscriptionType;
300 std::optional<std::string> eventFormatType2;
301 std::optional<std::string> retryPolicy;
302 std::optional<std::vector<std::string>> msgIds;
303 std::optional<std::vector<std::string>> regPrefixes;
Ed Tanousa14c9112024-09-04 10:46:47 -0700304 std::optional<std::vector<std::string>> originResources;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400305 std::optional<std::vector<std::string>> resTypes;
306 std::optional<std::vector<nlohmann::json::object_t>> headers;
307 std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray;
Ed Tanousfffb8c12022-02-07 23:53:03 -0800308
Myung Baeafc474a2024-10-09 00:53:29 -0700309 if (!json_util::readJsonPatch( //
310 req, asyncResp->res, //
311 "Context", context, //
312 "DeliveryRetryPolicy", retryPolicy, //
313 "Destination", destUrl, //
314 "EventFormatType", eventFormatType2, //
315 "HttpHeaders", headers, //
316 "MessageIds", msgIds, //
317 "MetricReportDefinitions", mrdJsonArray, //
318 "OriginResources", originResources, //
319 "Protocol", protocol, //
320 "RegistryPrefixes", regPrefixes, //
321 "ResourceTypes", resTypes, //
322 "SubscriptionType", subscriptionType, //
323 "VerifyCertificate", verifyCertificate //
324 ))
Ed Tanousfffb8c12022-02-07 23:53:03 -0800325 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700326 return;
327 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700328 // clang-format on
Chicago Duan3d307082020-11-26 14:12:12 +0800329
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400330 // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
331 static constexpr const uint16_t maxDestinationSize = 2000;
332 if (destUrl.size() > maxDestinationSize)
Ed Tanousfffb8c12022-02-07 23:53:03 -0800333 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400334 messages::stringValueTooLong(asyncResp->res, "Destination",
335 maxDestinationSize);
Ed Tanous002d39b2022-05-31 08:59:27 -0700336 return;
337 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700338
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400339 if (regPrefixes && msgIds)
Ed Tanous002d39b2022-05-31 08:59:27 -0700340 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400341 if (!regPrefixes->empty() && !msgIds->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700342 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400343 messages::propertyValueConflict(
344 asyncResp->res, "MessageIds", "RegistryPrefixes");
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +0530345 return;
346 }
Ed Tanousfffb8c12022-02-07 23:53:03 -0800347 }
P Dheeraj Srujan Kumarb304bd72021-01-29 17:46:35 +0530348
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400349 boost::system::result<boost::urls::url> url =
350 boost::urls::parse_absolute_uri(destUrl);
351 if (!url)
Ed Tanousfffb8c12022-02-07 23:53:03 -0800352 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400353 BMCWEB_LOG_WARNING(
354 "Failed to validate and split destination url");
355 messages::propertyValueFormatError(asyncResp->res, destUrl,
356 "Destination");
357 return;
358 }
359 url->normalize();
George Liub07942e2024-11-01 09:59:40 +0800360
361 // port_number returns zero if it is not a valid representable port
362 if (url->has_port() && url->port_number() == 0)
363 {
364 BMCWEB_LOG_WARNING("{} is an invalid port in destination url",
365 url->port());
366 messages::propertyValueFormatError(asyncResp->res, destUrl,
367 "Destination");
368 return;
369 }
370
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400371 crow::utility::setProtocolDefaults(*url, protocol);
372 crow::utility::setPortDefaults(*url);
373
374 if (url->path().empty())
375 {
376 url->set_path("/");
377 }
378
379 if (url->has_userinfo())
380 {
381 messages::propertyValueFormatError(asyncResp->res, destUrl,
382 "Destination");
383 return;
384 }
385
386 if (protocol == "SNMPv2c")
387 {
388 if (context)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700389 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400390 messages::propertyValueConflict(asyncResp->res, "Context",
391 "Protocol");
AppaRao Puli144b6312020-08-03 22:23:12 +0530392 return;
393 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400394 if (eventFormatType2)
395 {
396 messages::propertyValueConflict(
397 asyncResp->res, "EventFormatType", "Protocol");
398 return;
399 }
400 if (retryPolicy)
401 {
402 messages::propertyValueConflict(asyncResp->res,
403 "RetryPolicy", "Protocol");
404 return;
405 }
406 if (msgIds)
407 {
408 messages::propertyValueConflict(asyncResp->res,
409 "MessageIds", "Protocol");
410 return;
411 }
412 if (regPrefixes)
413 {
414 messages::propertyValueConflict(
415 asyncResp->res, "RegistryPrefixes", "Protocol");
416 return;
417 }
418 if (resTypes)
419 {
420 messages::propertyValueConflict(
421 asyncResp->res, "ResourceTypes", "Protocol");
422 return;
423 }
424 if (headers)
425 {
426 messages::propertyValueConflict(asyncResp->res,
427 "HttpHeaders", "Protocol");
428 return;
429 }
430 if (mrdJsonArray)
431 {
432 messages::propertyValueConflict(
433 asyncResp->res, "MetricReportDefinitions", "Protocol");
434 return;
435 }
436 if (url->scheme() != "snmp")
437 {
438 messages::propertyValueConflict(asyncResp->res,
439 "Destination", "Protocol");
440 return;
441 }
442
443 addSnmpTrapClient(asyncResp, url->host_address(),
444 url->port_number());
445 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700446 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700447
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400448 std::shared_ptr<Subscription> subValue =
Myung Bae21a94d52024-10-14 15:02:57 -0700449 std::make_shared<Subscription>(
Myung Bae5fe4ef32024-10-19 09:56:02 -0400450 std::make_shared<persistent_data::UserSubscription>(), *url,
451 app.ioContext());
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400452
453 if (subscriptionType)
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400455 if (*subscriptionType != "RedfishEvent")
456 {
457 messages::propertyValueNotInList(
458 asyncResp->res, *subscriptionType, "SubscriptionType");
459 return;
460 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400461 subValue->userSub->subscriptionType = *subscriptionType;
Ed Tanousfffb8c12022-02-07 23:53:03 -0800462 }
463 else
464 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700465 // Default
Myung Bae5fe4ef32024-10-19 09:56:02 -0400466 subValue->userSub->subscriptionType = "RedfishEvent";
Ed Tanousfffb8c12022-02-07 23:53:03 -0800467 }
AppaRao Puli156d6b02020-04-25 06:04:05 +0530468
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400469 if (protocol != "Redfish")
Ed Tanousfffb8c12022-02-07 23:53:03 -0800470 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400471 messages::propertyValueNotInList(asyncResp->res, protocol,
472 "Protocol");
473 return;
474 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400475 subValue->userSub->protocol = protocol;
Ed Tanousfffb8c12022-02-07 23:53:03 -0800476
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400477 if (verifyCertificate)
478 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400479 subValue->userSub->verifyCertificate = *verifyCertificate;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400480 }
481
482 if (eventFormatType2)
483 {
484 if (std::ranges::find(supportedEvtFormatTypes,
485 *eventFormatType2) ==
486 supportedEvtFormatTypes.end())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700487 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400488 messages::propertyValueNotInList(
489 asyncResp->res, *eventFormatType2, "EventFormatType");
490 return;
491 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400492 subValue->userSub->eventFormatType = *eventFormatType2;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400493 }
494 else
495 {
496 // If not specified, use default "Event"
Myung Bae5fe4ef32024-10-19 09:56:02 -0400497 subValue->userSub->eventFormatType = "Event";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400498 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700499
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400500 if (context)
501 {
502 // This value is selected arbitrarily.
503 constexpr const size_t maxContextSize = 256;
504 if (context->size() > maxContextSize)
505 {
506 messages::stringValueTooLong(asyncResp->res, "Context",
507 maxContextSize);
508 return;
509 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400510 subValue->userSub->customText = *context;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400511 }
512
513 if (headers)
514 {
515 size_t cumulativeLen = 0;
516
517 for (const nlohmann::json::object_t& headerChunk : *headers)
518 {
519 for (const auto& item : headerChunk)
Ed Tanous002d39b2022-05-31 08:59:27 -0700520 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400521 const std::string* value =
522 item.second.get_ptr<const std::string*>();
523 if (value == nullptr)
524 {
525 messages::propertyValueFormatError(
526 asyncResp->res, item.second,
527 "HttpHeaders/" + item.first);
528 return;
529 }
530 // Adding a new json value is the size of the key, +
531 // the size of the value + 2 * 2 quotes for each, +
532 // the colon and space between. example:
533 // "key": "value"
534 cumulativeLen += item.first.size() + value->size() + 6;
535 // This value is selected to mirror http_connection.hpp
536 constexpr const uint16_t maxHeaderSizeED = 8096;
537 if (cumulativeLen > maxHeaderSizeED)
538 {
539 messages::arraySizeTooLong(
540 asyncResp->res, "HttpHeaders", maxHeaderSizeED);
541 return;
542 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400543 subValue->userSub->httpHeaders.set(item.first, *value);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400544 }
545 }
546 }
547
548 if (regPrefixes)
549 {
550 for (const std::string& it : *regPrefixes)
551 {
552 if (std::ranges::find(supportedRegPrefixes, it) ==
553 supportedRegPrefixes.end())
554 {
555 messages::propertyValueNotInList(asyncResp->res, it,
556 "RegistryPrefixes");
557 return;
558 }
559 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400560 subValue->userSub->registryPrefixes = *regPrefixes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400561 }
562
Ed Tanousa14c9112024-09-04 10:46:47 -0700563 if (originResources)
564 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400565 subValue->userSub->originResources = *originResources;
Ed Tanousa14c9112024-09-04 10:46:47 -0700566 }
567
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400568 if (resTypes)
569 {
570 for (const std::string& it : *resTypes)
571 {
572 if (std::ranges::find(supportedResourceTypes, it) ==
573 supportedResourceTypes.end())
574 {
575 messages::propertyValueNotInList(asyncResp->res, it,
576 "ResourceTypes");
577 return;
578 }
579 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400580 subValue->userSub->resourceTypes = *resTypes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400581 }
582
583 if (msgIds)
584 {
585 std::vector<std::string> registryPrefix;
586
587 // If no registry prefixes are mentioned, consider all
588 // supported prefixes
Myung Bae5fe4ef32024-10-19 09:56:02 -0400589 if (subValue->userSub->registryPrefixes.empty())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400590 {
591 registryPrefix.assign(supportedRegPrefixes.begin(),
592 supportedRegPrefixes.end());
593 }
594 else
595 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400596 registryPrefix = subValue->userSub->registryPrefixes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400597 }
598
599 for (const std::string& id : *msgIds)
600 {
601 bool validId = false;
602
603 // Check for Message ID in each of the selected Registry
604 for (const std::string& it : registryPrefix)
605 {
606 const std::span<const redfish::registries::MessageEntry>
607 registry =
608 redfish::registries::getRegistryFromPrefix(it);
609
610 if (std::ranges::any_of(
611 registry,
612 [&id](const redfish::registries::MessageEntry&
613 messageEntry) {
614 return id == messageEntry.first;
615 }))
616 {
617 validId = true;
618 break;
619 }
620 }
621
622 if (!validId)
623 {
624 messages::propertyValueNotInList(asyncResp->res, id,
625 "MessageIds");
626 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700627 }
628 }
629
Myung Bae5fe4ef32024-10-19 09:56:02 -0400630 subValue->userSub->registryMsgIds = *msgIds;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400631 }
632
633 if (retryPolicy)
634 {
635 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) ==
636 supportedRetryPolicies.end())
Ed Tanous002d39b2022-05-31 08:59:27 -0700637 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400638 messages::propertyValueNotInList(
639 asyncResp->res, *retryPolicy, "DeliveryRetryPolicy");
Ed Tanousfffb8c12022-02-07 23:53:03 -0800640 return;
641 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400642 subValue->userSub->retryPolicy = *retryPolicy;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400643 }
644 else
645 {
646 // Default "TerminateAfterRetries"
Myung Bae5fe4ef32024-10-19 09:56:02 -0400647 subValue->userSub->retryPolicy = "TerminateAfterRetries";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400648 }
649
650 if (mrdJsonArray)
651 {
652 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray)
653 {
654 std::string mrdUri;
655
656 if (!json_util::readJsonObject(mrdObj, asyncResp->res,
657 "@odata.id", mrdUri))
658
659 {
660 return;
661 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400662 subValue->userSub->metricReportDefinitions.emplace_back(
Ed Tanous4b712a22023-08-02 12:56:52 -0700663 mrdUri);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400664 }
Ed Tanousfffb8c12022-02-07 23:53:03 -0800665 }
666
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400667 std::string id =
668 EventServiceManager::getInstance().addPushSubscription(
669 subValue);
670 if (id.empty())
Ed Tanousfffb8c12022-02-07 23:53:03 -0800671 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400672 messages::internalError(asyncResp->res);
Ed Tanousfffb8c12022-02-07 23:53:03 -0800673 return;
674 }
675
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400676 messages::created(asyncResp->res);
677 asyncResp->res.addHeader(
678 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
679 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700680}
681
682inline void requestRoutesEventDestination(App& app)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530683{
Ravi Teja9d41aec2021-07-23 01:57:01 -0500684 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700685 .privileges(redfish::privileges::getEventDestination)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700686 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700687 [&app](const crow::Request& req,
688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
689 const std::string& param) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400690 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
691 {
692 return;
693 }
Chicago Duan3d307082020-11-26 14:12:12 +0800694
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400695 if (param.starts_with("snmp"))
696 {
697 getSnmpTrapClient(asyncResp, param);
698 return;
699 }
Chicago Duan3d307082020-11-26 14:12:12 +0800700
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400701 std::shared_ptr<Subscription> subValue =
702 EventServiceManager::getInstance().getSubscription(param);
703 if (subValue == nullptr)
704 {
705 asyncResp->res.result(
706 boost::beast::http::status::not_found);
707 return;
708 }
709 const std::string& id = param;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530710
Ed Tanous4b712a22023-08-02 12:56:52 -0700711 const persistent_data::UserSubscription& userSub =
Myung Bae5fe4ef32024-10-19 09:56:02 -0400712 *subValue->userSub;
zhanghch058d1b46d2021-04-01 11:18:24 +0800713
Ed Tanous4b712a22023-08-02 12:56:52 -0700714 nlohmann::json& jVal = asyncResp->res.jsonValue;
715 jVal["@odata.type"] =
716 "#EventDestination.v1_14_1.EventDestination";
717 jVal["Protocol"] =
718 event_destination::EventDestinationProtocol::Redfish;
719 jVal["@odata.id"] = boost::urls::format(
720 "/redfish/v1/EventService/Subscriptions/{}", id);
721 jVal["Id"] = id;
722 jVal["Name"] = "Event Destination " + id;
723 jVal["Destination"] = userSub.destinationUrl;
724 jVal["Context"] = userSub.customText;
725 jVal["SubscriptionType"] = userSub.subscriptionType;
726 jVal["HttpHeaders"] = nlohmann::json::array();
727 jVal["EventFormatType"] = userSub.eventFormatType;
728 jVal["RegistryPrefixes"] = userSub.registryPrefixes;
729 jVal["ResourceTypes"] = userSub.resourceTypes;
730
731 jVal["MessageIds"] = userSub.registryMsgIds;
732 jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
733 jVal["VerifyCertificate"] = userSub.verifyCertificate;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530734
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400735 nlohmann::json::array_t mrdJsonArray;
Ed Tanous4b712a22023-08-02 12:56:52 -0700736 for (const auto& mdrUri : userSub.metricReportDefinitions)
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400737 {
738 nlohmann::json::object_t mdr;
739 mdr["@odata.id"] = mdrUri;
740 mrdJsonArray.emplace_back(std::move(mdr));
741 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700742 jVal["MetricReportDefinitions"] = mrdJsonArray;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400743 });
Ravi Teja9d41aec2021-07-23 01:57:01 -0500744 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700745 // The below privilege is wrong, it should be ConfigureManager OR
746 // ConfigureSelf
Abhishek Patel7eeafa72021-07-28 10:59:16 -0500747 // https://github.com/openbmc/bmcweb/issues/220
Ed Tanoused398212021-06-09 17:05:54 -0700748 //.privileges(redfish::privileges::patchEventDestination)
Ed Tanous432a8902021-06-14 15:28:56 -0700749 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700750 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700751 [&app](const crow::Request& req,
752 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
753 const std::string& param) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400754 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700755 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400756 return;
757 }
758 std::shared_ptr<Subscription> subValue =
759 EventServiceManager::getInstance().getSubscription(param);
760 if (subValue == nullptr)
761 {
762 asyncResp->res.result(
763 boost::beast::http::status::not_found);
764 return;
765 }
766
767 std::optional<std::string> context;
768 std::optional<std::string> retryPolicy;
769 std::optional<bool> verifyCertificate;
770 std::optional<std::vector<nlohmann::json::object_t>> headers;
771
Myung Baeafc474a2024-10-09 00:53:29 -0700772 if (!json_util::readJsonPatch( //
773 req, asyncResp->res, //
774 "Context", context, //
775 "DeliveryRetryPolicy", retryPolicy, //
776 "HttpHeaders", headers, //
777 "VerifyCertificate", verifyCertificate //
778 ))
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400779 {
780 return;
781 }
782
783 if (context)
784 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400785 subValue->userSub->customText = *context;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400786 }
787
788 if (headers)
789 {
790 boost::beast::http::fields fields;
791 for (const nlohmann::json::object_t& headerChunk : *headers)
Ed Tanous601c71a2021-09-08 16:40:12 -0700792 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400793 for (const auto& it : headerChunk)
794 {
795 const std::string* value =
796 it.second.get_ptr<const std::string*>();
797 if (value == nullptr)
798 {
799 messages::propertyValueFormatError(
800 asyncResp->res, it.second,
801 "HttpHeaders/" + it.first);
802 return;
803 }
804 fields.set(it.first, *value);
805 }
806 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400807 subValue->userSub->httpHeaders = std::move(fields);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400808 }
809
810 if (retryPolicy)
811 {
812 if (std::ranges::find(supportedRetryPolicies,
813 *retryPolicy) ==
814 supportedRetryPolicies.end())
815 {
816 messages::propertyValueNotInList(asyncResp->res,
817 *retryPolicy,
818 "DeliveryRetryPolicy");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700819 return;
820 }
Myung Bae5fe4ef32024-10-19 09:56:02 -0400821 subValue->userSub->retryPolicy = *retryPolicy;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700822 }
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530823
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400824 if (verifyCertificate)
825 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400826 subValue->userSub->verifyCertificate = *verifyCertificate;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400827 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700828
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400829 EventServiceManager::getInstance().updateSubscriptionData();
830 });
Ravi Teja9d41aec2021-07-23 01:57:01 -0500831 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700832 // The below privilege is wrong, it should be ConfigureManager OR
833 // ConfigureSelf
Abhishek Patel7eeafa72021-07-28 10:59:16 -0500834 // https://github.com/openbmc/bmcweb/issues/220
Ed Tanoused398212021-06-09 17:05:54 -0700835 //.privileges(redfish::privileges::deleteEventDestination)
Ed Tanous432a8902021-06-14 15:28:56 -0700836 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700837 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700838 [&app](const crow::Request& req,
839 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
840 const std::string& param) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400841 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
842 {
843 return;
844 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700845 EventServiceManager& event = EventServiceManager::getInstance();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400846 if (param.starts_with("snmp"))
847 {
848 deleteSnmpTrapClient(asyncResp, param);
Ed Tanous4b712a22023-08-02 12:56:52 -0700849 event.deleteSubscription(param);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400850 return;
851 }
Chicago Duan3d307082020-11-26 14:12:12 +0800852
Ed Tanous4b712a22023-08-02 12:56:52 -0700853 if (!event.deleteSubscription(param))
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400854 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700855 messages::resourceNotFound(asyncResp->res,
856 "EventDestination", param);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400857 return;
858 }
Ed Tanous4b712a22023-08-02 12:56:52 -0700859 messages::success(asyncResp->res);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400860 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700861}
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530862
863} // namespace redfish