blob: a221d721c921ea004d4180fdc7ad39240e5dd810 [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"
George Liu7a1dbc42022-12-07 16:03:22 +08009#include "dbus_utility.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -070010#include "generated/enums/power.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080011#include "query.hpp"
12#include "registries/privilege_registry.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070013#include "sensors.hpp"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000014#include "utils/chassis_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070015#include "utils/json_utils.hpp"
Janet Adkinsc9563602024-08-28 11:37:46 -050016#include "utils/sensor_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070017
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020018#include <sdbusplus/asio/property.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070019
George Liu7a1dbc42022-12-07 16:03:22 +080020#include <array>
Ed Tanous08850572024-03-06 15:09:17 -080021#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080022#include <string_view>
Ed Tanous08850572024-03-06 15:09:17 -080023#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080024
Ed Tanous2474adf2018-09-05 16:31:16 -070025namespace redfish
26{
Ed Tanous53b00f52024-03-08 09:05:10 -080027
28inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080030 uint32_t valueToSet, const boost::system::error_code& ec,
31 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070032{
Ed Tanous53b00f52024-03-08 09:05:10 -080033 if (ec)
34 {
35 messages::internalError(sensorsAsyncResp->asyncResp->res);
36 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
37 return;
38 }
39 if (!powerCapEnable)
40 {
41 messages::actionNotSupported(
42 sensorsAsyncResp->asyncResp->res,
43 "Setting LimitInWatts when PowerLimit feature is disabled");
44 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
45 return;
46 }
George Liu0fda0f12021-11-16 10:06:17 +080047
Ginu Georgee93abac2024-06-14 17:35:27 +053048 setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
49 "xyz.openbmc_project.Settings",
Asmitha Karunanithi87c44962024-04-04 18:28:33 +000050 sdbusplus::message::object_path(
51 "/xyz/openbmc_project/control/host0/power_cap"),
52 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ginu Georgee93abac2024-06-14 17:35:27 +053053 valueToSet);
John Edward Broadbent7e860f12021-04-08 15:57:16 -070054}
Ed Tanous53b00f52024-03-08 09:05:10 -080055
56inline void afterGetChassisPath(
57 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous08850572024-03-06 15:09:17 -080058 std::vector<nlohmann::json::object_t>& powerControlCollections,
Ed Tanous53b00f52024-03-08 09:05:10 -080059 const std::optional<std::string>& chassisPath)
60{
61 if (!chassisPath)
62 {
63 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
64 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
65 sensorsAsyncResp->chassisId);
66 return;
67 }
68
69 if (powerControlCollections.size() != 1)
70 {
71 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
72 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
73 "PowerControl");
74 return;
75 }
76
77 auto& item = powerControlCollections[0];
78
Ed Tanous53b00f52024-03-08 09:05:10 -080079 std::optional<uint32_t> value;
Myung Baeafc474a2024-10-09 00:53:29 -070080 if (!json_util::readJsonObject( //
81 item, sensorsAsyncResp->asyncResp->res, //
82 "PowerLimit/LimitInWatts", value //
83 ))
Ed Tanous53b00f52024-03-08 09:05:10 -080084 {
85 return;
86 }
87 if (!value)
88 {
89 return;
90 }
Ed Tanousdeae6a72024-11-11 21:58:57 -080091 dbus::utility::getProperty<bool>(
92 "xyz.openbmc_project.Settings",
Ed Tanous53b00f52024-03-08 09:05:10 -080093 "/xyz/openbmc_project/control/host0/power_cap",
94 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
95 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
96}
97
98inline void afterPowerCapSettingGet(
99 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
100 const boost::system::error_code& ec,
101 const dbus::utility::DBusPropertiesMap& properties)
102{
103 if (ec)
104 {
105 messages::internalError(sensorAsyncResp->asyncResp->res);
106 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
107 return;
108 }
109
110 nlohmann::json& tempArray =
111 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
112
113 // Put multiple "sensors" into a single PowerControl, 0,
114 // so only create the first one
115 if (tempArray.empty())
116 {
117 // Mandatory properties odata.id and MemberId
118 // A warning without a odata.type
119 nlohmann::json::object_t powerControl;
120 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400121 powerControl["@odata.id"] =
122 "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
123 "/Power#/PowerControl/0";
Ed Tanous53b00f52024-03-08 09:05:10 -0800124 powerControl["Name"] = "Chassis Power Control";
125 powerControl["MemberId"] = "0";
126 tempArray.emplace_back(std::move(powerControl));
127 }
128
129 nlohmann::json& sensorJson = tempArray.back();
130 bool enabled = false;
131 double powerCap = 0.0;
132 int64_t scale = 0;
133
134 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
135 property : properties)
136 {
137 if (property.first == "Scale")
138 {
139 const int64_t* i = std::get_if<int64_t>(&property.second);
140
141 if (i != nullptr)
142 {
143 scale = *i;
144 }
145 }
146 else if (property.first == "PowerCap")
147 {
148 const double* d = std::get_if<double>(&property.second);
149 const int64_t* i = std::get_if<int64_t>(&property.second);
150 const uint32_t* u = std::get_if<uint32_t>(&property.second);
151
152 if (d != nullptr)
153 {
154 powerCap = *d;
155 }
156 else if (i != nullptr)
157 {
158 powerCap = static_cast<double>(*i);
159 }
160 else if (u != nullptr)
161 {
162 powerCap = *u;
163 }
164 }
165 else if (property.first == "PowerCapEnable")
166 {
167 const bool* b = std::get_if<bool>(&property.second);
168
169 if (b != nullptr)
170 {
171 enabled = *b;
172 }
173 }
174 }
175
176 // LimitException is Mandatory attribute as per OCP
Janet Adkinsc9563602024-08-28 11:37:46 -0500177 // Baseline Profile - v1.0.0, so currently making it
Ed Tanous53b00f52024-03-08 09:05:10 -0800178 // "NoAction" as default value to make it OCP Compliant.
Ed Tanous539d8c62024-06-19 14:38:27 -0700179 sensorJson["PowerLimit"]["LimitException"] =
180 power::PowerLimitException::NoAction;
Ed Tanous53b00f52024-03-08 09:05:10 -0800181
182 if (enabled)
183 {
184 // Redfish specification indicates PowerLimit should
185 // be null if the limit is not enabled.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400186 sensorJson["PowerLimit"]["LimitInWatts"] =
187 powerCap * std::pow(10, scale);
Ed Tanous53b00f52024-03-08 09:05:10 -0800188 }
189}
190
191using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400192inline void afterGetChassis(
193 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
194 const boost::system::error_code& ec2, const Mapper& chassisPaths)
Ed Tanous53b00f52024-03-08 09:05:10 -0800195{
196 if (ec2)
197 {
198 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
199 ec2);
200 return;
201 }
202
203 bool found = false;
204 for (const std::string& chassis : chassisPaths)
205 {
206 size_t len = std::string::npos;
207 size_t lastPos = chassis.rfind('/');
208 if (lastPos == std::string::npos)
209 {
210 continue;
211 }
212
213 if (lastPos == chassis.size() - 1)
214 {
215 size_t end = lastPos;
216 lastPos = chassis.rfind('/', lastPos - 1);
217 if (lastPos == std::string::npos)
218 {
219 continue;
220 }
221
222 len = end - (lastPos + 1);
223 }
224
225 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
226 if (interfaceChassisName == sensorAsyncResp->chassisId)
227 {
228 found = true;
229 break;
230 }
231 }
232
233 if (!found)
234 {
235 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
236 sensorAsyncResp->chassisId);
237 return;
238 }
239
Ed Tanousdeae6a72024-11-11 21:58:57 -0800240 dbus::utility::getAllProperties(
241 "xyz.openbmc_project.Settings",
Ed Tanous53b00f52024-03-08 09:05:10 -0800242 "/xyz/openbmc_project/control/host0/power_cap",
243 "xyz.openbmc_project.Control.Power.Cap",
244 [sensorAsyncResp](const boost::system::error_code& ec,
245 const dbus::utility::DBusPropertiesMap& properties
246
247 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
248}
249
250inline void
251 handleChassisPowerGet(App& app, const crow::Request& req,
252 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
253 const std::string& chassisName)
254{
255 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
256 {
257 return;
258 }
259 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
260
261 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
262 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500263 sensor_utils::chassisSubNodeToString(
264 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800265
266 getChassisData(sensorAsyncResp);
267
268 // This callback verifies that the power limit is only provided
269 // for the chassis that implements the Chassis inventory item.
270 // This prevents things like power supplies providing the
271 // chassis power limit
272
273 constexpr std::array<std::string_view, 2> interfaces = {
274 "xyz.openbmc_project.Inventory.Item.Board",
275 "xyz.openbmc_project.Inventory.Item.Chassis"};
276
277 dbus::utility::getSubTreePaths(
278 "/xyz/openbmc_project/inventory", 0, interfaces,
279 std::bind_front(afterGetChassis, sensorAsyncResp));
280}
281
282inline void
283 handleChassisPowerPatch(App& app, const crow::Request& req,
284 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
285 const std::string& chassisName)
286{
287 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
288 {
289 return;
290 }
291 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
292 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500293 sensor_utils::chassisSubNodeToString(
294 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800295
Ed Tanous08850572024-03-06 15:09:17 -0800296 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
297 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800298
Myung Baeafc474a2024-10-09 00:53:29 -0700299 if (!json_util::readJsonPatch( //
300 req, sensorAsyncResp->asyncResp->res, //
301 "PowerControl", powerCtlCollections, //
302 "Voltages", voltageCollections //
303 ))
Ed Tanous53b00f52024-03-08 09:05:10 -0800304 {
305 return;
306 }
307
308 if (powerCtlCollections)
309 {
310 redfish::chassis_utils::getValidChassisPath(
311 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
312 std::bind_front(afterGetChassisPath, sensorAsyncResp,
313 *powerCtlCollections));
314 }
315 if (voltageCollections)
316 {
Ed Tanous08850572024-03-06 15:09:17 -0800317 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800318 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800319 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800320 setSensorsOverride(sensorAsyncResp, allCollections);
321 }
322}
323
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700324inline void requestRoutesPower(App& app)
325{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700326 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700327 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700328 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800329 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000330
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700331 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700332 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700333 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800334 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700335}
Ed Tanous2474adf2018-09-05 16:31:16 -0700336
337} // namespace redfish