blob: 50e466c3f2acf41bca41805aa055727f8fda4337 [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
George Liu7a1dbc42022-12-07 16:03:22 +080027#include <array>
Ed Tanousd7857202025-01-28 15:32:26 -080028#include <cmath>
29#include <cstddef>
30#include <cstdint>
31#include <functional>
32#include <memory>
33#include <optional>
Ed Tanous08850572024-03-06 15:09:17 -080034#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080035#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080036#include <unordered_map>
37#include <utility>
38#include <variant>
Ed Tanous08850572024-03-06 15:09:17 -080039#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080040
Ed Tanous2474adf2018-09-05 16:31:16 -070041namespace redfish
42{
Ed Tanous53b00f52024-03-08 09:05:10 -080043
44inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070045 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080046 uint32_t valueToSet, const boost::system::error_code& ec,
47 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070048{
Ed Tanous53b00f52024-03-08 09:05:10 -080049 if (ec)
50 {
51 messages::internalError(sensorsAsyncResp->asyncResp->res);
52 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
53 return;
54 }
55 if (!powerCapEnable)
56 {
57 messages::actionNotSupported(
58 sensorsAsyncResp->asyncResp->res,
59 "Setting LimitInWatts when PowerLimit feature is disabled");
60 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
61 return;
62 }
George Liu0fda0f12021-11-16 10:06:17 +080063
Ginu Georgee93abac2024-06-14 17:35:27 +053064 setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
65 "xyz.openbmc_project.Settings",
Asmitha Karunanithi87c44962024-04-04 18:28:33 +000066 sdbusplus::message::object_path(
67 "/xyz/openbmc_project/control/host0/power_cap"),
68 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ginu Georgee93abac2024-06-14 17:35:27 +053069 valueToSet);
John Edward Broadbent7e860f12021-04-08 15:57:16 -070070}
Ed Tanous53b00f52024-03-08 09:05:10 -080071
72inline void afterGetChassisPath(
73 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous08850572024-03-06 15:09:17 -080074 std::vector<nlohmann::json::object_t>& powerControlCollections,
Ed Tanous53b00f52024-03-08 09:05:10 -080075 const std::optional<std::string>& chassisPath)
76{
77 if (!chassisPath)
78 {
79 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
80 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
81 sensorsAsyncResp->chassisId);
82 return;
83 }
84
85 if (powerControlCollections.size() != 1)
86 {
87 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
88 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
89 "PowerControl");
90 return;
91 }
92
93 auto& item = powerControlCollections[0];
94
Ed Tanous53b00f52024-03-08 09:05:10 -080095 std::optional<uint32_t> value;
Patrick Williams504af5a2025-02-03 14:29:03 -050096 if (!json_util::readJsonObject( //
Myung Baeafc474a2024-10-09 00:53:29 -070097 item, sensorsAsyncResp->asyncResp->res, //
Patrick Williams504af5a2025-02-03 14:29:03 -050098 "PowerLimit/LimitInWatts", value //
Myung Baeafc474a2024-10-09 00:53:29 -070099 ))
Ed Tanous53b00f52024-03-08 09:05:10 -0800100 {
101 return;
102 }
103 if (!value)
104 {
105 return;
106 }
Ed Tanousdeae6a72024-11-11 21:58:57 -0800107 dbus::utility::getProperty<bool>(
108 "xyz.openbmc_project.Settings",
Ed Tanous53b00f52024-03-08 09:05:10 -0800109 "/xyz/openbmc_project/control/host0/power_cap",
110 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
111 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
112}
113
114inline void afterPowerCapSettingGet(
115 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
116 const boost::system::error_code& ec,
117 const dbus::utility::DBusPropertiesMap& properties)
118{
119 if (ec)
120 {
121 messages::internalError(sensorAsyncResp->asyncResp->res);
122 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
123 return;
124 }
125
126 nlohmann::json& tempArray =
127 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
128
129 // Put multiple "sensors" into a single PowerControl, 0,
130 // so only create the first one
131 if (tempArray.empty())
132 {
133 // Mandatory properties odata.id and MemberId
134 // A warning without a odata.type
135 nlohmann::json::object_t powerControl;
136 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400137 powerControl["@odata.id"] =
138 "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
139 "/Power#/PowerControl/0";
Ed Tanous53b00f52024-03-08 09:05:10 -0800140 powerControl["Name"] = "Chassis Power Control";
141 powerControl["MemberId"] = "0";
142 tempArray.emplace_back(std::move(powerControl));
143 }
144
145 nlohmann::json& sensorJson = tempArray.back();
146 bool enabled = false;
147 double powerCap = 0.0;
148 int64_t scale = 0;
149
150 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
151 property : properties)
152 {
153 if (property.first == "Scale")
154 {
155 const int64_t* i = std::get_if<int64_t>(&property.second);
156
157 if (i != nullptr)
158 {
159 scale = *i;
160 }
161 }
162 else if (property.first == "PowerCap")
163 {
164 const double* d = std::get_if<double>(&property.second);
165 const int64_t* i = std::get_if<int64_t>(&property.second);
166 const uint32_t* u = std::get_if<uint32_t>(&property.second);
167
168 if (d != nullptr)
169 {
170 powerCap = *d;
171 }
172 else if (i != nullptr)
173 {
174 powerCap = static_cast<double>(*i);
175 }
176 else if (u != nullptr)
177 {
178 powerCap = *u;
179 }
180 }
181 else if (property.first == "PowerCapEnable")
182 {
183 const bool* b = std::get_if<bool>(&property.second);
184
185 if (b != nullptr)
186 {
187 enabled = *b;
188 }
189 }
190 }
191
192 // LimitException is Mandatory attribute as per OCP
Janet Adkinsc9563602024-08-28 11:37:46 -0500193 // Baseline Profile - v1.0.0, so currently making it
Ed Tanous53b00f52024-03-08 09:05:10 -0800194 // "NoAction" as default value to make it OCP Compliant.
Ed Tanous539d8c62024-06-19 14:38:27 -0700195 sensorJson["PowerLimit"]["LimitException"] =
196 power::PowerLimitException::NoAction;
Ed Tanous53b00f52024-03-08 09:05:10 -0800197
198 if (enabled)
199 {
200 // Redfish specification indicates PowerLimit should
201 // be null if the limit is not enabled.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400202 sensorJson["PowerLimit"]["LimitInWatts"] =
203 powerCap * std::pow(10, scale);
Ed Tanous53b00f52024-03-08 09:05:10 -0800204 }
205}
206
207using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400208inline void afterGetChassis(
209 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
210 const boost::system::error_code& ec2, const Mapper& chassisPaths)
Ed Tanous53b00f52024-03-08 09:05:10 -0800211{
212 if (ec2)
213 {
214 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
215 ec2);
216 return;
217 }
218
219 bool found = false;
220 for (const std::string& chassis : chassisPaths)
221 {
222 size_t len = std::string::npos;
223 size_t lastPos = chassis.rfind('/');
224 if (lastPos == std::string::npos)
225 {
226 continue;
227 }
228
229 if (lastPos == chassis.size() - 1)
230 {
231 size_t end = lastPos;
232 lastPos = chassis.rfind('/', lastPos - 1);
233 if (lastPos == std::string::npos)
234 {
235 continue;
236 }
237
238 len = end - (lastPos + 1);
239 }
240
241 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
242 if (interfaceChassisName == sensorAsyncResp->chassisId)
243 {
244 found = true;
245 break;
246 }
247 }
248
249 if (!found)
250 {
251 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
252 sensorAsyncResp->chassisId);
253 return;
254 }
255
Ed Tanousdeae6a72024-11-11 21:58:57 -0800256 dbus::utility::getAllProperties(
257 "xyz.openbmc_project.Settings",
Ed Tanous53b00f52024-03-08 09:05:10 -0800258 "/xyz/openbmc_project/control/host0/power_cap",
259 "xyz.openbmc_project.Control.Power.Cap",
260 [sensorAsyncResp](const boost::system::error_code& ec,
261 const dbus::utility::DBusPropertiesMap& properties
262
263 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
264}
265
Patrick Williams504af5a2025-02-03 14:29:03 -0500266inline void handleChassisPowerGet(
267 App& app, const crow::Request& req,
268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
269 const std::string& chassisName)
Ed Tanous53b00f52024-03-08 09:05:10 -0800270{
271 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
272 {
273 return;
274 }
275 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
276
277 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
278 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500279 sensor_utils::chassisSubNodeToString(
280 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800281
282 getChassisData(sensorAsyncResp);
283
284 // This callback verifies that the power limit is only provided
285 // for the chassis that implements the Chassis inventory item.
286 // This prevents things like power supplies providing the
287 // chassis power limit
288
289 constexpr std::array<std::string_view, 2> interfaces = {
290 "xyz.openbmc_project.Inventory.Item.Board",
291 "xyz.openbmc_project.Inventory.Item.Chassis"};
292
293 dbus::utility::getSubTreePaths(
294 "/xyz/openbmc_project/inventory", 0, interfaces,
295 std::bind_front(afterGetChassis, sensorAsyncResp));
296}
297
Patrick Williams504af5a2025-02-03 14:29:03 -0500298inline void handleChassisPowerPatch(
299 App& app, const crow::Request& req,
300 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
301 const std::string& chassisName)
Ed Tanous53b00f52024-03-08 09:05:10 -0800302{
303 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
304 {
305 return;
306 }
307 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
308 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500309 sensor_utils::chassisSubNodeToString(
310 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800311
Ed Tanous08850572024-03-06 15:09:17 -0800312 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
313 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800314
Patrick Williams504af5a2025-02-03 14:29:03 -0500315 if (!json_util::readJsonPatch( //
Myung Baeafc474a2024-10-09 00:53:29 -0700316 req, sensorAsyncResp->asyncResp->res, //
Patrick Williams504af5a2025-02-03 14:29:03 -0500317 "PowerControl", powerCtlCollections, //
318 "Voltages", voltageCollections //
Myung Baeafc474a2024-10-09 00:53:29 -0700319 ))
Ed Tanous53b00f52024-03-08 09:05:10 -0800320 {
321 return;
322 }
323
324 if (powerCtlCollections)
325 {
326 redfish::chassis_utils::getValidChassisPath(
327 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
328 std::bind_front(afterGetChassisPath, sensorAsyncResp,
329 *powerCtlCollections));
330 }
331 if (voltageCollections)
332 {
Ed Tanous08850572024-03-06 15:09:17 -0800333 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800334 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800335 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800336 setSensorsOverride(sensorAsyncResp, allCollections);
337 }
338}
339
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700340inline void requestRoutesPower(App& app)
341{
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::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700344 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800345 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000346
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700347 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700348 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700349 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800350 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700351}
Ed Tanous2474adf2018-09-05 16:31:16 -0700352
353} // namespace redfish