blob: ec5a3efa2bc68c94ad25f47fe05bc7dc9f7b6199 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Albert Zhang4ca3ec32021-06-13 14:39:38 +08003#pragma once
4
5#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08006#include "async_resp.hpp"
George Liu378f1d62022-10-06 16:23:37 +08007#include "dbus_singleton.hpp"
8#include "dbus_utility.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009#include "error_messages.hpp"
10#include "http_request.hpp"
George Liu378f1d62022-10-06 16:23:37 +080011#include "logging.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080012#include "query.hpp"
13#include "registries/privilege_registry.hpp"
Albert Zhang4ca3ec32021-06-13 14:39:38 +080014#include "utils/chassis_utils.hpp"
George Liu378f1d62022-10-06 16:23:37 +080015#include "utils/sensor_utils.hpp"
Albert Zhang4ca3ec32021-06-13 14:39:38 +080016
Ed Tanousd7857202025-01-28 15:32:26 -080017#include <boost/beast/http/field.hpp>
18#include <boost/beast/http/verb.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070019#include <boost/url/format.hpp>
George Liu378f1d62022-10-06 16:23:37 +080020#include <nlohmann/json.hpp>
21#include <sdbusplus/asio/property.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070022
George Liu378f1d62022-10-06 16:23:37 +080023#include <array>
Ed Tanousd7857202025-01-28 15:32:26 -080024#include <functional>
Albert Zhang4ca3ec32021-06-13 14:39:38 +080025#include <memory>
26#include <optional>
27#include <string>
George Liu378f1d62022-10-06 16:23:37 +080028#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080029#include <utility>
Albert Zhang4ca3ec32021-06-13 14:39:38 +080030
31namespace redfish
32{
33
George Liu378f1d62022-10-06 16:23:37 +080034inline 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
64inline 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
108inline 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 */
162inline 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
Albert Zhang4ca3ec32021-06-13 14:39:38 +0800175inline 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
George Liu378f1d62022-10-06 16:23:37 +0800202inline 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
Albert Zhang4ca3ec32021-06-13 14:39:38 +0800226inline 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
George Liu378f1d62022-10-06 16:23:37 +0800236 redfish::chassis_utils::getValidChassisPath(
237 asyncResp, chassisId,
238 std::bind_front(doEnvironmentMetricsGet, asyncResp, chassisId));
Albert Zhang4ca3ec32021-06-13 14:39:38 +0800239}
240
241inline 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