blob: a0eadf56f971f9bebde09cfc638d9d4960393d43 [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 {
Gunnar Millsa7405d52023-02-22 13:23:23 -060042 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 {
Gunnar Millsa7405d52023-02-22 13:23:23 -060050 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);
87 BMCWEB_LOG_ERROR << "powerCapEnable Get handler: Dbus error "
88 << ec;
89 return;
90 }
91 if (!powerCapEnable)
92 {
93 messages::actionNotSupported(
94 sensorsAsyncResp->asyncResp->res,
95 "Setting LimitInWatts when PowerLimit feature is disabled");
96 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
97 return;
98 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070099
Ed Tanous002d39b2022-05-31 08:59:27 -0700100 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800101 [sensorsAsyncResp](const boost::system::error_code& ec2) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700102 if (ec2)
103 {
104 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: " << ec2;
105 messages::internalError(sensorsAsyncResp->asyncResp->res);
106 return;
107 }
108 sensorsAsyncResp->asyncResp->res.result(
109 boost::beast::http::status::no_content);
110 },
111 "xyz.openbmc_project.Settings",
112 "/xyz/openbmc_project/control/host0/power_cap",
113 "org.freedesktop.DBus.Properties", "Set",
114 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
115 std::variant<uint32_t>(*value));
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700116 });
George Liu0fda0f12021-11-16 10:06:17 +0800117 };
Zhenwei Chen0d7702c2022-07-12 16:42:08 +0000118 redfish::chassis_utils::getValidChassisPath(sensorsAsyncResp->asyncResp,
119 sensorsAsyncResp->chassisId,
120 std::move(getChassisPath));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700121}
122inline void requestRoutesPower(App& app)
123{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700124 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700125 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700126 .methods(boost::beast::http::verb::get)(
127 [&app](const crow::Request& req,
128 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
129 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000130 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700131 {
132 return;
133 }
134 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
135
136 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
137 asyncResp, chassisName, sensors::dbus::powerPaths,
138 sensors::node::power);
139
140 getChassisData(sensorAsyncResp);
141
142 // This callback verifies that the power limit is only provided
143 // for the chassis that implements the Chassis inventory item.
144 // This prevents things like power supplies providing the
145 // chassis power limit
146
147 using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
148 auto chassisHandler =
George Liu7a1dbc42022-12-07 16:03:22 +0800149 [sensorAsyncResp](const boost::system::error_code& e,
Ed Tanous002d39b2022-05-31 08:59:27 -0700150 const Mapper& chassisPaths) {
151 if (e)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700152 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700153 BMCWEB_LOG_ERROR
154 << "Power Limit GetSubTreePaths handler Dbus error " << e;
Ed Tanous45ca1b82022-03-25 13:07:27 -0700155 return;
156 }
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800157
Ed Tanous002d39b2022-05-31 08:59:27 -0700158 bool found = false;
159 for (const std::string& chassis : chassisPaths)
160 {
161 size_t len = std::string::npos;
162 size_t lastPos = chassis.rfind('/');
163 if (lastPos == std::string::npos)
Ed Tanous168e20c2021-12-13 14:39:53 -0800164 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700165 continue;
Ed Tanous168e20c2021-12-13 14:39:53 -0800166 }
167
Ed Tanous002d39b2022-05-31 08:59:27 -0700168 if (lastPos == chassis.size() - 1)
Ed Tanous168e20c2021-12-13 14:39:53 -0800169 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700170 size_t end = lastPos;
171 lastPos = chassis.rfind('/', lastPos - 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800172 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000173 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800174 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000175 }
176
Ed Tanous002d39b2022-05-31 08:59:27 -0700177 len = end - (lastPos + 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800178 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700179
Patrick Williams89492a12023-05-10 07:51:34 -0500180 std::string interfaceChassisName = chassis.substr(lastPos + 1,
181 len);
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 if (interfaceChassisName == sensorAsyncResp->chassisId)
Ed Tanous168e20c2021-12-13 14:39:53 -0800183 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700184 found = true;
185 break;
186 }
187 }
188
189 if (!found)
190 {
191 BMCWEB_LOG_DEBUG << "Power Limit not present for "
192 << sensorAsyncResp->chassisId;
193 return;
194 }
195
196 auto valueHandler =
197 [sensorAsyncResp](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800198 const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700199 const dbus::utility::DBusPropertiesMap& properties) {
200 if (ec)
201 {
202 messages::internalError(sensorAsyncResp->asyncResp->res);
203 BMCWEB_LOG_ERROR
204 << "Power Limit GetAll handler: Dbus error " << ec;
Ed Tanous168e20c2021-12-13 14:39:53 -0800205 return;
206 }
207
Ed Tanous002d39b2022-05-31 08:59:27 -0700208 nlohmann::json& tempArray =
209 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
210
211 // Put multiple "sensors" into a single PowerControl, 0,
212 // so only create the first one
213 if (tempArray.empty())
214 {
215 // Mandatory properties odata.id and MemberId
216 // A warning without a odata.type
217 nlohmann::json::object_t powerControl;
218 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
219 powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
220 sensorAsyncResp->chassisId +
221 "/Power#/PowerControl/0";
222 powerControl["Name"] = "Chassis Power Control";
223 powerControl["MemberId"] = "0";
224 tempArray.push_back(std::move(powerControl));
225 }
226
227 nlohmann::json& sensorJson = tempArray.back();
228 bool enabled = false;
229 double powerCap = 0.0;
230 int64_t scale = 0;
231
232 for (const std::pair<std::string,
233 dbus::utility::DbusVariantType>& property :
234 properties)
235 {
236 if (property.first == "Scale")
237 {
238 const int64_t* i =
239 std::get_if<int64_t>(&property.second);
240
241 if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700242 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 scale = *i;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700244 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700245 }
246 else if (property.first == "PowerCap")
247 {
248 const double* d = std::get_if<double>(&property.second);
249 const int64_t* i =
250 std::get_if<int64_t>(&property.second);
251 const uint32_t* u =
252 std::get_if<uint32_t>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700253
Ed Tanous002d39b2022-05-31 08:59:27 -0700254 if (d != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700255 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700256 powerCap = *d;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700257 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700258 else if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700259 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700260 powerCap = static_cast<double>(*i);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700261 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700262 else if (u != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700263 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700264 powerCap = *u;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700265 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700266 }
267 else if (property.first == "PowerCapEnable")
268 {
269 const bool* b = std::get_if<bool>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700270
Ed Tanous002d39b2022-05-31 08:59:27 -0700271 if (b != nullptr)
272 {
273 enabled = *b;
274 }
275 }
276 }
277
Ed Tanous002d39b2022-05-31 08:59:27 -0700278 // LimitException is Mandatory attribute as per OCP
279 // Baseline Profile – v1.0.0, so currently making it
280 // "NoAction" as default value to make it OCP Compliant.
281 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
282
283 if (enabled)
284 {
285 // Redfish specification indicates PowerLimit should
286 // be null if the limit is not enabled.
George Liud4413c52023-01-28 09:27:19 +0800287 sensorJson["PowerLimit"]["LimitInWatts"] =
288 powerCap * std::pow(10, scale);
Ed Tanous002d39b2022-05-31 08:59:27 -0700289 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800290 };
291
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200292 sdbusplus::asio::getAllProperties(
293 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
Ed Tanous002d39b2022-05-31 08:59:27 -0700294 "/xyz/openbmc_project/control/host0/power_cap",
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200295 "xyz.openbmc_project.Control.Power.Cap",
296 std::move(valueHandler));
Ed Tanous002d39b2022-05-31 08:59:27 -0700297 };
298
George Liu7a1dbc42022-12-07 16:03:22 +0800299 constexpr std::array<std::string_view, 2> interfaces = {
300 "xyz.openbmc_project.Inventory.Item.Board",
301 "xyz.openbmc_project.Inventory.Item.Chassis"};
302
303 dbus::utility::getSubTreePaths("/xyz/openbmc_project/inventory", 0,
304 interfaces, std::move(chassisHandler));
Ed Tanous168e20c2021-12-13 14:39:53 -0800305 });
Eddie James028f7eb2019-05-17 21:24:36 +0000306
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700307 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700308 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700309 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700310 [&app](const crow::Request& req,
311 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
312 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000313 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700314 {
315 return;
316 }
317 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
318 asyncResp, chassisName, sensors::dbus::powerPaths,
319 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800320
Ed Tanous002d39b2022-05-31 08:59:27 -0700321 std::optional<std::vector<nlohmann::json>> voltageCollections;
322 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
zhanghch058d1b46d2021-04-01 11:18:24 +0800323
Ed Tanous002d39b2022-05-31 08:59:27 -0700324 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
325 "PowerControl", powerCtlCollections,
326 "Voltages", voltageCollections))
327 {
328 return;
329 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800330
Ed Tanous002d39b2022-05-31 08:59:27 -0700331 if (powerCtlCollections)
332 {
333 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
334 }
335 if (voltageCollections)
336 {
337 std::unordered_map<std::string, std::vector<nlohmann::json>>
338 allCollections;
339 allCollections.emplace("Voltages", *std::move(voltageCollections));
340 setSensorsOverride(sensorAsyncResp, allCollections);
341 }
342 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700343}
Ed Tanous2474adf2018-09-05 16:31:16 -0700344
345} // namespace redfish