blob: dea6be3dc15a7c15579994300f7c8d94649e65ef [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"
20
John Edward Broadbent7e860f12021-04-08 15:57:16 -070021#include <app.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080022#include <dbus_utility.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070024
Ed Tanous2474adf2018-09-05 16:31:16 -070025namespace redfish
26{
Ed Tanous4f48d5f2021-06-21 08:27:45 -070027inline void setPowerCapOverride(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070028 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
29 std::vector<nlohmann::json>& powerControlCollections)
Ed Tanous2474adf2018-09-05 16:31:16 -070030{
George Liu0fda0f12021-11-16 10:06:17 +080031 auto getChassisPath = [sensorsAsyncResp, powerControlCollections](
32 const std::optional<std::string>&
33 chassisPath) mutable {
34 if (!chassisPath)
35 {
36 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
37 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
38 "Chassis", sensorsAsyncResp->chassisId);
39 return;
40 }
41
42 if (powerControlCollections.size() != 1)
43 {
44 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present ";
45 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
46 "Power", "PowerControl");
47 return;
48 }
49
50 auto& item = powerControlCollections[0];
51
52 std::optional<nlohmann::json> powerLimit;
53 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res,
54 "PowerLimit", powerLimit))
55 {
56 return;
57 }
58 if (!powerLimit)
59 {
60 return;
61 }
62 std::optional<uint32_t> value;
63 if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res,
64 "LimitInWatts", value))
65 {
66 return;
67 }
68 if (!value)
69 {
70 return;
71 }
72 auto valueHandler = [value, sensorsAsyncResp](
73 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -080074 const dbus::utility::DbusVariantType&
75 powerCapEnable) {
George Liu0fda0f12021-11-16 10:06:17 +080076 if (ec)
zhanghch058d1b46d2021-04-01 11:18:24 +080077 {
George Liu0fda0f12021-11-16 10:06:17 +080078 messages::internalError(sensorsAsyncResp->asyncResp->res);
79 BMCWEB_LOG_ERROR << "powerCapEnable Get handler: Dbus error "
80 << ec;
81 return;
82 }
83 // Check PowerCapEnable
84 const bool* b = std::get_if<bool>(&powerCapEnable);
85 if (b == nullptr)
86 {
87 messages::internalError(sensorsAsyncResp->asyncResp->res);
88 BMCWEB_LOG_ERROR << "Fail to get PowerCapEnable status ";
89 return;
90 }
91 if (!(*b))
92 {
93 messages::actionNotSupported(
94 sensorsAsyncResp->asyncResp->res,
95 "Setting LimitInWatts when PowerLimit feature is disabled");
96 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
zhanghch058d1b46d2021-04-01 11:18:24 +080097 return;
98 }
Carol Wang4bb3dc32019-10-17 18:15:02 +080099
zhanghch058d1b46d2021-04-01 11:18:24 +0800100 crow::connections::systemBus->async_method_call(
George Liu0fda0f12021-11-16 10:06:17 +0800101 [sensorsAsyncResp](const boost::system::error_code ec2) {
102 if (ec2)
103 {
104 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: "
105 << ec2;
106 messages::internalError(
107 sensorsAsyncResp->asyncResp->res);
108 return;
109 }
110 sensorsAsyncResp->asyncResp->res.result(
111 boost::beast::http::status::no_content);
112 },
113 "xyz.openbmc_project.Settings",
zhanghch058d1b46d2021-04-01 11:18:24 +0800114 "/xyz/openbmc_project/control/host0/power_cap",
George Liu0fda0f12021-11-16 10:06:17 +0800115 "org.freedesktop.DBus.Properties", "Set",
116 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ed Tanous168e20c2021-12-13 14:39:53 -0800117 dbus::utility::DbusVariantType(*value));
zhanghch058d1b46d2021-04-01 11:18:24 +0800118 };
George Liu0fda0f12021-11-16 10:06:17 +0800119 crow::connections::systemBus->async_method_call(
120 std::move(valueHandler), "xyz.openbmc_project.Settings",
121 "/xyz/openbmc_project/control/host0/power_cap",
122 "org.freedesktop.DBus.Properties", "Get",
123 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
124 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700125 getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath));
126}
127inline void requestRoutesPower(App& app)
128{
Ed Tanous2474adf2018-09-05 16:31:16 -0700129
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700130 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700131 .privileges(redfish::privileges::getPower)
Ed Tanous168e20c2021-12-13 14:39:53 -0800132 .methods(
133 boost::beast::http::verb::get)([](const crow::Request&,
134 const std::shared_ptr<
135 bmcweb::AsyncResp>& asyncResp,
136 const std::string& chassisName) {
137 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800138
Ed Tanous168e20c2021-12-13 14:39:53 -0800139 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
140 asyncResp, chassisName,
141 sensors::dbus::paths.at(sensors::node::power),
142 sensors::node::power);
Eddie James028f7eb2019-05-17 21:24:36 +0000143
Ed Tanous168e20c2021-12-13 14:39:53 -0800144 getChassisData(sensorAsyncResp);
Eddie James028f7eb2019-05-17 21:24:36 +0000145
Ed Tanous168e20c2021-12-13 14:39:53 -0800146 // This callback verifies that the power limit is only provided
147 // for the chassis that implements the Chassis inventory item.
148 // This prevents things like power supplies providing the
149 // chassis power limit
150 auto chassisHandler = [sensorAsyncResp](
151 const boost::system::error_code e,
152 const std::vector<std::string>&
153 chassisPaths) {
154 if (e)
155 {
156 BMCWEB_LOG_ERROR
157 << "Power Limit GetSubTreePaths handler Dbus error "
158 << e;
159 return;
160 }
161
162 bool found = false;
163 for (const std::string& chassis : chassisPaths)
164 {
165 size_t len = std::string::npos;
166 size_t lastPos = chassis.rfind('/');
167 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000168 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800169 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000170 }
171
Ed Tanous168e20c2021-12-13 14:39:53 -0800172 if (lastPos == chassis.size() - 1)
Eddie James028f7eb2019-05-17 21:24:36 +0000173 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800174 size_t end = lastPos;
175 lastPos = chassis.rfind('/', lastPos - 1);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700176 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000177 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700178 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000179 }
Eddie James028f7eb2019-05-17 21:24:36 +0000180
Ed Tanous168e20c2021-12-13 14:39:53 -0800181 len = end - (lastPos + 1);
Eddie James028f7eb2019-05-17 21:24:36 +0000182 }
183
Ed Tanous168e20c2021-12-13 14:39:53 -0800184 std::string interfaceChassisName =
185 chassis.substr(lastPos + 1, len);
186 if (!interfaceChassisName.compare(
187 sensorAsyncResp->chassisId))
Eddie James028f7eb2019-05-17 21:24:36 +0000188 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800189 found = true;
190 break;
Eddie James028f7eb2019-05-17 21:24:36 +0000191 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800192 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700193
Ed Tanous168e20c2021-12-13 14:39:53 -0800194 if (!found)
195 {
196 BMCWEB_LOG_DEBUG << "Power Limit not present for "
197 << sensorAsyncResp->chassisId;
198 return;
199 }
200
201 auto valueHandler =
202 [sensorAsyncResp](
203 const boost::system::error_code ec,
204 const std::vector<std::pair<
205 std::string, dbus::utility::DbusVariantType>>&
206 properties) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700207 if (ec)
208 {
209 messages::internalError(
210 sensorAsyncResp->asyncResp->res);
211 BMCWEB_LOG_ERROR
212 << "Power Limit GetAll handler: Dbus error "
213 << ec;
214 return;
215 }
216
217 nlohmann::json& tempArray =
218 sensorAsyncResp->asyncResp->res
219 .jsonValue["PowerControl"];
220
221 // Put multiple "sensors" into a single PowerControl, 0,
222 // so only create the first one
223 if (tempArray.empty())
224 {
225 // Mandatory properties odata.id and MemberId
226 // A warning without a odata.type
227 tempArray.push_back(
228 {{"@odata.type", "#Power.v1_0_0.PowerControl"},
229 {"@odata.id", "/redfish/v1/Chassis/" +
230 sensorAsyncResp->chassisId +
231 "/Power#/PowerControl/0"},
232 {"Name", "Chassis Power Control"},
233 {"MemberId", "0"}});
234 }
235
236 nlohmann::json& sensorJson = tempArray.back();
237 bool enabled = false;
238 double powerCap = 0.0;
239 int64_t scale = 0;
240
Ed Tanous168e20c2021-12-13 14:39:53 -0800241 for (const std::pair<std::string,
242 dbus::utility::DbusVariantType>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700243 property : properties)
244 {
245 if (!property.first.compare("Scale"))
246 {
247 const int64_t* i =
248 std::get_if<int64_t>(&property.second);
249
250 if (i)
251 {
252 scale = *i;
253 }
254 }
255 else if (!property.first.compare("PowerCap"))
256 {
257 const double* d =
258 std::get_if<double>(&property.second);
259 const int64_t* i =
260 std::get_if<int64_t>(&property.second);
261 const uint32_t* u =
262 std::get_if<uint32_t>(&property.second);
263
264 if (d)
265 {
266 powerCap = *d;
267 }
268 else if (i)
269 {
270 powerCap = static_cast<double>(*i);
271 }
272 else if (u)
273 {
274 powerCap = *u;
275 }
276 }
277 else if (!property.first.compare("PowerCapEnable"))
278 {
279 const bool* b =
280 std::get_if<bool>(&property.second);
281
282 if (b)
283 {
284 enabled = *b;
285 }
286 }
287 }
288
289 nlohmann::json& value =
290 sensorJson["PowerLimit"]["LimitInWatts"];
291
292 // LimitException is Mandatory attribute as per OCP
293 // Baseline Profile – v1.0.0, so currently making it
294 // "NoAction" as default value to make it OCP Compliant.
295 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
296
297 if (enabled)
298 {
299 // Redfish specification indicates PowerLimit should
300 // be null if the limit is not enabled.
301 value = powerCap * std::pow(10, scale);
302 }
303 };
304
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700305 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -0800306 std::move(valueHandler), "xyz.openbmc_project.Settings",
307 "/xyz/openbmc_project/control/host0/power_cap",
308 "org.freedesktop.DBus.Properties", "GetAll",
309 "xyz.openbmc_project.Control.Power.Cap");
310 };
311
312 crow::connections::systemBus->async_method_call(
313 std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
314 "/xyz/openbmc_project/object_mapper",
315 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
316 "/xyz/openbmc_project/inventory", 0,
317 std::array<const char*, 2>{
318 "xyz.openbmc_project.Inventory.Item.Board",
319 "xyz.openbmc_project.Inventory.Item.Chassis"});
320 });
Eddie James028f7eb2019-05-17 21:24:36 +0000321
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700322 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700323 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700324 .methods(boost::beast::http::verb::patch)(
325 [](const crow::Request& req,
326 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
327 const std::string& chassisName) {
328 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
329 asyncResp, chassisName,
330 sensors::dbus::paths.at(sensors::node::power),
331 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800332
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700333 std::optional<std::vector<nlohmann::json>> voltageCollections;
334 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
zhanghch058d1b46d2021-04-01 11:18:24 +0800335
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700336 if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res,
337 "PowerControl", powerCtlCollections,
338 "Voltages", voltageCollections))
339 {
340 return;
341 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800342
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700343 if (powerCtlCollections)
344 {
345 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
346 }
347 if (voltageCollections)
348 {
349 std::unordered_map<std::string, std::vector<nlohmann::json>>
350 allCollections;
351 allCollections.emplace("Voltages",
352 *std::move(voltageCollections));
Bruce Lee80ac4022021-06-04 15:41:39 +0800353 setSensorsOverride(sensorAsyncResp, allCollections);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700354 }
355 });
356}
Ed Tanous2474adf2018-09-05 16:31:16 -0700357
358} // namespace redfish