1 #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 10 #include <boost/url/format.hpp> 11 #include <sdbusplus/message/types.hpp> 12 13 #include <functional> 14 #include <memory> 15 #include <optional> 16 #include <string> 17 #include <string_view> 18 19 namespace redfish 20 { 21 constexpr std::array<std::string_view, 1> fanInterface = { 22 "xyz.openbmc_project.Inventory.Item.Fan"}; 23 24 inline void 25 updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26 const std::string& chassisId, 27 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) 28 { 29 nlohmann::json& fanList = asyncResp->res.jsonValue["Members"]; 30 for (const std::string& fanPath : fanPaths) 31 { 32 std::string fanName = 33 sdbusplus::message::object_path(fanPath).filename(); 34 if (fanName.empty()) 35 { 36 continue; 37 } 38 39 nlohmann::json item = nlohmann::json::object(); 40 item["@odata.id"] = boost::urls::format( 41 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, 42 fanName); 43 44 fanList.emplace_back(std::move(item)); 45 } 46 asyncResp->res.jsonValue["Members@odata.count"] = fanList.size(); 47 } 48 49 inline void getFanPaths( 50 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 51 const std::optional<std::string>& validChassisPath, 52 const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse& 53 fanPaths)>& callback) 54 { 55 sdbusplus::message::object_path endpointPath{*validChassisPath}; 56 endpointPath /= "cooled_by"; 57 58 dbus::utility::getAssociatedSubTreePaths( 59 endpointPath, 60 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, 61 fanInterface, 62 [asyncResp, callback]( 63 const boost::system::error_code& ec, 64 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { 65 if (ec) 66 { 67 if (ec.value() != EBADR) 68 { 69 BMCWEB_LOG_ERROR 70 << "DBUS response error for getAssociatedSubTreePaths " 71 << ec.value(); 72 messages::internalError(asyncResp->res); 73 } 74 return; 75 } 76 callback(subtreePaths); 77 }); 78 } 79 80 inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 81 const std::string& chassisId, 82 const std::optional<std::string>& validChassisPath) 83 { 84 if (!validChassisPath) 85 { 86 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 87 return; 88 } 89 90 asyncResp->res.addHeader( 91 boost::beast::http::field::link, 92 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); 93 asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection"; 94 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 95 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId); 96 asyncResp->res.jsonValue["Name"] = "Fan Collection"; 97 asyncResp->res.jsonValue["Description"] = 98 "The collection of Fan resource instances " + chassisId; 99 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 100 asyncResp->res.jsonValue["Members@odata.count"] = 0; 101 102 getFanPaths(asyncResp, validChassisPath, 103 std::bind_front(updateFanList, asyncResp, chassisId)); 104 } 105 106 inline void 107 handleFanCollectionHead(App& app, const crow::Request& req, 108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 109 const std::string& chassisId) 110 { 111 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 112 { 113 return; 114 } 115 116 redfish::chassis_utils::getValidChassisPath( 117 asyncResp, chassisId, 118 [asyncResp, 119 chassisId](const std::optional<std::string>& validChassisPath) { 120 if (!validChassisPath) 121 { 122 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 123 return; 124 } 125 asyncResp->res.addHeader( 126 boost::beast::http::field::link, 127 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); 128 }); 129 } 130 131 inline void 132 handleFanCollectionGet(App& app, const crow::Request& req, 133 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 134 const std::string& chassisId) 135 { 136 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 137 { 138 return; 139 } 140 141 redfish::chassis_utils::getValidChassisPath( 142 asyncResp, chassisId, 143 std::bind_front(doFanCollection, asyncResp, chassisId)); 144 } 145 146 inline void requestRoutesFanCollection(App& app) 147 { 148 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") 149 .privileges(redfish::privileges::headFanCollection) 150 .methods(boost::beast::http::verb::head)( 151 std::bind_front(handleFanCollectionHead, std::ref(app))); 152 153 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") 154 .privileges(redfish::privileges::getFanCollection) 155 .methods(boost::beast::http::verb::get)( 156 std::bind_front(handleFanCollectionGet, std::ref(app))); 157 } 158 159 } // namespace redfish 160