blob: 1c7a0099b35efbe614018709648bc6a02ff65ccb [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(
Ed Tanousb5a76932020-09-29 16:16:58 -070042 const std::shared_ptr<SensorsAsyncResp>& asyncResp,
Carol Wang4bb3dc32019-10-17 18:15:02 +080043 std::vector<nlohmann::json>& powerControlCollections)
44 {
45 auto getChassisPath =
46 [asyncResp, powerControlCollections](
47 const std::optional<std::string>& chassisPath) mutable {
48 if (!chassisPath)
49 {
50 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
51 messages::resourceNotFound(asyncResp->res, "Chassis",
52 asyncResp->chassisId);
53 return;
54 }
55
56 if (powerControlCollections.size() != 1)
57 {
58 BMCWEB_LOG_ERROR
59 << "Don't support multiple hosts at present ";
60 messages::resourceNotFound(asyncResp->res, "Power",
61 "PowerControl");
62 return;
63 }
64
65 auto& item = powerControlCollections[0];
66
67 std::optional<nlohmann::json> powerLimit;
68 if (!json_util::readJson(item, asyncResp->res, "PowerLimit",
69 powerLimit))
70 {
71 return;
72 }
73 if (!powerLimit)
74 {
75 return;
76 }
77 std::optional<uint32_t> value;
78 if (!json_util::readJson(*powerLimit, asyncResp->res,
79 "LimitInWatts", value))
80 {
81 return;
82 }
83 if (!value)
84 {
85 return;
86 }
87 auto valueHandler = [value, asyncResp](
88 const boost::system::error_code ec,
89 const SensorVariant& powerCapEnable) {
90 if (ec)
91 {
92 messages::internalError(asyncResp->res);
93 BMCWEB_LOG_ERROR
94 << "powerCapEnable Get handler: Dbus error " << ec;
95 return;
96 }
97 // Check PowerCapEnable
Patrick Williams8d78b7a2020-05-13 11:24:20 -050098 const bool* b = std::get_if<bool>(&powerCapEnable);
Carol Wang4bb3dc32019-10-17 18:15:02 +080099 if (b == nullptr)
100 {
101 messages::internalError(asyncResp->res);
102 BMCWEB_LOG_ERROR
103 << "Fail to get PowerCapEnable status ";
104 return;
105 }
106 if (!(*b))
107 {
108 messages::actionNotSupported(
109 asyncResp->res,
110 "Setting LimitInWatts when PowerLimit "
111 "feature is disabled");
112 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
113 return;
114 }
115
116 crow::connections::systemBus->async_method_call(
Ed Tanous23a21a12020-07-25 04:45:05 +0000117 [asyncResp](const boost::system::error_code ec2) {
118 if (ec2)
Carol Wang4bb3dc32019-10-17 18:15:02 +0800119 {
120 BMCWEB_LOG_DEBUG
Ed Tanous23a21a12020-07-25 04:45:05 +0000121 << "Power Limit Set: Dbus error: " << ec2;
Carol Wang4bb3dc32019-10-17 18:15:02 +0800122 messages::internalError(asyncResp->res);
123 return;
124 }
125 asyncResp->res.result(
126 boost::beast::http::status::no_content);
127 },
128 "xyz.openbmc_project.Settings",
129 "/xyz/openbmc_project/control/host0/power_cap",
130 "org.freedesktop.DBus.Properties", "Set",
131 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Patrick Williams19bd78d2020-05-13 17:38:24 -0500132 std::variant<uint32_t>(*value));
Carol Wang4bb3dc32019-10-17 18:15:02 +0800133 };
134 crow::connections::systemBus->async_method_call(
135 std::move(valueHandler), "xyz.openbmc_project.Settings",
136 "/xyz/openbmc_project/control/host0/power_cap",
137 "org.freedesktop.DBus.Properties", "Get",
138 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
139 };
140 getValidChassisPath(asyncResp, std::move(getChassisPath));
141 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000142 void doGet(crow::Response& res, const crow::Request&,
Ed Tanous2474adf2018-09-05 16:31:16 -0700143 const std::vector<std::string>& params) override
144 {
145 if (params.size() != 1)
146 {
147 res.result(boost::beast::http::status::internal_server_error);
148 res.end();
149 return;
150 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700151 const std::string& chassisName = params[0];
Ed Tanous2474adf2018-09-05 16:31:16 -0700152
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800153 res.jsonValue["PowerControl"] = nlohmann::json::array();
154
Ed Tanous2474adf2018-09-05 16:31:16 -0700155 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
Ed Tanous2c70f802020-09-28 14:29:23 -0700156 res, chassisName, sensors::dbus::types.at(sensors::node::power),
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200157 sensors::node::power);
Eddie James028f7eb2019-05-17 21:24:36 +0000158
Ed Tanous2474adf2018-09-05 16:31:16 -0700159 getChassisData(sensorAsyncResp);
Eddie James028f7eb2019-05-17 21:24:36 +0000160
161 // This callback verifies that the power limit is only provided for the
162 // chassis that implements the Chassis inventory item. This prevents
163 // things like power supplies providing the chassis power limit
164 auto chassisHandler = [sensorAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -0700165 const boost::system::error_code e,
Eddie James028f7eb2019-05-17 21:24:36 +0000166 const std::vector<std::string>&
167 chassisPaths) {
Ed Tanous271584a2019-07-09 16:24:22 -0700168 if (e)
Eddie James028f7eb2019-05-17 21:24:36 +0000169 {
170 BMCWEB_LOG_ERROR
Ed Tanous271584a2019-07-09 16:24:22 -0700171 << "Power Limit GetSubTreePaths handler Dbus error " << e;
Eddie James028f7eb2019-05-17 21:24:36 +0000172 return;
173 }
174
175 bool found = false;
176 for (const std::string& chassis : chassisPaths)
177 {
178 size_t len = std::string::npos;
Ed Tanousf23b7292020-10-15 09:41:17 -0700179 size_t lastPos = chassis.rfind('/');
Eddie James028f7eb2019-05-17 21:24:36 +0000180 if (lastPos == std::string::npos)
181 {
182 continue;
183 }
184
185 if (lastPos == chassis.size() - 1)
186 {
187 size_t end = lastPos;
Ed Tanousf23b7292020-10-15 09:41:17 -0700188 lastPos = chassis.rfind('/', lastPos - 1);
Eddie James028f7eb2019-05-17 21:24:36 +0000189 if (lastPos == std::string::npos)
190 {
191 continue;
192 }
193
194 len = end - (lastPos + 1);
195 }
196
197 std::string interfaceChassisName =
198 chassis.substr(lastPos + 1, len);
199 if (!interfaceChassisName.compare(sensorAsyncResp->chassisId))
200 {
201 found = true;
202 break;
203 }
204 }
205
206 if (!found)
207 {
208 BMCWEB_LOG_DEBUG << "Power Limit not present for "
209 << sensorAsyncResp->chassisId;
210 return;
211 }
212
213 auto valueHandler =
214 [sensorAsyncResp](
215 const boost::system::error_code ec,
216 const std::vector<std::pair<std::string, SensorVariant>>&
217 properties) {
218 if (ec)
219 {
220 messages::internalError(sensorAsyncResp->res);
221 BMCWEB_LOG_ERROR
222 << "Power Limit GetAll handler: Dbus error " << ec;
223 return;
224 }
225
226 nlohmann::json& tempArray =
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500227 sensorAsyncResp->res.jsonValue["PowerControl"];
Eddie James028f7eb2019-05-17 21:24:36 +0000228
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500229 // Put multiple "sensors" into a single PowerControl, 0, so
230 // only create the first one
Eddie James028f7eb2019-05-17 21:24:36 +0000231 if (tempArray.empty())
232 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500233 // Mandatory properties odata.id and MemberId
234 // A warning without a odata.type
235 tempArray.push_back(
236 {{"@odata.type", "#Power.v1_0_0.PowerControl"},
237 {"@odata.id", "/redfish/v1/Chassis/" +
238 sensorAsyncResp->chassisId +
239 "/Power#/PowerControl/0"},
240 {"Name", "Chassis Power Control"},
241 {"MemberId", "0"}});
Eddie James028f7eb2019-05-17 21:24:36 +0000242 }
243
244 nlohmann::json& sensorJson = tempArray.back();
245 bool enabled = false;
246 double powerCap = 0.0;
247 int64_t scale = 0;
248
249 for (const std::pair<std::string, SensorVariant>& property :
250 properties)
251 {
252 if (!property.first.compare("Scale"))
253 {
254 const int64_t* i =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500255 std::get_if<int64_t>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000256
257 if (i)
258 {
259 scale = *i;
260 }
261 }
262 else if (!property.first.compare("PowerCap"))
263 {
264 const double* d =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500265 std::get_if<double>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000266 const int64_t* i =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500267 std::get_if<int64_t>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000268 const uint32_t* u =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500269 std::get_if<uint32_t>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000270
271 if (d)
272 {
273 powerCap = *d;
274 }
275 else if (i)
276 {
Ed Tanous271584a2019-07-09 16:24:22 -0700277 powerCap = static_cast<double>(*i);
Eddie James028f7eb2019-05-17 21:24:36 +0000278 }
279 else if (u)
280 {
281 powerCap = *u;
282 }
283 }
284 else if (!property.first.compare("PowerCapEnable"))
285 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500286 const bool* b = std::get_if<bool>(&property.second);
Eddie James028f7eb2019-05-17 21:24:36 +0000287
288 if (b)
289 {
290 enabled = *b;
291 }
292 }
293 }
294
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500295 nlohmann::json& value =
296 sensorJson["PowerLimit"]["LimitInWatts"];
Eddie James028f7eb2019-05-17 21:24:36 +0000297
Joshi-Mansi5a64a6f2020-03-14 02:08:19 +0530298 // LimitException is Mandatory attribute as per OCP Baseline
299 // Profile – v1.0.0, so currently making it "NoAction"
300 // as default value to make it OCP Compliant.
301 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
302
Eddie James028f7eb2019-05-17 21:24:36 +0000303 if (enabled)
304 {
305 // Redfish specification indicates PowerLimit should be
306 // null if the limit is not enabled.
307 value = powerCap * std::pow(10, scale);
308 }
309 };
310
311 crow::connections::systemBus->async_method_call(
312 std::move(valueHandler), "xyz.openbmc_project.Settings",
313 "/xyz/openbmc_project/control/host0/power_cap",
314 "org.freedesktop.DBus.Properties", "GetAll",
315 "xyz.openbmc_project.Control.Power.Cap");
316 };
317
318 crow::connections::systemBus->async_method_call(
319 std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
320 "/xyz/openbmc_project/object_mapper",
321 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700322 "/xyz/openbmc_project/inventory", 0,
AppaRao Pulif857e9a2020-03-12 14:28:03 +0530323 std::array<const char*, 2>{
324 "xyz.openbmc_project.Inventory.Item.Board",
Eddie James028f7eb2019-05-17 21:24:36 +0000325 "xyz.openbmc_project.Inventory.Item.Chassis"});
Ed Tanous2474adf2018-09-05 16:31:16 -0700326 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530327 void doPatch(crow::Response& res, const crow::Request& req,
328 const std::vector<std::string>& params) override
329 {
Carol Wang4bb3dc32019-10-17 18:15:02 +0800330 if (params.size() != 1)
331 {
332 messages::internalError(res);
333 res.end();
334 return;
335 }
336
337 const std::string& chassisName = params[0];
Adrian Ambrożewicza0ec28b2020-04-10 14:47:28 +0200338 auto asyncResp = std::make_shared<SensorsAsyncResp>(
339 res, chassisName, sensors::dbus::types.at(sensors::node::power),
340 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800341
342 std::optional<std::vector<nlohmann::json>> voltageCollections;
343 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
344
345 if (!json_util::readJson(req, asyncResp->res, "PowerControl",
346 powerCtlCollections, "Voltages",
347 voltageCollections))
348 {
349 return;
350 }
351
352 if (powerCtlCollections)
353 {
354 setPowerCapOverride(asyncResp, *powerCtlCollections);
355 }
356 if (voltageCollections)
357 {
358 std::unordered_map<std::string, std::vector<nlohmann::json>>
359 allCollections;
360 allCollections.emplace("Voltages", *std::move(voltageCollections));
jayaprakash Mutyala397fd612020-02-06 23:33:34 +0000361 checkAndDoSensorsOverride(asyncResp, allCollections);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800362 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530363 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700364};
365
366} // namespace redfish