blob: d2843615e0ca376b70783009a4ea98991a5fa100 [file] [log] [blame]
Ed Tanous2474adf2018-09-05 16:31:16 -07001/*
Ed Tanous6be832e2024-09-10 11:44:48 -07002Copyright (c) 2018 Intel Corporation
3Copyright (c) 2018 Ampere Computing LLC
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
Ed Tanous2474adf2018-09-05 16:31:16 -070016*/
17#pragma once
18
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080019#include "app.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080020#include "dbus_utility.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -070021#include "generated/enums/power.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080022#include "query.hpp"
23#include "registries/privilege_registry.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070024#include "sensors.hpp"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000025#include "utils/chassis_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070026#include "utils/json_utils.hpp"
Janet Adkinsc9563602024-08-28 11:37:46 -050027#include "utils/sensor_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070028
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020029#include <sdbusplus/asio/property.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070030
George Liu7a1dbc42022-12-07 16:03:22 +080031#include <array>
Ed Tanous08850572024-03-06 15:09:17 -080032#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080033#include <string_view>
Ed Tanous08850572024-03-06 15:09:17 -080034#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080035
Ed Tanous2474adf2018-09-05 16:31:16 -070036namespace redfish
37{
Ed Tanous53b00f52024-03-08 09:05:10 -080038
39inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070040 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080041 uint32_t valueToSet, const boost::system::error_code& ec,
42 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070043{
Ed Tanous53b00f52024-03-08 09:05:10 -080044 if (ec)
45 {
46 messages::internalError(sensorsAsyncResp->asyncResp->res);
47 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
48 return;
49 }
50 if (!powerCapEnable)
51 {
52 messages::actionNotSupported(
53 sensorsAsyncResp->asyncResp->res,
54 "Setting LimitInWatts when PowerLimit feature is disabled");
55 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
56 return;
57 }
George Liu0fda0f12021-11-16 10:06:17 +080058
Ginu Georgee93abac2024-06-14 17:35:27 +053059 setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
60 "xyz.openbmc_project.Settings",
Asmitha Karunanithi87c44962024-04-04 18:28:33 +000061 sdbusplus::message::object_path(
62 "/xyz/openbmc_project/control/host0/power_cap"),
63 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ginu Georgee93abac2024-06-14 17:35:27 +053064 valueToSet);
John Edward Broadbent7e860f12021-04-08 15:57:16 -070065}
Ed Tanous53b00f52024-03-08 09:05:10 -080066
67inline void afterGetChassisPath(
68 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous08850572024-03-06 15:09:17 -080069 std::vector<nlohmann::json::object_t>& powerControlCollections,
Ed Tanous53b00f52024-03-08 09:05:10 -080070 const std::optional<std::string>& chassisPath)
71{
72 if (!chassisPath)
73 {
74 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
75 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
76 sensorsAsyncResp->chassisId);
77 return;
78 }
79
80 if (powerControlCollections.size() != 1)
81 {
82 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
83 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
84 "PowerControl");
85 return;
86 }
87
88 auto& item = powerControlCollections[0];
89
Ed Tanous53b00f52024-03-08 09:05:10 -080090 std::optional<uint32_t> value;
Ed Tanous08850572024-03-06 15:09:17 -080091 if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res,
92 "PowerLimit/LimitInWatts", value))
Ed Tanous53b00f52024-03-08 09:05:10 -080093 {
94 return;
95 }
96 if (!value)
97 {
98 return;
99 }
100 sdbusplus::asio::getProperty<bool>(
101 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
102 "/xyz/openbmc_project/control/host0/power_cap",
103 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
104 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
105}
106
107inline void afterPowerCapSettingGet(
108 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
109 const boost::system::error_code& ec,
110 const dbus::utility::DBusPropertiesMap& properties)
111{
112 if (ec)
113 {
114 messages::internalError(sensorAsyncResp->asyncResp->res);
115 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
116 return;
117 }
118
119 nlohmann::json& tempArray =
120 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
121
122 // Put multiple "sensors" into a single PowerControl, 0,
123 // so only create the first one
124 if (tempArray.empty())
125 {
126 // Mandatory properties odata.id and MemberId
127 // A warning without a odata.type
128 nlohmann::json::object_t powerControl;
129 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400130 powerControl["@odata.id"] =
131 "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
132 "/Power#/PowerControl/0";
Ed Tanous53b00f52024-03-08 09:05:10 -0800133 powerControl["Name"] = "Chassis Power Control";
134 powerControl["MemberId"] = "0";
135 tempArray.emplace_back(std::move(powerControl));
136 }
137
138 nlohmann::json& sensorJson = tempArray.back();
139 bool enabled = false;
140 double powerCap = 0.0;
141 int64_t scale = 0;
142
143 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
144 property : properties)
145 {
146 if (property.first == "Scale")
147 {
148 const int64_t* i = std::get_if<int64_t>(&property.second);
149
150 if (i != nullptr)
151 {
152 scale = *i;
153 }
154 }
155 else if (property.first == "PowerCap")
156 {
157 const double* d = std::get_if<double>(&property.second);
158 const int64_t* i = std::get_if<int64_t>(&property.second);
159 const uint32_t* u = std::get_if<uint32_t>(&property.second);
160
161 if (d != nullptr)
162 {
163 powerCap = *d;
164 }
165 else if (i != nullptr)
166 {
167 powerCap = static_cast<double>(*i);
168 }
169 else if (u != nullptr)
170 {
171 powerCap = *u;
172 }
173 }
174 else if (property.first == "PowerCapEnable")
175 {
176 const bool* b = std::get_if<bool>(&property.second);
177
178 if (b != nullptr)
179 {
180 enabled = *b;
181 }
182 }
183 }
184
185 // LimitException is Mandatory attribute as per OCP
Janet Adkinsc9563602024-08-28 11:37:46 -0500186 // Baseline Profile - v1.0.0, so currently making it
Ed Tanous53b00f52024-03-08 09:05:10 -0800187 // "NoAction" as default value to make it OCP Compliant.
Ed Tanous539d8c62024-06-19 14:38:27 -0700188 sensorJson["PowerLimit"]["LimitException"] =
189 power::PowerLimitException::NoAction;
Ed Tanous53b00f52024-03-08 09:05:10 -0800190
191 if (enabled)
192 {
193 // Redfish specification indicates PowerLimit should
194 // be null if the limit is not enabled.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400195 sensorJson["PowerLimit"]["LimitInWatts"] =
196 powerCap * std::pow(10, scale);
Ed Tanous53b00f52024-03-08 09:05:10 -0800197 }
198}
199
200using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400201inline void afterGetChassis(
202 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
203 const boost::system::error_code& ec2, const Mapper& chassisPaths)
Ed Tanous53b00f52024-03-08 09:05:10 -0800204{
205 if (ec2)
206 {
207 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
208 ec2);
209 return;
210 }
211
212 bool found = false;
213 for (const std::string& chassis : chassisPaths)
214 {
215 size_t len = std::string::npos;
216 size_t lastPos = chassis.rfind('/');
217 if (lastPos == std::string::npos)
218 {
219 continue;
220 }
221
222 if (lastPos == chassis.size() - 1)
223 {
224 size_t end = lastPos;
225 lastPos = chassis.rfind('/', lastPos - 1);
226 if (lastPos == std::string::npos)
227 {
228 continue;
229 }
230
231 len = end - (lastPos + 1);
232 }
233
234 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
235 if (interfaceChassisName == sensorAsyncResp->chassisId)
236 {
237 found = true;
238 break;
239 }
240 }
241
242 if (!found)
243 {
244 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
245 sensorAsyncResp->chassisId);
246 return;
247 }
248
249 sdbusplus::asio::getAllProperties(
250 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
251 "/xyz/openbmc_project/control/host0/power_cap",
252 "xyz.openbmc_project.Control.Power.Cap",
253 [sensorAsyncResp](const boost::system::error_code& ec,
254 const dbus::utility::DBusPropertiesMap& properties
255
256 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
257}
258
259inline void
260 handleChassisPowerGet(App& app, const crow::Request& req,
261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const std::string& chassisName)
263{
264 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
265 {
266 return;
267 }
268 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
269
270 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
271 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500272 sensor_utils::chassisSubNodeToString(
273 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800274
275 getChassisData(sensorAsyncResp);
276
277 // This callback verifies that the power limit is only provided
278 // for the chassis that implements the Chassis inventory item.
279 // This prevents things like power supplies providing the
280 // chassis power limit
281
282 constexpr std::array<std::string_view, 2> interfaces = {
283 "xyz.openbmc_project.Inventory.Item.Board",
284 "xyz.openbmc_project.Inventory.Item.Chassis"};
285
286 dbus::utility::getSubTreePaths(
287 "/xyz/openbmc_project/inventory", 0, interfaces,
288 std::bind_front(afterGetChassis, sensorAsyncResp));
289}
290
291inline void
292 handleChassisPowerPatch(App& app, const crow::Request& req,
293 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
294 const std::string& chassisName)
295{
296 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
297 {
298 return;
299 }
300 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
301 asyncResp, chassisName, sensors::dbus::powerPaths,
Janet Adkins0c728b42024-08-29 11:09:10 -0500302 sensor_utils::chassisSubNodeToString(
303 sensor_utils::ChassisSubNode::powerNode));
Ed Tanous53b00f52024-03-08 09:05:10 -0800304
Ed Tanous08850572024-03-06 15:09:17 -0800305 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
306 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800307
308 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
309 "PowerControl", powerCtlCollections,
310 "Voltages", voltageCollections))
311 {
312 return;
313 }
314
315 if (powerCtlCollections)
316 {
317 redfish::chassis_utils::getValidChassisPath(
318 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
319 std::bind_front(afterGetChassisPath, sensorAsyncResp,
320 *powerCtlCollections));
321 }
322 if (voltageCollections)
323 {
Ed Tanous08850572024-03-06 15:09:17 -0800324 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800325 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800326 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800327 setSensorsOverride(sensorAsyncResp, allCollections);
328 }
329}
330
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700331inline void requestRoutesPower(App& app)
332{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700333 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700334 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700335 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800336 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000337
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700338 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700339 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700340 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800341 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700342}
Ed Tanous2474adf2018-09-05 16:31:16 -0700343
344} // namespace redfish