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