| #pragma once |
| |
| #include "managed_store_types.hpp" |
| |
| #include <app.hpp> |
| #include <registries/privilege_registry.hpp> |
| #include <utils/chassis_utils.hpp> |
| #include <utils/json_utils.hpp> |
| |
| namespace redfish |
| { |
| |
| static constexpr const std::array<std::string_view, 1> fanInterfaces = { |
| "xyz.openbmc_project.Configuration.Fan"}; |
| static constexpr const std::array<std::string_view, 1> sensorInterfaces = { |
| "xyz.openbmc_project.Sensor.Value"}; |
| |
| inline void |
| getFanSensorValue(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, |
| const std::string& sensorName, |
| const std::string& sensorType) |
| { |
| if (sensorType != "fan_tach" && sensorType != "fan_pwm") |
| { |
| BMCWEB_LOG_DEBUG << "Unsupported sensor type"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| auto getFanSensorsHandler = |
| [asyncResp, chassisId, sensorName, sensorType, requestContext]( |
| const boost::system::error_code ec, |
| const std::vector<std::pair< |
| std::string, |
| std::vector<std::pair<std::string, std::vector<std::string>>>>>& |
| sensorsubtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| for (const auto& [objectPath, serviceNames] : sensorsubtree) |
| { |
| if (objectPath.empty() || serviceNames.size() != 1) |
| { |
| BMCWEB_LOG_DEBUG << "Error getting Fan D-Bus object!"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| sdbusplus::message::object_path path(objectPath); |
| const std::string& leaf = path.filename(); |
| if (leaf.empty()) |
| { |
| continue; |
| } |
| if (leaf != sensorName) |
| { |
| continue; |
| } |
| |
| const std::string& connectionName = serviceNames[0].first; |
| const std::string& tempPath = objectPath; |
| auto getAssociationHandler = |
| [asyncResp, tempPath, connectionName, chassisId, sensorName, |
| sensorType, |
| requestContext](const boost::system::error_code ec2, |
| const std::vector<std::string>& resp) { |
| BMCWEB_LOG_DEBUG << "Getting chassis association for sensor " |
| << sensorName; |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG |
| << "Error getting chassis association for the sensor!"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| if (resp.size() != 1) |
| { |
| BMCWEB_LOG_DEBUG |
| << "The number of associated chassis should be 1"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| sdbusplus::message::object_path path2(resp[0]); |
| std::string leaf2 = path2.filename(); |
| if (leaf2 != chassisId) |
| { |
| BMCWEB_LOG_DEBUG |
| << "The sensor doesn't belong to the chassis " |
| << chassisId; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| auto getFanPropertyHandler = |
| [asyncResp, chassisId, sensorName, |
| sensorType](const boost::system::error_code ec3, |
| const double& resp2) { |
| if (ec3) |
| { |
| BMCWEB_LOG_DEBUG |
| << "Error getting sensor value for sensor:" |
| << sensorName; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| if (sensorType == "fan_pwm") |
| { |
| asyncResp->res |
| .jsonValue["SpeedPercent"]["DataSourceUri"] = |
| std::string("/redfish/v1/Chassis/") |
| .append(chassisId) |
| .append("/Sensors/") |
| .append("fanpwm_" + sensorName) |
| .append("/"); |
| asyncResp->res.jsonValue["SpeedPercent"]["Reading"] = |
| resp2; |
| } |
| else |
| { |
| asyncResp->res.jsonValue["SpeedPercent"]["SpeedRPM"] = |
| resp2; |
| } |
| }; |
| dbus_utils::getProperty<double>( |
| connectionName, tempPath, |
| "xyz.openbmc_project.Sensor.Value", "Value", requestContext, |
| getFanPropertyHandler); |
| }; |
| dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", tempPath + "/chassis", |
| "xyz.openbmc_project.Association", "endpoints", requestContext, |
| getAssociationHandler); |
| } |
| }; |
| managedStore::managedObjectStore->getSubTree( |
| "/xyz/openbmc_project/sensors", 0, sensorInterfaces, requestContext, |
| getFanSensorsHandler); |
| } |
| |
| inline void |
| fillFanProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const boost::system::error_code ec, |
| const dbus::utility::DBusPropertiesMap& properties, |
| const std::string& chassisId) |
| { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| for (const auto& [propKey, propVariant] : properties) |
| { |
| if (propKey == "HotPluggable") |
| { |
| const bool* hotPluggable = std::get_if<bool>(&propVariant); |
| if (hotPluggable == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| asyncResp->res.jsonValue["HotPluggable"] = *hotPluggable; |
| } |
| else if (propKey == "LocationType") |
| { |
| const std::string* locationType = |
| std::get_if<std::string>(&propVariant); |
| if (locationType == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| asyncResp->res |
| .jsonValue["Location"]["PartLocation"]["LocationType"] = |
| *locationType; |
| } |
| else if (propKey == "ServiceLabel") |
| { |
| const std::string* serviceLabel = |
| std::get_if<std::string>(&propVariant); |
| if (serviceLabel == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| asyncResp->res |
| .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = |
| *serviceLabel; |
| } |
| else if (propKey == "TachSensor") |
| { |
| const std::string* tachSensor = |
| std::get_if<std::string>(&propVariant); |
| if (tachSensor == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| getFanSensorValue(asyncResp, chassisId, *tachSensor, "fan_tach"); |
| } |
| else if (propKey == "PWMSensor") |
| { |
| const std::string* pwmSensor = |
| std::get_if<std::string>(&propVariant); |
| if (pwmSensor == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| getFanSensorValue(asyncResp, chassisId, *pwmSensor, "fan_pwm"); |
| } |
| } |
| } |
| |
| inline void |
| getFanProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& fanObjectPath, |
| const dbus::utility::MapperServiceMap& serviceMap, |
| const std::string& chassisId) |
| { |
| BMCWEB_LOG_DEBUG << "Get Properties for Fan " << fanObjectPath; |
| for (const auto& [service, interfaces] : serviceMap) |
| { |
| for (const auto& interface : interfaces) |
| { |
| if (interface != fanInterfaces[0]) |
| { |
| continue; |
| } |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, chassisId]( |
| const boost::system::error_code ec, |
| const dbus::utility::DBusPropertiesMap& properties) { |
| fillFanProperties(asyncResp, ec, properties, chassisId); |
| }, |
| service, fanObjectPath, "org.freedesktop.DBus.Properties", |
| "GetAll", interface); |
| } |
| } |
| } |
| |
| inline void getValidFan(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId) |
| { |
| BMCWEB_LOG_DEBUG << "Get fan list associated to chassis = " << chassisId; |
| asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection"; |
| asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Chassis", chassisId, "ThermalSubsystem", "Fans"); |
| asyncResp->res.jsonValue["Name"] = "Fan Collection"; |
| asyncResp->res.jsonValue["Description"] = |
| "The collasync_method_callection of Fan resource instances " + |
| chassisId; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::managedObjectStore->getSubTree( |
| "/xyz/openbmc_project/inventory", 0, fanInterfaces, requestContext, |
| [asyncResp, chassisId]( |
| const boost::system::error_code ec, |
| const std::vector<std::pair< |
| std::string, |
| std::vector<std::pair<std::string, std::vector<std::string>>>>>& |
| fansubtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| nlohmann::json& fanList = asyncResp->res.jsonValue["Members"]; |
| fanList = nlohmann::json::array(); |
| |
| std::string newPath = |
| "/redfish/v1/Chassis/" + chassisId + "/ThermalSubsystem/Fans/"; |
| |
| for (const auto& [objectPath, serviceName] : fansubtree) |
| { |
| if (objectPath.empty() || serviceName.size() != 1) |
| { |
| BMCWEB_LOG_DEBUG << "Error getting D-Bus object!"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| const std::string& validPath = objectPath; |
| // const std::string& connectionName = serviceName[0].first; |
| std::string chassisName; |
| std::string fanName; |
| |
| // 5 and 6 below comes from |
| // /xyz/openbmc_project/inventory/system/chassis/chassisName/fanName |
| // 0 1 2 3 4 5 6 |
| if (!dbus::utility::getNthStringFromPath(validPath, 5, |
| chassisName) || |
| !dbus::utility::getNthStringFromPath(validPath, 6, fanName)) |
| { |
| BMCWEB_LOG_ERROR << "Got invalid path " << validPath; |
| messages::invalidObject(asyncResp->res, |
| crow::utility::urlFromPieces( |
| "xyz", "openbmc_project", |
| "inventory", "system", validPath)); |
| continue; |
| } |
| if (chassisName != chassisId) |
| { |
| BMCWEB_LOG_ERROR << "The fan obtained at this time " |
| "does not belong to this chassis "; |
| continue; |
| } |
| fanList.push_back({{"@odata.id", newPath + fanName + "/"}}); |
| } |
| |
| asyncResp->res.jsonValue["Members@odata.count"] = fanList.size(); |
| }); |
| } |
| |
| inline void requestRoutesFanCollection(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") |
| .privileges(redfish::privileges::getFanCollection) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| auto getChassisId = [asyncResp, chassisId]( |
| const std::optional<std::string>& chassisPath) { |
| if (!chassisPath) |
| { |
| BMCWEB_LOG_ERROR << "Not a valid chassis ID" << chassisId; |
| messages::resourceNotFound(asyncResp->res, "Chassis", |
| chassisId); |
| return; |
| } |
| getValidFan(asyncResp, chassisId); |
| }; |
| redfish::chassis_utils::getValidChassisPath(asyncResp, chassisId, |
| std::move(getChassisId)); |
| }); |
| } |
| |
| inline void requestRoutesFan(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/") |
| .privileges(redfish::privileges::getFan) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, const std::string& fanId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| auto getChassisId = [asyncResp, chassisId, fanId]( |
| const std::optional<std::string>& chassisPath) { |
| if (!chassisPath) |
| { |
| BMCWEB_LOG_ERROR << "Not a valid chassis ID" << chassisId; |
| messages::resourceNotFound(asyncResp->res, "Chassis", |
| chassisId); |
| return; |
| } |
| auto respHandler = |
| [asyncResp, chassisId, fanId]( |
| const boost::system::error_code ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| sdbusplus::message::object_path path(objectPath); |
| if (path.filename() != fanId) |
| { |
| continue; |
| } |
| std::string newPath = "/redfish/v1/Chassis/" + chassisId + |
| "/ThermalSubsystem/Fans/"; |
| asyncResp->res.jsonValue["@odata.type"] = "#Fan.v1_2_0.Fan"; |
| asyncResp->res.jsonValue["Name"] = fanId; |
| asyncResp->res.jsonValue["Id"] = fanId; |
| asyncResp->res.jsonValue["@odata.id"] = |
| crow::utility::urlFromPieces( |
| "redfish", "v1", "Chassis", chassisId, |
| "ThermalSubsystem", "Fans", fanId); |
| getFanProperties(asyncResp, objectPath, serviceMap, |
| chassisId); |
| return; |
| } |
| }; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::managedObjectStore->getSubTree( |
| "/xyz/openbmc_project/inventory", 0, fanInterfaces, |
| requestContext, respHandler); |
| }; |
| redfish::chassis_utils::getValidChassisPath(asyncResp, chassisId, |
| std::move(getChassisId)); |
| }); |
| } |
| |
| } // namespace redfish |