blob: 1d7a59f61d19d256caffdadd3d38f588840534d3 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4// SPDX-FileCopyrightText: Copyright 2018 Ampere Computing LLC
Ed Tanous6be832e2024-09-10 11:44:48 -07005
Ed Tanous2474adf2018-09-05 16:31:16 -07006#pragma once
7
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08008#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009#include "async_resp.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080010#include "dbus_utility.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080011#include "error_messages.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -070012#include "generated/enums/power.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080013#include "http_request.hpp"
14#include "logging.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080015#include "query.hpp"
16#include "registries/privilege_registry.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070017#include "sensors.hpp"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000018#include "utils/chassis_utils.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080019#include "utils/dbus_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070020#include "utils/json_utils.hpp"
Janet Adkinsc9563602024-08-28 11:37:46 -050021#include "utils/sensor_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070022
Ed Tanousd7857202025-01-28 15:32:26 -080023#include <boost/beast/http/verb.hpp>
24#include <nlohmann/json.hpp>
25#include <sdbusplus/message/native_types.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070026
Ed Tanousd7857202025-01-28 15:32:26 -080027#include <cmath>
28#include <cstddef>
29#include <cstdint>
30#include <functional>
31#include <memory>
32#include <optional>
Ed Tanous08850572024-03-06 15:09:17 -080033#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080034#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080035#include <unordered_map>
36#include <utility>
37#include <variant>
Ed Tanous08850572024-03-06 15:09:17 -080038#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080039
Ed Tanous2474adf2018-09-05 16:31:16 -070040namespace redfish
41{
Ed Tanous53b00f52024-03-08 09:05:10 -080042
43inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070044 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080045 uint32_t valueToSet, const boost::system::error_code& ec,
46 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070047{
Ed Tanous53b00f52024-03-08 09:05:10 -080048 if (ec)
49 {
50 messages::internalError(sensorsAsyncResp->asyncResp->res);
51 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
52 return;
53 }
54 if (!powerCapEnable)
55 {
56 messages::actionNotSupported(
57 sensorsAsyncResp->asyncResp->res,
58 "Setting LimitInWatts when PowerLimit feature is disabled");
59 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
60 return;
61 }
George Liu0fda0f12021-11-16 10:06:17 +080062
Ginu Georgee93abac2024-06-14 17:35:27 +053063 setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
64 "xyz.openbmc_project.Settings",
Asmitha Karunanithi87c44962024-04-04 18:28:33 +000065 sdbusplus::message::object_path(
66 "/xyz/openbmc_project/control/host0/power_cap"),
67 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ginu Georgee93abac2024-06-14 17:35:27 +053068 valueToSet);
John Edward Broadbent7e860f12021-04-08 15:57:16 -070069}
Ed Tanous53b00f52024-03-08 09:05:10 -080070
71inline void afterGetChassisPath(
72 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous08850572024-03-06 15:09:17 -080073 std::vector<nlohmann::json::object_t>& powerControlCollections,
Ed Tanous53b00f52024-03-08 09:05:10 -080074 const std::optional<std::string>& chassisPath)
75{
76 if (!chassisPath)
77 {
78 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
79 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
80 sensorsAsyncResp->chassisId);
81 return;
82 }
83
84 if (powerControlCollections.size() != 1)
85 {
86 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
87 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
88 "PowerControl");
89 return;
90 }
91
92 auto& item = powerControlCollections[0];
93
Ed Tanous53b00f52024-03-08 09:05:10 -080094 std::optional<uint32_t> value;
Patrick Williams504af5a2025-02-03 14:29:03 -050095 if (!json_util::readJsonObject( //
Myung Baeafc474a2024-10-09 00:53:29 -070096 item, sensorsAsyncResp->asyncResp->res, //
Patrick Williams504af5a2025-02-03 14:29:03 -050097 "PowerLimit/LimitInWatts", value //
Myung Baeafc474a2024-10-09 00:53:29 -070098 ))
Ed Tanous53b00f52024-03-08 09:05:10 -080099 {
100 return;
101 }
102 if (!value)
103 {
104 return;
105 }
Ed Tanousdeae6a72024-11-11 21:58:57 -0800106 dbus::utility::getProperty<bool>(
107 "xyz.openbmc_project.Settings",
Ed Tanous53b00f52024-03-08 09:05:10 -0800108 "/xyz/openbmc_project/control/host0/power_cap",
109 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
110 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
111}
112
113inline void afterPowerCapSettingGet(
114 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
115 const boost::system::error_code& ec,
116 const dbus::utility::DBusPropertiesMap& properties)
117{
118 if (ec)
119 {
120 messages::internalError(sensorAsyncResp->asyncResp->res);
121 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
122 return;
123 }
124
125 nlohmann::json& tempArray =
126 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
127
128 // Put multiple "sensors" into a single PowerControl, 0,
129 // so only create the first one
130 if (tempArray.empty())
131 {
132 // Mandatory properties odata.id and MemberId
133 // A warning without a odata.type
134 nlohmann::json::object_t powerControl;
135 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400136 powerControl["@odata.id"] =
137 "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
138 "/Power#/PowerControl/0";
Ed Tanous53b00f52024-03-08 09:05:10 -0800139 powerControl["Name"] = "Chassis Power Control";
140 powerControl["MemberId"] = "0";
141 tempArray.emplace_back(std::move(powerControl));
142 }
143
144 nlohmann::json& sensorJson = tempArray.back();
145 bool enabled = false;
146 double powerCap = 0.0;
147 int64_t scale = 0;
148
149 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
150 property : properties)
151 {
152 if (property.first == "Scale")
153 {
154 const int64_t* i = std::get_if<int64_t>(&property.second);
155
156 if (i != nullptr)
157 {
158 scale = *i;
159 }
160 }
161 else if (property.first == "PowerCap")
162 {
163 const double* d = std::get_if<double>(&property.second);
164 const int64_t* i = std::get_if<int64_t>(&property.second);
165 const uint32_t* u = std::get_if<uint32_t>(&property.second);
166
167 if (d != nullptr)
168 {
169 powerCap = *d;
170 }
171 else if (i != nullptr)
172 {
173 powerCap = static_cast<double>(*i);
174 }
175 else if (u != nullptr)
176 {
177 powerCap = *u;
178 }
179 }
180 else if (property.first == "PowerCapEnable")
181 {
182 const bool* b = std::get_if<bool>(&property.second);
183
184 if (b != nullptr)
185 {
186 enabled = *b;
187 }
188 }
189 }
190
191 // LimitException is Mandatory attribute as per OCP
Janet Adkinsc9563602024-08-28 11:37:46 -0500192 // Baseline Profile - v1.0.0, so currently making it
Ed Tanous53b00f52024-03-08 09:05:10 -0800193 // "NoAction" as default value to make it OCP Compliant.
Ed Tanous539d8c62024-06-19 14:38:27 -0700194 sensorJson["PowerLimit"]["LimitException"] =
195 power::PowerLimitException::NoAction;
Ed Tanous53b00f52024-03-08 09:05:10 -0800196
197 if (enabled)
198 {
199 // Redfish specification indicates PowerLimit should
200 // be null if the limit is not enabled.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400201 sensorJson["PowerLimit"]["LimitInWatts"] =
202 powerCap * std::pow(10, scale);
Ed Tanous53b00f52024-03-08 09:05:10 -0800203 }
204}
205
206using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400207inline void afterGetChassis(
208 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
209 const boost::system::error_code& ec2, const Mapper& chassisPaths)
Ed Tanous53b00f52024-03-08 09:05:10 -0800210{
211 if (ec2)
212 {
213 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
214 ec2);
215 return;
216 }
217
218 bool found = false;
219 for (const std::string& chassis : chassisPaths)
220 {
221 size_t len = std::string::npos;
222 size_t lastPos = chassis.rfind('/');
223 if (lastPos == std::string::npos)
224 {
225 continue;
226 }
227
228 if (lastPos == chassis.size() - 1)
229 {
230 size_t end = lastPos;
231 lastPos = chassis.rfind('/', lastPos - 1);
232 if (lastPos == std::string::npos)
233 {
234 continue;
235 }
236
237 len = end - (lastPos + 1);
238 }
239
240 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
241 if (interfaceChassisName == sensorAsyncResp->chassisId)
242 {
243 found = true;
244 break;
245 }
246 }
247
248 if (!found)
249 {
250 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
251 sensorAsyncResp->chassisId);
252 return;
253 }
254
Ed Tanousdeae6a72024-11-11 21:58:57 -0800255 dbus::utility::getAllProperties(
256 "xyz.openbmc_project.Settings",
Ed Tanous53b00f52024-03-08 09:05:10 -0800257 "/xyz/openbmc_project/control/host0/power_cap",
258 "xyz.openbmc_project.Control.Power.Cap",
259 [sensorAsyncResp](const boost::system::error_code& ec,
260 const dbus::utility::DBusPropertiesMap& properties
261
262 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
263}
264
Patrick Williams504af5a2025-02-03 14:29:03 -0500265inline void handleChassisPowerGet(
266 App& app, const crow::Request& req,
267 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
268 const std::string& chassisName)
Ed Tanous53b00f52024-03-08 09:05:10 -0800269{
270 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
271 {
272 return;
273 }
274 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
275
276 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
277 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500278 sensor_utils::chassisSubNodeToString(
279 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800280
281 getChassisData(sensorAsyncResp);
282
283 // This callback verifies that the power limit is only provided
284 // for the chassis that implements the Chassis inventory item.
285 // This prevents things like power supplies providing the
286 // chassis power limit
287
Ed Tanous53b00f52024-03-08 09:05:10 -0800288 dbus::utility::getSubTreePaths(
Myung Bae3f95a272024-03-13 07:32:02 -0700289 "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
Ed Tanous53b00f52024-03-08 09:05:10 -0800290 std::bind_front(afterGetChassis, sensorAsyncResp));
291}
292
Patrick Williams504af5a2025-02-03 14:29:03 -0500293inline void handleChassisPowerPatch(
294 App& app, const crow::Request& req,
295 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
296 const std::string& chassisName)
Ed Tanous53b00f52024-03-08 09:05:10 -0800297{
298 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
299 {
300 return;
301 }
302 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
303 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500304 sensor_utils::chassisSubNodeToString(
305 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800306
Ed Tanous08850572024-03-06 15:09:17 -0800307 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
308 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800309
Patrick Williams504af5a2025-02-03 14:29:03 -0500310 if (!json_util::readJsonPatch( //
Myung Baeafc474a2024-10-09 00:53:29 -0700311 req, sensorAsyncResp->asyncResp->res, //
Patrick Williams504af5a2025-02-03 14:29:03 -0500312 "PowerControl", powerCtlCollections, //
313 "Voltages", voltageCollections //
Myung Baeafc474a2024-10-09 00:53:29 -0700314 ))
Ed Tanous53b00f52024-03-08 09:05:10 -0800315 {
316 return;
317 }
318
319 if (powerCtlCollections)
320 {
321 redfish::chassis_utils::getValidChassisPath(
322 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
323 std::bind_front(afterGetChassisPath, sensorAsyncResp,
324 *powerCtlCollections));
325 }
326 if (voltageCollections)
327 {
Ed Tanous08850572024-03-06 15:09:17 -0800328 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800329 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800330 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800331 setSensorsOverride(sensorAsyncResp, allCollections);
332 }
333}
334
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700335inline void requestRoutesPower(App& app)
336{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700337 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700338 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700339 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800340 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000341
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700342 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700343 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700344 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800345 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700346}
Ed Tanous2474adf2018-09-05 16:31:16 -0700347
348} // namespace redfish