blob: 39b9e2abd7e553f84e9cc374c003d56e1f78e681 [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"
6#include "query.hpp"
7#include "registries/privilege_registry.hpp"
8#include "utils/chassis_utils.hpp"
9
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080010#include <boost/system/error_code.hpp>
George Liu9516f412022-09-29 17:21:41 +080011#include <boost/url/format.hpp>
Albert Zhang9f1ae5a2021-06-10 14:41:48 +080012#include <sdbusplus/asio/property.hpp>
George Liu9516f412022-09-29 17:21:41 +080013#include <sdbusplus/message/types.hpp>
14
15#include <functional>
16#include <memory>
17#include <optional>
18#include <string>
19#include <string_view>
20
21namespace redfish
22{
23constexpr std::array<std::string_view, 1> fanInterface = {
24 "xyz.openbmc_project.Inventory.Item.Fan"};
25
26inline void
27 updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
28 const std::string& chassisId,
29 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
30{
31 nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
32 for (const std::string& fanPath : fanPaths)
33 {
34 std::string fanName =
35 sdbusplus::message::object_path(fanPath).filename();
36 if (fanName.empty())
37 {
38 continue;
39 }
40
41 nlohmann::json item = nlohmann::json::object();
42 item["@odata.id"] = boost::urls::format(
43 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
44 fanName);
45
46 fanList.emplace_back(std::move(item));
47 }
48 asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
49}
50
51inline void getFanPaths(
52 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanousd547d8d2024-03-16 18:04:41 -070053 const std::string& validChassisPath,
George Liu9516f412022-09-29 17:21:41 +080054 const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
55 fanPaths)>& callback)
56{
Ed Tanousd547d8d2024-03-16 18:04:41 -070057 sdbusplus::message::object_path endpointPath{validChassisPath};
George Liu9516f412022-09-29 17:21:41 +080058 endpointPath /= "cooled_by";
59
60 dbus::utility::getAssociatedSubTreePaths(
61 endpointPath,
62 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
63 fanInterface,
64 [asyncResp, callback](
65 const boost::system::error_code& ec,
66 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
67 if (ec)
68 {
69 if (ec.value() != EBADR)
70 {
Ed Tanous62598e32023-07-17 17:06:25 -070071 BMCWEB_LOG_ERROR(
72 "DBUS response error for getAssociatedSubTreePaths {}",
73 ec.value());
George Liu9516f412022-09-29 17:21:41 +080074 messages::internalError(asyncResp->res);
75 }
76 return;
77 }
78 callback(subtreePaths);
Patrick Williams5a39f772023-10-20 11:20:21 -050079 });
George Liu9516f412022-09-29 17:21:41 +080080}
81
82inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
83 const std::string& chassisId,
84 const std::optional<std::string>& validChassisPath)
85{
86 if (!validChassisPath)
87 {
88 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
89 return;
90 }
91
92 asyncResp->res.addHeader(
93 boost::beast::http::field::link,
94 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
95 asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
96 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
97 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
98 asyncResp->res.jsonValue["Name"] = "Fan Collection";
99 asyncResp->res.jsonValue["Description"] =
100 "The collection of Fan resource instances " + chassisId;
101 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
102 asyncResp->res.jsonValue["Members@odata.count"] = 0;
103
Ed Tanousd547d8d2024-03-16 18:04:41 -0700104 getFanPaths(asyncResp, *validChassisPath,
George Liu9516f412022-09-29 17:21:41 +0800105 std::bind_front(updateFanList, asyncResp, chassisId));
106}
107
108inline void
109 handleFanCollectionHead(App& app, const crow::Request& req,
110 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
111 const std::string& chassisId)
112{
113 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
114 {
115 return;
116 }
117
118 redfish::chassis_utils::getValidChassisPath(
119 asyncResp, chassisId,
120 [asyncResp,
121 chassisId](const std::optional<std::string>& validChassisPath) {
122 if (!validChassisPath)
123 {
124 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
125 return;
126 }
127 asyncResp->res.addHeader(
128 boost::beast::http::field::link,
129 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
Patrick Williams5a39f772023-10-20 11:20:21 -0500130 });
George Liu9516f412022-09-29 17:21:41 +0800131}
132
133inline void
134 handleFanCollectionGet(App& app, const crow::Request& req,
135 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
136 const std::string& chassisId)
137{
138 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
139 {
140 return;
141 }
142
143 redfish::chassis_utils::getValidChassisPath(
144 asyncResp, chassisId,
145 std::bind_front(doFanCollection, asyncResp, chassisId));
146}
147
148inline void requestRoutesFanCollection(App& app)
149{
150 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
151 .privileges(redfish::privileges::headFanCollection)
152 .methods(boost::beast::http::verb::head)(
153 std::bind_front(handleFanCollectionHead, std::ref(app)));
154
155 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
156 .privileges(redfish::privileges::getFanCollection)
157 .methods(boost::beast::http::verb::get)(
158 std::bind_front(handleFanCollectionGet, std::ref(app)));
159}
160
George Liue4e54232022-09-30 09:08:11 +0800161inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
162{
163 std::string fanName = sdbusplus::message::object_path(fanPath).filename();
164
165 return !(fanName.empty() || fanName != fanId);
166}
167
168static inline void handleFanPath(
169 const std::string& fanId,
170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
171 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
172 const std::function<void(const std::string& fanPath,
173 const std::string& service)>& callback)
174{
175 for (const auto& fanPath : fanPaths)
176 {
177 if (!checkFanId(fanPath, fanId))
178 {
179 continue;
180 }
181 dbus::utility::getDbusObject(
182 fanPath, fanInterface,
183 [fanPath, asyncResp,
184 callback](const boost::system::error_code& ec,
185 const dbus::utility::MapperGetObject& object) {
186 if (ec || object.empty())
187 {
Ed Tanous62598e32023-07-17 17:06:25 -0700188 BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
189 ec.value());
George Liue4e54232022-09-30 09:08:11 +0800190 messages::internalError(asyncResp->res);
191 return;
192 }
193 callback(fanPath, object.begin()->first);
Patrick Williams5a39f772023-10-20 11:20:21 -0500194 });
George Liue4e54232022-09-30 09:08:11 +0800195
196 return;
197 }
Ed Tanous62598e32023-07-17 17:06:25 -0700198 BMCWEB_LOG_WARNING("Fan not found {}", fanId);
George Liue4e54232022-09-30 09:08:11 +0800199 messages::resourceNotFound(asyncResp->res, "Fan", fanId);
200}
201
202inline void getValidFanPath(
203 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
204 const std::string& validChassisPath, const std::string& fanId,
205 const std::function<void(const std::string& fanPath,
206 const std::string& service)>& callback)
207{
208 getFanPaths(
209 asyncResp, validChassisPath,
210 [fanId, asyncResp, callback](
211 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
212 handleFanPath(fanId, asyncResp, fanPaths, callback);
Patrick Williams5a39f772023-10-20 11:20:21 -0500213 });
George Liue4e54232022-09-30 09:08:11 +0800214}
215
216inline void addFanCommonProperties(crow::Response& resp,
217 const std::string& chassisId,
218 const std::string& fanId)
219{
220 resp.addHeader(boost::beast::http::field::link,
221 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
222 resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
223 resp.jsonValue["Name"] = "Fan";
224 resp.jsonValue["Id"] = fanId;
225 resp.jsonValue["@odata.id"] = boost::urls::format(
226 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800227 resp.jsonValue["Status"]["State"] = "Enabled";
228 resp.jsonValue["Status"]["Health"] = "OK";
229}
230
231inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
232 const std::string& fanPath, const std::string& service)
233{
234 sdbusplus::asio::getProperty<bool>(
235 *crow::connections::systemBus, service, fanPath,
236 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
237 [asyncResp](const boost::system::error_code& ec, const bool value) {
238 if (ec)
239 {
240 if (ec.value() != EBADR)
241 {
Ed Tanous62598e32023-07-17 17:06:25 -0700242 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
243 ec.value());
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800244 messages::internalError(asyncResp->res);
245 }
246 return;
247 }
248
249 if (!value)
250 {
251 asyncResp->res.jsonValue["Status"]["Health"] = "Critical";
252 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500253 });
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800254}
255
256inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
257 const std::string& fanPath, const std::string& service)
258{
259 sdbusplus::asio::getProperty<bool>(
260 *crow::connections::systemBus, service, fanPath,
261 "xyz.openbmc_project.Inventory.Item", "Present",
262 [asyncResp](const boost::system::error_code& ec, const bool value) {
263 if (ec)
264 {
265 if (ec.value() != EBADR)
266 {
Ed Tanous62598e32023-07-17 17:06:25 -0700267 BMCWEB_LOG_ERROR("DBUS response error for State {}",
268 ec.value());
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800269 messages::internalError(asyncResp->res);
270 }
271 return;
272 }
273
274 if (!value)
275 {
276 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
277 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500278 });
George Liue4e54232022-09-30 09:08:11 +0800279}
280
George Liu090ae7b2022-10-04 15:45:25 +0800281inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
282 const std::string& fanPath, const std::string& service)
283{
284 sdbusplus::asio::getAllProperties(
285 *crow::connections::systemBus, service, fanPath,
286 "xyz.openbmc_project.Inventory.Decorator.Asset",
287 [fanPath, asyncResp{asyncResp}](
288 const boost::system::error_code& ec,
289 const dbus::utility::DBusPropertiesMap& assetList) {
290 if (ec)
291 {
292 if (ec.value() != EBADR)
293 {
Ed Tanous62598e32023-07-17 17:06:25 -0700294 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
295 ec.value());
George Liu090ae7b2022-10-04 15:45:25 +0800296 messages::internalError(asyncResp->res);
297 }
298 return;
299 }
300 const std::string* manufacturer = nullptr;
301 const std::string* model = nullptr;
302 const std::string* partNumber = nullptr;
303 const std::string* serialNumber = nullptr;
304 const std::string* sparePartNumber = nullptr;
305
306 const bool success = sdbusplus::unpackPropertiesNoThrow(
307 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
308 manufacturer, "Model", model, "PartNumber", partNumber,
309 "SerialNumber", serialNumber, "SparePartNumber", sparePartNumber);
310 if (!success)
311 {
312 messages::internalError(asyncResp->res);
313 return;
314 }
315 if (manufacturer != nullptr)
316 {
317 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
318 }
319 if (model != nullptr)
320 {
321 asyncResp->res.jsonValue["Model"] = *model;
322 }
323 if (partNumber != nullptr)
324 {
325 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
326 }
327 if (serialNumber != nullptr)
328 {
329 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
330 }
331 if (sparePartNumber != nullptr && !sparePartNumber->empty())
332 {
333 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
334 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500335 });
George Liu090ae7b2022-10-04 15:45:25 +0800336}
337
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700338inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
George Liu4a2e4852022-10-04 16:13:44 +0800339 const std::string& fanPath,
340 const std::string& service)
341{
342 sdbusplus::asio::getProperty<std::string>(
343 *crow::connections::systemBus, service, fanPath,
344 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700345 [asyncResp](const boost::system::error_code& ec,
346 const std::string& property) {
George Liu4a2e4852022-10-04 16:13:44 +0800347 if (ec)
348 {
349 if (ec.value() != EBADR)
350 {
Ed Tanous62598e32023-07-17 17:06:25 -0700351 BMCWEB_LOG_ERROR("DBUS response error for Location{}",
352 ec.value());
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700353 messages::internalError(asyncResp->res);
George Liu4a2e4852022-10-04 16:13:44 +0800354 }
355 return;
356 }
Ed Tanousfc3edfd2023-07-20 12:41:30 -0700357 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
George Liu4a2e4852022-10-04 16:13:44 +0800358 property;
Patrick Williams5a39f772023-10-20 11:20:21 -0500359 });
George Liu4a2e4852022-10-04 16:13:44 +0800360}
361
George Liue4e54232022-09-30 09:08:11 +0800362inline void
363 afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
364 const std::string& chassisId, const std::string& fanId,
365 const std::string& fanPath, const std::string& service)
366{
George Liue4e54232022-09-30 09:08:11 +0800367 addFanCommonProperties(asyncResp->res, chassisId, fanId);
Albert Zhang9f1ae5a2021-06-10 14:41:48 +0800368 getFanState(asyncResp, fanPath, service);
369 getFanHealth(asyncResp, fanPath, service);
George Liu090ae7b2022-10-04 15:45:25 +0800370 getFanAsset(asyncResp, fanPath, service);
George Liu4a2e4852022-10-04 16:13:44 +0800371 getFanLocation(asyncResp, fanPath, service);
George Liue4e54232022-09-30 09:08:11 +0800372}
373
374inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
375 const std::string& chassisId, const std::string& fanId,
376 const std::optional<std::string>& validChassisPath)
377{
378 if (!validChassisPath)
379 {
380 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
381 return;
382 }
383
384 getValidFanPath(
385 asyncResp, *validChassisPath, fanId,
386 std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
387}
388
389inline void handleFanHead(App& app, const crow::Request& req,
390 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
391 const std::string& chassisId,
392 const std::string& fanId)
393{
394 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
395 {
396 return;
397 }
398
399 redfish::chassis_utils::getValidChassisPath(
400 asyncResp, chassisId,
401 [asyncResp, chassisId,
402 fanId](const std::optional<std::string>& validChassisPath) {
403 if (!validChassisPath)
404 {
405 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
406 return;
407 }
408 getValidFanPath(asyncResp, *validChassisPath, fanId,
409 [asyncResp](const std::string&, const std::string&) {
410 asyncResp->res.addHeader(
411 boost::beast::http::field::link,
412 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
413 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500414 });
George Liue4e54232022-09-30 09:08:11 +0800415}
416
417inline void handleFanGet(App& app, const crow::Request& req,
418 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
419 const std::string& chassisId, const std::string& fanId)
420{
421 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
422 {
423 return;
424 }
425
426 redfish::chassis_utils::getValidChassisPath(
427 asyncResp, chassisId,
428 std::bind_front(doFanGet, asyncResp, chassisId, fanId));
429}
430
431inline void requestRoutesFan(App& app)
432{
433 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
434 .privileges(redfish::privileges::headFan)
435 .methods(boost::beast::http::verb::head)(
436 std::bind_front(handleFanHead, std::ref(app)));
437
438 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
439 .privileges(redfish::privileges::getFan)
440 .methods(boost::beast::http::verb::get)(
441 std::bind_front(handleFanGet, std::ref(app)));
442}
443
George Liu9516f412022-09-29 17:21:41 +0800444} // namespace redfish