19516f41fSGeorge Liu #pragma once 29516f41fSGeorge Liu 39516f41fSGeorge Liu #include "app.hpp" 49516f41fSGeorge Liu #include "dbus_utility.hpp" 59516f41fSGeorge Liu #include "error_messages.hpp" 69516f41fSGeorge Liu #include "query.hpp" 79516f41fSGeorge Liu #include "registries/privilege_registry.hpp" 89516f41fSGeorge Liu #include "utils/chassis_utils.hpp" 99516f41fSGeorge Liu 109516f41fSGeorge Liu #include <boost/url/format.hpp> 119516f41fSGeorge Liu #include <sdbusplus/message/types.hpp> 129516f41fSGeorge Liu 139516f41fSGeorge Liu #include <functional> 149516f41fSGeorge Liu #include <memory> 159516f41fSGeorge Liu #include <optional> 169516f41fSGeorge Liu #include <string> 179516f41fSGeorge Liu #include <string_view> 189516f41fSGeorge Liu 199516f41fSGeorge Liu namespace redfish 209516f41fSGeorge Liu { 219516f41fSGeorge Liu constexpr std::array<std::string_view, 1> fanInterface = { 229516f41fSGeorge Liu "xyz.openbmc_project.Inventory.Item.Fan"}; 239516f41fSGeorge Liu 249516f41fSGeorge Liu inline void 259516f41fSGeorge Liu updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 269516f41fSGeorge Liu const std::string& chassisId, 279516f41fSGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) 289516f41fSGeorge Liu { 299516f41fSGeorge Liu nlohmann::json& fanList = asyncResp->res.jsonValue["Members"]; 309516f41fSGeorge Liu for (const std::string& fanPath : fanPaths) 319516f41fSGeorge Liu { 329516f41fSGeorge Liu std::string fanName = 339516f41fSGeorge Liu sdbusplus::message::object_path(fanPath).filename(); 349516f41fSGeorge Liu if (fanName.empty()) 359516f41fSGeorge Liu { 369516f41fSGeorge Liu continue; 379516f41fSGeorge Liu } 389516f41fSGeorge Liu 399516f41fSGeorge Liu nlohmann::json item = nlohmann::json::object(); 409516f41fSGeorge Liu item["@odata.id"] = boost::urls::format( 419516f41fSGeorge Liu "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, 429516f41fSGeorge Liu fanName); 439516f41fSGeorge Liu 449516f41fSGeorge Liu fanList.emplace_back(std::move(item)); 459516f41fSGeorge Liu } 469516f41fSGeorge Liu asyncResp->res.jsonValue["Members@odata.count"] = fanList.size(); 479516f41fSGeorge Liu } 489516f41fSGeorge Liu 499516f41fSGeorge Liu inline void getFanPaths( 509516f41fSGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 519516f41fSGeorge Liu const std::optional<std::string>& validChassisPath, 529516f41fSGeorge Liu const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse& 539516f41fSGeorge Liu fanPaths)>& callback) 549516f41fSGeorge Liu { 559516f41fSGeorge Liu sdbusplus::message::object_path endpointPath{*validChassisPath}; 569516f41fSGeorge Liu endpointPath /= "cooled_by"; 579516f41fSGeorge Liu 589516f41fSGeorge Liu dbus::utility::getAssociatedSubTreePaths( 599516f41fSGeorge Liu endpointPath, 609516f41fSGeorge Liu sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, 619516f41fSGeorge Liu fanInterface, 629516f41fSGeorge Liu [asyncResp, callback]( 639516f41fSGeorge Liu const boost::system::error_code& ec, 649516f41fSGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { 659516f41fSGeorge Liu if (ec) 669516f41fSGeorge Liu { 679516f41fSGeorge Liu if (ec.value() != EBADR) 689516f41fSGeorge Liu { 699516f41fSGeorge Liu BMCWEB_LOG_ERROR 709516f41fSGeorge Liu << "DBUS response error for getAssociatedSubTreePaths " 719516f41fSGeorge Liu << ec.value(); 729516f41fSGeorge Liu messages::internalError(asyncResp->res); 739516f41fSGeorge Liu } 749516f41fSGeorge Liu return; 759516f41fSGeorge Liu } 769516f41fSGeorge Liu callback(subtreePaths); 779516f41fSGeorge Liu }); 789516f41fSGeorge Liu } 799516f41fSGeorge Liu 809516f41fSGeorge Liu inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 819516f41fSGeorge Liu const std::string& chassisId, 829516f41fSGeorge Liu const std::optional<std::string>& validChassisPath) 839516f41fSGeorge Liu { 849516f41fSGeorge Liu if (!validChassisPath) 859516f41fSGeorge Liu { 869516f41fSGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 879516f41fSGeorge Liu return; 889516f41fSGeorge Liu } 899516f41fSGeorge Liu 909516f41fSGeorge Liu asyncResp->res.addHeader( 919516f41fSGeorge Liu boost::beast::http::field::link, 929516f41fSGeorge Liu "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); 939516f41fSGeorge Liu asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection"; 949516f41fSGeorge Liu asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 959516f41fSGeorge Liu "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId); 969516f41fSGeorge Liu asyncResp->res.jsonValue["Name"] = "Fan Collection"; 979516f41fSGeorge Liu asyncResp->res.jsonValue["Description"] = 989516f41fSGeorge Liu "The collection of Fan resource instances " + chassisId; 999516f41fSGeorge Liu asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 1009516f41fSGeorge Liu asyncResp->res.jsonValue["Members@odata.count"] = 0; 1019516f41fSGeorge Liu 1029516f41fSGeorge Liu getFanPaths(asyncResp, validChassisPath, 1039516f41fSGeorge Liu std::bind_front(updateFanList, asyncResp, chassisId)); 1049516f41fSGeorge Liu } 1059516f41fSGeorge Liu 1069516f41fSGeorge Liu inline void 1079516f41fSGeorge Liu handleFanCollectionHead(App& app, const crow::Request& req, 1089516f41fSGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1099516f41fSGeorge Liu const std::string& chassisId) 1109516f41fSGeorge Liu { 1119516f41fSGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1129516f41fSGeorge Liu { 1139516f41fSGeorge Liu return; 1149516f41fSGeorge Liu } 1159516f41fSGeorge Liu 1169516f41fSGeorge Liu redfish::chassis_utils::getValidChassisPath( 1179516f41fSGeorge Liu asyncResp, chassisId, 1189516f41fSGeorge Liu [asyncResp, 1199516f41fSGeorge Liu chassisId](const std::optional<std::string>& validChassisPath) { 1209516f41fSGeorge Liu if (!validChassisPath) 1219516f41fSGeorge Liu { 1229516f41fSGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 1239516f41fSGeorge Liu return; 1249516f41fSGeorge Liu } 1259516f41fSGeorge Liu asyncResp->res.addHeader( 1269516f41fSGeorge Liu boost::beast::http::field::link, 1279516f41fSGeorge Liu "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); 1289516f41fSGeorge Liu }); 1299516f41fSGeorge Liu } 1309516f41fSGeorge Liu 1319516f41fSGeorge Liu inline void 1329516f41fSGeorge Liu handleFanCollectionGet(App& app, const crow::Request& req, 1339516f41fSGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1349516f41fSGeorge Liu const std::string& chassisId) 1359516f41fSGeorge Liu { 1369516f41fSGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1379516f41fSGeorge Liu { 1389516f41fSGeorge Liu return; 1399516f41fSGeorge Liu } 1409516f41fSGeorge Liu 1419516f41fSGeorge Liu redfish::chassis_utils::getValidChassisPath( 1429516f41fSGeorge Liu asyncResp, chassisId, 1439516f41fSGeorge Liu std::bind_front(doFanCollection, asyncResp, chassisId)); 1449516f41fSGeorge Liu } 1459516f41fSGeorge Liu 1469516f41fSGeorge Liu inline void requestRoutesFanCollection(App& app) 1479516f41fSGeorge Liu { 1489516f41fSGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") 1499516f41fSGeorge Liu .privileges(redfish::privileges::headFanCollection) 1509516f41fSGeorge Liu .methods(boost::beast::http::verb::head)( 1519516f41fSGeorge Liu std::bind_front(handleFanCollectionHead, std::ref(app))); 1529516f41fSGeorge Liu 1539516f41fSGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") 1549516f41fSGeorge Liu .privileges(redfish::privileges::getFanCollection) 1559516f41fSGeorge Liu .methods(boost::beast::http::verb::get)( 1569516f41fSGeorge Liu std::bind_front(handleFanCollectionGet, std::ref(app))); 1579516f41fSGeorge Liu } 1589516f41fSGeorge Liu 159*e4e54232SGeorge Liu inline bool checkFanId(const std::string& fanPath, const std::string& fanId) 160*e4e54232SGeorge Liu { 161*e4e54232SGeorge Liu std::string fanName = sdbusplus::message::object_path(fanPath).filename(); 162*e4e54232SGeorge Liu 163*e4e54232SGeorge Liu return !(fanName.empty() || fanName != fanId); 164*e4e54232SGeorge Liu } 165*e4e54232SGeorge Liu 166*e4e54232SGeorge Liu static inline void handleFanPath( 167*e4e54232SGeorge Liu const std::string& fanId, 168*e4e54232SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 169*e4e54232SGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& fanPaths, 170*e4e54232SGeorge Liu const std::function<void(const std::string& fanPath, 171*e4e54232SGeorge Liu const std::string& service)>& callback) 172*e4e54232SGeorge Liu { 173*e4e54232SGeorge Liu for (const auto& fanPath : fanPaths) 174*e4e54232SGeorge Liu { 175*e4e54232SGeorge Liu if (!checkFanId(fanPath, fanId)) 176*e4e54232SGeorge Liu { 177*e4e54232SGeorge Liu continue; 178*e4e54232SGeorge Liu } 179*e4e54232SGeorge Liu dbus::utility::getDbusObject( 180*e4e54232SGeorge Liu fanPath, fanInterface, 181*e4e54232SGeorge Liu [fanPath, asyncResp, 182*e4e54232SGeorge Liu callback](const boost::system::error_code& ec, 183*e4e54232SGeorge Liu const dbus::utility::MapperGetObject& object) { 184*e4e54232SGeorge Liu if (ec || object.empty()) 185*e4e54232SGeorge Liu { 186*e4e54232SGeorge Liu BMCWEB_LOG_ERROR << "DBUS response error on getDbusObject " 187*e4e54232SGeorge Liu << ec.value(); 188*e4e54232SGeorge Liu messages::internalError(asyncResp->res); 189*e4e54232SGeorge Liu return; 190*e4e54232SGeorge Liu } 191*e4e54232SGeorge Liu callback(fanPath, object.begin()->first); 192*e4e54232SGeorge Liu }); 193*e4e54232SGeorge Liu 194*e4e54232SGeorge Liu return; 195*e4e54232SGeorge Liu } 196*e4e54232SGeorge Liu BMCWEB_LOG_WARNING << "Fan not found " << fanId; 197*e4e54232SGeorge Liu messages::resourceNotFound(asyncResp->res, "Fan", fanId); 198*e4e54232SGeorge Liu } 199*e4e54232SGeorge Liu 200*e4e54232SGeorge Liu inline void getValidFanPath( 201*e4e54232SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 202*e4e54232SGeorge Liu const std::string& validChassisPath, const std::string& fanId, 203*e4e54232SGeorge Liu const std::function<void(const std::string& fanPath, 204*e4e54232SGeorge Liu const std::string& service)>& callback) 205*e4e54232SGeorge Liu { 206*e4e54232SGeorge Liu getFanPaths( 207*e4e54232SGeorge Liu asyncResp, validChassisPath, 208*e4e54232SGeorge Liu [fanId, asyncResp, callback]( 209*e4e54232SGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) { 210*e4e54232SGeorge Liu handleFanPath(fanId, asyncResp, fanPaths, callback); 211*e4e54232SGeorge Liu }); 212*e4e54232SGeorge Liu } 213*e4e54232SGeorge Liu 214*e4e54232SGeorge Liu inline void addFanCommonProperties(crow::Response& resp, 215*e4e54232SGeorge Liu const std::string& chassisId, 216*e4e54232SGeorge Liu const std::string& fanId) 217*e4e54232SGeorge Liu { 218*e4e54232SGeorge Liu resp.addHeader(boost::beast::http::field::link, 219*e4e54232SGeorge Liu "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby"); 220*e4e54232SGeorge Liu resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan"; 221*e4e54232SGeorge Liu resp.jsonValue["Name"] = "Fan"; 222*e4e54232SGeorge Liu resp.jsonValue["Id"] = fanId; 223*e4e54232SGeorge Liu resp.jsonValue["@odata.id"] = boost::urls::format( 224*e4e54232SGeorge Liu "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId); 225*e4e54232SGeorge Liu } 226*e4e54232SGeorge Liu 227*e4e54232SGeorge Liu inline void 228*e4e54232SGeorge Liu afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 229*e4e54232SGeorge Liu const std::string& chassisId, const std::string& fanId, 230*e4e54232SGeorge Liu const std::string& fanPath, const std::string& service) 231*e4e54232SGeorge Liu { 232*e4e54232SGeorge Liu BMCWEB_LOG_DEBUG << "fanPath = " << fanPath << " service = " << service; 233*e4e54232SGeorge Liu addFanCommonProperties(asyncResp->res, chassisId, fanId); 234*e4e54232SGeorge Liu } 235*e4e54232SGeorge Liu 236*e4e54232SGeorge Liu inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 237*e4e54232SGeorge Liu const std::string& chassisId, const std::string& fanId, 238*e4e54232SGeorge Liu const std::optional<std::string>& validChassisPath) 239*e4e54232SGeorge Liu { 240*e4e54232SGeorge Liu if (!validChassisPath) 241*e4e54232SGeorge Liu { 242*e4e54232SGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 243*e4e54232SGeorge Liu return; 244*e4e54232SGeorge Liu } 245*e4e54232SGeorge Liu 246*e4e54232SGeorge Liu getValidFanPath( 247*e4e54232SGeorge Liu asyncResp, *validChassisPath, fanId, 248*e4e54232SGeorge Liu std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId)); 249*e4e54232SGeorge Liu } 250*e4e54232SGeorge Liu 251*e4e54232SGeorge Liu inline void handleFanHead(App& app, const crow::Request& req, 252*e4e54232SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 253*e4e54232SGeorge Liu const std::string& chassisId, 254*e4e54232SGeorge Liu const std::string& fanId) 255*e4e54232SGeorge Liu { 256*e4e54232SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 257*e4e54232SGeorge Liu { 258*e4e54232SGeorge Liu return; 259*e4e54232SGeorge Liu } 260*e4e54232SGeorge Liu 261*e4e54232SGeorge Liu redfish::chassis_utils::getValidChassisPath( 262*e4e54232SGeorge Liu asyncResp, chassisId, 263*e4e54232SGeorge Liu [asyncResp, chassisId, 264*e4e54232SGeorge Liu fanId](const std::optional<std::string>& validChassisPath) { 265*e4e54232SGeorge Liu if (!validChassisPath) 266*e4e54232SGeorge Liu { 267*e4e54232SGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 268*e4e54232SGeorge Liu return; 269*e4e54232SGeorge Liu } 270*e4e54232SGeorge Liu getValidFanPath(asyncResp, *validChassisPath, fanId, 271*e4e54232SGeorge Liu [asyncResp](const std::string&, const std::string&) { 272*e4e54232SGeorge Liu asyncResp->res.addHeader( 273*e4e54232SGeorge Liu boost::beast::http::field::link, 274*e4e54232SGeorge Liu "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby"); 275*e4e54232SGeorge Liu }); 276*e4e54232SGeorge Liu }); 277*e4e54232SGeorge Liu } 278*e4e54232SGeorge Liu 279*e4e54232SGeorge Liu inline void handleFanGet(App& app, const crow::Request& req, 280*e4e54232SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 281*e4e54232SGeorge Liu const std::string& chassisId, const std::string& fanId) 282*e4e54232SGeorge Liu { 283*e4e54232SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 284*e4e54232SGeorge Liu { 285*e4e54232SGeorge Liu return; 286*e4e54232SGeorge Liu } 287*e4e54232SGeorge Liu 288*e4e54232SGeorge Liu redfish::chassis_utils::getValidChassisPath( 289*e4e54232SGeorge Liu asyncResp, chassisId, 290*e4e54232SGeorge Liu std::bind_front(doFanGet, asyncResp, chassisId, fanId)); 291*e4e54232SGeorge Liu } 292*e4e54232SGeorge Liu 293*e4e54232SGeorge Liu inline void requestRoutesFan(App& app) 294*e4e54232SGeorge Liu { 295*e4e54232SGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/") 296*e4e54232SGeorge Liu .privileges(redfish::privileges::headFan) 297*e4e54232SGeorge Liu .methods(boost::beast::http::verb::head)( 298*e4e54232SGeorge Liu std::bind_front(handleFanHead, std::ref(app))); 299*e4e54232SGeorge Liu 300*e4e54232SGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/") 301*e4e54232SGeorge Liu .privileges(redfish::privileges::getFan) 302*e4e54232SGeorge Liu .methods(boost::beast::http::verb::get)( 303*e4e54232SGeorge Liu std::bind_front(handleFanGet, std::ref(app))); 304*e4e54232SGeorge Liu } 305*e4e54232SGeorge Liu 3069516f41fSGeorge Liu } // namespace redfish 307