xref: /openbmc/bmcweb/features/redfish/lib/fan.hpp (revision 539d8c6bc399e516d39bcaa18262cf206f4c1035)
19516f41fSGeorge Liu #pragma once
29516f41fSGeorge Liu 
39516f41fSGeorge Liu #include "app.hpp"
49516f41fSGeorge Liu #include "dbus_utility.hpp"
59516f41fSGeorge Liu #include "error_messages.hpp"
6*539d8c6bSEd Tanous #include "generated/enums/resource.hpp"
79516f41fSGeorge Liu #include "query.hpp"
89516f41fSGeorge Liu #include "registries/privilege_registry.hpp"
99516f41fSGeorge Liu #include "utils/chassis_utils.hpp"
109516f41fSGeorge Liu 
119f1ae5aeSAlbert Zhang #include <boost/system/error_code.hpp>
129516f41fSGeorge Liu #include <boost/url/format.hpp>
139f1ae5aeSAlbert Zhang #include <sdbusplus/asio/property.hpp>
149516f41fSGeorge Liu #include <sdbusplus/message/types.hpp>
159516f41fSGeorge Liu 
169516f41fSGeorge Liu #include <functional>
179516f41fSGeorge Liu #include <memory>
189516f41fSGeorge Liu #include <optional>
199516f41fSGeorge Liu #include <string>
209516f41fSGeorge Liu #include <string_view>
219516f41fSGeorge Liu 
229516f41fSGeorge Liu namespace redfish
239516f41fSGeorge Liu {
249516f41fSGeorge Liu constexpr std::array<std::string_view, 1> fanInterface = {
259516f41fSGeorge Liu     "xyz.openbmc_project.Inventory.Item.Fan"};
269516f41fSGeorge Liu 
279516f41fSGeorge Liu inline void
289516f41fSGeorge Liu     updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
299516f41fSGeorge Liu                   const std::string& chassisId,
309516f41fSGeorge Liu                   const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
319516f41fSGeorge Liu {
329516f41fSGeorge Liu     nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
339516f41fSGeorge Liu     for (const std::string& fanPath : fanPaths)
349516f41fSGeorge Liu     {
359516f41fSGeorge Liu         std::string fanName =
369516f41fSGeorge Liu             sdbusplus::message::object_path(fanPath).filename();
379516f41fSGeorge Liu         if (fanName.empty())
389516f41fSGeorge Liu         {
399516f41fSGeorge Liu             continue;
409516f41fSGeorge Liu         }
419516f41fSGeorge Liu 
429516f41fSGeorge Liu         nlohmann::json item = nlohmann::json::object();
439516f41fSGeorge Liu         item["@odata.id"] = boost::urls::format(
449516f41fSGeorge Liu             "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
459516f41fSGeorge Liu             fanName);
469516f41fSGeorge Liu 
479516f41fSGeorge Liu         fanList.emplace_back(std::move(item));
489516f41fSGeorge Liu     }
499516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
509516f41fSGeorge Liu }
519516f41fSGeorge Liu 
529516f41fSGeorge Liu inline void getFanPaths(
539516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
54d547d8d2SEd Tanous     const std::string& validChassisPath,
559516f41fSGeorge Liu     const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
569516f41fSGeorge Liu                                  fanPaths)>& callback)
579516f41fSGeorge Liu {
58d547d8d2SEd Tanous     sdbusplus::message::object_path endpointPath{validChassisPath};
599516f41fSGeorge Liu     endpointPath /= "cooled_by";
609516f41fSGeorge Liu 
619516f41fSGeorge Liu     dbus::utility::getAssociatedSubTreePaths(
629516f41fSGeorge Liu         endpointPath,
639516f41fSGeorge Liu         sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
649516f41fSGeorge Liu         fanInterface,
659516f41fSGeorge Liu         [asyncResp, callback](
669516f41fSGeorge Liu             const boost::system::error_code& ec,
679516f41fSGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
689516f41fSGeorge Liu         if (ec)
699516f41fSGeorge Liu         {
709516f41fSGeorge Liu             if (ec.value() != EBADR)
719516f41fSGeorge Liu             {
7262598e31SEd Tanous                 BMCWEB_LOG_ERROR(
7362598e31SEd Tanous                     "DBUS response error for getAssociatedSubTreePaths {}",
7462598e31SEd Tanous                     ec.value());
759516f41fSGeorge Liu                 messages::internalError(asyncResp->res);
769516f41fSGeorge Liu             }
779516f41fSGeorge Liu             return;
789516f41fSGeorge Liu         }
799516f41fSGeorge Liu         callback(subtreePaths);
809516f41fSGeorge Liu     });
819516f41fSGeorge Liu }
829516f41fSGeorge Liu 
839516f41fSGeorge Liu inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
849516f41fSGeorge Liu                             const std::string& chassisId,
859516f41fSGeorge Liu                             const std::optional<std::string>& validChassisPath)
869516f41fSGeorge Liu {
879516f41fSGeorge Liu     if (!validChassisPath)
889516f41fSGeorge Liu     {
899516f41fSGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
909516f41fSGeorge Liu         return;
919516f41fSGeorge Liu     }
929516f41fSGeorge Liu 
939516f41fSGeorge Liu     asyncResp->res.addHeader(
949516f41fSGeorge Liu         boost::beast::http::field::link,
959516f41fSGeorge Liu         "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
969516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
979516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
989516f41fSGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
999516f41fSGeorge Liu     asyncResp->res.jsonValue["Name"] = "Fan Collection";
1009516f41fSGeorge Liu     asyncResp->res.jsonValue["Description"] =
1019516f41fSGeorge Liu         "The collection of Fan resource instances " + chassisId;
1029516f41fSGeorge Liu     asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
1039516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = 0;
1049516f41fSGeorge Liu 
105d547d8d2SEd Tanous     getFanPaths(asyncResp, *validChassisPath,
1069516f41fSGeorge Liu                 std::bind_front(updateFanList, asyncResp, chassisId));
1079516f41fSGeorge Liu }
1089516f41fSGeorge Liu 
1099516f41fSGeorge Liu inline void
1109516f41fSGeorge Liu     handleFanCollectionHead(App& app, const crow::Request& req,
1119516f41fSGeorge Liu                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1129516f41fSGeorge Liu                             const std::string& chassisId)
1139516f41fSGeorge Liu {
1149516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1159516f41fSGeorge Liu     {
1169516f41fSGeorge Liu         return;
1179516f41fSGeorge Liu     }
1189516f41fSGeorge Liu 
1199516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
1209516f41fSGeorge Liu         asyncResp, chassisId,
1219516f41fSGeorge Liu         [asyncResp,
1229516f41fSGeorge Liu          chassisId](const std::optional<std::string>& validChassisPath) {
1239516f41fSGeorge Liu         if (!validChassisPath)
1249516f41fSGeorge Liu         {
1259516f41fSGeorge Liu             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
1269516f41fSGeorge Liu             return;
1279516f41fSGeorge Liu         }
1289516f41fSGeorge Liu         asyncResp->res.addHeader(
1299516f41fSGeorge Liu             boost::beast::http::field::link,
1309516f41fSGeorge Liu             "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
1319516f41fSGeorge Liu     });
1329516f41fSGeorge Liu }
1339516f41fSGeorge Liu 
1349516f41fSGeorge Liu inline void
1359516f41fSGeorge Liu     handleFanCollectionGet(App& app, const crow::Request& req,
1369516f41fSGeorge Liu                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1379516f41fSGeorge Liu                            const std::string& chassisId)
1389516f41fSGeorge Liu {
1399516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1409516f41fSGeorge Liu     {
1419516f41fSGeorge Liu         return;
1429516f41fSGeorge Liu     }
1439516f41fSGeorge Liu 
1449516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
1459516f41fSGeorge Liu         asyncResp, chassisId,
1469516f41fSGeorge Liu         std::bind_front(doFanCollection, asyncResp, chassisId));
1479516f41fSGeorge Liu }
1489516f41fSGeorge Liu 
1499516f41fSGeorge Liu inline void requestRoutesFanCollection(App& app)
1509516f41fSGeorge Liu {
1519516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
1529516f41fSGeorge Liu         .privileges(redfish::privileges::headFanCollection)
1539516f41fSGeorge Liu         .methods(boost::beast::http::verb::head)(
1549516f41fSGeorge Liu             std::bind_front(handleFanCollectionHead, std::ref(app)));
1559516f41fSGeorge Liu 
1569516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
1579516f41fSGeorge Liu         .privileges(redfish::privileges::getFanCollection)
1589516f41fSGeorge Liu         .methods(boost::beast::http::verb::get)(
1599516f41fSGeorge Liu             std::bind_front(handleFanCollectionGet, std::ref(app)));
1609516f41fSGeorge Liu }
1619516f41fSGeorge Liu 
162e4e54232SGeorge Liu inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
163e4e54232SGeorge Liu {
164e4e54232SGeorge Liu     std::string fanName = sdbusplus::message::object_path(fanPath).filename();
165e4e54232SGeorge Liu 
166e4e54232SGeorge Liu     return !(fanName.empty() || fanName != fanId);
167e4e54232SGeorge Liu }
168e4e54232SGeorge Liu 
169e4e54232SGeorge Liu static inline void handleFanPath(
170e4e54232SGeorge Liu     const std::string& fanId,
171e4e54232SGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
172e4e54232SGeorge Liu     const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
173e4e54232SGeorge Liu     const std::function<void(const std::string& fanPath,
174e4e54232SGeorge Liu                              const std::string& service)>& callback)
175e4e54232SGeorge Liu {
176e4e54232SGeorge Liu     for (const auto& fanPath : fanPaths)
177e4e54232SGeorge Liu     {
178e4e54232SGeorge Liu         if (!checkFanId(fanPath, fanId))
179e4e54232SGeorge Liu         {
180e4e54232SGeorge Liu             continue;
181e4e54232SGeorge Liu         }
182e4e54232SGeorge Liu         dbus::utility::getDbusObject(
183e4e54232SGeorge Liu             fanPath, fanInterface,
184e4e54232SGeorge Liu             [fanPath, asyncResp,
185e4e54232SGeorge Liu              callback](const boost::system::error_code& ec,
186e4e54232SGeorge Liu                        const dbus::utility::MapperGetObject& object) {
187e4e54232SGeorge Liu             if (ec || object.empty())
188e4e54232SGeorge Liu             {
18962598e31SEd Tanous                 BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
19062598e31SEd Tanous                                  ec.value());
191e4e54232SGeorge Liu                 messages::internalError(asyncResp->res);
192e4e54232SGeorge Liu                 return;
193e4e54232SGeorge Liu             }
194e4e54232SGeorge Liu             callback(fanPath, object.begin()->first);
195e4e54232SGeorge Liu         });
196e4e54232SGeorge Liu 
197e4e54232SGeorge Liu         return;
198e4e54232SGeorge Liu     }
19962598e31SEd Tanous     BMCWEB_LOG_WARNING("Fan not found {}", fanId);
200e4e54232SGeorge Liu     messages::resourceNotFound(asyncResp->res, "Fan", fanId);
201e4e54232SGeorge Liu }
202e4e54232SGeorge Liu 
203e4e54232SGeorge Liu inline void getValidFanPath(
204e4e54232SGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
205e4e54232SGeorge Liu     const std::string& validChassisPath, const std::string& fanId,
206e4e54232SGeorge Liu     const std::function<void(const std::string& fanPath,
207e4e54232SGeorge Liu                              const std::string& service)>& callback)
208e4e54232SGeorge Liu {
209e4e54232SGeorge Liu     getFanPaths(
210e4e54232SGeorge Liu         asyncResp, validChassisPath,
211e4e54232SGeorge Liu         [fanId, asyncResp, callback](
212e4e54232SGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
213e4e54232SGeorge Liu         handleFanPath(fanId, asyncResp, fanPaths, callback);
214e4e54232SGeorge Liu     });
215e4e54232SGeorge Liu }
216e4e54232SGeorge Liu 
217e4e54232SGeorge Liu inline void addFanCommonProperties(crow::Response& resp,
218e4e54232SGeorge Liu                                    const std::string& chassisId,
219e4e54232SGeorge Liu                                    const std::string& fanId)
220e4e54232SGeorge Liu {
221e4e54232SGeorge Liu     resp.addHeader(boost::beast::http::field::link,
222e4e54232SGeorge Liu                    "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
223e4e54232SGeorge Liu     resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
224e4e54232SGeorge Liu     resp.jsonValue["Name"] = "Fan";
225e4e54232SGeorge Liu     resp.jsonValue["Id"] = fanId;
226e4e54232SGeorge Liu     resp.jsonValue["@odata.id"] = boost::urls::format(
227e4e54232SGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
228*539d8c6bSEd Tanous     resp.jsonValue["Status"]["State"] = resource::State::Enabled;
229*539d8c6bSEd Tanous     resp.jsonValue["Status"]["Health"] = resource::Health::OK;
2309f1ae5aeSAlbert Zhang }
2319f1ae5aeSAlbert Zhang 
2329f1ae5aeSAlbert Zhang inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2339f1ae5aeSAlbert Zhang                          const std::string& fanPath, const std::string& service)
2349f1ae5aeSAlbert Zhang {
2359f1ae5aeSAlbert Zhang     sdbusplus::asio::getProperty<bool>(
2369f1ae5aeSAlbert Zhang         *crow::connections::systemBus, service, fanPath,
2379f1ae5aeSAlbert Zhang         "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
2389f1ae5aeSAlbert Zhang         [asyncResp](const boost::system::error_code& ec, const bool value) {
2399f1ae5aeSAlbert Zhang         if (ec)
2409f1ae5aeSAlbert Zhang         {
2419f1ae5aeSAlbert Zhang             if (ec.value() != EBADR)
2429f1ae5aeSAlbert Zhang             {
24362598e31SEd Tanous                 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
24462598e31SEd Tanous                                  ec.value());
2459f1ae5aeSAlbert Zhang                 messages::internalError(asyncResp->res);
2469f1ae5aeSAlbert Zhang             }
2479f1ae5aeSAlbert Zhang             return;
2489f1ae5aeSAlbert Zhang         }
2499f1ae5aeSAlbert Zhang 
2509f1ae5aeSAlbert Zhang         if (!value)
2519f1ae5aeSAlbert Zhang         {
252*539d8c6bSEd Tanous             asyncResp->res.jsonValue["Status"]["Health"] =
253*539d8c6bSEd Tanous                 resource::Health::Critical;
2549f1ae5aeSAlbert Zhang         }
2559f1ae5aeSAlbert Zhang     });
2569f1ae5aeSAlbert Zhang }
2579f1ae5aeSAlbert Zhang 
2589f1ae5aeSAlbert Zhang inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2599f1ae5aeSAlbert Zhang                         const std::string& fanPath, const std::string& service)
2609f1ae5aeSAlbert Zhang {
2619f1ae5aeSAlbert Zhang     sdbusplus::asio::getProperty<bool>(
2629f1ae5aeSAlbert Zhang         *crow::connections::systemBus, service, fanPath,
2639f1ae5aeSAlbert Zhang         "xyz.openbmc_project.Inventory.Item", "Present",
2649f1ae5aeSAlbert Zhang         [asyncResp](const boost::system::error_code& ec, const bool value) {
2659f1ae5aeSAlbert Zhang         if (ec)
2669f1ae5aeSAlbert Zhang         {
2679f1ae5aeSAlbert Zhang             if (ec.value() != EBADR)
2689f1ae5aeSAlbert Zhang             {
26962598e31SEd Tanous                 BMCWEB_LOG_ERROR("DBUS response error for State {}",
27062598e31SEd Tanous                                  ec.value());
2719f1ae5aeSAlbert Zhang                 messages::internalError(asyncResp->res);
2729f1ae5aeSAlbert Zhang             }
2739f1ae5aeSAlbert Zhang             return;
2749f1ae5aeSAlbert Zhang         }
2759f1ae5aeSAlbert Zhang 
2769f1ae5aeSAlbert Zhang         if (!value)
2779f1ae5aeSAlbert Zhang         {
278*539d8c6bSEd Tanous             asyncResp->res.jsonValue["Status"]["State"] =
279*539d8c6bSEd Tanous                 resource::State::Absent;
2809f1ae5aeSAlbert Zhang         }
2819f1ae5aeSAlbert Zhang     });
282e4e54232SGeorge Liu }
283e4e54232SGeorge Liu 
284090ae7baSGeorge Liu inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
285090ae7baSGeorge Liu                         const std::string& fanPath, const std::string& service)
286090ae7baSGeorge Liu {
287090ae7baSGeorge Liu     sdbusplus::asio::getAllProperties(
288090ae7baSGeorge Liu         *crow::connections::systemBus, service, fanPath,
289090ae7baSGeorge Liu         "xyz.openbmc_project.Inventory.Decorator.Asset",
290090ae7baSGeorge Liu         [fanPath, asyncResp{asyncResp}](
291090ae7baSGeorge Liu             const boost::system::error_code& ec,
292090ae7baSGeorge Liu             const dbus::utility::DBusPropertiesMap& assetList) {
293090ae7baSGeorge Liu         if (ec)
294090ae7baSGeorge Liu         {
295090ae7baSGeorge Liu             if (ec.value() != EBADR)
296090ae7baSGeorge Liu             {
29762598e31SEd Tanous                 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
29862598e31SEd Tanous                                  ec.value());
299090ae7baSGeorge Liu                 messages::internalError(asyncResp->res);
300090ae7baSGeorge Liu             }
301090ae7baSGeorge Liu             return;
302090ae7baSGeorge Liu         }
303090ae7baSGeorge Liu         const std::string* manufacturer = nullptr;
304090ae7baSGeorge Liu         const std::string* model = nullptr;
305090ae7baSGeorge Liu         const std::string* partNumber = nullptr;
306090ae7baSGeorge Liu         const std::string* serialNumber = nullptr;
307090ae7baSGeorge Liu         const std::string* sparePartNumber = nullptr;
308090ae7baSGeorge Liu 
309090ae7baSGeorge Liu         const bool success = sdbusplus::unpackPropertiesNoThrow(
310090ae7baSGeorge Liu             dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
311090ae7baSGeorge Liu             manufacturer, "Model", model, "PartNumber", partNumber,
312090ae7baSGeorge Liu             "SerialNumber", serialNumber, "SparePartNumber", sparePartNumber);
313090ae7baSGeorge Liu         if (!success)
314090ae7baSGeorge Liu         {
315090ae7baSGeorge Liu             messages::internalError(asyncResp->res);
316090ae7baSGeorge Liu             return;
317090ae7baSGeorge Liu         }
318090ae7baSGeorge Liu         if (manufacturer != nullptr)
319090ae7baSGeorge Liu         {
320090ae7baSGeorge Liu             asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
321090ae7baSGeorge Liu         }
322090ae7baSGeorge Liu         if (model != nullptr)
323090ae7baSGeorge Liu         {
324090ae7baSGeorge Liu             asyncResp->res.jsonValue["Model"] = *model;
325090ae7baSGeorge Liu         }
326090ae7baSGeorge Liu         if (partNumber != nullptr)
327090ae7baSGeorge Liu         {
328090ae7baSGeorge Liu             asyncResp->res.jsonValue["PartNumber"] = *partNumber;
329090ae7baSGeorge Liu         }
330090ae7baSGeorge Liu         if (serialNumber != nullptr)
331090ae7baSGeorge Liu         {
332090ae7baSGeorge Liu             asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
333090ae7baSGeorge Liu         }
334090ae7baSGeorge Liu         if (sparePartNumber != nullptr && !sparePartNumber->empty())
335090ae7baSGeorge Liu         {
336090ae7baSGeorge Liu             asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
337090ae7baSGeorge Liu         }
338090ae7baSGeorge Liu     });
339090ae7baSGeorge Liu }
340090ae7baSGeorge Liu 
341fc3edfddSEd Tanous inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3424a2e485dSGeorge Liu                            const std::string& fanPath,
3434a2e485dSGeorge Liu                            const std::string& service)
3444a2e485dSGeorge Liu {
3454a2e485dSGeorge Liu     sdbusplus::asio::getProperty<std::string>(
3464a2e485dSGeorge Liu         *crow::connections::systemBus, service, fanPath,
3474a2e485dSGeorge Liu         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
348fc3edfddSEd Tanous         [asyncResp](const boost::system::error_code& ec,
3494a2e485dSGeorge Liu                     const std::string& property) {
3504a2e485dSGeorge Liu         if (ec)
3514a2e485dSGeorge Liu         {
3524a2e485dSGeorge Liu             if (ec.value() != EBADR)
3534a2e485dSGeorge Liu             {
35462598e31SEd Tanous                 BMCWEB_LOG_ERROR("DBUS response error for Location{}",
35562598e31SEd Tanous                                  ec.value());
356fc3edfddSEd Tanous                 messages::internalError(asyncResp->res);
3574a2e485dSGeorge Liu             }
3584a2e485dSGeorge Liu             return;
3594a2e485dSGeorge Liu         }
360fc3edfddSEd Tanous         asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
3614a2e485dSGeorge Liu             property;
3624a2e485dSGeorge Liu     });
3634a2e485dSGeorge Liu }
3644a2e485dSGeorge Liu 
365e4e54232SGeorge Liu inline void
366e4e54232SGeorge Liu     afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
367e4e54232SGeorge Liu                          const std::string& chassisId, const std::string& fanId,
368e4e54232SGeorge Liu                          const std::string& fanPath, const std::string& service)
369e4e54232SGeorge Liu {
370e4e54232SGeorge Liu     addFanCommonProperties(asyncResp->res, chassisId, fanId);
3719f1ae5aeSAlbert Zhang     getFanState(asyncResp, fanPath, service);
3729f1ae5aeSAlbert Zhang     getFanHealth(asyncResp, fanPath, service);
373090ae7baSGeorge Liu     getFanAsset(asyncResp, fanPath, service);
3744a2e485dSGeorge Liu     getFanLocation(asyncResp, fanPath, service);
375e4e54232SGeorge Liu }
376e4e54232SGeorge Liu 
377e4e54232SGeorge Liu inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
378e4e54232SGeorge Liu                      const std::string& chassisId, const std::string& fanId,
379e4e54232SGeorge Liu                      const std::optional<std::string>& validChassisPath)
380e4e54232SGeorge Liu {
381e4e54232SGeorge Liu     if (!validChassisPath)
382e4e54232SGeorge Liu     {
383e4e54232SGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
384e4e54232SGeorge Liu         return;
385e4e54232SGeorge Liu     }
386e4e54232SGeorge Liu 
387e4e54232SGeorge Liu     getValidFanPath(
388e4e54232SGeorge Liu         asyncResp, *validChassisPath, fanId,
389e4e54232SGeorge Liu         std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
390e4e54232SGeorge Liu }
391e4e54232SGeorge Liu 
392e4e54232SGeorge Liu inline void handleFanHead(App& app, const crow::Request& req,
393e4e54232SGeorge Liu                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
394e4e54232SGeorge Liu                           const std::string& chassisId,
395e4e54232SGeorge Liu                           const std::string& fanId)
396e4e54232SGeorge Liu {
397e4e54232SGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
398e4e54232SGeorge Liu     {
399e4e54232SGeorge Liu         return;
400e4e54232SGeorge Liu     }
401e4e54232SGeorge Liu 
402e4e54232SGeorge Liu     redfish::chassis_utils::getValidChassisPath(
403e4e54232SGeorge Liu         asyncResp, chassisId,
404e4e54232SGeorge Liu         [asyncResp, chassisId,
405e4e54232SGeorge Liu          fanId](const std::optional<std::string>& validChassisPath) {
406e4e54232SGeorge Liu         if (!validChassisPath)
407e4e54232SGeorge Liu         {
408e4e54232SGeorge Liu             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
409e4e54232SGeorge Liu             return;
410e4e54232SGeorge Liu         }
411e4e54232SGeorge Liu         getValidFanPath(asyncResp, *validChassisPath, fanId,
412e4e54232SGeorge Liu                         [asyncResp](const std::string&, const std::string&) {
413e4e54232SGeorge Liu             asyncResp->res.addHeader(
414e4e54232SGeorge Liu                 boost::beast::http::field::link,
415e4e54232SGeorge Liu                 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
416e4e54232SGeorge Liu         });
417e4e54232SGeorge Liu     });
418e4e54232SGeorge Liu }
419e4e54232SGeorge Liu 
420e4e54232SGeorge Liu inline void handleFanGet(App& app, const crow::Request& req,
421e4e54232SGeorge Liu                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
422e4e54232SGeorge Liu                          const std::string& chassisId, const std::string& fanId)
423e4e54232SGeorge Liu {
424e4e54232SGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
425e4e54232SGeorge Liu     {
426e4e54232SGeorge Liu         return;
427e4e54232SGeorge Liu     }
428e4e54232SGeorge Liu 
429e4e54232SGeorge Liu     redfish::chassis_utils::getValidChassisPath(
430e4e54232SGeorge Liu         asyncResp, chassisId,
431e4e54232SGeorge Liu         std::bind_front(doFanGet, asyncResp, chassisId, fanId));
432e4e54232SGeorge Liu }
433e4e54232SGeorge Liu 
434e4e54232SGeorge Liu inline void requestRoutesFan(App& app)
435e4e54232SGeorge Liu {
436e4e54232SGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
437e4e54232SGeorge Liu         .privileges(redfish::privileges::headFan)
438e4e54232SGeorge Liu         .methods(boost::beast::http::verb::head)(
439e4e54232SGeorge Liu             std::bind_front(handleFanHead, std::ref(app)));
440e4e54232SGeorge Liu 
441e4e54232SGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
442e4e54232SGeorge Liu         .privileges(redfish::privileges::getFan)
443e4e54232SGeorge Liu         .methods(boost::beast::http::verb::get)(
444e4e54232SGeorge Liu             std::bind_front(handleFanGet, std::ref(app)));
445e4e54232SGeorge Liu }
446e4e54232SGeorge Liu 
4479516f41fSGeorge Liu } // namespace redfish
448