blob: 1c5095c7b2ebc47f97b98e9bbb47011cbe2fee7e [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"
6#include "dbus_utility.hpp"
7#include "error_messages.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -07008#include "generated/enums/resource.hpp"
George Liu9516f412022-09-29 17:21:41 +08009#include "query.hpp"
10#include "registries/privilege_registry.hpp"
11#include "utils/chassis_utils.hpp"
12
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080013#include <boost/system/error_code.hpp>
George Liu9516f412022-09-29 17:21:41 +080014#include <boost/url/format.hpp>
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080015#include <sdbusplus/asio/property.hpp>
George Liu9516f412022-09-29 17:21:41 +080016#include <sdbusplus/message/types.hpp>
17
18#include <functional>
19#include <memory>
20#include <optional>
21#include <string>
22#include <string_view>
23
24namespace redfish
25{
26constexpr std::array<std::string_view, 1> fanInterface = {
27 "xyz.openbmc_project.Inventory.Item.Fan"};
28
29inline void
30 updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
31 const std::string& chassisId,
32 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
33{
34 nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
35 for (const std::string& fanPath : fanPaths)
36 {
37 std::string fanName =
38 sdbusplus::message::object_path(fanPath).filename();
39 if (fanName.empty())
40 {
41 continue;
42 }
43
44 nlohmann::json item = nlohmann::json::object();
45 item["@odata.id"] = boost::urls::format(
46 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
47 fanName);
48
49 fanList.emplace_back(std::move(item));
50 }
51 asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
52}
53
54inline void getFanPaths(
55 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanousd547d8d2024-03-16 18:04:41 -070056 const std::string& validChassisPath,
George Liu9516f412022-09-29 17:21:41 +080057 const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
58 fanPaths)>& callback)
59{
Ed Tanousd547d8d2024-03-16 18:04:41 -070060 sdbusplus::message::object_path endpointPath{validChassisPath};
George Liu9516f412022-09-29 17:21:41 +080061 endpointPath /= "cooled_by";
62
63 dbus::utility::getAssociatedSubTreePaths(
64 endpointPath,
65 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
66 fanInterface,
67 [asyncResp, callback](
68 const boost::system::error_code& ec,
69 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040070 if (ec)
George Liu9516f412022-09-29 17:21:41 +080071 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040072 if (ec.value() != EBADR)
73 {
74 BMCWEB_LOG_ERROR(
75 "DBUS response error for getAssociatedSubTreePaths {}",
76 ec.value());
77 messages::internalError(asyncResp->res);
78 }
79 return;
George Liu9516f412022-09-29 17:21:41 +080080 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -040081 callback(subtreePaths);
82 });
George Liu9516f412022-09-29 17:21:41 +080083}
84
85inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
86 const std::string& chassisId,
87 const std::optional<std::string>& validChassisPath)
88{
89 if (!validChassisPath)
90 {
91 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
92 return;
93 }
94
95 asyncResp->res.addHeader(
96 boost::beast::http::field::link,
97 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
98 asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
99 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
100 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
101 asyncResp->res.jsonValue["Name"] = "Fan Collection";
102 asyncResp->res.jsonValue["Description"] =
103 "The collection of Fan resource instances " + chassisId;
104 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
105 asyncResp->res.jsonValue["Members@odata.count"] = 0;
106
Ed Tanousd547d8d2024-03-16 18:04:41 -0700107 getFanPaths(asyncResp, *validChassisPath,
George Liu9516f412022-09-29 17:21:41 +0800108 std::bind_front(updateFanList, asyncResp, chassisId));
109}
110
111inline void
112 handleFanCollectionHead(App& app, const crow::Request& req,
113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114 const std::string& chassisId)
115{
116 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
117 {
118 return;
119 }
120
121 redfish::chassis_utils::getValidChassisPath(
122 asyncResp, chassisId,
123 [asyncResp,
124 chassisId](const std::optional<std::string>& validChassisPath) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400125 if (!validChassisPath)
126 {
127 messages::resourceNotFound(asyncResp->res, "Chassis",
128 chassisId);
129 return;
130 }
131 asyncResp->res.addHeader(
132 boost::beast::http::field::link,
133 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
134 });
George Liu9516f412022-09-29 17:21:41 +0800135}
136
137inline void
138 handleFanCollectionGet(App& app, const crow::Request& req,
139 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
140 const std::string& chassisId)
141{
142 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
143 {
144 return;
145 }
146
147 redfish::chassis_utils::getValidChassisPath(
148 asyncResp, chassisId,
149 std::bind_front(doFanCollection, asyncResp, chassisId));
150}
151
152inline void requestRoutesFanCollection(App& app)
153{
154 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
155 .privileges(redfish::privileges::headFanCollection)
156 .methods(boost::beast::http::verb::head)(
157 std::bind_front(handleFanCollectionHead, std::ref(app)));
158
159 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
160 .privileges(redfish::privileges::getFanCollection)
161 .methods(boost::beast::http::verb::get)(
162 std::bind_front(handleFanCollectionGet, std::ref(app)));
163}
164
George Liue4e54232022-09-30 09:08:11 +0800165inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
166{
167 std::string fanName = sdbusplus::message::object_path(fanPath).filename();
168
169 return !(fanName.empty() || fanName != fanId);
170}
171
Ed Tanous4ff0f1f2024-09-04 17:27:37 -0700172inline void handleFanPath(
George Liue4e54232022-09-30 09:08:11 +0800173 const std::string& fanId,
174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
175 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
176 const std::function<void(const std::string& fanPath,
177 const std::string& service)>& callback)
178{
179 for (const auto& fanPath : fanPaths)
180 {
181 if (!checkFanId(fanPath, fanId))
182 {
183 continue;
184 }
185 dbus::utility::getDbusObject(
186 fanPath, fanInterface,
187 [fanPath, asyncResp,
188 callback](const boost::system::error_code& ec,
189 const dbus::utility::MapperGetObject& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400190 if (ec || object.empty())
191 {
192 BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
193 ec.value());
194 messages::internalError(asyncResp->res);
195 return;
196 }
197 callback(fanPath, object.begin()->first);
198 });
George Liue4e54232022-09-30 09:08:11 +0800199
200 return;
201 }
Ed Tanous62598e32023-07-17 17:06:25 -0700202 BMCWEB_LOG_WARNING("Fan not found {}", fanId);
George Liue4e54232022-09-30 09:08:11 +0800203 messages::resourceNotFound(asyncResp->res, "Fan", fanId);
204}
205
206inline void getValidFanPath(
207 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
208 const std::string& validChassisPath, const std::string& fanId,
209 const std::function<void(const std::string& fanPath,
210 const std::string& service)>& callback)
211{
212 getFanPaths(
213 asyncResp, validChassisPath,
214 [fanId, asyncResp, callback](
215 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400216 handleFanPath(fanId, asyncResp, fanPaths, callback);
217 });
George Liue4e54232022-09-30 09:08:11 +0800218}
219
220inline void addFanCommonProperties(crow::Response& resp,
221 const std::string& chassisId,
222 const std::string& fanId)
223{
224 resp.addHeader(boost::beast::http::field::link,
225 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
226 resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
227 resp.jsonValue["Name"] = "Fan";
228 resp.jsonValue["Id"] = fanId;
229 resp.jsonValue["@odata.id"] = boost::urls::format(
230 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
Ed Tanous539d8c62024-06-19 14:38:27 -0700231 resp.jsonValue["Status"]["State"] = resource::State::Enabled;
232 resp.jsonValue["Status"]["Health"] = resource::Health::OK;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800233}
234
235inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
236 const std::string& fanPath, const std::string& service)
237{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800238 dbus::utility::getProperty<bool>(
239 service, fanPath,
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800240 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
241 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400242 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800243 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400244 if (ec.value() != EBADR)
245 {
246 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
247 ec.value());
248 messages::internalError(asyncResp->res);
249 }
250 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800251 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800252
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400253 if (!value)
254 {
255 asyncResp->res.jsonValue["Status"]["Health"] =
256 resource::Health::Critical;
257 }
258 });
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800259}
260
261inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const std::string& fanPath, const std::string& service)
263{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800264 dbus::utility::getProperty<bool>(
265 service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present",
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800266 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400267 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800268 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400269 if (ec.value() != EBADR)
270 {
271 BMCWEB_LOG_ERROR("DBUS response error for State {}",
272 ec.value());
273 messages::internalError(asyncResp->res);
274 }
275 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800276 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800277
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400278 if (!value)
279 {
280 asyncResp->res.jsonValue["Status"]["State"] =
281 resource::State::Absent;
282 }
283 });
George Liue4e54232022-09-30 09:08:11 +0800284}
285
George Liu090ae7b2022-10-04 15:45:25 +0800286inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
287 const std::string& fanPath, const std::string& service)
288{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800289 dbus::utility::getAllProperties(
290 service, fanPath, "xyz.openbmc_project.Inventory.Decorator.Asset",
George Liu090ae7b2022-10-04 15:45:25 +0800291 [fanPath, asyncResp{asyncResp}](
292 const boost::system::error_code& ec,
293 const dbus::utility::DBusPropertiesMap& assetList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400294 if (ec)
George Liu090ae7b2022-10-04 15:45:25 +0800295 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400296 if (ec.value() != EBADR)
297 {
298 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
299 ec.value());
300 messages::internalError(asyncResp->res);
301 }
302 return;
George Liu090ae7b2022-10-04 15:45:25 +0800303 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400304 const std::string* manufacturer = nullptr;
305 const std::string* model = nullptr;
306 const std::string* partNumber = nullptr;
307 const std::string* serialNumber = nullptr;
308 const std::string* sparePartNumber = nullptr;
George Liu090ae7b2022-10-04 15:45:25 +0800309
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400310 const bool success = sdbusplus::unpackPropertiesNoThrow(
311 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
312 manufacturer, "Model", model, "PartNumber", partNumber,
313 "SerialNumber", serialNumber, "SparePartNumber",
314 sparePartNumber);
315 if (!success)
316 {
317 messages::internalError(asyncResp->res);
318 return;
319 }
320 if (manufacturer != nullptr)
321 {
322 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
323 }
324 if (model != nullptr)
325 {
326 asyncResp->res.jsonValue["Model"] = *model;
327 }
328 if (partNumber != nullptr)
329 {
330 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
331 }
332 if (serialNumber != nullptr)
333 {
334 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
335 }
336 if (sparePartNumber != nullptr && !sparePartNumber->empty())
337 {
338 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
339 }
340 });
George Liu090ae7b2022-10-04 15:45:25 +0800341}
342
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700343inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
George Liu4a2e4852022-10-04 16:13:44 +0800344 const std::string& fanPath,
345 const std::string& service)
346{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800347 dbus::utility::getProperty<std::string>(
348 service, fanPath,
George Liu4a2e4852022-10-04 16:13:44 +0800349 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700350 [asyncResp](const boost::system::error_code& ec,
351 const std::string& property) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400352 if (ec)
George Liu4a2e4852022-10-04 16:13:44 +0800353 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400354 if (ec.value() != EBADR)
355 {
356 BMCWEB_LOG_ERROR("DBUS response error for Location{}",
357 ec.value());
358 messages::internalError(asyncResp->res);
359 }
360 return;
George Liu4a2e4852022-10-04 16:13:44 +0800361 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400362 asyncResp->res
363 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
364 property;
365 });
George Liu4a2e4852022-10-04 16:13:44 +0800366}
367
George Liue4e54232022-09-30 09:08:11 +0800368inline void
369 afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
370 const std::string& chassisId, const std::string& fanId,
371 const std::string& fanPath, const std::string& service)
372{
George Liue4e54232022-09-30 09:08:11 +0800373 addFanCommonProperties(asyncResp->res, chassisId, fanId);
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800374 getFanState(asyncResp, fanPath, service);
375 getFanHealth(asyncResp, fanPath, service);
George Liu090ae7b2022-10-04 15:45:25 +0800376 getFanAsset(asyncResp, fanPath, service);
George Liu4a2e4852022-10-04 16:13:44 +0800377 getFanLocation(asyncResp, fanPath, service);
George Liue4e54232022-09-30 09:08:11 +0800378}
379
380inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
381 const std::string& chassisId, const std::string& fanId,
382 const std::optional<std::string>& validChassisPath)
383{
384 if (!validChassisPath)
385 {
386 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
387 return;
388 }
389
390 getValidFanPath(
391 asyncResp, *validChassisPath, fanId,
392 std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
393}
394
395inline void handleFanHead(App& app, const crow::Request& req,
396 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
397 const std::string& chassisId,
398 const std::string& fanId)
399{
400 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
401 {
402 return;
403 }
404
405 redfish::chassis_utils::getValidChassisPath(
406 asyncResp, chassisId,
407 [asyncResp, chassisId,
408 fanId](const std::optional<std::string>& validChassisPath) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400409 if (!validChassisPath)
410 {
411 messages::resourceNotFound(asyncResp->res, "Chassis",
412 chassisId);
413 return;
414 }
415 getValidFanPath(
416 asyncResp, *validChassisPath, fanId,
417 [asyncResp](const std::string&, const std::string&) {
418 asyncResp->res.addHeader(
419 boost::beast::http::field::link,
420 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
421 });
George Liue4e54232022-09-30 09:08:11 +0800422 });
George Liue4e54232022-09-30 09:08:11 +0800423}
424
425inline void handleFanGet(App& app, const crow::Request& req,
426 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
427 const std::string& chassisId, const std::string& fanId)
428{
429 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
430 {
431 return;
432 }
433
434 redfish::chassis_utils::getValidChassisPath(
435 asyncResp, chassisId,
436 std::bind_front(doFanGet, asyncResp, chassisId, fanId));
437}
438
439inline void requestRoutesFan(App& app)
440{
441 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
442 .privileges(redfish::privileges::headFan)
443 .methods(boost::beast::http::verb::head)(
444 std::bind_front(handleFanHead, std::ref(app)));
445
446 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
447 .privileges(redfish::privileges::getFan)
448 .methods(boost::beast::http::verb::get)(
449 std::bind_front(handleFanGet, std::ref(app)));
450}
451
George Liu9516f412022-09-29 17:21:41 +0800452} // namespace redfish