#pragma once #include "app.hpp" #include "dbus_utility.hpp" #include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/chassis_utils.hpp" #include "utils/dbus_utils.hpp" #include "utils/json_utils.hpp" #include #include #include #include #include namespace redfish { static constexpr std::array powerSupplyInterface = { "xyz.openbmc_project.Inventory.Item.PowerSupply"}; inline void updatePowerSupplyList( const std::shared_ptr& asyncResp, const std::string& chassisId, const dbus::utility::MapperGetSubTreePathsResponse& powerSupplyPaths) { nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"]; for (const std::string& powerSupplyPath : powerSupplyPaths) { std::string powerSupplyName = sdbusplus::message::object_path(powerSupplyPath).filename(); if (powerSupplyName.empty()) { continue; } nlohmann::json item = nlohmann::json::object(); item["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, powerSupplyName); powerSupplyList.emplace_back(std::move(item)); } asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size(); } inline void doPowerSupplyCollection(const std::shared_ptr& asyncResp, const std::string& chassisId, const std::optional& validChassisPath) { if (!validChassisPath) { messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); asyncResp->res.jsonValue["@odata.type"] = "#PowerSupplyCollection.PowerSupplyCollection"; asyncResp->res.jsonValue["Name"] = "Power Supply Collection"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId); asyncResp->res.jsonValue["Description"] = "The collection of PowerSupply resource instances."; asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); asyncResp->res.jsonValue["Members@odata.count"] = 0; std::string powerPath = *validChassisPath + "/powered_by"; dbus::utility::getAssociatedSubTreePaths( powerPath, sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, powerSupplyInterface, [asyncResp, chassisId]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error{}", ec.value()); messages::internalError(asyncResp->res); } return; } updatePowerSupplyList(asyncResp, chassisId, subtreePaths); }); } inline void handlePowerSupplyCollectionHead( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& chassisId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } redfish::chassis_utils::getValidChassisPath( asyncResp, chassisId, [asyncResp, chassisId](const std::optional& validChassisPath) { if (!validChassisPath) { messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); }); } inline void handlePowerSupplyCollectionGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& chassisId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } redfish::chassis_utils::getValidChassisPath( asyncResp, chassisId, std::bind_front(doPowerSupplyCollection, asyncResp, chassisId)); } inline void requestRoutesPowerSupplyCollection(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Chassis//PowerSubsystem/PowerSupplies/") .privileges(redfish::privileges::headPowerSupplyCollection) .methods(boost::beast::http::verb::head)( std::bind_front(handlePowerSupplyCollectionHead, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Chassis//PowerSubsystem/PowerSupplies/") .privileges(redfish::privileges::getPowerSupplyCollection) .methods(boost::beast::http::verb::get)( std::bind_front(handlePowerSupplyCollectionGet, std::ref(app))); } inline bool checkPowerSupplyId(const std::string& powerSupplyPath, const std::string& powerSupplyId) { std::string powerSupplyName = sdbusplus::message::object_path(powerSupplyPath).filename(); return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId); } inline void getValidPowerSupplyPath( const std::shared_ptr& asyncResp, const std::string& validChassisPath, const std::string& powerSupplyId, std::function&& callback) { std::string powerPath = validChassisPath + "/powered_by"; dbus::utility::getAssociatedSubTreePaths( powerPath, sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, powerSupplyInterface, [asyncResp, powerSupplyId, callback{std::move(callback)}]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR( "DBUS response error for getAssociatedSubTreePaths{}", ec.value()); messages::internalError(asyncResp->res); return; } messages::resourceNotFound(asyncResp->res, "PowerSupplies", powerSupplyId); return; } for (const std::string& path : subtreePaths) { if (checkPowerSupplyId(path, powerSupplyId)) { callback(path); return; } } if (!subtreePaths.empty()) { BMCWEB_LOG_WARNING("Power supply not found: {}", powerSupplyId); messages::resourceNotFound(asyncResp->res, "PowerSupplies", powerSupplyId); return; } }); } inline void getPowerSupplyState(const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { sdbusplus::asio::getProperty( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp](const boost::system::error_code& ec, const bool value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error for State {}", ec.value()); messages::internalError(asyncResp->res); } return; } if (!value) { asyncResp->res.jsonValue["Status"]["State"] = resource::State::Absent; } }); } inline void getPowerSupplyHealth(const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { sdbusplus::asio::getProperty( *crow::connections::systemBus, service, path, "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", [asyncResp](const boost::system::error_code& ec, const bool value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error for Health {}", ec.value()); messages::internalError(asyncResp->res); } return; } if (!value) { asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::Critical; } }); } inline void getPowerSupplyAsset(const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Decorator.Asset", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error for Asset {}", ec.value()); messages::internalError(asyncResp->res); } return; } const std::string* partNumber = nullptr; const std::string* serialNumber = nullptr; const std::string* manufacturer = nullptr; const std::string* model = nullptr; const std::string* sparePartNumber = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer, "Model", model, "SparePartNumber", sparePartNumber); if (!success) { messages::internalError(asyncResp->res); return; } if (partNumber != nullptr) { asyncResp->res.jsonValue["PartNumber"] = *partNumber; } if (serialNumber != nullptr) { asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; } if (manufacturer != nullptr) { asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; } if (model != nullptr) { asyncResp->res.jsonValue["Model"] = *model; } // SparePartNumber is optional on D-Bus so skip if it is empty if (sparePartNumber != nullptr && !sparePartNumber->empty()) { asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; } }); } inline void getPowerSupplyFirmwareVersion( const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { sdbusplus::asio::getProperty( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Software.Version", "Version", [asyncResp](const boost::system::error_code& ec, const std::string& value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR( "DBUS response error for FirmwareVersion {}", ec.value()); messages::internalError(asyncResp->res); } return; } asyncResp->res.jsonValue["FirmwareVersion"] = value; }); } inline void getPowerSupplyLocation(const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { sdbusplus::asio::getProperty( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp](const boost::system::error_code& ec, const std::string& value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error for Location {}", ec.value()); messages::internalError(asyncResp->res); } return; } asyncResp->res .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = value; }); } inline void handleGetEfficiencyResponse( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, uint32_t value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error for DeratingFactor {}", ec.value()); messages::internalError(asyncResp->res); } return; } // The PDI default value is 0, if it hasn't been set leave off if (value == 0) { return; } nlohmann::json::array_t efficiencyRatings; nlohmann::json::object_t efficiencyPercent; efficiencyPercent["EfficiencyPercent"] = value; efficiencyRatings.emplace_back(std::move(efficiencyPercent)); asyncResp->res.jsonValue["EfficiencyRatings"] = std::move(efficiencyRatings); } inline void handlePowerSupplyAttributesSubTreeResponse( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error for EfficiencyPercent {}", ec.value()); messages::internalError(asyncResp->res); } return; } if (subtree.empty()) { BMCWEB_LOG_DEBUG("Can't find Power Supply Attributes!"); return; } if (subtree.size() != 1) { BMCWEB_LOG_ERROR( "Unexpected number of paths returned by getSubTree: {}", subtree.size()); messages::internalError(asyncResp->res); return; } const auto& [path, serviceMap] = *subtree.begin(); const auto& [service, interfaces] = *serviceMap.begin(); sdbusplus::asio::getProperty( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor", [asyncResp](const boost::system::error_code& ec1, uint32_t value) { handleGetEfficiencyResponse(asyncResp, ec1, value); }); } inline void getEfficiencyPercent(const std::shared_ptr& asyncResp) { constexpr std::array efficiencyIntf = { "xyz.openbmc_project.Control.PowerSupplyAttributes"}; dbus::utility::getSubTree( "/xyz/openbmc_project", 0, efficiencyIntf, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { handlePowerSupplyAttributesSubTreeResponse(asyncResp, ec, subtree); }); } inline void doPowerSupplyGet( const std::shared_ptr& asyncResp, const std::string& chassisId, const std::string& powerSupplyId, const std::optional& validChassisPath) { if (!validChassisPath) { messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); return; } // Get the correct Path and Service that match the input parameters getValidPowerSupplyPath( asyncResp, *validChassisPath, powerSupplyId, [asyncResp, chassisId, powerSupplyId](const std::string& powerSupplyPath) { asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); asyncResp->res.jsonValue["@odata.type"] = "#PowerSupply.v1_5_0.PowerSupply"; asyncResp->res.jsonValue["Name"] = "Power Supply"; asyncResp->res.jsonValue["Id"] = powerSupplyId; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, powerSupplyId); asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; dbus::utility::getDbusObject( powerSupplyPath, powerSupplyInterface, [asyncResp, powerSupplyPath]( const boost::system::error_code& ec, const dbus::utility::MapperGetObject& object) { if (ec || object.empty()) { messages::internalError(asyncResp->res); return; } getPowerSupplyState(asyncResp, object.begin()->first, powerSupplyPath); getPowerSupplyHealth(asyncResp, object.begin()->first, powerSupplyPath); getPowerSupplyAsset(asyncResp, object.begin()->first, powerSupplyPath); getPowerSupplyFirmwareVersion( asyncResp, object.begin()->first, powerSupplyPath); getPowerSupplyLocation(asyncResp, object.begin()->first, powerSupplyPath); }); getEfficiencyPercent(asyncResp); }); } inline void handlePowerSupplyHead( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& chassisId, const std::string& powerSupplyId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } redfish::chassis_utils::getValidChassisPath( asyncResp, chassisId, [asyncResp, chassisId, powerSupplyId](const std::optional& validChassisPath) { if (!validChassisPath) { messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); return; } // Get the correct Path and Service that match the input parameters getValidPowerSupplyPath( asyncResp, *validChassisPath, powerSupplyId, [asyncResp](const std::string&) { asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); }); }); } inline void handlePowerSupplyGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& chassisId, const std::string& powerSupplyId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } redfish::chassis_utils::getValidChassisPath( asyncResp, chassisId, std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId)); } inline void requestRoutesPowerSupply(App& app) { BMCWEB_ROUTE( app, "/redfish/v1/Chassis//PowerSubsystem/PowerSupplies//") .privileges(redfish::privileges::headPowerSupply) .methods(boost::beast::http::verb::head)( std::bind_front(handlePowerSupplyHead, std::ref(app))); BMCWEB_ROUTE( app, "/redfish/v1/Chassis//PowerSubsystem/PowerSupplies//") .privileges(redfish::privileges::getPowerSupply) .methods(boost::beast::http::verb::get)( std::bind_front(handlePowerSupplyGet, std::ref(app))); } } // namespace redfish