blob: baa25b333455f775aca8a50f815b6b4da6154197 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
George Liu9516f412022-09-29 17:21:41 +08003#pragma once
4
5#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08006#include "async_resp.hpp"
George Liu9516f412022-09-29 17:21:41 +08007#include "dbus_utility.hpp"
8#include "error_messages.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -07009#include "generated/enums/resource.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080010#include "http_request.hpp"
11#include "logging.hpp"
George Liu9516f412022-09-29 17:21:41 +080012#include "query.hpp"
13#include "registries/privilege_registry.hpp"
14#include "utils/chassis_utils.hpp"
Ed Tanous177612a2025-02-14 15:16:09 -080015#include "utils/dbus_utils.hpp"
George Liu9516f412022-09-29 17:21:41 +080016
Ed Tanousd7857202025-01-28 15:32:26 -080017#include <asm-generic/errno.h>
18
19#include <boost/beast/http/field.hpp>
20#include <boost/beast/http/verb.hpp>
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080021#include <boost/system/error_code.hpp>
George Liu9516f412022-09-29 17:21:41 +080022#include <boost/url/format.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080023#include <nlohmann/json.hpp>
24#include <sdbusplus/unpack_properties.hpp>
George Liu9516f412022-09-29 17:21:41 +080025
Ed Tanousd7857202025-01-28 15:32:26 -080026#include <array>
George Liu9516f412022-09-29 17:21:41 +080027#include <functional>
28#include <memory>
29#include <optional>
30#include <string>
31#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080032#include <utility>
George Liu9516f412022-09-29 17:21:41 +080033
34namespace redfish
35{
36constexpr std::array<std::string_view, 1> fanInterface = {
37 "xyz.openbmc_project.Inventory.Item.Fan"};
38
Patrick Williams504af5a2025-02-03 14:29:03 -050039inline void updateFanList(
40 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
41 const std::string& chassisId,
42 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
George Liu9516f412022-09-29 17:21:41 +080043{
44 nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
45 for (const std::string& fanPath : fanPaths)
46 {
47 std::string fanName =
48 sdbusplus::message::object_path(fanPath).filename();
49 if (fanName.empty())
50 {
51 continue;
52 }
53
54 nlohmann::json item = nlohmann::json::object();
55 item["@odata.id"] = boost::urls::format(
56 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
57 fanName);
58
59 fanList.emplace_back(std::move(item));
60 }
61 asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
62}
63
64inline void getFanPaths(
65 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanousd547d8d2024-03-16 18:04:41 -070066 const std::string& validChassisPath,
George Liu9516f412022-09-29 17:21:41 +080067 const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
68 fanPaths)>& callback)
69{
Ed Tanousd547d8d2024-03-16 18:04:41 -070070 sdbusplus::message::object_path endpointPath{validChassisPath};
George Liu9516f412022-09-29 17:21:41 +080071 endpointPath /= "cooled_by";
72
73 dbus::utility::getAssociatedSubTreePaths(
74 endpointPath,
75 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
76 fanInterface,
77 [asyncResp, callback](
78 const boost::system::error_code& ec,
79 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040080 if (ec)
George Liu9516f412022-09-29 17:21:41 +080081 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040082 if (ec.value() != EBADR)
83 {
84 BMCWEB_LOG_ERROR(
85 "DBUS response error for getAssociatedSubTreePaths {}",
86 ec.value());
87 messages::internalError(asyncResp->res);
88 }
89 return;
George Liu9516f412022-09-29 17:21:41 +080090 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -040091 callback(subtreePaths);
92 });
George Liu9516f412022-09-29 17:21:41 +080093}
94
95inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
96 const std::string& chassisId,
97 const std::optional<std::string>& validChassisPath)
98{
99 if (!validChassisPath)
100 {
101 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
102 return;
103 }
104
105 asyncResp->res.addHeader(
106 boost::beast::http::field::link,
107 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
108 asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
109 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
110 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
111 asyncResp->res.jsonValue["Name"] = "Fan Collection";
112 asyncResp->res.jsonValue["Description"] =
113 "The collection of Fan resource instances " + chassisId;
114 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
115 asyncResp->res.jsonValue["Members@odata.count"] = 0;
116
Ed Tanousd547d8d2024-03-16 18:04:41 -0700117 getFanPaths(asyncResp, *validChassisPath,
George Liu9516f412022-09-29 17:21:41 +0800118 std::bind_front(updateFanList, asyncResp, chassisId));
119}
120
Patrick Williams504af5a2025-02-03 14:29:03 -0500121inline void handleFanCollectionHead(
122 App& app, const crow::Request& req,
123 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
124 const std::string& chassisId)
George Liu9516f412022-09-29 17:21:41 +0800125{
126 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
127 {
128 return;
129 }
130
131 redfish::chassis_utils::getValidChassisPath(
132 asyncResp, chassisId,
133 [asyncResp,
134 chassisId](const std::optional<std::string>& validChassisPath) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400135 if (!validChassisPath)
136 {
137 messages::resourceNotFound(asyncResp->res, "Chassis",
138 chassisId);
139 return;
140 }
141 asyncResp->res.addHeader(
142 boost::beast::http::field::link,
143 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
144 });
George Liu9516f412022-09-29 17:21:41 +0800145}
146
Patrick Williams504af5a2025-02-03 14:29:03 -0500147inline void handleFanCollectionGet(
148 App& app, const crow::Request& req,
149 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
150 const std::string& chassisId)
George Liu9516f412022-09-29 17:21:41 +0800151{
152 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
153 {
154 return;
155 }
156
157 redfish::chassis_utils::getValidChassisPath(
158 asyncResp, chassisId,
159 std::bind_front(doFanCollection, asyncResp, chassisId));
160}
161
162inline void requestRoutesFanCollection(App& app)
163{
164 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
165 .privileges(redfish::privileges::headFanCollection)
166 .methods(boost::beast::http::verb::head)(
167 std::bind_front(handleFanCollectionHead, std::ref(app)));
168
169 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
170 .privileges(redfish::privileges::getFanCollection)
171 .methods(boost::beast::http::verb::get)(
172 std::bind_front(handleFanCollectionGet, std::ref(app)));
173}
174
George Liue4e54232022-09-30 09:08:11 +0800175inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
176{
177 std::string fanName = sdbusplus::message::object_path(fanPath).filename();
178
179 return !(fanName.empty() || fanName != fanId);
180}
181
Ed Tanous4ff0f1f2024-09-04 17:27:37 -0700182inline void handleFanPath(
George Liue4e54232022-09-30 09:08:11 +0800183 const std::string& fanId,
184 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
185 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
186 const std::function<void(const std::string& fanPath,
187 const std::string& service)>& callback)
188{
189 for (const auto& fanPath : fanPaths)
190 {
191 if (!checkFanId(fanPath, fanId))
192 {
193 continue;
194 }
195 dbus::utility::getDbusObject(
196 fanPath, fanInterface,
197 [fanPath, asyncResp,
198 callback](const boost::system::error_code& ec,
199 const dbus::utility::MapperGetObject& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400200 if (ec || object.empty())
201 {
202 BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
203 ec.value());
204 messages::internalError(asyncResp->res);
205 return;
206 }
207 callback(fanPath, object.begin()->first);
208 });
George Liue4e54232022-09-30 09:08:11 +0800209
210 return;
211 }
Ed Tanous62598e32023-07-17 17:06:25 -0700212 BMCWEB_LOG_WARNING("Fan not found {}", fanId);
George Liue4e54232022-09-30 09:08:11 +0800213 messages::resourceNotFound(asyncResp->res, "Fan", fanId);
214}
215
216inline void getValidFanPath(
217 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
218 const std::string& validChassisPath, const std::string& fanId,
219 const std::function<void(const std::string& fanPath,
220 const std::string& service)>& callback)
221{
222 getFanPaths(
223 asyncResp, validChassisPath,
224 [fanId, asyncResp, callback](
225 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400226 handleFanPath(fanId, asyncResp, fanPaths, callback);
227 });
George Liue4e54232022-09-30 09:08:11 +0800228}
229
230inline void addFanCommonProperties(crow::Response& resp,
231 const std::string& chassisId,
232 const std::string& fanId)
233{
234 resp.addHeader(boost::beast::http::field::link,
235 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
236 resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
237 resp.jsonValue["Name"] = "Fan";
238 resp.jsonValue["Id"] = fanId;
239 resp.jsonValue["@odata.id"] = boost::urls::format(
240 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
Ed Tanous539d8c62024-06-19 14:38:27 -0700241 resp.jsonValue["Status"]["State"] = resource::State::Enabled;
242 resp.jsonValue["Status"]["Health"] = resource::Health::OK;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800243}
244
245inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
246 const std::string& fanPath, const std::string& service)
247{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800248 dbus::utility::getProperty<bool>(
249 service, fanPath,
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800250 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
251 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400252 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800253 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400254 if (ec.value() != EBADR)
255 {
256 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
257 ec.value());
258 messages::internalError(asyncResp->res);
259 }
260 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800261 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800262
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400263 if (!value)
264 {
265 asyncResp->res.jsonValue["Status"]["Health"] =
266 resource::Health::Critical;
267 }
268 });
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800269}
270
271inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
272 const std::string& fanPath, const std::string& service)
273{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800274 dbus::utility::getProperty<bool>(
275 service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present",
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800276 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400277 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800278 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400279 if (ec.value() != EBADR)
280 {
281 BMCWEB_LOG_ERROR("DBUS response error for State {}",
282 ec.value());
283 messages::internalError(asyncResp->res);
284 }
285 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800286 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800287
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400288 if (!value)
289 {
290 asyncResp->res.jsonValue["Status"]["State"] =
291 resource::State::Absent;
292 }
293 });
George Liue4e54232022-09-30 09:08:11 +0800294}
295
George Liu090ae7b2022-10-04 15:45:25 +0800296inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
297 const std::string& fanPath, const std::string& service)
298{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800299 dbus::utility::getAllProperties(
300 service, fanPath, "xyz.openbmc_project.Inventory.Decorator.Asset",
George Liu090ae7b2022-10-04 15:45:25 +0800301 [fanPath, asyncResp{asyncResp}](
302 const boost::system::error_code& ec,
303 const dbus::utility::DBusPropertiesMap& assetList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400304 if (ec)
George Liu090ae7b2022-10-04 15:45:25 +0800305 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400306 if (ec.value() != EBADR)
307 {
308 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
309 ec.value());
310 messages::internalError(asyncResp->res);
311 }
312 return;
George Liu090ae7b2022-10-04 15:45:25 +0800313 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400314 const std::string* manufacturer = nullptr;
315 const std::string* model = nullptr;
316 const std::string* partNumber = nullptr;
317 const std::string* serialNumber = nullptr;
318 const std::string* sparePartNumber = nullptr;
George Liu090ae7b2022-10-04 15:45:25 +0800319
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400320 const bool success = sdbusplus::unpackPropertiesNoThrow(
321 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
322 manufacturer, "Model", model, "PartNumber", partNumber,
323 "SerialNumber", serialNumber, "SparePartNumber",
324 sparePartNumber);
325 if (!success)
326 {
327 messages::internalError(asyncResp->res);
328 return;
329 }
330 if (manufacturer != nullptr)
331 {
332 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
333 }
334 if (model != nullptr)
335 {
336 asyncResp->res.jsonValue["Model"] = *model;
337 }
338 if (partNumber != nullptr)
339 {
340 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
341 }
342 if (serialNumber != nullptr)
343 {
344 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
345 }
346 if (sparePartNumber != nullptr && !sparePartNumber->empty())
347 {
348 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
349 }
350 });
George Liu090ae7b2022-10-04 15:45:25 +0800351}
352
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700353inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
George Liu4a2e4852022-10-04 16:13:44 +0800354 const std::string& fanPath,
355 const std::string& service)
356{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800357 dbus::utility::getProperty<std::string>(
358 service, fanPath,
George Liu4a2e4852022-10-04 16:13:44 +0800359 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700360 [asyncResp](const boost::system::error_code& ec,
361 const std::string& property) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400362 if (ec)
George Liu4a2e4852022-10-04 16:13:44 +0800363 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400364 if (ec.value() != EBADR)
365 {
366 BMCWEB_LOG_ERROR("DBUS response error for Location{}",
367 ec.value());
368 messages::internalError(asyncResp->res);
369 }
370 return;
George Liu4a2e4852022-10-04 16:13:44 +0800371 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400372 asyncResp->res
373 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
374 property;
375 });
George Liu4a2e4852022-10-04 16:13:44 +0800376}
377
Patrick Williams504af5a2025-02-03 14:29:03 -0500378inline void afterGetValidFanPath(
379 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
380 const std::string& chassisId, const std::string& fanId,
381 const std::string& fanPath, const std::string& service)
George Liue4e54232022-09-30 09:08:11 +0800382{
George Liue4e54232022-09-30 09:08:11 +0800383 addFanCommonProperties(asyncResp->res, chassisId, fanId);
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800384 getFanState(asyncResp, fanPath, service);
385 getFanHealth(asyncResp, fanPath, service);
George Liu090ae7b2022-10-04 15:45:25 +0800386 getFanAsset(asyncResp, fanPath, service);
George Liu4a2e4852022-10-04 16:13:44 +0800387 getFanLocation(asyncResp, fanPath, service);
George Liue4e54232022-09-30 09:08:11 +0800388}
389
390inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
391 const std::string& chassisId, const std::string& fanId,
392 const std::optional<std::string>& validChassisPath)
393{
394 if (!validChassisPath)
395 {
396 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
397 return;
398 }
399
400 getValidFanPath(
401 asyncResp, *validChassisPath, fanId,
402 std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
403}
404
405inline void handleFanHead(App& app, const crow::Request& req,
406 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
407 const std::string& chassisId,
408 const std::string& fanId)
409{
410 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
411 {
412 return;
413 }
414
415 redfish::chassis_utils::getValidChassisPath(
416 asyncResp, chassisId,
417 [asyncResp, chassisId,
418 fanId](const std::optional<std::string>& validChassisPath) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400419 if (!validChassisPath)
420 {
421 messages::resourceNotFound(asyncResp->res, "Chassis",
422 chassisId);
423 return;
424 }
425 getValidFanPath(
426 asyncResp, *validChassisPath, fanId,
427 [asyncResp](const std::string&, const std::string&) {
428 asyncResp->res.addHeader(
429 boost::beast::http::field::link,
430 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
431 });
George Liue4e54232022-09-30 09:08:11 +0800432 });
George Liue4e54232022-09-30 09:08:11 +0800433}
434
435inline void handleFanGet(App& app, const crow::Request& req,
436 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
437 const std::string& chassisId, const std::string& fanId)
438{
439 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
440 {
441 return;
442 }
443
444 redfish::chassis_utils::getValidChassisPath(
445 asyncResp, chassisId,
446 std::bind_front(doFanGet, asyncResp, chassisId, fanId));
447}
448
449inline void requestRoutesFan(App& app)
450{
451 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
452 .privileges(redfish::privileges::headFan)
453 .methods(boost::beast::http::verb::head)(
454 std::bind_front(handleFanHead, std::ref(app)));
455
456 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
457 .privileges(redfish::privileges::getFan)
458 .methods(boost::beast::http::verb::get)(
459 std::bind_front(handleFanGet, std::ref(app)));
460}
461
George Liu9516f412022-09-29 17:21:41 +0800462} // namespace redfish