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