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