blob: 7dbf6603bbba123e6893f369a64c868a9b0a7574 [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 {
42 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
43 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
44 "Chassis", sensorsAsyncResp->chassisId);
45 return;
46 }
47
48 if (powerControlCollections.size() != 1)
49 {
50 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present ";
51 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{
Ed Tanous2474adf2018-09-05 16:31:16 -0700124
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700125 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700126 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700127 .methods(boost::beast::http::verb::get)(
128 [&app](const crow::Request& req,
129 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
130 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000131 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700132 {
133 return;
134 }
135 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
136
137 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
138 asyncResp, chassisName, sensors::dbus::powerPaths,
139 sensors::node::power);
140
141 getChassisData(sensorAsyncResp);
142
143 // This callback verifies that the power limit is only provided
144 // for the chassis that implements the Chassis inventory item.
145 // This prevents things like power supplies providing the
146 // chassis power limit
147
148 using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
149 auto chassisHandler =
George Liu7a1dbc42022-12-07 16:03:22 +0800150 [sensorAsyncResp](const boost::system::error_code& e,
Ed Tanous002d39b2022-05-31 08:59:27 -0700151 const Mapper& chassisPaths) {
152 if (e)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700153 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700154 BMCWEB_LOG_ERROR
155 << "Power Limit GetSubTreePaths handler Dbus error " << e;
Ed Tanous45ca1b82022-03-25 13:07:27 -0700156 return;
157 }
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800158
Ed Tanous002d39b2022-05-31 08:59:27 -0700159 bool found = false;
160 for (const std::string& chassis : chassisPaths)
161 {
162 size_t len = std::string::npos;
163 size_t lastPos = chassis.rfind('/');
164 if (lastPos == std::string::npos)
Ed Tanous168e20c2021-12-13 14:39:53 -0800165 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700166 continue;
Ed Tanous168e20c2021-12-13 14:39:53 -0800167 }
168
Ed Tanous002d39b2022-05-31 08:59:27 -0700169 if (lastPos == chassis.size() - 1)
Ed Tanous168e20c2021-12-13 14:39:53 -0800170 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700171 size_t end = lastPos;
172 lastPos = chassis.rfind('/', lastPos - 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800173 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000174 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800175 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000176 }
177
Ed Tanous002d39b2022-05-31 08:59:27 -0700178 len = end - (lastPos + 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800179 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700180
Ed Tanous002d39b2022-05-31 08:59:27 -0700181 std::string interfaceChassisName =
182 chassis.substr(lastPos + 1, len);
183 if (interfaceChassisName == sensorAsyncResp->chassisId)
Ed Tanous168e20c2021-12-13 14:39:53 -0800184 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700185 found = true;
186 break;
187 }
188 }
189
190 if (!found)
191 {
192 BMCWEB_LOG_DEBUG << "Power Limit not present for "
193 << sensorAsyncResp->chassisId;
194 return;
195 }
196
197 auto valueHandler =
198 [sensorAsyncResp](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800199 const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700200 const dbus::utility::DBusPropertiesMap& properties) {
201 if (ec)
202 {
203 messages::internalError(sensorAsyncResp->asyncResp->res);
204 BMCWEB_LOG_ERROR
205 << "Power Limit GetAll handler: Dbus error " << ec;
Ed Tanous168e20c2021-12-13 14:39:53 -0800206 return;
207 }
208
Ed Tanous002d39b2022-05-31 08:59:27 -0700209 nlohmann::json& tempArray =
210 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
211
212 // Put multiple "sensors" into a single PowerControl, 0,
213 // so only create the first one
214 if (tempArray.empty())
215 {
216 // Mandatory properties odata.id and MemberId
217 // A warning without a odata.type
218 nlohmann::json::object_t powerControl;
219 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
220 powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
221 sensorAsyncResp->chassisId +
222 "/Power#/PowerControl/0";
223 powerControl["Name"] = "Chassis Power Control";
224 powerControl["MemberId"] = "0";
225 tempArray.push_back(std::move(powerControl));
226 }
227
228 nlohmann::json& sensorJson = tempArray.back();
229 bool enabled = false;
230 double powerCap = 0.0;
231 int64_t scale = 0;
232
233 for (const std::pair<std::string,
234 dbus::utility::DbusVariantType>& property :
235 properties)
236 {
237 if (property.first == "Scale")
238 {
239 const int64_t* i =
240 std::get_if<int64_t>(&property.second);
241
242 if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700243 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700244 scale = *i;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700245 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700246 }
247 else if (property.first == "PowerCap")
248 {
249 const double* d = std::get_if<double>(&property.second);
250 const int64_t* i =
251 std::get_if<int64_t>(&property.second);
252 const uint32_t* u =
253 std::get_if<uint32_t>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700254
Ed Tanous002d39b2022-05-31 08:59:27 -0700255 if (d != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700256 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700257 powerCap = *d;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700258 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700259 else if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700260 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700261 powerCap = static_cast<double>(*i);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700262 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700263 else if (u != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700264 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700265 powerCap = *u;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700266 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700267 }
268 else if (property.first == "PowerCapEnable")
269 {
270 const bool* b = std::get_if<bool>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700271
Ed Tanous002d39b2022-05-31 08:59:27 -0700272 if (b != nullptr)
273 {
274 enabled = *b;
275 }
276 }
277 }
278
Ed Tanous002d39b2022-05-31 08:59:27 -0700279 // LimitException is Mandatory attribute as per OCP
280 // Baseline Profile – v1.0.0, so currently making it
281 // "NoAction" as default value to make it OCP Compliant.
282 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
283
284 if (enabled)
285 {
286 // Redfish specification indicates PowerLimit should
287 // be null if the limit is not enabled.
George Liud4413c52023-01-28 09:27:19 +0800288 sensorJson["PowerLimit"]["LimitInWatts"] =
289 powerCap * std::pow(10, scale);
Ed Tanous002d39b2022-05-31 08:59:27 -0700290 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800291 };
292
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200293 sdbusplus::asio::getAllProperties(
294 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
Ed Tanous002d39b2022-05-31 08:59:27 -0700295 "/xyz/openbmc_project/control/host0/power_cap",
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200296 "xyz.openbmc_project.Control.Power.Cap",
297 std::move(valueHandler));
Ed Tanous002d39b2022-05-31 08:59:27 -0700298 };
299
George Liu7a1dbc42022-12-07 16:03:22 +0800300 constexpr std::array<std::string_view, 2> interfaces = {
301 "xyz.openbmc_project.Inventory.Item.Board",
302 "xyz.openbmc_project.Inventory.Item.Chassis"};
303
304 dbus::utility::getSubTreePaths("/xyz/openbmc_project/inventory", 0,
305 interfaces, std::move(chassisHandler));
Ed Tanous168e20c2021-12-13 14:39:53 -0800306 });
Eddie James028f7eb2019-05-17 21:24:36 +0000307
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700308 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700309 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700310 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700311 [&app](const crow::Request& req,
312 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
313 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000314 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700315 {
316 return;
317 }
318 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
319 asyncResp, chassisName, sensors::dbus::powerPaths,
320 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800321
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 std::optional<std::vector<nlohmann::json>> voltageCollections;
323 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
zhanghch058d1b46d2021-04-01 11:18:24 +0800324
Ed Tanous002d39b2022-05-31 08:59:27 -0700325 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
326 "PowerControl", powerCtlCollections,
327 "Voltages", voltageCollections))
328 {
329 return;
330 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800331
Ed Tanous002d39b2022-05-31 08:59:27 -0700332 if (powerCtlCollections)
333 {
334 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
335 }
336 if (voltageCollections)
337 {
338 std::unordered_map<std::string, std::vector<nlohmann::json>>
339 allCollections;
340 allCollections.emplace("Voltages", *std::move(voltageCollections));
341 setSensorsOverride(sensorAsyncResp, allCollections);
342 }
343 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700344}
Ed Tanous2474adf2018-09-05 16:31:16 -0700345
346} // namespace redfish