blob: 4173ce8cc5c521810908caeecaa806c73f601c66 [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
19#include "node.hpp"
20#include "sensors.hpp"
21
22namespace redfish
23{
24
25class Power : public Node
26{
27 public:
Ed Tanous52cc1122020-07-18 13:51:21 -070028 Power(App& app) :
Ed Tanous2474adf2018-09-05 16:31:16 -070029 Node((app), "/redfish/v1/Chassis/<str>/Power/", std::string())
30 {
Ed Tanous2474adf2018-09-05 16:31:16 -070031 entityPrivileges = {
32 {boost::beast::http::verb::get, {{"Login"}}},
33 {boost::beast::http::verb::head, {{"Login"}}},
jayaprakash Mutyala1b1b43f2020-03-28 22:56:06 +000034 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
Ed Tanous2474adf2018-09-05 16:31:16 -070035 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
36 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
37 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
38 }
39
40 private:
Carol Wang4bb3dc32019-10-17 18:15:02 +080041 void setPowerCapOverride(
zhanghch058d1b46d2021-04-01 11:18:24 +080042 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Carol Wang4bb3dc32019-10-17 18:15:02 +080043 std::vector<nlohmann::json>& powerControlCollections)
44 {
zhanghch058d1b46d2021-04-01 11:18:24 +080045 auto getChassisPath = [sensorsAsyncResp, powerControlCollections](
46 const std::optional<std::string>&
47 chassisPath) mutable {
48 if (!chassisPath)
49 {
50 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
51 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
52 "Chassis",
53 sensorsAsyncResp->chassisId);
54 return;
55 }
Carol Wang4bb3dc32019-10-17 18:15:02 +080056
zhanghch058d1b46d2021-04-01 11:18:24 +080057 if (powerControlCollections.size() != 1)
58 {
59 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present ";
60 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
61 "Power", "PowerControl");
62 return;
63 }
64
65 auto& item = powerControlCollections[0];
66
67 std::optional<nlohmann::json> powerLimit;
68 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res,
69 "PowerLimit", powerLimit))
70 {
71 return;
72 }
73 if (!powerLimit)
74 {
75 return;
76 }
77 std::optional<uint32_t> value;
78 if (!json_util::readJson(*powerLimit,
79 sensorsAsyncResp->asyncResp->res,
80 "LimitInWatts", value))
81 {
82 return;
83 }
84 if (!value)
85 {
86 return;
87 }
88 auto valueHandler = [value, sensorsAsyncResp](
89 const boost::system::error_code ec,
90 const SensorVariant& powerCapEnable) {
91 if (ec)
Carol Wang4bb3dc32019-10-17 18:15:02 +080092 {
zhanghch058d1b46d2021-04-01 11:18:24 +080093 messages::internalError(sensorsAsyncResp->asyncResp->res);
Carol Wang4bb3dc32019-10-17 18:15:02 +080094 BMCWEB_LOG_ERROR
zhanghch058d1b46d2021-04-01 11:18:24 +080095 << "powerCapEnable Get handler: Dbus error " << ec;
96 return;
97 }
98 // Check PowerCapEnable
99 const bool* b = std::get_if<bool>(&powerCapEnable);
100 if (b == nullptr)
101 {
102 messages::internalError(sensorsAsyncResp->asyncResp->res);
103 BMCWEB_LOG_ERROR << "Fail to get PowerCapEnable status ";
104 return;
105 }
106 if (!(*b))
107 {
108 messages::actionNotSupported(
109 sensorsAsyncResp->asyncResp->res,
110 "Setting LimitInWatts when PowerLimit "
111 "feature is disabled");
112 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
Carol Wang4bb3dc32019-10-17 18:15:02 +0800113 return;
114 }
115
Carol Wang4bb3dc32019-10-17 18:15:02 +0800116 crow::connections::systemBus->async_method_call(
zhanghch058d1b46d2021-04-01 11:18:24 +0800117 [sensorsAsyncResp](const boost::system::error_code ec2) {
118 if (ec2)
119 {
120 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: "
121 << ec2;
122 messages::internalError(
123 sensorsAsyncResp->asyncResp->res);
124 return;
125 }
126 sensorsAsyncResp->asyncResp->res.result(
127 boost::beast::http::status::no_content);
128 },
129 "xyz.openbmc_project.Settings",
Carol Wang4bb3dc32019-10-17 18:15:02 +0800130 "/xyz/openbmc_project/control/host0/power_cap",
zhanghch058d1b46d2021-04-01 11:18:24 +0800131 "org.freedesktop.DBus.Properties", "Set",
132 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
133 std::variant<uint32_t>(*value));
Carol Wang4bb3dc32019-10-17 18:15:02 +0800134 };
zhanghch058d1b46d2021-04-01 11:18:24 +0800135 crow::connections::systemBus->async_method_call(
136 std::move(valueHandler), "xyz.openbmc_project.Settings",
137 "/xyz/openbmc_project/control/host0/power_cap",
138 "org.freedesktop.DBus.Properties", "Get",
139 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
140 };
141 getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath));
Carol Wang4bb3dc32019-10-17 18:15:02 +0800142 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800143 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
144 const crow::Request&,
Ed Tanous2474adf2018-09-05 16:31:16 -0700145 const std::vector<std::string>& params) override
146 {
147 if (params.size() != 1)
148 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800149 asyncResp->res.result(
150 boost::beast::http::status::internal_server_error);
Ed Tanous2474adf2018-09-05 16:31:16 -0700151 return;
152 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700153 const std::string& chassisName = params[0];
Ed Tanous2474adf2018-09-05 16:31:16 -0700154
zhanghch058d1b46d2021-04-01 11:18:24 +0800155 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800156
Ed Tanous2474adf2018-09-05 16:31:16 -0700157 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
zhanghch058d1b46d2021-04-01 11:18:24 +0800158 asyncResp, chassisName,
159 sensors::dbus::paths.at(sensors::node::power),
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200160 sensors::node::power);
Eddie James028f7eb2019-05-17 21:24:36 +0000161
Ed Tanous2474adf2018-09-05 16:31:16 -0700162 getChassisData(sensorAsyncResp);
Eddie James028f7eb2019-05-17 21:24:36 +0000163
164 // This callback verifies that the power limit is only provided for the
165 // chassis that implements the Chassis inventory item. This prevents
166 // things like power supplies providing the chassis power limit
167 auto chassisHandler = [sensorAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -0700168 const boost::system::error_code e,
Eddie James028f7eb2019-05-17 21:24:36 +0000169 const std::vector<std::string>&
170 chassisPaths) {
Ed Tanous271584a2019-07-09 16:24:22 -0700171 if (e)
Eddie James028f7eb2019-05-17 21:24:36 +0000172 {
173 BMCWEB_LOG_ERROR
Ed Tanous271584a2019-07-09 16:24:22 -0700174 << "Power Limit GetSubTreePaths handler Dbus error " << e;
Eddie James028f7eb2019-05-17 21:24:36 +0000175 return;
176 }
177
178 bool found = false;
179 for (const std::string& chassis : chassisPaths)
180 {
181 size_t len = std::string::npos;
Ed Tanousf23b7292020-10-15 09:41:17 -0700182 size_t lastPos = chassis.rfind('/');
Eddie James028f7eb2019-05-17 21:24:36 +0000183 if (lastPos == std::string::npos)
184 {
185 continue;
186 }
187
188 if (lastPos == chassis.size() - 1)
189 {
190 size_t end = lastPos;
Ed Tanousf23b7292020-10-15 09:41:17 -0700191 lastPos = chassis.rfind('/', lastPos - 1);
Eddie James028f7eb2019-05-17 21:24:36 +0000192 if (lastPos == std::string::npos)
193 {
194 continue;
195 }
196
197 len = end - (lastPos + 1);
198 }
199
200 std::string interfaceChassisName =
201 chassis.substr(lastPos + 1, len);
202 if (!interfaceChassisName.compare(sensorAsyncResp->chassisId))
203 {
204 found = true;
205 break;
206 }
207 }
208
209 if (!found)
210 {
211 BMCWEB_LOG_DEBUG << "Power Limit not present for "
212 << sensorAsyncResp->chassisId;
213 return;
214 }
215
216 auto valueHandler =
217 [sensorAsyncResp](
218 const boost::system::error_code ec,
219 const std::vector<std::pair<std::string, SensorVariant>>&
220 properties) {
221 if (ec)
222 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800223 messages::internalError(
224 sensorAsyncResp->asyncResp->res);
Eddie James028f7eb2019-05-17 21:24:36 +0000225 BMCWEB_LOG_ERROR
226 << "Power Limit GetAll handler: Dbus error " << ec;
227 return;
228 }
229
zhanghch058d1b46d2021-04-01 11:18:24 +0800230 nlohmann::json& tempArray = sensorAsyncResp->asyncResp->res
231 .jsonValue["PowerControl"];
Eddie James028f7eb2019-05-17 21:24:36 +0000232
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500233 // Put multiple "sensors" into a single PowerControl, 0, so
234 // only create the first one
Eddie James028f7eb2019-05-17 21:24:36 +0000235 if (tempArray.empty())
236 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500237 // Mandatory properties odata.id and MemberId
238 // A warning without a odata.type
239 tempArray.push_back(
240 {{"@odata.type", "#Power.v1_0_0.PowerControl"},
241 {"@odata.id", "/redfish/v1/Chassis/" +
242 sensorAsyncResp->chassisId +
243 "/Power#/PowerControl/0"},
244 {"Name", "Chassis Power Control"},
245 {"MemberId", "0"}});
Eddie James028f7eb2019-05-17 21:24:36 +0000246 }
247
248 nlohmann::json& sensorJson = tempArray.back();
249 bool enabled = false;
250 double powerCap = 0.0;
251 int64_t scale = 0;
252
253 for (const std::pair<std::string, SensorVariant>& property :
254 properties)
255 {
256 if (!property.first.compare("Scale"))
257 {
258 const int64_t* i =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500259 std::get_if<int64_t>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000260
261 if (i)
262 {
263 scale = *i;
264 }
265 }
266 else if (!property.first.compare("PowerCap"))
267 {
268 const double* d =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500269 std::get_if<double>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000270 const int64_t* i =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500271 std::get_if<int64_t>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000272 const uint32_t* u =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500273 std::get_if<uint32_t>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000274
275 if (d)
276 {
277 powerCap = *d;
278 }
279 else if (i)
280 {
Ed Tanous271584a2019-07-09 16:24:22 -0700281 powerCap = static_cast<double>(*i);
Eddie James028f7eb2019-05-17 21:24:36 +0000282 }
283 else if (u)
284 {
285 powerCap = *u;
286 }
287 }
288 else if (!property.first.compare("PowerCapEnable"))
289 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500290 const bool* b = std::get_if<bool>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000291
292 if (b)
293 {
294 enabled = *b;
295 }
296 }
297 }
298
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500299 nlohmann::json& value =
300 sensorJson["PowerLimit"]["LimitInWatts"];
Eddie James028f7eb2019-05-17 21:24:36 +0000301
Joshi-Mansi5a64a6f2020-03-14 02:08:19 +0530302 // LimitException is Mandatory attribute as per OCP Baseline
303 // Profile – v1.0.0, so currently making it "NoAction"
304 // as default value to make it OCP Compliant.
305 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
306
Eddie James028f7eb2019-05-17 21:24:36 +0000307 if (enabled)
308 {
309 // Redfish specification indicates PowerLimit should be
310 // null if the limit is not enabled.
311 value = powerCap * std::pow(10, scale);
312 }
313 };
314
315 crow::connections::systemBus->async_method_call(
316 std::move(valueHandler), "xyz.openbmc_project.Settings",
317 "/xyz/openbmc_project/control/host0/power_cap",
318 "org.freedesktop.DBus.Properties", "GetAll",
319 "xyz.openbmc_project.Control.Power.Cap");
320 };
321
322 crow::connections::systemBus->async_method_call(
323 std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
324 "/xyz/openbmc_project/object_mapper",
325 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700326 "/xyz/openbmc_project/inventory", 0,
AppaRao Pulif857e9a2020-03-12 14:28:03 +0530327 std::array<const char*, 2>{
328 "xyz.openbmc_project.Inventory.Item.Board",
Eddie James028f7eb2019-05-17 21:24:36 +0000329 "xyz.openbmc_project.Inventory.Item.Chassis"});
Ed Tanous2474adf2018-09-05 16:31:16 -0700330 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800331 void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
332 const crow::Request& req,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530333 const std::vector<std::string>& params) override
334 {
Carol Wang4bb3dc32019-10-17 18:15:02 +0800335 if (params.size() != 1)
336 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800337 messages::internalError(asyncResp->res);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800338 return;
339 }
340
341 const std::string& chassisName = params[0];
zhanghch058d1b46d2021-04-01 11:18:24 +0800342
343 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
344 asyncResp, chassisName,
345 sensors::dbus::paths.at(sensors::node::power),
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200346 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800347
348 std::optional<std::vector<nlohmann::json>> voltageCollections;
349 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
350
zhanghch058d1b46d2021-04-01 11:18:24 +0800351 if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res,
352 "PowerControl", powerCtlCollections,
353 "Voltages", voltageCollections))
Carol Wang4bb3dc32019-10-17 18:15:02 +0800354 {
355 return;
356 }
357
358 if (powerCtlCollections)
359 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800360 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800361 }
362 if (voltageCollections)
363 {
364 std::unordered_map<std::string, std::vector<nlohmann::json>>
365 allCollections;
366 allCollections.emplace("Voltages", *std::move(voltageCollections));
zhanghch058d1b46d2021-04-01 11:18:24 +0800367 checkAndDoSensorsOverride(sensorAsyncResp, allCollections);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800368 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530369 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700370};
371
372} // namespace redfish