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/sensor_utils.hpp"
16
17 #include <boost/beast/http/field.hpp>
18 #include <boost/beast/http/verb.hpp>
19 #include <boost/url/format.hpp>
20 #include <nlohmann/json.hpp>
21 #include <sdbusplus/asio/property.hpp>
22
23 #include <array>
24 #include <functional>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <string_view>
29 #include <utility>
30
31 namespace redfish
32 {
33
afterGetPowerWatts(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)34 inline void afterGetPowerWatts(
35 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
36 const std::string& chassisId, const std::string& path,
37 const boost::system::error_code& ec,
38 const dbus::utility::DBusPropertiesMap& valuesDict)
39 {
40 if (ec)
41 {
42 if (ec != boost::system::errc::io_error)
43 {
44 BMCWEB_LOG_ERROR("DBUS response error for PowerWatts {}", ec);
45 messages::internalError(asyncResp->res);
46 }
47 return;
48 }
49
50 nlohmann::json item = nlohmann::json::object();
51
52 /* Don't return an error for a failure to fill in properties from the
53 * single sensor. Just skip adding it.
54 */
55 if (sensor_utils::objectExcerptToJson(
56 path, chassisId,
57 sensor_utils::ChassisSubNode::environmentMetricsNode, "power",
58 valuesDict, item))
59 {
60 asyncResp->res.jsonValue["PowerWatts"] = std::move(item);
61 }
62 }
63
handleTotalPowerList(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const std::shared_ptr<sensor_utils::SensorServicePathList> & sensorList)64 inline void handleTotalPowerList(
65 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
66 const std::string& chassisId, const boost::system::error_code& ec,
67 const std::shared_ptr<sensor_utils::SensorServicePathList>& sensorList)
68 {
69 BMCWEB_LOG_DEBUG("handleTotalPowerList: {}", sensorList->size());
70
71 if (ec)
72 {
73 if (ec != boost::system::errc::io_error)
74 {
75 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
76 messages::internalError(asyncResp->res);
77 }
78 return;
79 }
80
81 // TotalPower cannot be supplied by multiple sensors
82 if (sensorList->size() != 1)
83 {
84 if (sensorList->empty())
85 {
86 // None found, not an error
87 return;
88 }
89 BMCWEB_LOG_ERROR("Too many total power sensors found {}. Expected 1.",
90 sensorList->size());
91 messages::internalError(asyncResp->res);
92 return;
93 }
94
95 const std::string& serviceName = (*sensorList)[0].first;
96 const std::string& sensorPath = (*sensorList)[0].second;
97 sdbusplus::asio::getAllProperties(
98 *crow::connections::systemBus, serviceName, sensorPath,
99 "xyz.openbmc_project.Sensor.Value",
100 [asyncResp, chassisId,
101 sensorPath](const boost::system::error_code& ec1,
102 const dbus::utility::DBusPropertiesMap& propertiesList) {
103 afterGetPowerWatts(asyncResp, chassisId, sensorPath, ec1,
104 propertiesList);
105 });
106 }
107
getTotalPowerSensor(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const sensor_utils::SensorServicePathList & sensorsServiceAndPath)108 inline void getTotalPowerSensor(
109 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
110 const std::string& chassisId, const boost::system::error_code& ec,
111 const sensor_utils::SensorServicePathList& sensorsServiceAndPath)
112 {
113 BMCWEB_LOG_DEBUG("getTotalPowerSensor {}", sensorsServiceAndPath.size());
114
115 if (ec)
116 {
117 if (ec != boost::system::errc::io_error)
118 {
119 BMCWEB_LOG_ERROR("DBUS response error {}", ec);
120 messages::internalError(asyncResp->res);
121 }
122 // None found, not an error
123 return;
124 }
125
126 if (sensorsServiceAndPath.empty())
127 {
128 // No power sensors implement Sensor.Purpose, not an error
129 return;
130 }
131
132 // Create vector to hold list of sensors with totalPower purpose
133 std::shared_ptr<sensor_utils::SensorServicePathList> sensorList =
134 std::make_shared<sensor_utils::SensorServicePathList>();
135
136 sensor_utils::getSensorsByPurpose(
137 asyncResp, sensorsServiceAndPath,
138 sensor_utils::SensorPurpose::totalPower, sensorList,
139 std::bind_front(handleTotalPowerList, asyncResp, chassisId));
140 }
141
142 /**
143 * @brief Find sensor providing totalPower and fill in response
144 *
145 * Multiple D-Bus calls are needed to find the sensor providing the totalPower
146 * details:
147 *
148 * 1. Retrieve list of power sensors associated with specified chassis which
149 * implement the Sensor.Purpose interface.
150 *
151 * 2. For each of those power sensors retrieve the actual purpose of the sensor
152 * to find the sensor implementing totalPower purpose. Expect no more than
153 * one sensor to implement this purpose.
154 *
155 * 3. If a totalPower sensor is found then retrieve its properties to fill in
156 * PowerWatts in the response.
157 *
158 * @param asyncResp Response data
159 * @param validChassisPath Path to chassis, caller confirms path is valid
160 * @param chassisId Chassis id matching <validChassisPath>
161 */
getPowerWatts(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & validChassisPath,const std::string & chassisId)162 inline void getPowerWatts(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
163 const std::string& validChassisPath,
164 const std::string& chassisId)
165 {
166 BMCWEB_LOG_DEBUG("getPowerWatts: {}", validChassisPath);
167
168 constexpr std::array<std::string_view, 1> interfaces = {
169 "xyz.openbmc_project.Sensor.Purpose"};
170 sensor_utils::getAllSensorObjects(
171 validChassisPath, "/xyz/openbmc_project/sensors/power", interfaces, 1,
172 std::bind_front(getTotalPowerSensor, asyncResp, chassisId));
173 }
174
handleEnvironmentMetricsHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)175 inline void handleEnvironmentMetricsHead(
176 App& app, const crow::Request& req,
177 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
178 const std::string& chassisId)
179 {
180 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
181 {
182 return;
183 }
184
185 auto respHandler = [asyncResp, chassisId](
186 const std::optional<std::string>& validChassisPath) {
187 if (!validChassisPath)
188 {
189 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
190 return;
191 }
192
193 asyncResp->res.addHeader(
194 boost::beast::http::field::link,
195 "</redfish/v1/JsonSchemas/EnvironmentMetrics/EnvironmentMetrics.json>; rel=describedby");
196 };
197
198 redfish::chassis_utils::getValidChassisPath(asyncResp, chassisId,
199 std::move(respHandler));
200 }
201
doEnvironmentMetricsGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::optional<std::string> & validChassisPath)202 inline void doEnvironmentMetricsGet(
203 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
204 const std::string& chassisId,
205 const std::optional<std::string>& validChassisPath)
206 {
207 if (!validChassisPath)
208 {
209 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
210 return;
211 }
212
213 asyncResp->res.addHeader(
214 boost::beast::http::field::link,
215 "</redfish/v1/JsonSchemas/EnvironmentMetrics/EnvironmentMetrics.json>; rel=describedby");
216 asyncResp->res.jsonValue["@odata.type"] =
217 "#EnvironmentMetrics.v1_3_0.EnvironmentMetrics";
218 asyncResp->res.jsonValue["Name"] = "Chassis Environment Metrics";
219 asyncResp->res.jsonValue["Id"] = "EnvironmentMetrics";
220 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
221 "/redfish/v1/Chassis/{}/EnvironmentMetrics", chassisId);
222
223 getPowerWatts(asyncResp, *validChassisPath, chassisId);
224 }
225
handleEnvironmentMetricsGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)226 inline void handleEnvironmentMetricsGet(
227 App& app, const crow::Request& req,
228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
229 const std::string& chassisId)
230 {
231 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
232 {
233 return;
234 }
235
236 redfish::chassis_utils::getValidChassisPath(
237 asyncResp, chassisId,
238 std::bind_front(doEnvironmentMetricsGet, asyncResp, chassisId));
239 }
240
requestRoutesEnvironmentMetrics(App & app)241 inline void requestRoutesEnvironmentMetrics(App& app)
242 {
243 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/EnvironmentMetrics/")
244 .privileges(redfish::privileges::headEnvironmentMetrics)
245 .methods(boost::beast::http::verb::head)(
246 std::bind_front(handleEnvironmentMetricsHead, std::ref(app)));
247
248 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/EnvironmentMetrics/")
249 .privileges(redfish::privileges::getEnvironmentMetrics)
250 .methods(boost::beast::http::verb::get)(
251 std::bind_front(handleEnvironmentMetricsGet, std::ref(app)));
252 }
253
254 } // namespace redfish
255