xref: /openbmc/bmcweb/features/redfish/lib/thermal_metrics.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
35ae1f7f3Szhanghch05 #pragma once
45ae1f7f3Szhanghch05 
55ae1f7f3Szhanghch05 #include "app.hpp"
6d7857201SEd Tanous #include "async_resp.hpp"
7d7857201SEd Tanous #include "dbus_singleton.hpp"
86fe8751cSGeorge Liu #include "dbus_utility.hpp"
9d7857201SEd Tanous #include "error_messages.hpp"
10d7857201SEd Tanous #include "http_request.hpp"
11d7857201SEd Tanous #include "logging.hpp"
125ae1f7f3Szhanghch05 #include "query.hpp"
135ae1f7f3Szhanghch05 #include "registries/privilege_registry.hpp"
145ae1f7f3Szhanghch05 #include "utils/chassis_utils.hpp"
156fe8751cSGeorge Liu #include "utils/json_utils.hpp"
166fe8751cSGeorge Liu #include "utils/sensor_utils.hpp"
175ae1f7f3Szhanghch05 
18d7857201SEd Tanous #include <asm-generic/errno.h>
19d7857201SEd Tanous 
20d7857201SEd Tanous #include <boost/beast/http/field.hpp>
21d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
226fe8751cSGeorge Liu #include <boost/system/error_code.hpp>
23d7857201SEd Tanous #include <boost/url/format.hpp>
24d7857201SEd Tanous #include <nlohmann/json.hpp>
256fe8751cSGeorge Liu 
266fe8751cSGeorge Liu #include <array>
275ae1f7f3Szhanghch05 #include <functional>
285ae1f7f3Szhanghch05 #include <memory>
295ae1f7f3Szhanghch05 #include <optional>
305ae1f7f3Szhanghch05 #include <string>
316fe8751cSGeorge Liu #include <string_view>
32d7857201SEd Tanous #include <utility>
335ae1f7f3Szhanghch05 
345ae1f7f3Szhanghch05 namespace redfish
355ae1f7f3Szhanghch05 {
366fe8751cSGeorge Liu inline void afterGetTemperatureValue(
376fe8751cSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
386fe8751cSGeorge Liu     const std::string& chassisId, const std::string& path,
396fe8751cSGeorge Liu     const boost::system::error_code& ec,
406fe8751cSGeorge Liu     const dbus::utility::DBusPropertiesMap& valuesDict)
416fe8751cSGeorge Liu {
426fe8751cSGeorge Liu     if (ec)
436fe8751cSGeorge Liu     {
446fe8751cSGeorge Liu         if (ec.value() != EBADR)
456fe8751cSGeorge Liu         {
466fe8751cSGeorge Liu             BMCWEB_LOG_ERROR("DBUS response error for getAllProperties {}",
476fe8751cSGeorge Liu                              ec.value());
486fe8751cSGeorge Liu             messages::internalError(asyncResp->res);
496fe8751cSGeorge Liu         }
506fe8751cSGeorge Liu         return;
516fe8751cSGeorge Liu     }
526fe8751cSGeorge Liu 
536fe8751cSGeorge Liu     nlohmann::json item = nlohmann::json::object();
546fe8751cSGeorge Liu 
556fe8751cSGeorge Liu     /* Don't return an error for a failure to fill in properties from any of
566fe8751cSGeorge Liu      * the sensors in the list. Just skip it.
576fe8751cSGeorge Liu      */
586fe8751cSGeorge Liu     if (sensor_utils::objectExcerptToJson(
596fe8751cSGeorge Liu             path, chassisId, sensor_utils::ChassisSubNode::thermalMetricsNode,
606fe8751cSGeorge Liu             "temperature", valuesDict, item))
616fe8751cSGeorge Liu     {
626fe8751cSGeorge Liu         nlohmann::json& temperatureReadings =
636fe8751cSGeorge Liu             asyncResp->res.jsonValue["TemperatureReadingsCelsius"];
646fe8751cSGeorge Liu         nlohmann::json::array_t* temperatureArray =
656fe8751cSGeorge Liu             temperatureReadings.get_ptr<nlohmann::json::array_t*>();
666fe8751cSGeorge Liu         if (temperatureArray == nullptr)
676fe8751cSGeorge Liu         {
686fe8751cSGeorge Liu             BMCWEB_LOG_ERROR("Missing TemperatureReadingsCelsius Json array");
696fe8751cSGeorge Liu             messages::internalError(asyncResp->res);
706fe8751cSGeorge Liu             return;
716fe8751cSGeorge Liu         }
726fe8751cSGeorge Liu 
736fe8751cSGeorge Liu         temperatureArray->emplace_back(std::move(item));
746fe8751cSGeorge Liu         asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] =
756fe8751cSGeorge Liu             temperatureArray->size();
766fe8751cSGeorge Liu 
776fe8751cSGeorge Liu         json_util::sortJsonArrayByKey(*temperatureArray, "DataSourceUri");
786fe8751cSGeorge Liu     }
796fe8751cSGeorge Liu }
806fe8751cSGeorge Liu 
816fe8751cSGeorge Liu inline void handleTemperatureReadingsCelsius(
826fe8751cSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
836fe8751cSGeorge Liu     const std::string& chassisId, const boost::system::error_code& ec,
846fe8751cSGeorge Liu     const sensor_utils::SensorServicePathList& sensorsServiceAndPath)
856fe8751cSGeorge Liu {
866fe8751cSGeorge Liu     if (ec)
876fe8751cSGeorge Liu     {
886fe8751cSGeorge Liu         if (ec.value() != EBADR)
896fe8751cSGeorge Liu         {
906fe8751cSGeorge Liu             BMCWEB_LOG_ERROR("DBUS response error for getAssociatedSubTree {}",
916fe8751cSGeorge Liu                              ec.value());
926fe8751cSGeorge Liu             messages::internalError(asyncResp->res);
936fe8751cSGeorge Liu         }
946fe8751cSGeorge Liu         return;
956fe8751cSGeorge Liu     }
966fe8751cSGeorge Liu 
976fe8751cSGeorge Liu     asyncResp->res.jsonValue["TemperatureReadingsCelsius"] =
986fe8751cSGeorge Liu         nlohmann::json::array_t();
996fe8751cSGeorge Liu     asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] = 0;
1006fe8751cSGeorge Liu 
1016fe8751cSGeorge Liu     for (const auto& [service, sensorPath] : sensorsServiceAndPath)
1026fe8751cSGeorge Liu     {
103deae6a78SEd Tanous         dbus::utility::getAllProperties(
1046fe8751cSGeorge Liu             *crow::connections::systemBus, service, sensorPath,
1056fe8751cSGeorge Liu             "xyz.openbmc_project.Sensor.Value",
1066fe8751cSGeorge Liu             [asyncResp, chassisId,
1076fe8751cSGeorge Liu              sensorPath](const boost::system::error_code& ec1,
1086fe8751cSGeorge Liu                          const dbus::utility::DBusPropertiesMap& properties) {
1096fe8751cSGeorge Liu                 afterGetTemperatureValue(asyncResp, chassisId, sensorPath, ec1,
1106fe8751cSGeorge Liu                                          properties);
1116fe8751cSGeorge Liu             });
1126fe8751cSGeorge Liu     }
1136fe8751cSGeorge Liu }
1146fe8751cSGeorge Liu 
1156fe8751cSGeorge Liu inline void getTemperatureReadingsCelsius(
1166fe8751cSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1176fe8751cSGeorge Liu     const std::string& validChassisPath, const std::string& chassisId)
1186fe8751cSGeorge Liu {
1196fe8751cSGeorge Liu     constexpr std::array<std::string_view, 1> interfaces = {
1206fe8751cSGeorge Liu         "xyz.openbmc_project.Sensor.Value"};
1216fe8751cSGeorge Liu 
1226fe8751cSGeorge Liu     sensor_utils::getAllSensorObjects(
1236fe8751cSGeorge Liu         validChassisPath, "/xyz/openbmc_project/sensors/temperature",
1246fe8751cSGeorge Liu         interfaces, 1,
1256fe8751cSGeorge Liu         std::bind_front(handleTemperatureReadingsCelsius, asyncResp,
1266fe8751cSGeorge Liu                         chassisId));
1276fe8751cSGeorge Liu }
1286fe8751cSGeorge Liu 
129*504af5a0SPatrick Williams inline void doThermalMetrics(
130*504af5a0SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1315ae1f7f3Szhanghch05     const std::string& chassisId,
1325ae1f7f3Szhanghch05     const std::optional<std::string>& validChassisPath)
1335ae1f7f3Szhanghch05 {
1345ae1f7f3Szhanghch05     if (!validChassisPath)
1355ae1f7f3Szhanghch05     {
1365ae1f7f3Szhanghch05         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
1375ae1f7f3Szhanghch05         return;
1385ae1f7f3Szhanghch05     }
1395ae1f7f3Szhanghch05 
1405ae1f7f3Szhanghch05     asyncResp->res.addHeader(
1415ae1f7f3Szhanghch05         boost::beast::http::field::link,
1425ae1f7f3Szhanghch05         "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby");
1435ae1f7f3Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
1445ae1f7f3Szhanghch05         "#ThermalMetrics.v1_0_1.ThermalMetrics";
1455ae1f7f3Szhanghch05     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1465ae1f7f3Szhanghch05         "/redfish/v1/Chassis/{}/ThermalSubsystem/ThermalMetrics", chassisId);
1475ae1f7f3Szhanghch05     asyncResp->res.jsonValue["Id"] = "ThermalMetrics";
1485ae1f7f3Szhanghch05     asyncResp->res.jsonValue["Name"] = "Thermal Metrics";
1496fe8751cSGeorge Liu 
1506fe8751cSGeorge Liu     getTemperatureReadingsCelsius(asyncResp, *validChassisPath, chassisId);
1515ae1f7f3Szhanghch05 }
1525ae1f7f3Szhanghch05 
1535ae1f7f3Szhanghch05 inline void handleThermalMetricsHead(
1545ae1f7f3Szhanghch05     App& app, const crow::Request& req,
1555ae1f7f3Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1565ae1f7f3Szhanghch05     const std::string& chassisId)
1575ae1f7f3Szhanghch05 {
1585ae1f7f3Szhanghch05     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1595ae1f7f3Szhanghch05     {
1605ae1f7f3Szhanghch05         return;
1615ae1f7f3Szhanghch05     }
1625ae1f7f3Szhanghch05 
1635ae1f7f3Szhanghch05     redfish::chassis_utils::getValidChassisPath(
1645ae1f7f3Szhanghch05         asyncResp, chassisId,
1655ae1f7f3Szhanghch05         [asyncResp,
1665ae1f7f3Szhanghch05          chassisId](const std::optional<std::string>& validChassisPath) {
1675ae1f7f3Szhanghch05             if (!validChassisPath)
1685ae1f7f3Szhanghch05             {
169bd79bce8SPatrick Williams                 messages::resourceNotFound(asyncResp->res, "Chassis",
170bd79bce8SPatrick Williams                                            chassisId);
1715ae1f7f3Szhanghch05                 return;
1725ae1f7f3Szhanghch05             }
1735ae1f7f3Szhanghch05             asyncResp->res.addHeader(
1745ae1f7f3Szhanghch05                 boost::beast::http::field::link,
1755ae1f7f3Szhanghch05                 "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby");
1765ae1f7f3Szhanghch05         });
1775ae1f7f3Szhanghch05 }
1785ae1f7f3Szhanghch05 
179*504af5a0SPatrick Williams inline void handleThermalMetricsGet(
180*504af5a0SPatrick Williams     App& app, const crow::Request& req,
1815ae1f7f3Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1825ae1f7f3Szhanghch05     const std::string& chassisId)
1835ae1f7f3Szhanghch05 {
1845ae1f7f3Szhanghch05     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1855ae1f7f3Szhanghch05     {
1865ae1f7f3Szhanghch05         return;
1875ae1f7f3Szhanghch05     }
1885ae1f7f3Szhanghch05 
1895ae1f7f3Szhanghch05     redfish::chassis_utils::getValidChassisPath(
1905ae1f7f3Szhanghch05         asyncResp, chassisId,
1915ae1f7f3Szhanghch05         std::bind_front(doThermalMetrics, asyncResp, chassisId));
1925ae1f7f3Szhanghch05 }
1935ae1f7f3Szhanghch05 
1945ae1f7f3Szhanghch05 inline void requestRoutesThermalMetrics(App& app)
1955ae1f7f3Szhanghch05 {
1965ae1f7f3Szhanghch05     BMCWEB_ROUTE(app,
1975ae1f7f3Szhanghch05                  "/redfish/v1/Chassis/<str>/ThermalSubsystem/ThermalMetrics/")
1985ae1f7f3Szhanghch05         .privileges(redfish::privileges::headThermalMetrics)
1995ae1f7f3Szhanghch05         .methods(boost::beast::http::verb::head)(
2005ae1f7f3Szhanghch05             std::bind_front(handleThermalMetricsHead, std::ref(app)));
2015ae1f7f3Szhanghch05 
2025ae1f7f3Szhanghch05     BMCWEB_ROUTE(app,
2035ae1f7f3Szhanghch05                  "/redfish/v1/Chassis/<str>/ThermalSubsystem/ThermalMetrics/")
2045ae1f7f3Szhanghch05         .privileges(redfish::privileges::getThermalMetrics)
2055ae1f7f3Szhanghch05         .methods(boost::beast::http::verb::get)(
2065ae1f7f3Szhanghch05             std::bind_front(handleThermalMetricsGet, std::ref(app)));
2075ae1f7f3Szhanghch05 }
2085ae1f7f3Szhanghch05 } // namespace redfish
209