xref: /openbmc/bmcweb/features/redfish/lib/fan.hpp (revision f7e62c142ced153e9400b519bdd66062dd6bbf0e)
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"
111da1ff5fSMyung Bae #include "led.hpp"
12d7857201SEd Tanous #include "logging.hpp"
139516f41fSGeorge Liu #include "query.hpp"
149516f41fSGeorge Liu #include "registries/privilege_registry.hpp"
15*f7e62c14SMyung Bae #include "utils/asset_utils.hpp"
169516f41fSGeorge Liu #include "utils/chassis_utils.hpp"
17177612aaSEd Tanous #include "utils/dbus_utils.hpp"
181da1ff5fSMyung Bae #include "utils/json_utils.hpp"
199516f41fSGeorge Liu 
20d7857201SEd Tanous #include <asm-generic/errno.h>
21d7857201SEd Tanous 
22d7857201SEd Tanous #include <boost/beast/http/field.hpp>
23d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
249f1ae5aeSAlbert Zhang #include <boost/system/error_code.hpp>
259516f41fSGeorge Liu #include <boost/url/format.hpp>
26d7857201SEd Tanous #include <nlohmann/json.hpp>
27d7857201SEd Tanous #include <sdbusplus/unpack_properties.hpp>
289516f41fSGeorge Liu 
29d7857201SEd Tanous #include <array>
309516f41fSGeorge Liu #include <functional>
319516f41fSGeorge Liu #include <memory>
329516f41fSGeorge Liu #include <optional>
339516f41fSGeorge Liu #include <string>
349516f41fSGeorge Liu #include <string_view>
35d7857201SEd Tanous #include <utility>
369516f41fSGeorge Liu 
379516f41fSGeorge Liu namespace redfish
389516f41fSGeorge Liu {
399516f41fSGeorge Liu constexpr std::array<std::string_view, 1> fanInterface = {
409516f41fSGeorge Liu     "xyz.openbmc_project.Inventory.Item.Fan"};
419516f41fSGeorge Liu 
42504af5a0SPatrick Williams inline void updateFanList(
43504af5a0SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
449516f41fSGeorge Liu     const std::string& chassisId,
459516f41fSGeorge Liu     const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
469516f41fSGeorge Liu {
479516f41fSGeorge Liu     nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
489516f41fSGeorge Liu     for (const std::string& fanPath : fanPaths)
499516f41fSGeorge Liu     {
509516f41fSGeorge Liu         std::string fanName =
519516f41fSGeorge Liu             sdbusplus::message::object_path(fanPath).filename();
529516f41fSGeorge Liu         if (fanName.empty())
539516f41fSGeorge Liu         {
549516f41fSGeorge Liu             continue;
559516f41fSGeorge Liu         }
569516f41fSGeorge Liu 
579516f41fSGeorge Liu         nlohmann::json item = nlohmann::json::object();
589516f41fSGeorge Liu         item["@odata.id"] = boost::urls::format(
599516f41fSGeorge Liu             "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
609516f41fSGeorge Liu             fanName);
619516f41fSGeorge Liu 
629516f41fSGeorge Liu         fanList.emplace_back(std::move(item));
639516f41fSGeorge Liu     }
649516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
659516f41fSGeorge Liu }
669516f41fSGeorge Liu 
679516f41fSGeorge Liu inline void getFanPaths(
689516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
69d547d8d2SEd Tanous     const std::string& validChassisPath,
709516f41fSGeorge Liu     const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
719516f41fSGeorge Liu                                  fanPaths)>& callback)
729516f41fSGeorge Liu {
73d547d8d2SEd Tanous     sdbusplus::message::object_path endpointPath{validChassisPath};
749516f41fSGeorge Liu     endpointPath /= "cooled_by";
759516f41fSGeorge Liu 
769516f41fSGeorge Liu     dbus::utility::getAssociatedSubTreePaths(
779516f41fSGeorge Liu         endpointPath,
789516f41fSGeorge Liu         sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
799516f41fSGeorge Liu         fanInterface,
809516f41fSGeorge Liu         [asyncResp, callback](
819516f41fSGeorge Liu             const boost::system::error_code& ec,
829516f41fSGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
839516f41fSGeorge Liu             if (ec)
849516f41fSGeorge Liu             {
859516f41fSGeorge Liu                 if (ec.value() != EBADR)
869516f41fSGeorge Liu                 {
8762598e31SEd Tanous                     BMCWEB_LOG_ERROR(
8862598e31SEd Tanous                         "DBUS response error for getAssociatedSubTreePaths {}",
8962598e31SEd Tanous                         ec.value());
909516f41fSGeorge Liu                     messages::internalError(asyncResp->res);
919516f41fSGeorge Liu                 }
929516f41fSGeorge Liu                 return;
939516f41fSGeorge Liu             }
949516f41fSGeorge Liu             callback(subtreePaths);
959516f41fSGeorge Liu         });
969516f41fSGeorge Liu }
979516f41fSGeorge Liu 
989516f41fSGeorge Liu inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
999516f41fSGeorge Liu                             const std::string& chassisId,
1009516f41fSGeorge Liu                             const std::optional<std::string>& validChassisPath)
1019516f41fSGeorge Liu {
1029516f41fSGeorge Liu     if (!validChassisPath)
1039516f41fSGeorge Liu     {
1049516f41fSGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
1059516f41fSGeorge Liu         return;
1069516f41fSGeorge Liu     }
1079516f41fSGeorge Liu 
1089516f41fSGeorge Liu     asyncResp->res.addHeader(
1099516f41fSGeorge Liu         boost::beast::http::field::link,
1109516f41fSGeorge Liu         "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
1119516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
1129516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1139516f41fSGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
1149516f41fSGeorge Liu     asyncResp->res.jsonValue["Name"] = "Fan Collection";
1159516f41fSGeorge Liu     asyncResp->res.jsonValue["Description"] =
1169516f41fSGeorge Liu         "The collection of Fan resource instances " + chassisId;
1179516f41fSGeorge Liu     asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
1189516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = 0;
1199516f41fSGeorge Liu 
120d547d8d2SEd Tanous     getFanPaths(asyncResp, *validChassisPath,
1219516f41fSGeorge Liu                 std::bind_front(updateFanList, asyncResp, chassisId));
1229516f41fSGeorge Liu }
1239516f41fSGeorge Liu 
124504af5a0SPatrick Williams inline void handleFanCollectionHead(
125504af5a0SPatrick Williams     App& app, const crow::Request& req,
1269516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1279516f41fSGeorge Liu     const std::string& chassisId)
1289516f41fSGeorge Liu {
1299516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1309516f41fSGeorge Liu     {
1319516f41fSGeorge Liu         return;
1329516f41fSGeorge Liu     }
1339516f41fSGeorge Liu 
1349516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
1359516f41fSGeorge Liu         asyncResp, chassisId,
1369516f41fSGeorge Liu         [asyncResp,
1379516f41fSGeorge Liu          chassisId](const std::optional<std::string>& validChassisPath) {
1389516f41fSGeorge Liu             if (!validChassisPath)
1399516f41fSGeorge Liu             {
140bd79bce8SPatrick Williams                 messages::resourceNotFound(asyncResp->res, "Chassis",
141bd79bce8SPatrick Williams                                            chassisId);
1429516f41fSGeorge Liu                 return;
1439516f41fSGeorge Liu             }
1449516f41fSGeorge Liu             asyncResp->res.addHeader(
1459516f41fSGeorge Liu                 boost::beast::http::field::link,
1469516f41fSGeorge Liu                 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
1479516f41fSGeorge Liu         });
1489516f41fSGeorge Liu }
1499516f41fSGeorge Liu 
150504af5a0SPatrick Williams inline void handleFanCollectionGet(
151504af5a0SPatrick Williams     App& app, const crow::Request& req,
1529516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1539516f41fSGeorge Liu     const std::string& chassisId)
1549516f41fSGeorge Liu {
1559516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1569516f41fSGeorge Liu     {
1579516f41fSGeorge Liu         return;
1589516f41fSGeorge Liu     }
1599516f41fSGeorge Liu 
1609516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
1619516f41fSGeorge Liu         asyncResp, chassisId,
1629516f41fSGeorge Liu         std::bind_front(doFanCollection, asyncResp, chassisId));
1639516f41fSGeorge Liu }
1649516f41fSGeorge Liu 
1659516f41fSGeorge Liu inline void requestRoutesFanCollection(App& app)
1669516f41fSGeorge Liu {
1679516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
1689516f41fSGeorge Liu         .privileges(redfish::privileges::headFanCollection)
1699516f41fSGeorge Liu         .methods(boost::beast::http::verb::head)(
1709516f41fSGeorge Liu             std::bind_front(handleFanCollectionHead, std::ref(app)));
1719516f41fSGeorge Liu 
1729516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
1739516f41fSGeorge Liu         .privileges(redfish::privileges::getFanCollection)
1749516f41fSGeorge Liu         .methods(boost::beast::http::verb::get)(
1759516f41fSGeorge Liu             std::bind_front(handleFanCollectionGet, std::ref(app)));
1769516f41fSGeorge Liu }
1779516f41fSGeorge Liu 
178e4e54232SGeorge Liu inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
179e4e54232SGeorge Liu {
180e4e54232SGeorge Liu     std::string fanName = sdbusplus::message::object_path(fanPath).filename();
181e4e54232SGeorge Liu 
182e4e54232SGeorge Liu     return !(fanName.empty() || fanName != fanId);
183e4e54232SGeorge Liu }
184e4e54232SGeorge Liu 
1854ff0f1f4SEd Tanous inline void handleFanPath(
186e4e54232SGeorge Liu     const std::string& fanId,
187e4e54232SGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
188e4e54232SGeorge Liu     const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
189e4e54232SGeorge Liu     const std::function<void(const std::string& fanPath,
190e4e54232SGeorge Liu                              const std::string& service)>& callback)
191e4e54232SGeorge Liu {
192e4e54232SGeorge Liu     for (const auto& fanPath : fanPaths)
193e4e54232SGeorge Liu     {
194e4e54232SGeorge Liu         if (!checkFanId(fanPath, fanId))
195e4e54232SGeorge Liu         {
196e4e54232SGeorge Liu             continue;
197e4e54232SGeorge Liu         }
198e4e54232SGeorge Liu         dbus::utility::getDbusObject(
199e4e54232SGeorge Liu             fanPath, fanInterface,
200e4e54232SGeorge Liu             [fanPath, asyncResp,
201e4e54232SGeorge Liu              callback](const boost::system::error_code& ec,
202e4e54232SGeorge Liu                        const dbus::utility::MapperGetObject& object) {
203e4e54232SGeorge Liu                 if (ec || object.empty())
204e4e54232SGeorge Liu                 {
20562598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
20662598e31SEd Tanous                                      ec.value());
207e4e54232SGeorge Liu                     messages::internalError(asyncResp->res);
208e4e54232SGeorge Liu                     return;
209e4e54232SGeorge Liu                 }
210e4e54232SGeorge Liu                 callback(fanPath, object.begin()->first);
211e4e54232SGeorge Liu             });
212e4e54232SGeorge Liu 
213e4e54232SGeorge Liu         return;
214e4e54232SGeorge Liu     }
21562598e31SEd Tanous     BMCWEB_LOG_WARNING("Fan not found {}", fanId);
216e4e54232SGeorge Liu     messages::resourceNotFound(asyncResp->res, "Fan", fanId);
217e4e54232SGeorge Liu }
218e4e54232SGeorge Liu 
2191da1ff5fSMyung Bae inline void getValidFanObject(
220e4e54232SGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
221e4e54232SGeorge Liu     const std::string& validChassisPath, const std::string& fanId,
222e4e54232SGeorge Liu     const std::function<void(const std::string& fanPath,
223e4e54232SGeorge Liu                              const std::string& service)>& callback)
224e4e54232SGeorge Liu {
225e4e54232SGeorge Liu     getFanPaths(
226e4e54232SGeorge Liu         asyncResp, validChassisPath,
227e4e54232SGeorge Liu         [fanId, asyncResp, callback](
228e4e54232SGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
229e4e54232SGeorge Liu             handleFanPath(fanId, asyncResp, fanPaths, callback);
230e4e54232SGeorge Liu         });
231e4e54232SGeorge Liu }
232e4e54232SGeorge Liu 
233e4e54232SGeorge Liu inline void addFanCommonProperties(crow::Response& resp,
234e4e54232SGeorge Liu                                    const std::string& chassisId,
235e4e54232SGeorge Liu                                    const std::string& fanId)
236e4e54232SGeorge Liu {
237e4e54232SGeorge Liu     resp.addHeader(boost::beast::http::field::link,
238e4e54232SGeorge Liu                    "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
239e4e54232SGeorge Liu     resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
240e4e54232SGeorge Liu     resp.jsonValue["Name"] = "Fan";
241e4e54232SGeorge Liu     resp.jsonValue["Id"] = fanId;
242e4e54232SGeorge Liu     resp.jsonValue["@odata.id"] = boost::urls::format(
243e4e54232SGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
244539d8c6bSEd Tanous     resp.jsonValue["Status"]["State"] = resource::State::Enabled;
245539d8c6bSEd Tanous     resp.jsonValue["Status"]["Health"] = resource::Health::OK;
2469f1ae5aeSAlbert Zhang }
2479f1ae5aeSAlbert Zhang 
2489f1ae5aeSAlbert Zhang inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2499f1ae5aeSAlbert Zhang                          const std::string& fanPath, const std::string& service)
2509f1ae5aeSAlbert Zhang {
251deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
252deae6a78SEd Tanous         service, fanPath,
2539f1ae5aeSAlbert Zhang         "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
2549f1ae5aeSAlbert Zhang         [asyncResp](const boost::system::error_code& ec, const bool value) {
2559f1ae5aeSAlbert Zhang             if (ec)
2569f1ae5aeSAlbert Zhang             {
2579f1ae5aeSAlbert Zhang                 if (ec.value() != EBADR)
2589f1ae5aeSAlbert Zhang                 {
25962598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for Health {}",
26062598e31SEd Tanous                                      ec.value());
2619f1ae5aeSAlbert Zhang                     messages::internalError(asyncResp->res);
2629f1ae5aeSAlbert Zhang                 }
2639f1ae5aeSAlbert Zhang                 return;
2649f1ae5aeSAlbert Zhang             }
2659f1ae5aeSAlbert Zhang 
2669f1ae5aeSAlbert Zhang             if (!value)
2679f1ae5aeSAlbert Zhang             {
268539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["Health"] =
269539d8c6bSEd Tanous                     resource::Health::Critical;
2709f1ae5aeSAlbert Zhang             }
2719f1ae5aeSAlbert Zhang         });
2729f1ae5aeSAlbert Zhang }
2739f1ae5aeSAlbert Zhang 
2749f1ae5aeSAlbert Zhang inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2759f1ae5aeSAlbert Zhang                         const std::string& fanPath, const std::string& service)
2769f1ae5aeSAlbert Zhang {
277deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
278deae6a78SEd Tanous         service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present",
2799f1ae5aeSAlbert Zhang         [asyncResp](const boost::system::error_code& ec, const bool value) {
2809f1ae5aeSAlbert Zhang             if (ec)
2819f1ae5aeSAlbert Zhang             {
2829f1ae5aeSAlbert Zhang                 if (ec.value() != EBADR)
2839f1ae5aeSAlbert Zhang                 {
28462598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for State {}",
28562598e31SEd Tanous                                      ec.value());
2869f1ae5aeSAlbert Zhang                     messages::internalError(asyncResp->res);
2879f1ae5aeSAlbert Zhang                 }
2889f1ae5aeSAlbert Zhang                 return;
2899f1ae5aeSAlbert Zhang             }
2909f1ae5aeSAlbert Zhang 
2919f1ae5aeSAlbert Zhang             if (!value)
2929f1ae5aeSAlbert Zhang             {
293539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
294539d8c6bSEd Tanous                     resource::State::Absent;
2959f1ae5aeSAlbert Zhang             }
2969f1ae5aeSAlbert Zhang         });
297e4e54232SGeorge Liu }
298e4e54232SGeorge Liu 
299fc3edfddSEd Tanous inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3004a2e485dSGeorge Liu                            const std::string& fanPath,
3014a2e485dSGeorge Liu                            const std::string& service)
3024a2e485dSGeorge Liu {
303deae6a78SEd Tanous     dbus::utility::getProperty<std::string>(
304deae6a78SEd Tanous         service, fanPath,
3054a2e485dSGeorge Liu         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
306fc3edfddSEd Tanous         [asyncResp](const boost::system::error_code& ec,
3074a2e485dSGeorge Liu                     const std::string& property) {
3084a2e485dSGeorge Liu             if (ec)
3094a2e485dSGeorge Liu             {
3104a2e485dSGeorge Liu                 if (ec.value() != EBADR)
3114a2e485dSGeorge Liu                 {
31262598e31SEd Tanous                     BMCWEB_LOG_ERROR("DBUS response error for Location{}",
31362598e31SEd Tanous                                      ec.value());
314fc3edfddSEd Tanous                     messages::internalError(asyncResp->res);
3154a2e485dSGeorge Liu                 }
3164a2e485dSGeorge Liu                 return;
3174a2e485dSGeorge Liu             }
318bd79bce8SPatrick Williams             asyncResp->res
319bd79bce8SPatrick Williams                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
3204a2e485dSGeorge Liu                 property;
3214a2e485dSGeorge Liu         });
3224a2e485dSGeorge Liu }
3234a2e485dSGeorge Liu 
3241da1ff5fSMyung Bae inline void afterGetValidFanObject(
325504af5a0SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
326e4e54232SGeorge Liu     const std::string& chassisId, const std::string& fanId,
327e4e54232SGeorge Liu     const std::string& fanPath, const std::string& service)
328e4e54232SGeorge Liu {
329e4e54232SGeorge Liu     addFanCommonProperties(asyncResp->res, chassisId, fanId);
3309f1ae5aeSAlbert Zhang     getFanState(asyncResp, fanPath, service);
3319f1ae5aeSAlbert Zhang     getFanHealth(asyncResp, fanPath, service);
332*f7e62c14SMyung Bae     asset_utils::getAssetInfo(asyncResp, service, fanPath, ""_json_pointer,
333*f7e62c14SMyung Bae                               true);
3344a2e485dSGeorge Liu     getFanLocation(asyncResp, fanPath, service);
3351da1ff5fSMyung Bae     getLocationIndicatorActive(asyncResp, fanPath);
336e4e54232SGeorge Liu }
337e4e54232SGeorge Liu 
338e4e54232SGeorge Liu inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
339e4e54232SGeorge Liu                      const std::string& chassisId, const std::string& fanId,
340e4e54232SGeorge Liu                      const std::optional<std::string>& validChassisPath)
341e4e54232SGeorge Liu {
342e4e54232SGeorge Liu     if (!validChassisPath)
343e4e54232SGeorge Liu     {
344e4e54232SGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
345e4e54232SGeorge Liu         return;
346e4e54232SGeorge Liu     }
347e4e54232SGeorge Liu 
3481da1ff5fSMyung Bae     getValidFanObject(
349e4e54232SGeorge Liu         asyncResp, *validChassisPath, fanId,
3501da1ff5fSMyung Bae         std::bind_front(afterGetValidFanObject, asyncResp, chassisId, fanId));
351e4e54232SGeorge Liu }
352e4e54232SGeorge Liu 
353e4e54232SGeorge Liu inline void handleFanHead(App& app, const crow::Request& req,
354e4e54232SGeorge Liu                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
355e4e54232SGeorge Liu                           const std::string& chassisId,
356e4e54232SGeorge Liu                           const std::string& fanId)
357e4e54232SGeorge Liu {
358e4e54232SGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
359e4e54232SGeorge Liu     {
360e4e54232SGeorge Liu         return;
361e4e54232SGeorge Liu     }
362e4e54232SGeorge Liu 
363e4e54232SGeorge Liu     redfish::chassis_utils::getValidChassisPath(
364e4e54232SGeorge Liu         asyncResp, chassisId,
365e4e54232SGeorge Liu         [asyncResp, chassisId,
366e4e54232SGeorge Liu          fanId](const std::optional<std::string>& validChassisPath) {
367e4e54232SGeorge Liu             if (!validChassisPath)
368e4e54232SGeorge Liu             {
369bd79bce8SPatrick Williams                 messages::resourceNotFound(asyncResp->res, "Chassis",
370bd79bce8SPatrick Williams                                            chassisId);
371e4e54232SGeorge Liu                 return;
372e4e54232SGeorge Liu             }
3731da1ff5fSMyung Bae             getValidFanObject(
374bd79bce8SPatrick Williams                 asyncResp, *validChassisPath, fanId,
375e4e54232SGeorge Liu                 [asyncResp](const std::string&, const std::string&) {
376e4e54232SGeorge Liu                     asyncResp->res.addHeader(
377e4e54232SGeorge Liu                         boost::beast::http::field::link,
378e4e54232SGeorge Liu                         "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
379e4e54232SGeorge Liu                 });
380e4e54232SGeorge Liu         });
381e4e54232SGeorge Liu }
382e4e54232SGeorge Liu 
383e4e54232SGeorge Liu inline void handleFanGet(App& app, const crow::Request& req,
384e4e54232SGeorge Liu                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
385e4e54232SGeorge Liu                          const std::string& chassisId, const std::string& fanId)
386e4e54232SGeorge Liu {
387e4e54232SGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
388e4e54232SGeorge Liu     {
389e4e54232SGeorge Liu         return;
390e4e54232SGeorge Liu     }
391e4e54232SGeorge Liu 
392e4e54232SGeorge Liu     redfish::chassis_utils::getValidChassisPath(
393e4e54232SGeorge Liu         asyncResp, chassisId,
394e4e54232SGeorge Liu         std::bind_front(doFanGet, asyncResp, chassisId, fanId));
395e4e54232SGeorge Liu }
396e4e54232SGeorge Liu 
3971da1ff5fSMyung Bae inline void handleSetFanPathById(
3981da1ff5fSMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3991da1ff5fSMyung Bae     const std::string& chassisId, const std::string& fanId,
4001da1ff5fSMyung Bae     bool locationIndicatorActive, const boost::system::error_code& ec,
4011da1ff5fSMyung Bae     const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
4021da1ff5fSMyung Bae {
4031da1ff5fSMyung Bae     if (ec)
4041da1ff5fSMyung Bae     {
4051da1ff5fSMyung Bae         if (ec.value() == boost::system::errc::io_error)
4061da1ff5fSMyung Bae         {
4071da1ff5fSMyung Bae             BMCWEB_LOG_WARNING("Chassis {} not found", chassisId);
4081da1ff5fSMyung Bae             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
4091da1ff5fSMyung Bae             return;
4101da1ff5fSMyung Bae         }
4111da1ff5fSMyung Bae 
4121da1ff5fSMyung Bae         BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
4131da1ff5fSMyung Bae         messages::internalError(asyncResp->res);
4141da1ff5fSMyung Bae         return;
4151da1ff5fSMyung Bae     }
4161da1ff5fSMyung Bae 
4171da1ff5fSMyung Bae     for (const auto& fanPath : fanPaths)
4181da1ff5fSMyung Bae     {
4191da1ff5fSMyung Bae         if (checkFanId(fanPath, fanId))
4201da1ff5fSMyung Bae         {
4211da1ff5fSMyung Bae             setLocationIndicatorActive(asyncResp, fanPath,
4221da1ff5fSMyung Bae                                        locationIndicatorActive);
4231da1ff5fSMyung Bae             return;
4241da1ff5fSMyung Bae         }
4251da1ff5fSMyung Bae     }
4261da1ff5fSMyung Bae     BMCWEB_LOG_WARNING("Fan {} not found", fanId);
4271da1ff5fSMyung Bae     messages::resourceNotFound(asyncResp->res, "Fan", fanId);
4281da1ff5fSMyung Bae }
4291da1ff5fSMyung Bae 
4301da1ff5fSMyung Bae inline void handleFanPatch(App& app, const crow::Request& req,
4311da1ff5fSMyung Bae                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4321da1ff5fSMyung Bae                            const std::string& chassisId,
4331da1ff5fSMyung Bae                            const std::string& fanId)
4341da1ff5fSMyung Bae {
4351da1ff5fSMyung Bae     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
4361da1ff5fSMyung Bae     {
4371da1ff5fSMyung Bae         return;
4381da1ff5fSMyung Bae     }
4391da1ff5fSMyung Bae 
4401da1ff5fSMyung Bae     std::optional<bool> locationIndicatorActive;
4411da1ff5fSMyung Bae     if (!json_util::readJsonPatch(req, asyncResp->res,
4421da1ff5fSMyung Bae                                   "LocationIndicatorActive",
4431da1ff5fSMyung Bae                                   locationIndicatorActive))
4441da1ff5fSMyung Bae     {
4451da1ff5fSMyung Bae         return;
4461da1ff5fSMyung Bae     }
4471da1ff5fSMyung Bae 
4481da1ff5fSMyung Bae     if (locationIndicatorActive)
4491da1ff5fSMyung Bae     {
4501da1ff5fSMyung Bae         dbus::utility::getAssociatedSubTreePathsById(
4511da1ff5fSMyung Bae             chassisId, "/xyz/openbmc_project/inventory", chassisInterfaces,
4521da1ff5fSMyung Bae             "cooled_by", fanInterface,
4531da1ff5fSMyung Bae             [asyncResp, chassisId, fanId, locationIndicatorActive](
4541da1ff5fSMyung Bae                 const boost::system::error_code& ec,
4551da1ff5fSMyung Bae                 const dbus::utility::MapperGetSubTreePathsResponse&
4561da1ff5fSMyung Bae                     subtreePaths) {
4571da1ff5fSMyung Bae                 handleSetFanPathById(asyncResp, chassisId, fanId,
4581da1ff5fSMyung Bae                                      *locationIndicatorActive, ec,
4591da1ff5fSMyung Bae                                      subtreePaths);
4601da1ff5fSMyung Bae             });
4611da1ff5fSMyung Bae     }
4621da1ff5fSMyung Bae }
4631da1ff5fSMyung Bae 
464e4e54232SGeorge Liu inline void requestRoutesFan(App& app)
465e4e54232SGeorge Liu {
466e4e54232SGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
467e4e54232SGeorge Liu         .privileges(redfish::privileges::headFan)
468e4e54232SGeorge Liu         .methods(boost::beast::http::verb::head)(
469e4e54232SGeorge Liu             std::bind_front(handleFanHead, std::ref(app)));
470e4e54232SGeorge Liu 
471e4e54232SGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
472e4e54232SGeorge Liu         .privileges(redfish::privileges::getFan)
473e4e54232SGeorge Liu         .methods(boost::beast::http::verb::get)(
474e4e54232SGeorge Liu             std::bind_front(handleFanGet, std::ref(app)));
4751da1ff5fSMyung Bae 
4761da1ff5fSMyung Bae     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
4771da1ff5fSMyung Bae         .privileges(redfish::privileges::patchFan)
4781da1ff5fSMyung Bae         .methods(boost::beast::http::verb::patch)(
4791da1ff5fSMyung Bae             std::bind_front(handleFanPatch, std::ref(app)));
480e4e54232SGeorge Liu }
481e4e54232SGeorge Liu 
4829516f41fSGeorge Liu } // namespace redfish
483