blob: 80276d2817850b1ec8ed40989f2992f6b4cbc4bc [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 Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "query.hpp"
22#include "registries/privilege_registry.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070023#include "sensors.hpp"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000024#include "utils/chassis_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070025
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020026#include <sdbusplus/asio/property.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070027
George Liu7a1dbc42022-12-07 16:03:22 +080028#include <array>
Ed Tanous08850572024-03-06 15:09:17 -080029#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080030#include <string_view>
Ed Tanous08850572024-03-06 15:09:17 -080031#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080032
Ed Tanous2474adf2018-09-05 16:31:16 -070033namespace redfish
34{
Ed Tanous53b00f52024-03-08 09:05:10 -080035
36inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070037 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080038 uint32_t valueToSet, const boost::system::error_code& ec,
39 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070040{
Ed Tanous53b00f52024-03-08 09:05:10 -080041 if (ec)
42 {
43 messages::internalError(sensorsAsyncResp->asyncResp->res);
44 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
45 return;
46 }
47 if (!powerCapEnable)
48 {
49 messages::actionNotSupported(
50 sensorsAsyncResp->asyncResp->res,
51 "Setting LimitInWatts when PowerLimit feature is disabled");
52 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
53 return;
54 }
George Liu0fda0f12021-11-16 10:06:17 +080055
Ed Tanous53b00f52024-03-08 09:05:10 -080056 sdbusplus::asio::setProperty(
57 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
58 "/xyz/openbmc_project/control/host0/power_cap",
59 "xyz.openbmc_project.Control.Power.Cap", "PowerCap", valueToSet,
60 [sensorsAsyncResp](const boost::system::error_code& ec2) {
61 if (ec2)
George Liu0fda0f12021-11-16 10:06:17 +080062 {
Ed Tanous53b00f52024-03-08 09:05:10 -080063 BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2);
64 messages::internalError(sensorsAsyncResp->asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +080065 return;
66 }
Ed Tanous53b00f52024-03-08 09:05:10 -080067 sensorsAsyncResp->asyncResp->res.result(
68 boost::beast::http::status::no_content);
69 });
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;
Ed Tanous08850572024-03-06 15:09:17 -080096 if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res,
97 "PowerLimit/LimitInWatts", value))
Ed Tanous53b00f52024-03-08 09:05:10 -080098 {
99 return;
100 }
101 if (!value)
102 {
103 return;
104 }
105 sdbusplus::asio::getProperty<bool>(
106 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
107 "/xyz/openbmc_project/control/host0/power_cap",
108 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
109 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
110}
111
112inline void afterPowerCapSettingGet(
113 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
114 const boost::system::error_code& ec,
115 const dbus::utility::DBusPropertiesMap& properties)
116{
117 if (ec)
118 {
119 messages::internalError(sensorAsyncResp->asyncResp->res);
120 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
121 return;
122 }
123
124 nlohmann::json& tempArray =
125 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
126
127 // Put multiple "sensors" into a single PowerControl, 0,
128 // so only create the first one
129 if (tempArray.empty())
130 {
131 // Mandatory properties odata.id and MemberId
132 // A warning without a odata.type
133 nlohmann::json::object_t powerControl;
134 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
135 powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
136 sensorAsyncResp->chassisId +
137 "/Power#/PowerControl/0";
138 powerControl["Name"] = "Chassis Power Control";
139 powerControl["MemberId"] = "0";
140 tempArray.emplace_back(std::move(powerControl));
141 }
142
143 nlohmann::json& sensorJson = tempArray.back();
144 bool enabled = false;
145 double powerCap = 0.0;
146 int64_t scale = 0;
147
148 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
149 property : properties)
150 {
151 if (property.first == "Scale")
152 {
153 const int64_t* i = std::get_if<int64_t>(&property.second);
154
155 if (i != nullptr)
156 {
157 scale = *i;
158 }
159 }
160 else if (property.first == "PowerCap")
161 {
162 const double* d = std::get_if<double>(&property.second);
163 const int64_t* i = std::get_if<int64_t>(&property.second);
164 const uint32_t* u = std::get_if<uint32_t>(&property.second);
165
166 if (d != nullptr)
167 {
168 powerCap = *d;
169 }
170 else if (i != nullptr)
171 {
172 powerCap = static_cast<double>(*i);
173 }
174 else if (u != nullptr)
175 {
176 powerCap = *u;
177 }
178 }
179 else if (property.first == "PowerCapEnable")
180 {
181 const bool* b = std::get_if<bool>(&property.second);
182
183 if (b != nullptr)
184 {
185 enabled = *b;
186 }
187 }
188 }
189
190 // LimitException is Mandatory attribute as per OCP
191 // Baseline Profile – v1.0.0, so currently making it
192 // "NoAction" as default value to make it OCP Compliant.
193 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
194
195 if (enabled)
196 {
197 // Redfish specification indicates PowerLimit should
198 // be null if the limit is not enabled.
199 sensorJson["PowerLimit"]["LimitInWatts"] = powerCap *
200 std::pow(10, scale);
201 }
202}
203
204using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
205inline void
206 afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
207 const boost::system::error_code& ec2,
208 const Mapper& chassisPaths)
209{
210 if (ec2)
211 {
212 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
213 ec2);
214 return;
215 }
216
217 bool found = false;
218 for (const std::string& chassis : chassisPaths)
219 {
220 size_t len = std::string::npos;
221 size_t lastPos = chassis.rfind('/');
222 if (lastPos == std::string::npos)
223 {
224 continue;
225 }
226
227 if (lastPos == chassis.size() - 1)
228 {
229 size_t end = lastPos;
230 lastPos = chassis.rfind('/', lastPos - 1);
231 if (lastPos == std::string::npos)
232 {
233 continue;
234 }
235
236 len = end - (lastPos + 1);
237 }
238
239 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
240 if (interfaceChassisName == sensorAsyncResp->chassisId)
241 {
242 found = true;
243 break;
244 }
245 }
246
247 if (!found)
248 {
249 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
250 sensorAsyncResp->chassisId);
251 return;
252 }
253
254 sdbusplus::asio::getAllProperties(
255 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
256 "/xyz/openbmc_project/control/host0/power_cap",
257 "xyz.openbmc_project.Control.Power.Cap",
258 [sensorAsyncResp](const boost::system::error_code& ec,
259 const dbus::utility::DBusPropertiesMap& properties
260
261 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
262}
263
264inline void
265 handleChassisPowerGet(App& app, const crow::Request& req,
266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
267 const std::string& chassisName)
268{
269 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
270 {
271 return;
272 }
273 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
274
275 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
276 asyncResp, chassisName, sensors::dbus::powerPaths,
277 sensors::node::power);
278
279 getChassisData(sensorAsyncResp);
280
281 // This callback verifies that the power limit is only provided
282 // for the chassis that implements the Chassis inventory item.
283 // This prevents things like power supplies providing the
284 // chassis power limit
285
286 constexpr std::array<std::string_view, 2> interfaces = {
287 "xyz.openbmc_project.Inventory.Item.Board",
288 "xyz.openbmc_project.Inventory.Item.Chassis"};
289
290 dbus::utility::getSubTreePaths(
291 "/xyz/openbmc_project/inventory", 0, interfaces,
292 std::bind_front(afterGetChassis, sensorAsyncResp));
293}
294
295inline void
296 handleChassisPowerPatch(App& app, const crow::Request& req,
297 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
298 const std::string& chassisName)
299{
300 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
301 {
302 return;
303 }
304 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
305 asyncResp, chassisName, sensors::dbus::powerPaths,
306 sensors::node::power);
307
Ed Tanous08850572024-03-06 15:09:17 -0800308 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
309 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800310
311 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
312 "PowerControl", powerCtlCollections,
313 "Voltages", voltageCollections))
314 {
315 return;
316 }
317
318 if (powerCtlCollections)
319 {
320 redfish::chassis_utils::getValidChassisPath(
321 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
322 std::bind_front(afterGetChassisPath, sensorAsyncResp,
323 *powerCtlCollections));
324 }
325 if (voltageCollections)
326 {
Ed Tanous08850572024-03-06 15:09:17 -0800327 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800328 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800329 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800330 setSensorsOverride(sensorAsyncResp, allCollections);
331 }
332}
333
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700334inline void requestRoutesPower(App& app)
335{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700336 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700337 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700338 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800339 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000340
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700341 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700342 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700343 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800344 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700345}
Ed Tanous2474adf2018-09-05 16:31:16 -0700346
347} // namespace redfish