blob: 28b995e7948a30dede2d8c7e1407894af8621c92 [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{
236 sdbusplus::asio::getProperty<bool>(
237 *crow::connections::systemBus, service, fanPath,
238 "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{
262 sdbusplus::asio::getProperty<bool>(
263 *crow::connections::systemBus, service, fanPath,
264 "xyz.openbmc_project.Inventory.Item", "Present",
265 [asyncResp](const boost::system::error_code& ec, const bool value) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400266 if (ec)
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800267 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400268 if (ec.value() != EBADR)
269 {
270 BMCWEB_LOG_ERROR("DBUS response error for State {}",
271 ec.value());
272 messages::internalError(asyncResp->res);
273 }
274 return;
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800275 }
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800276
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400277 if (!value)
278 {
279 asyncResp->res.jsonValue["Status"]["State"] =
280 resource::State::Absent;
281 }
282 });
George Liue4e54232022-09-30 09:08:11 +0800283}
284
George Liu090ae7b2022-10-04 15:45:25 +0800285inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
286 const std::string& fanPath, const std::string& service)
287{
288 sdbusplus::asio::getAllProperties(
289 *crow::connections::systemBus, service, fanPath,
290 "xyz.openbmc_project.Inventory.Decorator.Asset",
291 [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{
347 sdbusplus::asio::getProperty<std::string>(
348 *crow::connections::systemBus, service, fanPath,
349 "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