| #pragma once |
| |
| #include "app.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "managed_store.hpp" |
| #include "managed_store_types.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utils/chassis_utils.hpp" |
| #include "utils/json_utils.hpp" |
| #include "utils/location_utils.hpp" |
| |
| #include <boost/system/error_code.hpp> |
| #include <boost/url/format.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/message/types.hpp> |
| |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| namespace redfish |
| { |
| |
| constexpr std::array<std::string_view, 1> fanInterfaces = { |
| "xyz.openbmc_project.Configuration.Fan"}; |
| constexpr std::array<std::string_view, 1> sensorInterfaces = { |
| "xyz.openbmc_project.Sensor.Value"}; |
| |
| inline void |
| updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, |
| const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) |
| { |
| nlohmann::json& fanList = asyncResp->res.jsonValue["Members"]; |
| for (const std::string& fanPath : fanPaths) |
| { |
| if (fanPath.empty()) |
| { |
| BMCWEB_LOG_DEBUG << "Error getting D-Bus object!"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| std::string fanName = |
| sdbusplus::message::object_path(fanPath).filename(); |
| |
| std::string chassisName; |
| |
| // 5 below comes from |
| // /xyz/openbmc_project/inventory/system/chassis/chassisName/fanName |
| // 0 1 2 3 4 5 6 |
| if (!dbus::utility::getNthStringFromPath(fanPath, 5, chassisName) || |
| fanName.empty()) |
| { |
| BMCWEB_LOG_ERROR << "Got invalid path " << fanPath; |
| messages::invalidObject( |
| asyncResp->res, |
| crow::utility::urlFromPieces("xyz", "openbmc_project", |
| "inventory", "system", fanPath)); |
| continue; |
| } |
| |
| if (chassisName != chassisId) |
| { |
| BMCWEB_LOG_ERROR << "The fan obtained at this time " |
| "does not belong to this chassis "; |
| continue; |
| } |
| |
| nlohmann::json item = nlohmann::json::object(); |
| item["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, |
| fanName); |
| |
| fanList.emplace_back(std::move(item)); |
| } |
| asyncResp->res.jsonValue["Members@odata.count"] = fanList.size(); |
| } |
| |
| inline void getFanPaths( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::optional<std::string>& validChassisPath, |
| const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse& |
| fanPaths)>& callback) |
| { |
| sdbusplus::message::object_path endpointPath{*validChassisPath}; |
| endpointPath /= "cooled_by"; |
| |
| // TODO(tomtung): enable this after setting up associations. |
| // dbus::utility::getAssociatedSubTreePaths( |
| // endpointPath, |
| // sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, |
| // fanInterfaces, |
| // [asyncResp, 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; |
| // } |
| // callback(subtreePaths); |
| // }); |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, fanInterfaces, context, |
| [asyncResp, callback]( |
| const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| callback(subtreePaths); |
| }); |
| } |
| |
| inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, |
| const std::optional<std::string>& validChassisPath) |
| { |
| if (!validChassisPath) |
| { |
| BMCWEB_LOG_ERROR << "Not a valid chassis ID" << chassisId; |
| messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Get fan list associated to chassis = " << chassisId; |
| asyncResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); |
| asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection"; |
| asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId); |
| asyncResp->res.jsonValue["Name"] = "Fan Collection"; |
| asyncResp->res.jsonValue["Description"] = |
| "The collection of Fan resource instances " + chassisId; |
| asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); |
| asyncResp->res.jsonValue["Members@odata.count"] = 0; |
| |
| getFanPaths(asyncResp, validChassisPath, |
| std::bind_front(updateFanList, asyncResp, chassisId)); |
| } |
| |
| inline void |
| getFanSensorValue(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, |
| const std::string& sensorName, |
| const std::string& sensorType) |
| { |
| constexpr std::array<std::string_view, 4> supportedSensorTypes = { |
| "fan_tach", "fan_pwm", "secondary_fan_tach", "secondary_fan_pwm"}; |
| if (std::find(supportedSensorTypes.begin(), supportedSensorTypes.end(), |
| sensorType) == supportedSensorTypes.end()) |
| { |
| 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 (serviceNames.size() > 1) |
| { |
| BMCWEB_LOG_DEBUG |
| << "Skipping over ambiguous sensor served by multiple services: " |
| << objectPath; |
| continue; |
| } |
| 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; |
| } |
| |
| // There is no reason to populate PartLocationContext only for |
| // fan_pwm but it happens so that both fan_pwm and fan_tach |
| // sensors are associated with the same chassis through entity |
| // manager config today so we arbitraty picked fan_pwm to find |
| // the associated chassis. |
| if (sensorType == "fan_pwm") |
| { |
| // Get PartLocationContext |
| location_util::getPartLocationContext( |
| asyncResp, "/Location"_json_pointer, path.str + "/chassis"); |
| } |
| |
| 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" || |
| sensorType == "secondary_fan_pwm") |
| { |
| const std::string jsonKey = |
| (sensorType == "fan_pwm") ? "SpeedPercent" |
| : "SecondarySpeedPercent"; |
| asyncResp->res.jsonValue[jsonKey]["DataSourceUri"] = |
| std::string("/redfish/v1/Chassis/") |
| .append(chassisId) |
| .append("/Sensors/") |
| .append("fanpwm_" + sensorName) |
| .append("/"); |
| asyncResp->res.jsonValue[jsonKey]["Reading"] = resp2; |
| } |
| else if (sensorType == "fan_tach") |
| { |
| asyncResp->res.jsonValue["SpeedPercent"]["SpeedRPM"] = |
| resp2; |
| } |
| else if (sensorType == "secondary_fan_tach") |
| { |
| asyncResp->res |
| .jsonValue["SecondarySpeedPercent"]["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::GetManagedObjectStore()->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"); |
| } |
| else if (propKey == "SecondaryTachSensor") |
| { |
| const std::string* tachSensor = |
| std::get_if<std::string>(&propVariant); |
| if (tachSensor == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| getFanSensorValue(asyncResp, chassisId, *tachSensor, |
| "secondary_fan_tach"); |
| } |
| else if (propKey == "SecondaryPWMSensor") |
| { |
| const std::string* pwmSensor = |
| std::get_if<std::string>(&propVariant); |
| if (pwmSensor == nullptr) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| getFanSensorValue(asyncResp, chassisId, *pwmSensor, |
| "secondary_fan_pwm"); |
| } |
| } |
| } |
| |
| inline void |
| addFanProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& fanObjectPath, |
| const std::string& service, const std::string& chassisId) |
| { |
| BMCWEB_LOG_DEBUG << "Get Properties for Fan " << fanObjectPath; |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| managedStore::GetManagedObjectStore()->getAllProperties( |
| service, fanObjectPath, "xyz.openbmc_project.Configuration.Fan", |
| context, |
| [asyncResp, |
| chassisId](const boost::system::error_code ec, |
| const dbus::utility::DBusPropertiesMap& properties) { |
| fillFanProperties(asyncResp, ec, properties, chassisId); |
| }); |
| } |
| |
| inline void |
| handleFanCollectionHead(App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| redfish::chassis_utils::getValidChassisPath( |
| asyncResp, chassisId, |
| [asyncResp, |
| chassisId](const std::optional<std::string>& validChassisPath) { |
| if (!validChassisPath) |
| { |
| messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); |
| return; |
| } |
| asyncResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); |
| }); |
| } |
| |
| inline void |
| handleFanCollectionGet(App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| redfish::chassis_utils::getValidChassisPath( |
| asyncResp, chassisId, |
| std::bind_front(doFanCollection, asyncResp, chassisId)); |
| } |
| |
| inline void requestRoutesFanCollection(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") |
| .privileges(redfish::privileges::headFanCollection) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleFanCollectionHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/") |
| .privileges(redfish::privileges::getFanCollection) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleFanCollectionGet, std::ref(app))); |
| } |
| |
| inline bool checkChassisId(const std::string& fanPath, |
| const std::string& chassisId) |
| { |
| return fanPath.find("/" + chassisId + "/") != std::string::npos; |
| } |
| |
| inline bool checkFanId(const std::string& fanPath, const std::string& fanId) |
| { |
| std::string fanName = sdbusplus::message::object_path(fanPath).filename(); |
| |
| return !(fanName.empty() || fanName != fanId); |
| } |
| |
| static inline void handleFanPath( |
| const std::string& chassisId, const std::string& fanId, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreePathsResponse& fanPaths, |
| const std::function<void(const std::string& fanPath, |
| const std::string& service)>& callback) |
| { |
| for (const auto& fanPath : fanPaths) |
| { |
| if (!checkChassisId(fanPath, chassisId) || !checkFanId(fanPath, fanId)) |
| { |
| continue; |
| } |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getDbusObject( |
| fanPath, fanInterfaces, context, |
| [fanPath, asyncResp, |
| callback](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetObject& object) { |
| if (ec || object.empty()) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error on getDbusObject " |
| << ec.value(); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| callback(fanPath, object.begin()->first); |
| }); |
| |
| return; |
| } |
| BMCWEB_LOG_WARNING << "Fan not found " << fanId; |
| messages::resourceNotFound(asyncResp->res, "Fan", fanId); |
| } |
| |
| inline void getValidFanPath( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& validChassisPath, const std::string& chassisId, |
| const std::string& fanId, |
| const std::function<void(const std::string& fanPath, |
| const std::string& service)>& callback) |
| { |
| getFanPaths( |
| asyncResp, validChassisPath, |
| [chassisId, fanId, asyncResp, callback]( |
| const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) { |
| handleFanPath(chassisId, fanId, asyncResp, fanPaths, callback); |
| }); |
| } |
| |
| inline void addFanCommonProperties(crow::Response& resp, |
| const std::string& chassisId, |
| const std::string& fanId) |
| { |
| resp.addHeader(boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby"); |
| resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan"; |
| resp.jsonValue["Name"] = fanId; |
| resp.jsonValue["Id"] = fanId; |
| resp.jsonValue["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId); |
| resp.jsonValue["Status"]["State"] = "Enabled"; |
| resp.jsonValue["Status"]["Health"] = "OK"; |
| } |
| |
| inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& fanPath, const std::string& service) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<bool>( |
| service, fanPath, |
| "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", |
| context, |
| [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"] = "Critical"; |
| } |
| }); |
| } |
| |
| inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& fanPath, const std::string& service) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| dbus_utils::getProperty<bool>( |
| service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present", |
| context, |
| [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"] = "Absent"; |
| } |
| }); |
| } |
| |
| inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& fanPath, const std::string& service) |
| { |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| managedStore::GetManagedObjectStore()->getAllProperties( |
| service, fanPath, "xyz.openbmc_project.Inventory.Decorator.Asset", |
| context, |
| [fanPath, asyncResp{asyncResp}]( |
| const boost::system::error_code& ec, |
| const dbus::utility::DBusPropertiesMap& assetList) { |
| if (ec) |
| { |
| if (ec.value() != EBADR) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error for Properties" |
| << ec.value(); |
| messages::internalError(asyncResp->res); |
| } |
| return; |
| } |
| const std::string* manufacturer = nullptr; |
| const std::string* model = nullptr; |
| const std::string* partNumber = nullptr; |
| const std::string* serialNumber = nullptr; |
| const std::string* sparePartNumber = nullptr; |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", |
| manufacturer, "Model", model, "PartNumber", partNumber, |
| "SerialNumber", serialNumber, "SparePartNumber", sparePartNumber); |
| if (!success) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| if (manufacturer != nullptr) |
| { |
| asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; |
| } |
| if (model != nullptr) |
| { |
| asyncResp->res.jsonValue["Model"] = *model; |
| } |
| if (partNumber != nullptr) |
| { |
| asyncResp->res.jsonValue["PartNumber"] = *partNumber; |
| } |
| if (serialNumber != nullptr) |
| { |
| asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; |
| } |
| if (sparePartNumber != nullptr && !sparePartNumber->empty()) |
| { |
| asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; |
| } |
| }); |
| } |
| |
| inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& fanPath, |
| const std::string& service) |
| { |
| managedStore::ManagedObjectStoreContext context(aResp); |
| dbus_utils::getProperty<std::string>( |
| service, fanPath, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", |
| context, |
| [aResp](const boost::system::error_code& ec, |
| const std::string& property) { |
| if (ec) |
| { |
| if (ec.value() != EBADR) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error for Location" |
| << ec.value(); |
| messages::internalError(aResp->res); |
| } |
| return; |
| } |
| aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = |
| property; |
| }); |
| } |
| |
| inline void |
| afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, const std::string& fanId, |
| const std::string& fanPath, const std::string& service) |
| { |
| addFanCommonProperties(asyncResp->res, chassisId, fanId); |
| getFanState(asyncResp, fanPath, service); |
| getFanHealth(asyncResp, fanPath, service); |
| getFanAsset(asyncResp, fanPath, service); |
| getFanLocation(asyncResp, fanPath, service); |
| addFanProperties(asyncResp, fanPath, service, chassisId); |
| } |
| |
| inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, const std::string& fanId, |
| const std::optional<std::string>& validChassisPath) |
| { |
| if (!validChassisPath) |
| { |
| BMCWEB_LOG_ERROR << "Not a valid chassis ID" << chassisId; |
| messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); |
| return; |
| } |
| |
| getValidFanPath( |
| asyncResp, *validChassisPath, chassisId, fanId, |
| std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId)); |
| } |
| |
| inline void handleFanHead(App& 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; |
| } |
| |
| redfish::chassis_utils::getValidChassisPath( |
| asyncResp, chassisId, |
| [asyncResp, chassisId, |
| fanId](const std::optional<std::string>& validChassisPath) { |
| if (!validChassisPath) |
| { |
| messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); |
| return; |
| } |
| getValidFanPath(asyncResp, *validChassisPath, chassisId, fanId, |
| [asyncResp](const std::string&, const std::string&) { |
| asyncResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby"); |
| }); |
| }); |
| } |
| |
| inline void handleFanGet(App& 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; |
| } |
| |
| redfish::chassis_utils::getValidChassisPath( |
| asyncResp, chassisId, |
| std::bind_front(doFanGet, asyncResp, chassisId, fanId)); |
| } |
| |
| inline void requestRoutesFan(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/") |
| .privileges(redfish::privileges::headFan) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleFanHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/") |
| .privileges(redfish::privileges::getFan) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleFanGet, std::ref(app))); |
| } |
| |
| } // namespace redfish |