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