blob: e266514e2e9b4e0b057eac18fd44fdd09fb8e137 [file] [log] [blame]
#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