| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-FileCopyrightText: Copyright OpenBMC Authors |
| #pragma once |
| |
| #include "app.hpp" |
| #include "async_resp.hpp" |
| #include "dbus_singleton.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "http_request.hpp" |
| #include "logging.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utils/chassis_utils.hpp" |
| #include "utils/sensor_utils.hpp" |
| |
| #include <boost/beast/http/field.hpp> |
| #include <boost/beast/http/verb.hpp> |
| #include <boost/url/format.hpp> |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| |
| #include <array> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| |
| namespace redfish |
| { |
| |
| inline void afterGetPowerWatts( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, const std::string& path, |
| const boost::system::error_code& ec, |
| const dbus::utility::DBusPropertiesMap& valuesDict) |
| { |
| if (ec) |
| { |
| if (ec != boost::system::errc::io_error) |
| { |
| BMCWEB_LOG_ERROR("DBUS response error for PowerWatts {}", ec); |
| messages::internalError(asyncResp->res); |
| } |
| return; |
| } |
| |
| nlohmann::json item = nlohmann::json::object(); |
| |
| /* Don't return an error for a failure to fill in properties from the |
| * single sensor. Just skip adding it. |
| */ |
| if (sensor_utils::objectExcerptToJson( |
| path, chassisId, |
| sensor_utils::ChassisSubNode::environmentMetricsNode, "power", |
| valuesDict, item)) |
| { |
| asyncResp->res.jsonValue["PowerWatts"] = std::move(item); |
| } |
| } |
| |
| inline void handleTotalPowerList( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, const boost::system::error_code& ec, |
| const std::shared_ptr<sensor_utils::SensorServicePathList>& sensorList) |
| { |
| BMCWEB_LOG_DEBUG("handleTotalPowerList: {}", sensorList->size()); |
| |
| if (ec) |
| { |
| if (ec != boost::system::errc::io_error) |
| { |
| BMCWEB_LOG_ERROR("D-Bus response error {}", ec); |
| messages::internalError(asyncResp->res); |
| } |
| return; |
| } |
| |
| // TotalPower cannot be supplied by multiple sensors |
| if (sensorList->size() != 1) |
| { |
| if (sensorList->empty()) |
| { |
| // None found, not an error |
| return; |
| } |
| BMCWEB_LOG_ERROR("Too many total power sensors found {}. Expected 1.", |
| sensorList->size()); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| const std::string& serviceName = (*sensorList)[0].first; |
| const std::string& sensorPath = (*sensorList)[0].second; |
| sdbusplus::asio::getAllProperties( |
| *crow::connections::systemBus, serviceName, sensorPath, |
| "xyz.openbmc_project.Sensor.Value", |
| [asyncResp, chassisId, |
| sensorPath](const boost::system::error_code& ec1, |
| const dbus::utility::DBusPropertiesMap& propertiesList) { |
| afterGetPowerWatts(asyncResp, chassisId, sensorPath, ec1, |
| propertiesList); |
| }); |
| } |
| |
| inline void getTotalPowerSensor( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, const boost::system::error_code& ec, |
| const sensor_utils::SensorServicePathList& sensorsServiceAndPath) |
| { |
| BMCWEB_LOG_DEBUG("getTotalPowerSensor {}", sensorsServiceAndPath.size()); |
| |
| if (ec) |
| { |
| if (ec != boost::system::errc::io_error) |
| { |
| BMCWEB_LOG_ERROR("DBUS response error {}", ec); |
| messages::internalError(asyncResp->res); |
| } |
| // None found, not an error |
| return; |
| } |
| |
| if (sensorsServiceAndPath.empty()) |
| { |
| // No power sensors implement Sensor.Purpose, not an error |
| return; |
| } |
| |
| // Create vector to hold list of sensors with totalPower purpose |
| std::shared_ptr<sensor_utils::SensorServicePathList> sensorList = |
| std::make_shared<sensor_utils::SensorServicePathList>(); |
| |
| sensor_utils::getSensorsByPurpose( |
| asyncResp, sensorsServiceAndPath, |
| sensor_utils::SensorPurpose::totalPower, sensorList, |
| std::bind_front(handleTotalPowerList, asyncResp, chassisId)); |
| } |
| |
| /** |
| * @brief Find sensor providing totalPower and fill in response |
| * |
| * Multiple D-Bus calls are needed to find the sensor providing the totalPower |
| * details: |
| * |
| * 1. Retrieve list of power sensors associated with specified chassis which |
| * implement the Sensor.Purpose interface. |
| * |
| * 2. For each of those power sensors retrieve the actual purpose of the sensor |
| * to find the sensor implementing totalPower purpose. Expect no more than |
| * one sensor to implement this purpose. |
| * |
| * 3. If a totalPower sensor is found then retrieve its properties to fill in |
| * PowerWatts in the response. |
| * |
| * @param asyncResp Response data |
| * @param validChassisPath Path to chassis, caller confirms path is valid |
| * @param chassisId Chassis id matching <validChassisPath> |
| */ |
| inline void getPowerWatts(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& validChassisPath, |
| const std::string& chassisId) |
| { |
| BMCWEB_LOG_DEBUG("getPowerWatts: {}", validChassisPath); |
| |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Sensor.Purpose"}; |
| sensor_utils::getAllSensorObjects( |
| validChassisPath, "/xyz/openbmc_project/sensors/power", interfaces, 1, |
| std::bind_front(getTotalPowerSensor, asyncResp, chassisId)); |
| } |
| |
| inline void handleEnvironmentMetricsHead( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| auto respHandler = [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/EnvironmentMetrics/EnvironmentMetrics.json>; rel=describedby"); |
| }; |
| |
| redfish::chassis_utils::getValidChassisPath(asyncResp, chassisId, |
| std::move(respHandler)); |
| } |
| |
| inline void doEnvironmentMetricsGet( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& 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/EnvironmentMetrics/EnvironmentMetrics.json>; rel=describedby"); |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#EnvironmentMetrics.v1_3_0.EnvironmentMetrics"; |
| asyncResp->res.jsonValue["Name"] = "Chassis Environment Metrics"; |
| asyncResp->res.jsonValue["Id"] = "EnvironmentMetrics"; |
| asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Chassis/{}/EnvironmentMetrics", chassisId); |
| |
| getPowerWatts(asyncResp, *validChassisPath, chassisId); |
| } |
| |
| inline void handleEnvironmentMetricsGet( |
| 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(doEnvironmentMetricsGet, asyncResp, chassisId)); |
| } |
| |
| inline void requestRoutesEnvironmentMetrics(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/EnvironmentMetrics/") |
| .privileges(redfish::privileges::headEnvironmentMetrics) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleEnvironmentMetricsHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/EnvironmentMetrics/") |
| .privileges(redfish::privileges::getEnvironmentMetrics) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleEnvironmentMetricsGet, std::ref(app))); |
| } |
| |
| } // namespace redfish |