blob: c1f41b80304c8bcaa8ccf0e91f85eb4a65ded6a5 [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:
28 Power(CrowApp& app) :
29 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:
Ed Tanous85e14242019-06-27 15:04:09 -070041 std::vector<const char*> typeList = {"/xyz/openbmc_project/sensors/voltage",
42 "/xyz/openbmc_project/sensors/power"};
Carol Wang4bb3dc32019-10-17 18:15:02 +080043 void setPowerCapOverride(
44 std::shared_ptr<SensorsAsyncResp> asyncResp,
45 std::vector<nlohmann::json>& powerControlCollections)
46 {
47 auto getChassisPath =
48 [asyncResp, powerControlCollections](
49 const std::optional<std::string>& chassisPath) mutable {
50 if (!chassisPath)
51 {
52 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
53 messages::resourceNotFound(asyncResp->res, "Chassis",
54 asyncResp->chassisId);
55 return;
56 }
57
58 if (powerControlCollections.size() != 1)
59 {
60 BMCWEB_LOG_ERROR
61 << "Don't support multiple hosts at present ";
62 messages::resourceNotFound(asyncResp->res, "Power",
63 "PowerControl");
64 return;
65 }
66
67 auto& item = powerControlCollections[0];
68
69 std::optional<nlohmann::json> powerLimit;
70 if (!json_util::readJson(item, asyncResp->res, "PowerLimit",
71 powerLimit))
72 {
73 return;
74 }
75 if (!powerLimit)
76 {
77 return;
78 }
79 std::optional<uint32_t> value;
80 if (!json_util::readJson(*powerLimit, asyncResp->res,
81 "LimitInWatts", value))
82 {
83 return;
84 }
85 if (!value)
86 {
87 return;
88 }
89 auto valueHandler = [value, asyncResp](
90 const boost::system::error_code ec,
91 const SensorVariant& powerCapEnable) {
92 if (ec)
93 {
94 messages::internalError(asyncResp->res);
95 BMCWEB_LOG_ERROR
96 << "powerCapEnable Get handler: Dbus error " << ec;
97 return;
98 }
99 // Check PowerCapEnable
100 const bool* b =
101 sdbusplus::message::variant_ns::get_if<bool>(
102 &powerCapEnable);
103 if (b == nullptr)
104 {
105 messages::internalError(asyncResp->res);
106 BMCWEB_LOG_ERROR
107 << "Fail to get PowerCapEnable status ";
108 return;
109 }
110 if (!(*b))
111 {
112 messages::actionNotSupported(
113 asyncResp->res,
114 "Setting LimitInWatts when PowerLimit "
115 "feature is disabled");
116 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
117 return;
118 }
119
120 crow::connections::systemBus->async_method_call(
121 [asyncResp](const boost::system::error_code ec) {
122 if (ec)
123 {
124 BMCWEB_LOG_DEBUG
125 << "Power Limit Set: Dbus error: " << ec;
126 messages::internalError(asyncResp->res);
127 return;
128 }
129 asyncResp->res.result(
130 boost::beast::http::status::no_content);
131 },
132 "xyz.openbmc_project.Settings",
133 "/xyz/openbmc_project/control/host0/power_cap",
134 "org.freedesktop.DBus.Properties", "Set",
135 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
136 sdbusplus::message::variant<uint32_t>(*value));
137 };
138 crow::connections::systemBus->async_method_call(
139 std::move(valueHandler), "xyz.openbmc_project.Settings",
140 "/xyz/openbmc_project/control/host0/power_cap",
141 "org.freedesktop.DBus.Properties", "Get",
142 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
143 };
144 getValidChassisPath(asyncResp, std::move(getChassisPath));
145 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700146 void doGet(crow::Response& res, const crow::Request& req,
147 const std::vector<std::string>& params) override
148 {
149 if (params.size() != 1)
150 {
151 res.result(boost::beast::http::status::internal_server_error);
152 res.end();
153 return;
154 }
155 const std::string& chassis_name = params[0];
156
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800157 res.jsonValue["PowerControl"] = nlohmann::json::array();
158
Ed Tanous2474adf2018-09-05 16:31:16 -0700159 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530160 res, chassis_name, typeList, "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;
182 size_t lastPos = chassis.rfind("/");
183 if (lastPos == std::string::npos)
184 {
185 continue;
186 }
187
188 if (lastPos == chassis.size() - 1)
189 {
190 size_t end = lastPos;
191 lastPos = chassis.rfind("/", lastPos - 1);
192 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 {
223 messages::internalError(sensorAsyncResp->res);
224 BMCWEB_LOG_ERROR
225 << "Power Limit GetAll handler: Dbus error " << ec;
226 return;
227 }
228
229 nlohmann::json& tempArray =
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500230 sensorAsyncResp->res.jsonValue["PowerControl"];
Eddie James028f7eb2019-05-17 21:24:36 +0000231
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500232 // Put multiple "sensors" into a single PowerControl, 0, so
233 // only create the first one
Eddie James028f7eb2019-05-17 21:24:36 +0000234 if (tempArray.empty())
235 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500236 // Mandatory properties odata.id and MemberId
237 // A warning without a odata.type
238 tempArray.push_back(
239 {{"@odata.type", "#Power.v1_0_0.PowerControl"},
240 {"@odata.id", "/redfish/v1/Chassis/" +
241 sensorAsyncResp->chassisId +
242 "/Power#/PowerControl/0"},
243 {"Name", "Chassis Power Control"},
244 {"MemberId", "0"}});
Eddie James028f7eb2019-05-17 21:24:36 +0000245 }
246
247 nlohmann::json& sensorJson = tempArray.back();
248 bool enabled = false;
249 double powerCap = 0.0;
250 int64_t scale = 0;
251
252 for (const std::pair<std::string, SensorVariant>& property :
253 properties)
254 {
255 if (!property.first.compare("Scale"))
256 {
257 const int64_t* i =
258 sdbusplus::message::variant_ns::get_if<int64_t>(
259 &property.second);
260
261 if (i)
262 {
263 scale = *i;
264 }
265 }
266 else if (!property.first.compare("PowerCap"))
267 {
268 const double* d =
269 sdbusplus::message::variant_ns::get_if<double>(
270 &property.second);
271 const int64_t* i =
272 sdbusplus::message::variant_ns::get_if<int64_t>(
273 &property.second);
274 const uint32_t* u =
275 sdbusplus::message::variant_ns::get_if<
276 uint32_t>(&property.second);
277
278 if (d)
279 {
280 powerCap = *d;
281 }
282 else if (i)
283 {
Ed Tanous271584a2019-07-09 16:24:22 -0700284 powerCap = static_cast<double>(*i);
Eddie James028f7eb2019-05-17 21:24:36 +0000285 }
286 else if (u)
287 {
288 powerCap = *u;
289 }
290 }
291 else if (!property.first.compare("PowerCapEnable"))
292 {
293 const bool* b =
294 sdbusplus::message::variant_ns::get_if<bool>(
295 &property.second);
296
297 if (b)
298 {
299 enabled = *b;
300 }
301 }
302 }
303
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500304 nlohmann::json& value =
305 sensorJson["PowerLimit"]["LimitInWatts"];
Eddie James028f7eb2019-05-17 21:24:36 +0000306
Joshi-Mansi5a64a6f2020-03-14 02:08:19 +0530307 // LimitException is Mandatory attribute as per OCP Baseline
308 // Profile – v1.0.0, so currently making it "NoAction"
309 // as default value to make it OCP Compliant.
310 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
311
Eddie James028f7eb2019-05-17 21:24:36 +0000312 if (enabled)
313 {
314 // Redfish specification indicates PowerLimit should be
315 // null if the limit is not enabled.
316 value = powerCap * std::pow(10, scale);
317 }
318 };
319
320 crow::connections::systemBus->async_method_call(
321 std::move(valueHandler), "xyz.openbmc_project.Settings",
322 "/xyz/openbmc_project/control/host0/power_cap",
323 "org.freedesktop.DBus.Properties", "GetAll",
324 "xyz.openbmc_project.Control.Power.Cap");
325 };
326
327 crow::connections::systemBus->async_method_call(
328 std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
329 "/xyz/openbmc_project/object_mapper",
330 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700331 "/xyz/openbmc_project/inventory", 0,
AppaRao Pulif857e9a2020-03-12 14:28:03 +0530332 std::array<const char*, 2>{
333 "xyz.openbmc_project.Inventory.Item.Board",
Eddie James028f7eb2019-05-17 21:24:36 +0000334 "xyz.openbmc_project.Inventory.Item.Chassis"});
Ed Tanous2474adf2018-09-05 16:31:16 -0700335 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530336 void doPatch(crow::Response& res, const crow::Request& req,
337 const std::vector<std::string>& params) override
338 {
Carol Wang4bb3dc32019-10-17 18:15:02 +0800339 if (params.size() != 1)
340 {
341 messages::internalError(res);
342 res.end();
343 return;
344 }
345
346 const std::string& chassisName = params[0];
347 auto asyncResp = std::make_shared<SensorsAsyncResp>(res, chassisName,
348 typeList, "Power");
349
350 std::optional<std::vector<nlohmann::json>> voltageCollections;
351 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
352
353 if (!json_util::readJson(req, asyncResp->res, "PowerControl",
354 powerCtlCollections, "Voltages",
355 voltageCollections))
356 {
357 return;
358 }
359
360 if (powerCtlCollections)
361 {
362 setPowerCapOverride(asyncResp, *powerCtlCollections);
363 }
364 if (voltageCollections)
365 {
366 std::unordered_map<std::string, std::vector<nlohmann::json>>
367 allCollections;
368 allCollections.emplace("Voltages", *std::move(voltageCollections));
jayaprakash Mutyala397fd612020-02-06 23:33:34 +0000369 checkAndDoSensorsOverride(asyncResp, allCollections);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800370 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530371 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700372};
373
374} // namespace redfish