blob: e7094ad05e522b792f429258806dbe756edbd522 [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>
29#include <string_view>
30
Ed Tanous2474adf2018-09-05 16:31:16 -070031namespace redfish
32{
Ed Tanous4f48d5f2021-06-21 08:27:45 -070033inline void setPowerCapOverride(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070034 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
35 std::vector<nlohmann::json>& powerControlCollections)
Ed Tanous2474adf2018-09-05 16:31:16 -070036{
Ed Tanous002d39b2022-05-31 08:59:27 -070037 auto getChassisPath =
38 [sensorsAsyncResp, powerControlCollections](
39 const std::optional<std::string>& chassisPath) mutable {
George Liu0fda0f12021-11-16 10:06:17 +080040 if (!chassisPath)
41 {
Ed Tanous62598e32023-07-17 17:06:25 -070042 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
George Liu0fda0f12021-11-16 10:06:17 +080043 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
44 "Chassis", sensorsAsyncResp->chassisId);
45 return;
46 }
47
48 if (powerControlCollections.size() != 1)
49 {
Ed Tanous62598e32023-07-17 17:06:25 -070050 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
George Liu0fda0f12021-11-16 10:06:17 +080051 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
52 "Power", "PowerControl");
53 return;
54 }
55
56 auto& item = powerControlCollections[0];
57
58 std::optional<nlohmann::json> powerLimit;
59 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res,
60 "PowerLimit", powerLimit))
61 {
62 return;
63 }
64 if (!powerLimit)
65 {
66 return;
67 }
68 std::optional<uint32_t> value;
69 if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res,
70 "LimitInWatts", value))
71 {
72 return;
73 }
74 if (!value)
75 {
76 return;
77 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070078 sdbusplus::asio::getProperty<bool>(
79 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0fda0f12021-11-16 10:06:17 +080080 "/xyz/openbmc_project/control/host0/power_cap",
Jonathan Doman1e1e5982021-06-11 09:36:17 -070081 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -080082 [value, sensorsAsyncResp](const boost::system::error_code& ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -070083 bool powerCapEnable) {
Ed Tanous002d39b2022-05-31 08:59:27 -070084 if (ec)
85 {
86 messages::internalError(sensorsAsyncResp->asyncResp->res);
Ed Tanous62598e32023-07-17 17:06:25 -070087 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}",
88 ec);
Ed Tanous002d39b2022-05-31 08:59:27 -070089 return;
90 }
91 if (!powerCapEnable)
92 {
93 messages::actionNotSupported(
94 sensorsAsyncResp->asyncResp->res,
95 "Setting LimitInWatts when PowerLimit feature is disabled");
Ed Tanous62598e32023-07-17 17:06:25 -070096 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
Ed Tanous002d39b2022-05-31 08:59:27 -070097 return;
98 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070099
George Liu9ae226f2023-06-21 17:56:46 +0800100 sdbusplus::asio::setProperty(
101 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
102 "/xyz/openbmc_project/control/host0/power_cap",
103 "xyz.openbmc_project.Control.Power.Cap", "PowerCap", *value,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800104 [sensorsAsyncResp](const boost::system::error_code& ec2) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700105 if (ec2)
106 {
Ed Tanous62598e32023-07-17 17:06:25 -0700107 BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2);
Ed Tanous002d39b2022-05-31 08:59:27 -0700108 messages::internalError(sensorsAsyncResp->asyncResp->res);
109 return;
110 }
111 sensorsAsyncResp->asyncResp->res.result(
112 boost::beast::http::status::no_content);
George Liu9ae226f2023-06-21 17:56:46 +0800113 });
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700114 });
George Liu0fda0f12021-11-16 10:06:17 +0800115 };
Zhenwei Chen0d7702c2022-07-12 16:42:08 +0000116 redfish::chassis_utils::getValidChassisPath(sensorsAsyncResp->asyncResp,
117 sensorsAsyncResp->chassisId,
118 std::move(getChassisPath));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700119}
120inline void requestRoutesPower(App& app)
121{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700122 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700123 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700124 .methods(boost::beast::http::verb::get)(
125 [&app](const crow::Request& req,
126 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
127 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000128 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700129 {
130 return;
131 }
132 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
133
134 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
135 asyncResp, chassisName, sensors::dbus::powerPaths,
136 sensors::node::power);
137
138 getChassisData(sensorAsyncResp);
139
140 // This callback verifies that the power limit is only provided
141 // for the chassis that implements the Chassis inventory item.
142 // This prevents things like power supplies providing the
143 // chassis power limit
144
145 using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
146 auto chassisHandler =
Ed Tanous8b242752023-06-27 17:17:13 -0700147 [sensorAsyncResp](const boost::system::error_code& ec2,
Ed Tanous002d39b2022-05-31 08:59:27 -0700148 const Mapper& chassisPaths) {
Ed Tanous8b242752023-06-27 17:17:13 -0700149 if (ec2)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700150 {
Ed Tanous62598e32023-07-17 17:06:25 -0700151 BMCWEB_LOG_ERROR(
152 "Power Limit GetSubTreePaths handler Dbus error {}", ec2);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700153 return;
154 }
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800155
Ed Tanous002d39b2022-05-31 08:59:27 -0700156 bool found = false;
157 for (const std::string& chassis : chassisPaths)
158 {
159 size_t len = std::string::npos;
160 size_t lastPos = chassis.rfind('/');
161 if (lastPos == std::string::npos)
Ed Tanous168e20c2021-12-13 14:39:53 -0800162 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700163 continue;
Ed Tanous168e20c2021-12-13 14:39:53 -0800164 }
165
Ed Tanous002d39b2022-05-31 08:59:27 -0700166 if (lastPos == chassis.size() - 1)
Ed Tanous168e20c2021-12-13 14:39:53 -0800167 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700168 size_t end = lastPos;
169 lastPos = chassis.rfind('/', lastPos - 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800170 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000171 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800172 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000173 }
174
Ed Tanous002d39b2022-05-31 08:59:27 -0700175 len = end - (lastPos + 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800176 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700177
Patrick Williams89492a12023-05-10 07:51:34 -0500178 std::string interfaceChassisName = chassis.substr(lastPos + 1,
179 len);
Ed Tanous002d39b2022-05-31 08:59:27 -0700180 if (interfaceChassisName == sensorAsyncResp->chassisId)
Ed Tanous168e20c2021-12-13 14:39:53 -0800181 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 found = true;
183 break;
184 }
185 }
186
187 if (!found)
188 {
Ed Tanous62598e32023-07-17 17:06:25 -0700189 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
190 sensorAsyncResp->chassisId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700191 return;
192 }
193
194 auto valueHandler =
195 [sensorAsyncResp](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800196 const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700197 const dbus::utility::DBusPropertiesMap& properties) {
198 if (ec)
199 {
200 messages::internalError(sensorAsyncResp->asyncResp->res);
Ed Tanous62598e32023-07-17 17:06:25 -0700201 BMCWEB_LOG_ERROR(
202 "Power Limit GetAll handler: Dbus error {}", ec);
Ed Tanous168e20c2021-12-13 14:39:53 -0800203 return;
204 }
205
Ed Tanous002d39b2022-05-31 08:59:27 -0700206 nlohmann::json& tempArray =
207 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
208
209 // Put multiple "sensors" into a single PowerControl, 0,
210 // so only create the first one
211 if (tempArray.empty())
212 {
213 // Mandatory properties odata.id and MemberId
214 // A warning without a odata.type
215 nlohmann::json::object_t powerControl;
216 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
217 powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
218 sensorAsyncResp->chassisId +
219 "/Power#/PowerControl/0";
220 powerControl["Name"] = "Chassis Power Control";
221 powerControl["MemberId"] = "0";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500222 tempArray.emplace_back(std::move(powerControl));
Ed Tanous002d39b2022-05-31 08:59:27 -0700223 }
224
225 nlohmann::json& sensorJson = tempArray.back();
226 bool enabled = false;
227 double powerCap = 0.0;
228 int64_t scale = 0;
229
230 for (const std::pair<std::string,
231 dbus::utility::DbusVariantType>& property :
232 properties)
233 {
234 if (property.first == "Scale")
235 {
236 const int64_t* i =
237 std::get_if<int64_t>(&property.second);
238
239 if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700240 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700241 scale = *i;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700242 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 }
244 else if (property.first == "PowerCap")
245 {
246 const double* d = std::get_if<double>(&property.second);
247 const int64_t* i =
248 std::get_if<int64_t>(&property.second);
249 const uint32_t* u =
250 std::get_if<uint32_t>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700251
Ed Tanous002d39b2022-05-31 08:59:27 -0700252 if (d != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700253 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700254 powerCap = *d;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700255 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700256 else if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700257 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700258 powerCap = static_cast<double>(*i);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700259 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700260 else if (u != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700261 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700262 powerCap = *u;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700263 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700264 }
265 else if (property.first == "PowerCapEnable")
266 {
267 const bool* b = std::get_if<bool>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700268
Ed Tanous002d39b2022-05-31 08:59:27 -0700269 if (b != nullptr)
270 {
271 enabled = *b;
272 }
273 }
274 }
275
Ed Tanous002d39b2022-05-31 08:59:27 -0700276 // LimitException is Mandatory attribute as per OCP
277 // Baseline Profile – v1.0.0, so currently making it
278 // "NoAction" as default value to make it OCP Compliant.
279 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
280
281 if (enabled)
282 {
283 // Redfish specification indicates PowerLimit should
284 // be null if the limit is not enabled.
George Liud4413c52023-01-28 09:27:19 +0800285 sensorJson["PowerLimit"]["LimitInWatts"] =
286 powerCap * std::pow(10, scale);
Ed Tanous002d39b2022-05-31 08:59:27 -0700287 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800288 };
289
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200290 sdbusplus::asio::getAllProperties(
291 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
Ed Tanous002d39b2022-05-31 08:59:27 -0700292 "/xyz/openbmc_project/control/host0/power_cap",
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200293 "xyz.openbmc_project.Control.Power.Cap",
294 std::move(valueHandler));
Ed Tanous002d39b2022-05-31 08:59:27 -0700295 };
296
George Liu7a1dbc42022-12-07 16:03:22 +0800297 constexpr std::array<std::string_view, 2> interfaces = {
298 "xyz.openbmc_project.Inventory.Item.Board",
299 "xyz.openbmc_project.Inventory.Item.Chassis"};
300
301 dbus::utility::getSubTreePaths("/xyz/openbmc_project/inventory", 0,
302 interfaces, std::move(chassisHandler));
Ed Tanous168e20c2021-12-13 14:39:53 -0800303 });
Eddie James028f7eb2019-05-17 21:24:36 +0000304
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700305 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700306 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700307 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700308 [&app](const crow::Request& req,
309 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
310 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000311 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700312 {
313 return;
314 }
315 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
316 asyncResp, chassisName, sensors::dbus::powerPaths,
317 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800318
Ed Tanous002d39b2022-05-31 08:59:27 -0700319 std::optional<std::vector<nlohmann::json>> voltageCollections;
320 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
zhanghch058d1b46d2021-04-01 11:18:24 +0800321
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
323 "PowerControl", powerCtlCollections,
324 "Voltages", voltageCollections))
325 {
326 return;
327 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800328
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 if (powerCtlCollections)
330 {
331 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
332 }
333 if (voltageCollections)
334 {
335 std::unordered_map<std::string, std::vector<nlohmann::json>>
336 allCollections;
337 allCollections.emplace("Voltages", *std::move(voltageCollections));
338 setSensorsOverride(sensorAsyncResp, allCollections);
339 }
340 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700341}
Ed Tanous2474adf2018-09-05 16:31:16 -0700342
343} // namespace redfish