xref: /openbmc/bmcweb/redfish-core/lib/thermal_metrics.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 
5 #include "app.hpp"
6 #include "async_resp.hpp"
7 #include "dbus_singleton.hpp"
8 #include "dbus_utility.hpp"
9 #include "error_messages.hpp"
10 #include "http_request.hpp"
11 #include "logging.hpp"
12 #include "query.hpp"
13 #include "registries/privilege_registry.hpp"
14 #include "utils/chassis_utils.hpp"
15 #include "utils/json_utils.hpp"
16 #include "utils/sensor_utils.hpp"
17 
18 #include <asm-generic/errno.h>
19 
20 #include <boost/beast/http/field.hpp>
21 #include <boost/beast/http/verb.hpp>
22 #include <boost/system/error_code.hpp>
23 #include <boost/url/format.hpp>
24 #include <nlohmann/json.hpp>
25 
26 #include <array>
27 #include <functional>
28 #include <memory>
29 #include <optional>
30 #include <string>
31 #include <string_view>
32 #include <utility>
33 
34 namespace redfish
35 {
afterGetTemperatureValue(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)36 inline void afterGetTemperatureValue(
37     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
38     const std::string& chassisId, const std::string& path,
39     const boost::system::error_code& ec,
40     const dbus::utility::DBusPropertiesMap& valuesDict)
41 {
42     if (ec)
43     {
44         if (ec.value() != EBADR)
45         {
46             BMCWEB_LOG_ERROR("DBUS response error for getAllProperties {}",
47                              ec.value());
48             messages::internalError(asyncResp->res);
49         }
50         return;
51     }
52 
53     nlohmann::json item = nlohmann::json::object();
54 
55     /* Don't return an error for a failure to fill in properties from any of
56      * the sensors in the list. Just skip it.
57      */
58     if (sensor_utils::objectExcerptToJson(
59             path, chassisId, sensor_utils::ChassisSubNode::thermalMetricsNode,
60             "temperature", valuesDict, item))
61     {
62         nlohmann::json& temperatureReadings =
63             asyncResp->res.jsonValue["TemperatureReadingsCelsius"];
64         nlohmann::json::array_t* temperatureArray =
65             temperatureReadings.get_ptr<nlohmann::json::array_t*>();
66         if (temperatureArray == nullptr)
67         {
68             BMCWEB_LOG_ERROR("Missing TemperatureReadingsCelsius Json array");
69             messages::internalError(asyncResp->res);
70             return;
71         }
72 
73         temperatureArray->emplace_back(std::move(item));
74         asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] =
75             temperatureArray->size();
76 
77         json_util::sortJsonArrayByKey(*temperatureArray, "DataSourceUri");
78     }
79 }
80 
handleTemperatureReadingsCelsius(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const sensor_utils::SensorServicePathList & sensorsServiceAndPath)81 inline void handleTemperatureReadingsCelsius(
82     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
83     const std::string& chassisId, const boost::system::error_code& ec,
84     const sensor_utils::SensorServicePathList& sensorsServiceAndPath)
85 {
86     if (ec)
87     {
88         if (ec.value() != EBADR)
89         {
90             BMCWEB_LOG_ERROR("DBUS response error for getAssociatedSubTree {}",
91                              ec.value());
92             messages::internalError(asyncResp->res);
93         }
94         return;
95     }
96 
97     asyncResp->res.jsonValue["TemperatureReadingsCelsius"] =
98         nlohmann::json::array_t();
99     asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] = 0;
100 
101     for (const auto& [service, sensorPath] : sensorsServiceAndPath)
102     {
103         dbus::utility::getAllProperties(
104             *crow::connections::systemBus, service, sensorPath,
105             "xyz.openbmc_project.Sensor.Value",
106             [asyncResp, chassisId,
107              sensorPath](const boost::system::error_code& ec1,
108                          const dbus::utility::DBusPropertiesMap& properties) {
109                 afterGetTemperatureValue(asyncResp, chassisId, sensorPath, ec1,
110                                          properties);
111             });
112     }
113 }
114 
getTemperatureReadingsCelsius(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & validChassisPath,const std::string & chassisId)115 inline void getTemperatureReadingsCelsius(
116     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
117     const std::string& validChassisPath, const std::string& chassisId)
118 {
119     constexpr std::array<std::string_view, 1> interfaces = {
120         "xyz.openbmc_project.Sensor.Value"};
121 
122     sensor_utils::getAllSensorObjects(
123         validChassisPath, "/xyz/openbmc_project/sensors/temperature",
124         interfaces, 1,
125         std::bind_front(handleTemperatureReadingsCelsius, asyncResp,
126                         chassisId));
127 }
128 
doThermalMetrics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::optional<std::string> & validChassisPath)129 inline void doThermalMetrics(
130     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
131     const std::string& chassisId,
132     const std::optional<std::string>& validChassisPath)
133 {
134     if (!validChassisPath)
135     {
136         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
137         return;
138     }
139 
140     asyncResp->res.addHeader(
141         boost::beast::http::field::link,
142         "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby");
143     asyncResp->res.jsonValue["@odata.type"] =
144         "#ThermalMetrics.v1_0_1.ThermalMetrics";
145     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
146         "/redfish/v1/Chassis/{}/ThermalSubsystem/ThermalMetrics", chassisId);
147     asyncResp->res.jsonValue["Id"] = "ThermalMetrics";
148     asyncResp->res.jsonValue["Name"] = "Thermal Metrics";
149 
150     getTemperatureReadingsCelsius(asyncResp, *validChassisPath, chassisId);
151 }
152 
handleThermalMetricsHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)153 inline void handleThermalMetricsHead(
154     App& app, const crow::Request& req,
155     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
156     const std::string& chassisId)
157 {
158     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
159     {
160         return;
161     }
162 
163     redfish::chassis_utils::getValidChassisPath(
164         asyncResp, chassisId,
165         [asyncResp,
166          chassisId](const std::optional<std::string>& validChassisPath) {
167             if (!validChassisPath)
168             {
169                 messages::resourceNotFound(asyncResp->res, "Chassis",
170                                            chassisId);
171                 return;
172             }
173             asyncResp->res.addHeader(
174                 boost::beast::http::field::link,
175                 "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby");
176         });
177 }
178 
handleThermalMetricsGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)179 inline void handleThermalMetricsGet(
180     App& app, const crow::Request& req,
181     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
182     const std::string& chassisId)
183 {
184     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
185     {
186         return;
187     }
188 
189     redfish::chassis_utils::getValidChassisPath(
190         asyncResp, chassisId,
191         std::bind_front(doThermalMetrics, asyncResp, chassisId));
192 }
193 
requestRoutesThermalMetrics(App & app)194 inline void requestRoutesThermalMetrics(App& app)
195 {
196     BMCWEB_ROUTE(app,
197                  "/redfish/v1/Chassis/<str>/ThermalSubsystem/ThermalMetrics/")
198         .privileges(redfish::privileges::headThermalMetrics)
199         .methods(boost::beast::http::verb::head)(
200             std::bind_front(handleThermalMetricsHead, std::ref(app)));
201 
202     BMCWEB_ROUTE(app,
203                  "/redfish/v1/Chassis/<str>/ThermalSubsystem/ThermalMetrics/")
204         .privileges(redfish::privileges::getThermalMetrics)
205         .methods(boost::beast::http::verb::get)(
206             std::bind_front(handleThermalMetricsGet, std::ref(app)));
207 }
208 } // namespace redfish
209