blob: 4dbe58d91dc841266982411ab072e72500109010 [file] [log] [blame]
George Liu9516f412022-09-29 17:21:41 +08001#pragma once
2
3#include "app.hpp"
4#include "dbus_utility.hpp"
5#include "error_messages.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -07006#include "generated/enums/resource.hpp"
George Liu9516f412022-09-29 17:21:41 +08007#include "query.hpp"
8#include "registries/privilege_registry.hpp"
9#include "utils/chassis_utils.hpp"
10
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080011#include <boost/system/error_code.hpp>
George Liu9516f412022-09-29 17:21:41 +080012#include <boost/url/format.hpp>
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080013#include <sdbusplus/asio/property.hpp>
George Liu9516f412022-09-29 17:21:41 +080014#include <sdbusplus/message/types.hpp>
15
16#include <functional>
17#include <memory>
18#include <optional>
19#include <string>
20#include <string_view>
21
22namespace redfish
23{
24constexpr std::array<std::string_view, 1> fanInterface = {
25 "xyz.openbmc_project.Inventory.Item.Fan"};
26
27inline void
28 updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
29 const std::string& chassisId,
30 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
31{
32 nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
33 for (const std::string& fanPath : fanPaths)
34 {
35 std::string fanName =
36 sdbusplus::message::object_path(fanPath).filename();
37 if (fanName.empty())
38 {
39 continue;
40 }
41
42 nlohmann::json item = nlohmann::json::object();
43 item["@odata.id"] = boost::urls::format(
44 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
45 fanName);
46
47 fanList.emplace_back(std::move(item));
48 }
49 asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
50}
51
52inline void getFanPaths(
53 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanousd547d8d2024-03-16 18:04:41 -070054 const std::string& validChassisPath,
George Liu9516f412022-09-29 17:21:41 +080055 const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
56 fanPaths)>& callback)
57{
Ed Tanousd547d8d2024-03-16 18:04:41 -070058 sdbusplus::message::object_path endpointPath{validChassisPath};
George Liu9516f412022-09-29 17:21:41 +080059 endpointPath /= "cooled_by";
60
61 dbus::utility::getAssociatedSubTreePaths(
62 endpointPath,
63 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
64 fanInterface,
65 [asyncResp, callback](
66 const boost::system::error_code& ec,
67 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040068 if (ec)
George Liu9516f412022-09-29 17:21:41 +080069 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040070 if (ec.value() != EBADR)
71 {
72 BMCWEB_LOG_ERROR(
73 "DBUS response error for getAssociatedSubTreePaths {}",
74 ec.value());
75 messages::internalError(asyncResp->res);
76 }
77 return;
George Liu9516f412022-09-29 17:21:41 +080078 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -040079 callback(subtreePaths);
80 });
George Liu9516f412022-09-29 17:21:41 +080081}
82
83inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
84 const std::string& chassisId,
85 const std::optional<std::string>& validChassisPath)
86{
87 if (!validChassisPath)
88 {
89 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
90 return;
91 }
92
93 asyncResp->res.addHeader(
94 boost::beast::http::field::link,
95 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
96 asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
97 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
98 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
99 asyncResp->res.jsonValue["Name"] = "Fan Collection";
100 asyncResp->res.jsonValue["Description"] =
101 "The collection of Fan resource instances " + chassisId;
102 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
103 asyncResp->res.jsonValue["Members@odata.count"] = 0;
104
Ed Tanousd547d8d2024-03-16 18:04:41 -0700105 getFanPaths(asyncResp, *validChassisPath,
George Liu9516f412022-09-29 17:21:41 +0800106 std::bind_front(updateFanList, asyncResp, chassisId));
107}
108
109inline void
110 handleFanCollectionHead(App& app, const crow::Request& req,
111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
112 const std::string& chassisId)
113{
114 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
115 {
116 return;
117 }
118
119 redfish::chassis_utils::getValidChassisPath(
120 asyncResp, chassisId,
121 [asyncResp,
122 chassisId](const std::optional<std::string>& validChassisPath) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400123 if (!validChassisPath)
124 {
125 messages::resourceNotFound(asyncResp->res, "Chassis",
126 chassisId);
127 return;
128 }
129 asyncResp->res.addHeader(
130 boost::beast::http::field::link,
131 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
132 });
George Liu9516f412022-09-29 17:21:41 +0800133}
134
135inline void
136 handleFanCollectionGet(App& app, const crow::Request& req,
137 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
138 const std::string& chassisId)
139{
140 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
141 {
142 return;
143 }
144
145 redfish::chassis_utils::getValidChassisPath(
146 asyncResp, chassisId,
147 std::bind_front(doFanCollection, asyncResp, chassisId));
148}
149
150inline void requestRoutesFanCollection(App& app)
151{
152 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
153 .privileges(redfish::privileges::headFanCollection)
154 .methods(boost::beast::http::verb::head)(
155 std::bind_front(handleFanCollectionHead, std::ref(app)));
156
157 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
158 .privileges(redfish::privileges::getFanCollection)
159 .methods(boost::beast::http::verb::get)(
160 std::bind_front(handleFanCollectionGet, std::ref(app)));
161}
162
George Liue4e54232022-09-30 09:08:11 +0800163inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
164{
165 std::string fanName = sdbusplus::message::object_path(fanPath).filename();
166
167 return !(fanName.empty() || fanName != fanId);
168}
169
Ed Tanous4ff0f1f2024-09-04 17:27:37 -0700170inline void handleFanPath(
George Liue4e54232022-09-30 09:08:11 +0800171 const std::string& fanId,
172 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
173 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
174 const std::function<void(const std::string& fanPath,
175 const std::string& service)>& callback)
176{
177 for (const auto& fanPath : fanPaths)
178 {
179 if (!checkFanId(fanPath, fanId))
180 {
181 continue;
182 }
183 dbus::utility::getDbusObject(
184 fanPath, fanInterface,
185 [fanPath, asyncResp,
186 callback](const boost::system::error_code& ec,
187 const dbus::utility::MapperGetObject& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400188 if (ec || object.empty())
189 {
190 BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
191 ec.value());
192 messages::internalError(asyncResp->res);
193 return;
194 }
195 callback(fanPath, object.begin()->first);
196 });
George Liue4e54232022-09-30 09:08:11 +0800197
198 return;
199 }
Ed Tanous62598e32023-07-17 17:06:25 -0700200 BMCWEB_LOG_WARNING("Fan not found {}", fanId);
George Liue4e54232022-09-30 09:08:11 +0800201 messages::resourceNotFound(asyncResp->res, "Fan", fanId);
202}
203
204inline void getValidFanPath(
205 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
206 const std::string& validChassisPath, const std::string& fanId,
207 const std::function<void(const std::string& fanPath,
208 const std::string& service)>& callback)
209{
210 getFanPaths(
211 asyncResp, validChassisPath,
212 [fanId, asyncResp, callback](
213 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400214 handleFanPath(fanId, asyncResp, fanPaths, callback);
215 });
George Liue4e54232022-09-30 09:08:11 +0800216}
217
218inline void addFanCommonProperties(crow::Response& resp,
219 const std::string& chassisId,
220 const std::string& fanId)
221{
222 resp.addHeader(boost::beast::http::field::link,
223 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
224 resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
225 resp.jsonValue["Name"] = "Fan";
226 resp.jsonValue["Id"] = fanId;
227 resp.jsonValue["@odata.id"] = boost::urls::format(
228 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
Ed Tanous539d8c62024-06-19 14:38:27 -0700229 resp.jsonValue["Status"]["State"] = resource::State::Enabled;
230 resp.jsonValue["Status"]["Health"] = resource::Health::OK;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800231}
232
233inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
234 const std::string& fanPath, const std::string& service)
235{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800236 dbus::utility::getProperty<bool>(
237 service, fanPath,
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800238 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
239 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400240 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800241 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400242 if (ec.value() != EBADR)
243 {
244 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
245 ec.value());
246 messages::internalError(asyncResp->res);
247 }
248 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800249 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800250
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400251 if (!value)
252 {
253 asyncResp->res.jsonValue["Status"]["Health"] =
254 resource::Health::Critical;
255 }
256 });
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800257}
258
259inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
260 const std::string& fanPath, const std::string& service)
261{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800262 dbus::utility::getProperty<bool>(
263 service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present",
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800264 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400265 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800266 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400267 if (ec.value() != EBADR)
268 {
269 BMCWEB_LOG_ERROR("DBUS response error for State {}",
270 ec.value());
271 messages::internalError(asyncResp->res);
272 }
273 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800274 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800275
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400276 if (!value)
277 {
278 asyncResp->res.jsonValue["Status"]["State"] =
279 resource::State::Absent;
280 }
281 });
George Liue4e54232022-09-30 09:08:11 +0800282}
283
George Liu090ae7b2022-10-04 15:45:25 +0800284inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
285 const std::string& fanPath, const std::string& service)
286{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800287 dbus::utility::getAllProperties(
288 service, fanPath, "xyz.openbmc_project.Inventory.Decorator.Asset",
George Liu090ae7b2022-10-04 15:45:25 +0800289 [fanPath, asyncResp{asyncResp}](
290 const boost::system::error_code& ec,
291 const dbus::utility::DBusPropertiesMap& assetList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400292 if (ec)
George Liu090ae7b2022-10-04 15:45:25 +0800293 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400294 if (ec.value() != EBADR)
295 {
296 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
297 ec.value());
298 messages::internalError(asyncResp->res);
299 }
300 return;
George Liu090ae7b2022-10-04 15:45:25 +0800301 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400302 const std::string* manufacturer = nullptr;
303 const std::string* model = nullptr;
304 const std::string* partNumber = nullptr;
305 const std::string* serialNumber = nullptr;
306 const std::string* sparePartNumber = nullptr;
George Liu090ae7b2022-10-04 15:45:25 +0800307
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400308 const bool success = sdbusplus::unpackPropertiesNoThrow(
309 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
310 manufacturer, "Model", model, "PartNumber", partNumber,
311 "SerialNumber", serialNumber, "SparePartNumber",
312 sparePartNumber);
313 if (!success)
314 {
315 messages::internalError(asyncResp->res);
316 return;
317 }
318 if (manufacturer != nullptr)
319 {
320 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
321 }
322 if (model != nullptr)
323 {
324 asyncResp->res.jsonValue["Model"] = *model;
325 }
326 if (partNumber != nullptr)
327 {
328 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
329 }
330 if (serialNumber != nullptr)
331 {
332 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
333 }
334 if (sparePartNumber != nullptr && !sparePartNumber->empty())
335 {
336 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
337 }
338 });
George Liu090ae7b2022-10-04 15:45:25 +0800339}
340
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700341inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
George Liu4a2e4852022-10-04 16:13:44 +0800342 const std::string& fanPath,
343 const std::string& service)
344{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800345 dbus::utility::getProperty<std::string>(
346 service, fanPath,
George Liu4a2e4852022-10-04 16:13:44 +0800347 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700348 [asyncResp](const boost::system::error_code& ec,
349 const std::string& property) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400350 if (ec)
George Liu4a2e4852022-10-04 16:13:44 +0800351 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400352 if (ec.value() != EBADR)
353 {
354 BMCWEB_LOG_ERROR("DBUS response error for Location{}",
355 ec.value());
356 messages::internalError(asyncResp->res);
357 }
358 return;
George Liu4a2e4852022-10-04 16:13:44 +0800359 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400360 asyncResp->res
361 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
362 property;
363 });
George Liu4a2e4852022-10-04 16:13:44 +0800364}
365
George Liue4e54232022-09-30 09:08:11 +0800366inline void
367 afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
368 const std::string& chassisId, const std::string& fanId,
369 const std::string& fanPath, const std::string& service)
370{
George Liue4e54232022-09-30 09:08:11 +0800371 addFanCommonProperties(asyncResp->res, chassisId, fanId);
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800372 getFanState(asyncResp, fanPath, service);
373 getFanHealth(asyncResp, fanPath, service);
George Liu090ae7b2022-10-04 15:45:25 +0800374 getFanAsset(asyncResp, fanPath, service);
George Liu4a2e4852022-10-04 16:13:44 +0800375 getFanLocation(asyncResp, fanPath, service);
George Liue4e54232022-09-30 09:08:11 +0800376}
377
378inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
379 const std::string& chassisId, const std::string& fanId,
380 const std::optional<std::string>& validChassisPath)
381{
382 if (!validChassisPath)
383 {
384 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
385 return;
386 }
387
388 getValidFanPath(
389 asyncResp, *validChassisPath, fanId,
390 std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
391}
392
393inline void handleFanHead(App& app, const crow::Request& req,
394 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
395 const std::string& chassisId,
396 const std::string& fanId)
397{
398 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
399 {
400 return;
401 }
402
403 redfish::chassis_utils::getValidChassisPath(
404 asyncResp, chassisId,
405 [asyncResp, chassisId,
406 fanId](const std::optional<std::string>& validChassisPath) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400407 if (!validChassisPath)
408 {
409 messages::resourceNotFound(asyncResp->res, "Chassis",
410 chassisId);
411 return;
412 }
413 getValidFanPath(
414 asyncResp, *validChassisPath, fanId,
415 [asyncResp](const std::string&, const std::string&) {
416 asyncResp->res.addHeader(
417 boost::beast::http::field::link,
418 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
419 });
George Liue4e54232022-09-30 09:08:11 +0800420 });
George Liue4e54232022-09-30 09:08:11 +0800421}
422
423inline void handleFanGet(App& app, const crow::Request& req,
424 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
425 const std::string& chassisId, const std::string& fanId)
426{
427 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
428 {
429 return;
430 }
431
432 redfish::chassis_utils::getValidChassisPath(
433 asyncResp, chassisId,
434 std::bind_front(doFanGet, asyncResp, chassisId, fanId));
435}
436
437inline void requestRoutesFan(App& app)
438{
439 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
440 .privileges(redfish::privileges::headFan)
441 .methods(boost::beast::http::verb::head)(
442 std::bind_front(handleFanHead, std::ref(app)));
443
444 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
445 .privileges(redfish::privileges::getFan)
446 .methods(boost::beast::http::verb::get)(
447 std::bind_front(handleFanGet, std::ref(app)));
448}
449
George Liu9516f412022-09-29 17:21:41 +0800450} // namespace redfish