blob: c914cee6c9e8fa5631e2c0ac6fd72b69f1d44ec3 [file] [log] [blame]
Ed Tanous2474adf2018-09-05 16:31:16 -07001/*
2// Copyright (c) 2018 Intel Corporation
3// Copyright (c) 2018 Ampere Computing LLC
4/
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16*/
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"
Ed Tanous2474adf2018-09-05 16:31:16 -070027
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020028#include <sdbusplus/asio/property.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029
George Liu7a1dbc42022-12-07 16:03:22 +080030#include <array>
Ed Tanous08850572024-03-06 15:09:17 -080031#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080032#include <string_view>
Ed Tanous08850572024-03-06 15:09:17 -080033#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080034
Ed Tanous2474adf2018-09-05 16:31:16 -070035namespace redfish
36{
Ed Tanous53b00f52024-03-08 09:05:10 -080037
38inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070039 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080040 uint32_t valueToSet, const boost::system::error_code& ec,
41 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070042{
Ed Tanous53b00f52024-03-08 09:05:10 -080043 if (ec)
44 {
45 messages::internalError(sensorsAsyncResp->asyncResp->res);
46 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
47 return;
48 }
49 if (!powerCapEnable)
50 {
51 messages::actionNotSupported(
52 sensorsAsyncResp->asyncResp->res,
53 "Setting LimitInWatts when PowerLimit feature is disabled");
54 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
55 return;
56 }
George Liu0fda0f12021-11-16 10:06:17 +080057
Ginu Georgee93abac2024-06-14 17:35:27 +053058 setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
59 "xyz.openbmc_project.Settings",
Asmitha Karunanithi87c44962024-04-04 18:28:33 +000060 sdbusplus::message::object_path(
61 "/xyz/openbmc_project/control/host0/power_cap"),
62 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ginu Georgee93abac2024-06-14 17:35:27 +053063 valueToSet);
John Edward Broadbent7e860f12021-04-08 15:57:16 -070064}
Ed Tanous53b00f52024-03-08 09:05:10 -080065
66inline void afterGetChassisPath(
67 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous08850572024-03-06 15:09:17 -080068 std::vector<nlohmann::json::object_t>& powerControlCollections,
Ed Tanous53b00f52024-03-08 09:05:10 -080069 const std::optional<std::string>& chassisPath)
70{
71 if (!chassisPath)
72 {
73 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
74 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
75 sensorsAsyncResp->chassisId);
76 return;
77 }
78
79 if (powerControlCollections.size() != 1)
80 {
81 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
82 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
83 "PowerControl");
84 return;
85 }
86
87 auto& item = powerControlCollections[0];
88
Ed Tanous53b00f52024-03-08 09:05:10 -080089 std::optional<uint32_t> value;
Ed Tanous08850572024-03-06 15:09:17 -080090 if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res,
91 "PowerLimit/LimitInWatts", value))
Ed Tanous53b00f52024-03-08 09:05:10 -080092 {
93 return;
94 }
95 if (!value)
96 {
97 return;
98 }
99 sdbusplus::asio::getProperty<bool>(
100 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
101 "/xyz/openbmc_project/control/host0/power_cap",
102 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
103 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
104}
105
106inline void afterPowerCapSettingGet(
107 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
108 const boost::system::error_code& ec,
109 const dbus::utility::DBusPropertiesMap& properties)
110{
111 if (ec)
112 {
113 messages::internalError(sensorAsyncResp->asyncResp->res);
114 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
115 return;
116 }
117
118 nlohmann::json& tempArray =
119 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
120
121 // Put multiple "sensors" into a single PowerControl, 0,
122 // so only create the first one
123 if (tempArray.empty())
124 {
125 // Mandatory properties odata.id and MemberId
126 // A warning without a odata.type
127 nlohmann::json::object_t powerControl;
128 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400129 powerControl["@odata.id"] =
130 "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
131 "/Power#/PowerControl/0";
Ed Tanous53b00f52024-03-08 09:05:10 -0800132 powerControl["Name"] = "Chassis Power Control";
133 powerControl["MemberId"] = "0";
134 tempArray.emplace_back(std::move(powerControl));
135 }
136
137 nlohmann::json& sensorJson = tempArray.back();
138 bool enabled = false;
139 double powerCap = 0.0;
140 int64_t scale = 0;
141
142 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
143 property : properties)
144 {
145 if (property.first == "Scale")
146 {
147 const int64_t* i = std::get_if<int64_t>(&property.second);
148
149 if (i != nullptr)
150 {
151 scale = *i;
152 }
153 }
154 else if (property.first == "PowerCap")
155 {
156 const double* d = std::get_if<double>(&property.second);
157 const int64_t* i = std::get_if<int64_t>(&property.second);
158 const uint32_t* u = std::get_if<uint32_t>(&property.second);
159
160 if (d != nullptr)
161 {
162 powerCap = *d;
163 }
164 else if (i != nullptr)
165 {
166 powerCap = static_cast<double>(*i);
167 }
168 else if (u != nullptr)
169 {
170 powerCap = *u;
171 }
172 }
173 else if (property.first == "PowerCapEnable")
174 {
175 const bool* b = std::get_if<bool>(&property.second);
176
177 if (b != nullptr)
178 {
179 enabled = *b;
180 }
181 }
182 }
183
184 // LimitException is Mandatory attribute as per OCP
185 // Baseline Profile – v1.0.0, so currently making it
186 // "NoAction" as default value to make it OCP Compliant.
Ed Tanous539d8c62024-06-19 14:38:27 -0700187 sensorJson["PowerLimit"]["LimitException"] =
188 power::PowerLimitException::NoAction;
Ed Tanous53b00f52024-03-08 09:05:10 -0800189
190 if (enabled)
191 {
192 // Redfish specification indicates PowerLimit should
193 // be null if the limit is not enabled.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400194 sensorJson["PowerLimit"]["LimitInWatts"] =
195 powerCap * std::pow(10, scale);
Ed Tanous53b00f52024-03-08 09:05:10 -0800196 }
197}
198
199using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400200inline void afterGetChassis(
201 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
202 const boost::system::error_code& ec2, const Mapper& chassisPaths)
Ed Tanous53b00f52024-03-08 09:05:10 -0800203{
204 if (ec2)
205 {
206 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
207 ec2);
208 return;
209 }
210
211 bool found = false;
212 for (const std::string& chassis : chassisPaths)
213 {
214 size_t len = std::string::npos;
215 size_t lastPos = chassis.rfind('/');
216 if (lastPos == std::string::npos)
217 {
218 continue;
219 }
220
221 if (lastPos == chassis.size() - 1)
222 {
223 size_t end = lastPos;
224 lastPos = chassis.rfind('/', lastPos - 1);
225 if (lastPos == std::string::npos)
226 {
227 continue;
228 }
229
230 len = end - (lastPos + 1);
231 }
232
233 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
234 if (interfaceChassisName == sensorAsyncResp->chassisId)
235 {
236 found = true;
237 break;
238 }
239 }
240
241 if (!found)
242 {
243 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
244 sensorAsyncResp->chassisId);
245 return;
246 }
247
248 sdbusplus::asio::getAllProperties(
249 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
250 "/xyz/openbmc_project/control/host0/power_cap",
251 "xyz.openbmc_project.Control.Power.Cap",
252 [sensorAsyncResp](const boost::system::error_code& ec,
253 const dbus::utility::DBusPropertiesMap& properties
254
255 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
256}
257
258inline void
259 handleChassisPowerGet(App& app, const crow::Request& req,
260 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
261 const std::string& chassisName)
262{
263 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
264 {
265 return;
266 }
267 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
268
269 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
270 asyncResp, chassisName, sensors::dbus::powerPaths,
271 sensors::node::power);
272
273 getChassisData(sensorAsyncResp);
274
275 // This callback verifies that the power limit is only provided
276 // for the chassis that implements the Chassis inventory item.
277 // This prevents things like power supplies providing the
278 // chassis power limit
279
280 constexpr std::array<std::string_view, 2> interfaces = {
281 "xyz.openbmc_project.Inventory.Item.Board",
282 "xyz.openbmc_project.Inventory.Item.Chassis"};
283
284 dbus::utility::getSubTreePaths(
285 "/xyz/openbmc_project/inventory", 0, interfaces,
286 std::bind_front(afterGetChassis, sensorAsyncResp));
287}
288
289inline void
290 handleChassisPowerPatch(App& app, const crow::Request& req,
291 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
292 const std::string& chassisName)
293{
294 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
295 {
296 return;
297 }
298 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
299 asyncResp, chassisName, sensors::dbus::powerPaths,
300 sensors::node::power);
301
Ed Tanous08850572024-03-06 15:09:17 -0800302 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
303 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800304
305 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
306 "PowerControl", powerCtlCollections,
307 "Voltages", voltageCollections))
308 {
309 return;
310 }
311
312 if (powerCtlCollections)
313 {
314 redfish::chassis_utils::getValidChassisPath(
315 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
316 std::bind_front(afterGetChassisPath, sensorAsyncResp,
317 *powerCtlCollections));
318 }
319 if (voltageCollections)
320 {
Ed Tanous08850572024-03-06 15:09:17 -0800321 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800322 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800323 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800324 setSensorsOverride(sensorAsyncResp, allCollections);
325 }
326}
327
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700328inline void requestRoutesPower(App& app)
329{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700330 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700331 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700332 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800333 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000334
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700335 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700336 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700337 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800338 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700339}
Ed Tanous2474adf2018-09-05 16:31:16 -0700340
341} // namespace redfish