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