blob: 47f0c5d24f9d9c2c8ce3644c863e89fe477aa3c5 [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 Tanous2474adf2018-09-05 16:31:16 -070019#include "sensors.hpp"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000020#include "utils/chassis_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070021
John Edward Broadbent7e860f12021-04-08 15:57:16 -070022#include <app.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080023#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070024#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070025#include <registries/privilege_registry.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020026#include <sdbusplus/asio/property.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070027
Ed Tanous2474adf2018-09-05 16:31:16 -070028namespace redfish
29{
Ed Tanous4f48d5f2021-06-21 08:27:45 -070030inline void setPowerCapOverride(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070031 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
32 std::vector<nlohmann::json>& powerControlCollections)
Ed Tanous2474adf2018-09-05 16:31:16 -070033{
Ed Tanous002d39b2022-05-31 08:59:27 -070034 auto getChassisPath =
35 [sensorsAsyncResp, powerControlCollections](
36 const std::optional<std::string>& chassisPath) mutable {
George Liu0fda0f12021-11-16 10:06:17 +080037 if (!chassisPath)
38 {
39 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
40 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
41 "Chassis", sensorsAsyncResp->chassisId);
42 return;
43 }
44
45 if (powerControlCollections.size() != 1)
46 {
47 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present ";
48 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
49 "Power", "PowerControl");
50 return;
51 }
52
53 auto& item = powerControlCollections[0];
54
55 std::optional<nlohmann::json> powerLimit;
56 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res,
57 "PowerLimit", powerLimit))
58 {
59 return;
60 }
61 if (!powerLimit)
62 {
63 return;
64 }
65 std::optional<uint32_t> value;
66 if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res,
67 "LimitInWatts", value))
68 {
69 return;
70 }
71 if (!value)
72 {
73 return;
74 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070075 sdbusplus::asio::getProperty<bool>(
76 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0fda0f12021-11-16 10:06:17 +080077 "/xyz/openbmc_project/control/host0/power_cap",
Jonathan Doman1e1e5982021-06-11 09:36:17 -070078 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
79 [value, sensorsAsyncResp](const boost::system::error_code ec,
80 bool powerCapEnable) {
Ed Tanous002d39b2022-05-31 08:59:27 -070081 if (ec)
82 {
83 messages::internalError(sensorsAsyncResp->asyncResp->res);
84 BMCWEB_LOG_ERROR << "powerCapEnable Get handler: Dbus error "
85 << ec;
86 return;
87 }
88 if (!powerCapEnable)
89 {
90 messages::actionNotSupported(
91 sensorsAsyncResp->asyncResp->res,
92 "Setting LimitInWatts when PowerLimit feature is disabled");
93 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
94 return;
95 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070096
Ed Tanous002d39b2022-05-31 08:59:27 -070097 crow::connections::systemBus->async_method_call(
98 [sensorsAsyncResp](const boost::system::error_code ec2) {
99 if (ec2)
100 {
101 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: " << ec2;
102 messages::internalError(sensorsAsyncResp->asyncResp->res);
103 return;
104 }
105 sensorsAsyncResp->asyncResp->res.result(
106 boost::beast::http::status::no_content);
107 },
108 "xyz.openbmc_project.Settings",
109 "/xyz/openbmc_project/control/host0/power_cap",
110 "org.freedesktop.DBus.Properties", "Set",
111 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
112 std::variant<uint32_t>(*value));
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700113 });
George Liu0fda0f12021-11-16 10:06:17 +0800114 };
Zhenwei Chen0d7702c2022-07-12 16:42:08 +0000115 redfish::chassis_utils::getValidChassisPath(sensorsAsyncResp->asyncResp,
116 sensorsAsyncResp->chassisId,
117 std::move(getChassisPath));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700118}
119inline void requestRoutesPower(App& app)
120{
Ed Tanous2474adf2018-09-05 16:31:16 -0700121
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 =
147 [sensorAsyncResp](const boost::system::error_code e,
148 const Mapper& chassisPaths) {
149 if (e)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700150 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700151 BMCWEB_LOG_ERROR
152 << "Power Limit GetSubTreePaths handler Dbus error " << e;
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
Ed Tanous002d39b2022-05-31 08:59:27 -0700178 std::string interfaceChassisName =
179 chassis.substr(lastPos + 1, len);
180 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 {
189 BMCWEB_LOG_DEBUG << "Power Limit not present for "
190 << sensorAsyncResp->chassisId;
191 return;
192 }
193
194 auto valueHandler =
195 [sensorAsyncResp](
196 const boost::system::error_code ec,
197 const dbus::utility::DBusPropertiesMap& properties) {
198 if (ec)
199 {
200 messages::internalError(sensorAsyncResp->asyncResp->res);
201 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";
222 tempArray.push_back(std::move(powerControl));
223 }
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
276 nlohmann::json& value =
277 sensorJson["PowerLimit"]["LimitInWatts"];
278
279 // 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.
288 value = powerCap * std::pow(10, scale);
289 }
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
299 crow::connections::systemBus->async_method_call(
300 std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
301 "/xyz/openbmc_project/object_mapper",
302 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
303 "/xyz/openbmc_project/inventory", 0,
304 std::array<const char*, 2>{
305 "xyz.openbmc_project.Inventory.Item.Board",
306 "xyz.openbmc_project.Inventory.Item.Chassis"});
Ed Tanous168e20c2021-12-13 14:39:53 -0800307 });
Eddie James028f7eb2019-05-17 21:24:36 +0000308
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700309 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700310 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700311 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700312 [&app](const crow::Request& req,
313 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
314 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000315 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700316 {
317 return;
318 }
319 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
320 asyncResp, chassisName, sensors::dbus::powerPaths,
321 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800322
Ed Tanous002d39b2022-05-31 08:59:27 -0700323 std::optional<std::vector<nlohmann::json>> voltageCollections;
324 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
zhanghch058d1b46d2021-04-01 11:18:24 +0800325
Ed Tanous002d39b2022-05-31 08:59:27 -0700326 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
327 "PowerControl", powerCtlCollections,
328 "Voltages", voltageCollections))
329 {
330 return;
331 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800332
Ed Tanous002d39b2022-05-31 08:59:27 -0700333 if (powerCtlCollections)
334 {
335 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
336 }
337 if (voltageCollections)
338 {
339 std::unordered_map<std::string, std::vector<nlohmann::json>>
340 allCollections;
341 allCollections.emplace("Voltages", *std::move(voltageCollections));
342 setSensorsOverride(sensorAsyncResp, allCollections);
343 }
344 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700345}
Ed Tanous2474adf2018-09-05 16:31:16 -0700346
347} // namespace redfish