xref: /openbmc/bmcweb/features/redfish/lib/fan.hpp (revision 177612aaa0633cf9d5aef0b763a43135cf552d9b)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
39516f41fSGeorge Liu #pragma once
49516f41fSGeorge Liu 
59516f41fSGeorge Liu #include "app.hpp"
6d7857201SEd Tanous #include "async_resp.hpp"
79516f41fSGeorge Liu #include "dbus_utility.hpp"
89516f41fSGeorge Liu #include "error_messages.hpp"
9539d8c6bSEd Tanous #include "generated/enums/resource.hpp"
10d7857201SEd Tanous #include "http_request.hpp"
11d7857201SEd Tanous #include "logging.hpp"
129516f41fSGeorge Liu #include "query.hpp"
139516f41fSGeorge Liu #include "registries/privilege_registry.hpp"
149516f41fSGeorge Liu #include "utils/chassis_utils.hpp"
15*177612aaSEd Tanous #include "utils/dbus_utils.hpp"
169516f41fSGeorge Liu 
17d7857201SEd Tanous #include <asm-generic/errno.h>
18d7857201SEd Tanous 
19d7857201SEd Tanous #include <boost/beast/http/field.hpp>
20d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
219f1ae5aeSAlbert Zhang #include <boost/system/error_code.hpp>
229516f41fSGeorge Liu #include <boost/url/format.hpp>
23d7857201SEd Tanous #include <nlohmann/json.hpp>
24d7857201SEd Tanous #include <sdbusplus/unpack_properties.hpp>
259516f41fSGeorge Liu 
26d7857201SEd Tanous #include <array>
279516f41fSGeorge Liu #include <functional>
289516f41fSGeorge Liu #include <memory>
299516f41fSGeorge Liu #include <optional>
309516f41fSGeorge Liu #include <string>
319516f41fSGeorge Liu #include <string_view>
32d7857201SEd Tanous #include <utility>
339516f41fSGeorge Liu 
349516f41fSGeorge Liu namespace redfish
359516f41fSGeorge Liu {
369516f41fSGeorge Liu constexpr std::array<std::string_view, 1> fanInterface = {
379516f41fSGeorge Liu     "xyz.openbmc_project.Inventory.Item.Fan"};
389516f41fSGeorge Liu 
39504af5a0SPatrick Williams inline void updateFanList(
40504af5a0SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
419516f41fSGeorge Liu     const std::string& chassisId,
429516f41fSGeorge Liu     const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
439516f41fSGeorge Liu {
449516f41fSGeorge Liu     nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
459516f41fSGeorge Liu     for (const std::string& fanPath : fanPaths)
469516f41fSGeorge Liu     {
479516f41fSGeorge Liu         std::string fanName =
489516f41fSGeorge Liu             sdbusplus::message::object_path(fanPath).filename();
499516f41fSGeorge Liu         if (fanName.empty())
509516f41fSGeorge Liu         {
519516f41fSGeorge Liu             continue;
529516f41fSGeorge Liu         }
539516f41fSGeorge Liu 
549516f41fSGeorge Liu         nlohmann::json item = nlohmann::json::object();
559516f41fSGeorge Liu         item["@odata.id"] = boost::urls::format(
569516f41fSGeorge Liu             "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
579516f41fSGeorge Liu             fanName);
589516f41fSGeorge Liu 
599516f41fSGeorge Liu         fanList.emplace_back(std::move(item));
609516f41fSGeorge Liu     }
619516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
629516f41fSGeorge Liu }
639516f41fSGeorge Liu 
649516f41fSGeorge Liu inline void getFanPaths(
659516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
66d547d8d2SEd Tanous     const std::string& validChassisPath,
679516f41fSGeorge Liu     const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
689516f41fSGeorge Liu                                  fanPaths)>& callback)
699516f41fSGeorge Liu {
70d547d8d2SEd Tanous     sdbusplus::message::object_path endpointPath{validChassisPath};
719516f41fSGeorge Liu     endpointPath /= "cooled_by";
729516f41fSGeorge Liu 
739516f41fSGeorge Liu     dbus::utility::getAssociatedSubTreePaths(
749516f41fSGeorge Liu         endpointPath,
759516f41fSGeorge Liu         sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
769516f41fSGeorge Liu         fanInterface,
779516f41fSGeorge Liu         [asyncResp, callback](
789516f41fSGeorge Liu             const boost::system::error_code& ec,
799516f41fSGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
809516f41fSGeorge Liu             if (ec)
819516f41fSGeorge Liu             {
829516f41fSGeorge Liu                 if (ec.value() != EBADR)
839516f41fSGeorge Liu                 {
8462598e31SEd Tanous                     BMCWEB_LOG_ERROR(
8562598e31SEd Tanous                         "DBUS response error for getAssociatedSubTreePaths {}",
8662598e31SEd Tanous                         ec.value());
879516f41fSGeorge Liu                     messages::internalError(asyncResp->res);
889516f41fSGeorge Liu                 }
899516f41fSGeorge Liu                 return;
909516f41fSGeorge Liu             }
919516f41fSGeorge Liu             callback(subtreePaths);
929516f41fSGeorge Liu         });
939516f41fSGeorge Liu }
949516f41fSGeorge Liu 
959516f41fSGeorge Liu inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
969516f41fSGeorge Liu                             const std::string& chassisId,
979516f41fSGeorge Liu                             const std::optional<std::string>& validChassisPath)
989516f41fSGeorge Liu {
999516f41fSGeorge Liu     if (!validChassisPath)
1009516f41fSGeorge Liu     {
1019516f41fSGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
1029516f41fSGeorge Liu         return;
1039516f41fSGeorge Liu     }
1049516f41fSGeorge Liu 
1059516f41fSGeorge Liu     asyncResp->res.addHeader(
1069516f41fSGeorge Liu         boost::beast::http::field::link,
1079516f41fSGeorge Liu         "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
1089516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
1099516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1109516f41fSGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
1119516f41fSGeorge Liu     asyncResp->res.jsonValue["Name"] = "Fan Collection";
1129516f41fSGeorge Liu     asyncResp->res.jsonValue["Description"] =
1139516f41fSGeorge Liu         "The collection of Fan resource instances " + chassisId;
1149516f41fSGeorge Liu     asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
1159516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = 0;
1169516f41fSGeorge Liu 
117d547d8d2SEd Tanous     getFanPaths(asyncResp, *validChassisPath,
1189516f41fSGeorge Liu                 std::bind_front(updateFanList, asyncResp, chassisId));
1199516f41fSGeorge Liu }
1209516f41fSGeorge Liu 
121504af5a0SPatrick Williams inline void handleFanCollectionHead(
122504af5a0SPatrick Williams     App& app, const crow::Request& req,
1239516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249516f41fSGeorge Liu     const std::string& chassisId)
1259516f41fSGeorge Liu {
1269516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1279516f41fSGeorge Liu     {
1289516f41fSGeorge Liu         return;
1299516f41fSGeorge Liu     }
1309516f41fSGeorge Liu 
1319516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
1329516f41fSGeorge Liu         asyncResp, chassisId,
1339516f41fSGeorge Liu         [asyncResp,
1349516f41fSGeorge Liu          chassisId](const std::optional<std::string>& validChassisPath) {
1359516f41fSGeorge Liu             if (!validChassisPath)
1369516f41fSGeorge Liu             {
137bd79bce8SPatrick Williams                 messages::resourceNotFound(asyncResp->res, "Chassis",
138bd79bce8SPatrick Williams                                            chassisId);
1399516f41fSGeorge Liu                 return;
1409516f41fSGeorge Liu             }
1419516f41fSGeorge Liu             asyncResp->res.addHeader(
1429516f41fSGeorge Liu                 boost::beast::http::field::link,
1439516f41fSGeorge Liu                 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
1449516f41fSGeorge Liu         });
1459516f41fSGeorge Liu }
1469516f41fSGeorge Liu 
147504af5a0SPatrick Williams inline void handleFanCollectionGet(
148504af5a0SPatrick Williams     App& app, const crow::Request& req,
1499516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1509516f41fSGeorge Liu     const std::string& chassisId)
1519516f41fSGeorge Liu {
1529516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1539516f41fSGeorge Liu     {
1549516f41fSGeorge Liu         return;
1559516f41fSGeorge Liu     }
1569516f41fSGeorge Liu 
1579516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
1589516f41fSGeorge Liu         asyncResp, chassisId,
1599516f41fSGeorge Liu         std::bind_front(doFanCollection, asyncResp, chassisId));
1609516f41fSGeorge Liu }
1619516f41fSGeorge Liu 
1629516f41fSGeorge Liu inline void requestRoutesFanCollection(App& app)
1639516f41fSGeorge Liu {
1649516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
1659516f41fSGeorge Liu         .privileges(redfish::privileges::headFanCollection)
1669516f41fSGeorge Liu         .methods(boost::beast::http::verb::head)(
1679516f41fSGeorge Liu             std::bind_front(handleFanCollectionHead, std::ref(app)));
1689516f41fSGeorge Liu 
1699516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
1709516f41fSGeorge Liu         .privileges(redfish::privileges::getFanCollection)
1719516f41fSGeorge Liu         .methods(boost::beast::http::verb::get)(
1729516f41fSGeorge Liu             std::bind_front(handleFanCollectionGet, std::ref(app)));
1739516f41fSGeorge Liu }
1749516f41fSGeorge Liu 
175e4e54232SGeorge Liu inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
176e4e54232SGeorge Liu {
177e4e54232SGeorge Liu     std::string fanName = sdbusplus::message::object_path(fanPath).filename();
178e4e54232SGeorge Liu 
179e4e54232SGeorge Liu     return !(fanName.empty() || fanName != fanId);
180e4e54232SGeorge Liu }
181e4e54232SGeorge Liu 
1824ff0f1f4SEd Tanous inline void handleFanPath(
183e4e54232SGeorge Liu     const std::string& fanId,
184e4e54232SGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
185e4e54232SGeorge Liu     const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
186e4e54232SGeorge Liu     const std::function<void(const std::string& fanPath,
187e4e54232SGeorge Liu                              const std::string& service)>& callback)
188e4e54232SGeorge Liu {
189e4e54232SGeorge Liu     for (const auto& fanPath : fanPaths)
190e4e54232SGeorge Liu     {
191e4e54232SGeorge Liu         if (!checkFanId(fanPath, fanId))
192e4e54232SGeorge Liu         {
193e4e54232SGeorge Liu             continue;
194e4e54232SGeorge Liu         }
195e4e54232SGeorge Liu         dbus::utility::getDbusObject(
196e4e54232SGeorge Liu             fanPath, fanInterface,
197e4e54232SGeorge Liu             [fanPath, asyncResp,
198e4e54232SGeorge Liu              callback](const boost::system::error_code& ec,
199e4e54232SGeorge Liu                        const dbus::utility::MapperGetObject& object) {
200e4e54232SGeorge Liu                 if (ec || object.empty())
201e4e54232SGeorge Liu                 {
20262598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
20362598e31SEd Tanous                                      ec.value());
204e4e54232SGeorge Liu                     messages::internalError(asyncResp->res);
205e4e54232SGeorge Liu                     return;
206e4e54232SGeorge Liu                 }
207e4e54232SGeorge Liu                 callback(fanPath, object.begin()->first);
208e4e54232SGeorge Liu             });
209e4e54232SGeorge Liu 
210e4e54232SGeorge Liu         return;
211e4e54232SGeorge Liu     }
21262598e31SEd Tanous     BMCWEB_LOG_WARNING("Fan not found {}", fanId);
213e4e54232SGeorge Liu     messages::resourceNotFound(asyncResp->res, "Fan", fanId);
214e4e54232SGeorge Liu }
215e4e54232SGeorge Liu 
216e4e54232SGeorge Liu inline void getValidFanPath(
217e4e54232SGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
218e4e54232SGeorge Liu     const std::string& validChassisPath, const std::string& fanId,
219e4e54232SGeorge Liu     const std::function<void(const std::string& fanPath,
220e4e54232SGeorge Liu                              const std::string& service)>& callback)
221e4e54232SGeorge Liu {
222e4e54232SGeorge Liu     getFanPaths(
223e4e54232SGeorge Liu         asyncResp, validChassisPath,
224e4e54232SGeorge Liu         [fanId, asyncResp, callback](
225e4e54232SGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
226e4e54232SGeorge Liu             handleFanPath(fanId, asyncResp, fanPaths, callback);
227e4e54232SGeorge Liu         });
228e4e54232SGeorge Liu }
229e4e54232SGeorge Liu 
230e4e54232SGeorge Liu inline void addFanCommonProperties(crow::Response& resp,
231e4e54232SGeorge Liu                                    const std::string& chassisId,
232e4e54232SGeorge Liu                                    const std::string& fanId)
233e4e54232SGeorge Liu {
234e4e54232SGeorge Liu     resp.addHeader(boost::beast::http::field::link,
235e4e54232SGeorge Liu                    "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
236e4e54232SGeorge Liu     resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
237e4e54232SGeorge Liu     resp.jsonValue["Name"] = "Fan";
238e4e54232SGeorge Liu     resp.jsonValue["Id"] = fanId;
239e4e54232SGeorge Liu     resp.jsonValue["@odata.id"] = boost::urls::format(
240e4e54232SGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
241539d8c6bSEd Tanous     resp.jsonValue["Status"]["State"] = resource::State::Enabled;
242539d8c6bSEd Tanous     resp.jsonValue["Status"]["Health"] = resource::Health::OK;
2439f1ae5aeSAlbert Zhang }
2449f1ae5aeSAlbert Zhang 
2459f1ae5aeSAlbert Zhang inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2469f1ae5aeSAlbert Zhang                          const std::string& fanPath, const std::string& service)
2479f1ae5aeSAlbert Zhang {
248deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
249deae6a78SEd Tanous         service, fanPath,
2509f1ae5aeSAlbert Zhang         "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
2519f1ae5aeSAlbert Zhang         [asyncResp](const boost::system::error_code& ec, const bool value) {
2529f1ae5aeSAlbert Zhang             if (ec)
2539f1ae5aeSAlbert Zhang             {
2549f1ae5aeSAlbert Zhang                 if (ec.value() != EBADR)
2559f1ae5aeSAlbert Zhang                 {
25662598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for Health {}",
25762598e31SEd Tanous                                      ec.value());
2589f1ae5aeSAlbert Zhang                     messages::internalError(asyncResp->res);
2599f1ae5aeSAlbert Zhang                 }
2609f1ae5aeSAlbert Zhang                 return;
2619f1ae5aeSAlbert Zhang             }
2629f1ae5aeSAlbert Zhang 
2639f1ae5aeSAlbert Zhang             if (!value)
2649f1ae5aeSAlbert Zhang             {
265539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["Health"] =
266539d8c6bSEd Tanous                     resource::Health::Critical;
2679f1ae5aeSAlbert Zhang             }
2689f1ae5aeSAlbert Zhang         });
2699f1ae5aeSAlbert Zhang }
2709f1ae5aeSAlbert Zhang 
2719f1ae5aeSAlbert Zhang inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2729f1ae5aeSAlbert Zhang                         const std::string& fanPath, const std::string& service)
2739f1ae5aeSAlbert Zhang {
274deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
275deae6a78SEd Tanous         service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present",
2769f1ae5aeSAlbert Zhang         [asyncResp](const boost::system::error_code& ec, const bool value) {
2779f1ae5aeSAlbert Zhang             if (ec)
2789f1ae5aeSAlbert Zhang             {
2799f1ae5aeSAlbert Zhang                 if (ec.value() != EBADR)
2809f1ae5aeSAlbert Zhang                 {
28162598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for State {}",
28262598e31SEd Tanous                                      ec.value());
2839f1ae5aeSAlbert Zhang                     messages::internalError(asyncResp->res);
2849f1ae5aeSAlbert Zhang                 }
2859f1ae5aeSAlbert Zhang                 return;
2869f1ae5aeSAlbert Zhang             }
2879f1ae5aeSAlbert Zhang 
2889f1ae5aeSAlbert Zhang             if (!value)
2899f1ae5aeSAlbert Zhang             {
290539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
291539d8c6bSEd Tanous                     resource::State::Absent;
2929f1ae5aeSAlbert Zhang             }
2939f1ae5aeSAlbert Zhang         });
294e4e54232SGeorge Liu }
295e4e54232SGeorge Liu 
296090ae7baSGeorge Liu inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
297090ae7baSGeorge Liu                         const std::string& fanPath, const std::string& service)
298090ae7baSGeorge Liu {
299deae6a78SEd Tanous     dbus::utility::getAllProperties(
300deae6a78SEd Tanous         service, fanPath, "xyz.openbmc_project.Inventory.Decorator.Asset",
301090ae7baSGeorge Liu         [fanPath, asyncResp{asyncResp}](
302090ae7baSGeorge Liu             const boost::system::error_code& ec,
303090ae7baSGeorge Liu             const dbus::utility::DBusPropertiesMap& assetList) {
304090ae7baSGeorge Liu             if (ec)
305090ae7baSGeorge Liu             {
306090ae7baSGeorge Liu                 if (ec.value() != EBADR)
307090ae7baSGeorge Liu                 {
30862598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
30962598e31SEd Tanous                                      ec.value());
310090ae7baSGeorge Liu                     messages::internalError(asyncResp->res);
311090ae7baSGeorge Liu                 }
312090ae7baSGeorge Liu                 return;
313090ae7baSGeorge Liu             }
314090ae7baSGeorge Liu             const std::string* manufacturer = nullptr;
315090ae7baSGeorge Liu             const std::string* model = nullptr;
316090ae7baSGeorge Liu             const std::string* partNumber = nullptr;
317090ae7baSGeorge Liu             const std::string* serialNumber = nullptr;
318090ae7baSGeorge Liu             const std::string* sparePartNumber = nullptr;
319090ae7baSGeorge Liu 
320090ae7baSGeorge Liu             const bool success = sdbusplus::unpackPropertiesNoThrow(
321090ae7baSGeorge Liu                 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
322090ae7baSGeorge Liu                 manufacturer, "Model", model, "PartNumber", partNumber,
323bd79bce8SPatrick Williams                 "SerialNumber", serialNumber, "SparePartNumber",
324bd79bce8SPatrick Williams                 sparePartNumber);
325090ae7baSGeorge Liu             if (!success)
326090ae7baSGeorge Liu             {
327090ae7baSGeorge Liu                 messages::internalError(asyncResp->res);
328090ae7baSGeorge Liu                 return;
329090ae7baSGeorge Liu             }
330090ae7baSGeorge Liu             if (manufacturer != nullptr)
331090ae7baSGeorge Liu             {
332090ae7baSGeorge Liu                 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
333090ae7baSGeorge Liu             }
334090ae7baSGeorge Liu             if (model != nullptr)
335090ae7baSGeorge Liu             {
336090ae7baSGeorge Liu                 asyncResp->res.jsonValue["Model"] = *model;
337090ae7baSGeorge Liu             }
338090ae7baSGeorge Liu             if (partNumber != nullptr)
339090ae7baSGeorge Liu             {
340090ae7baSGeorge Liu                 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
341090ae7baSGeorge Liu             }
342090ae7baSGeorge Liu             if (serialNumber != nullptr)
343090ae7baSGeorge Liu             {
344090ae7baSGeorge Liu                 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
345090ae7baSGeorge Liu             }
346090ae7baSGeorge Liu             if (sparePartNumber != nullptr && !sparePartNumber->empty())
347090ae7baSGeorge Liu             {
348090ae7baSGeorge Liu                 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
349090ae7baSGeorge Liu             }
350090ae7baSGeorge Liu         });
351090ae7baSGeorge Liu }
352090ae7baSGeorge Liu 
353fc3edfddSEd Tanous inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3544a2e485dSGeorge Liu                            const std::string& fanPath,
3554a2e485dSGeorge Liu                            const std::string& service)
3564a2e485dSGeorge Liu {
357deae6a78SEd Tanous     dbus::utility::getProperty<std::string>(
358deae6a78SEd Tanous         service, fanPath,
3594a2e485dSGeorge Liu         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
360fc3edfddSEd Tanous         [asyncResp](const boost::system::error_code& ec,
3614a2e485dSGeorge Liu                     const std::string& property) {
3624a2e485dSGeorge Liu             if (ec)
3634a2e485dSGeorge Liu             {
3644a2e485dSGeorge Liu                 if (ec.value() != EBADR)
3654a2e485dSGeorge Liu                 {
36662598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for Location{}",
36762598e31SEd Tanous                                      ec.value());
368fc3edfddSEd Tanous                     messages::internalError(asyncResp->res);
3694a2e485dSGeorge Liu                 }
3704a2e485dSGeorge Liu                 return;
3714a2e485dSGeorge Liu             }
372bd79bce8SPatrick Williams             asyncResp->res
373bd79bce8SPatrick Williams                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
3744a2e485dSGeorge Liu                 property;
3754a2e485dSGeorge Liu         });
3764a2e485dSGeorge Liu }
3774a2e485dSGeorge Liu 
378504af5a0SPatrick Williams inline void afterGetValidFanPath(
379504af5a0SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
380e4e54232SGeorge Liu     const std::string& chassisId, const std::string& fanId,
381e4e54232SGeorge Liu     const std::string& fanPath, const std::string& service)
382e4e54232SGeorge Liu {
383e4e54232SGeorge Liu     addFanCommonProperties(asyncResp->res, chassisId, fanId);
3849f1ae5aeSAlbert Zhang     getFanState(asyncResp, fanPath, service);
3859f1ae5aeSAlbert Zhang     getFanHealth(asyncResp, fanPath, service);
386090ae7baSGeorge Liu     getFanAsset(asyncResp, fanPath, service);
3874a2e485dSGeorge Liu     getFanLocation(asyncResp, fanPath, service);
388e4e54232SGeorge Liu }
389e4e54232SGeorge Liu 
390e4e54232SGeorge Liu inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
391e4e54232SGeorge Liu                      const std::string& chassisId, const std::string& fanId,
392e4e54232SGeorge Liu                      const std::optional<std::string>& validChassisPath)
393e4e54232SGeorge Liu {
394e4e54232SGeorge Liu     if (!validChassisPath)
395e4e54232SGeorge Liu     {
396e4e54232SGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
397e4e54232SGeorge Liu         return;
398e4e54232SGeorge Liu     }
399e4e54232SGeorge Liu 
400e4e54232SGeorge Liu     getValidFanPath(
401e4e54232SGeorge Liu         asyncResp, *validChassisPath, fanId,
402e4e54232SGeorge Liu         std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
403e4e54232SGeorge Liu }
404e4e54232SGeorge Liu 
405e4e54232SGeorge Liu inline void handleFanHead(App& app, const crow::Request& req,
406e4e54232SGeorge Liu                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
407e4e54232SGeorge Liu                           const std::string& chassisId,
408e4e54232SGeorge Liu                           const std::string& fanId)
409e4e54232SGeorge Liu {
410e4e54232SGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
411e4e54232SGeorge Liu     {
412e4e54232SGeorge Liu         return;
413e4e54232SGeorge Liu     }
414e4e54232SGeorge Liu 
415e4e54232SGeorge Liu     redfish::chassis_utils::getValidChassisPath(
416e4e54232SGeorge Liu         asyncResp, chassisId,
417e4e54232SGeorge Liu         [asyncResp, chassisId,
418e4e54232SGeorge Liu          fanId](const std::optional<std::string>& validChassisPath) {
419e4e54232SGeorge Liu             if (!validChassisPath)
420e4e54232SGeorge Liu             {
421bd79bce8SPatrick Williams                 messages::resourceNotFound(asyncResp->res, "Chassis",
422bd79bce8SPatrick Williams                                            chassisId);
423e4e54232SGeorge Liu                 return;
424e4e54232SGeorge Liu             }
425bd79bce8SPatrick Williams             getValidFanPath(
426bd79bce8SPatrick Williams                 asyncResp, *validChassisPath, fanId,
427e4e54232SGeorge Liu                 [asyncResp](const std::string&, const std::string&) {
428e4e54232SGeorge Liu                     asyncResp->res.addHeader(
429e4e54232SGeorge Liu                         boost::beast::http::field::link,
430e4e54232SGeorge Liu                         "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
431e4e54232SGeorge Liu                 });
432e4e54232SGeorge Liu         });
433e4e54232SGeorge Liu }
434e4e54232SGeorge Liu 
435e4e54232SGeorge Liu inline void handleFanGet(App& app, const crow::Request& req,
436e4e54232SGeorge Liu                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
437e4e54232SGeorge Liu                          const std::string& chassisId, const std::string& fanId)
438e4e54232SGeorge Liu {
439e4e54232SGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
440e4e54232SGeorge Liu     {
441e4e54232SGeorge Liu         return;
442e4e54232SGeorge Liu     }
443e4e54232SGeorge Liu 
444e4e54232SGeorge Liu     redfish::chassis_utils::getValidChassisPath(
445e4e54232SGeorge Liu         asyncResp, chassisId,
446e4e54232SGeorge Liu         std::bind_front(doFanGet, asyncResp, chassisId, fanId));
447e4e54232SGeorge Liu }
448e4e54232SGeorge Liu 
449e4e54232SGeorge Liu inline void requestRoutesFan(App& app)
450e4e54232SGeorge Liu {
451e4e54232SGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
452e4e54232SGeorge Liu         .privileges(redfish::privileges::headFan)
453e4e54232SGeorge Liu         .methods(boost::beast::http::verb::head)(
454e4e54232SGeorge Liu             std::bind_front(handleFanHead, std::ref(app)));
455e4e54232SGeorge Liu 
456e4e54232SGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
457e4e54232SGeorge Liu         .privileges(redfish::privileges::getFan)
458e4e54232SGeorge Liu         .methods(boost::beast::http::verb::get)(
459e4e54232SGeorge Liu             std::bind_front(handleFanGet, std::ref(app)));
460e4e54232SGeorge Liu }
461e4e54232SGeorge Liu 
4629516f41fSGeorge Liu } // namespace redfish
463