blob: bf332502c15c634085b74081969b367ec967c1fe [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 Tanoused398212021-06-09 17:05:54 -070022#include <registries/privilege_registry.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070023
Ed Tanous2474adf2018-09-05 16:31:16 -070024namespace redfish
25{
Ed Tanous4f48d5f2021-06-21 08:27:45 -070026inline void setPowerCapOverride(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070027 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
28 std::vector<nlohmann::json>& powerControlCollections)
Ed Tanous2474adf2018-09-05 16:31:16 -070029{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070030 auto getChassisPath =
31 [sensorsAsyncResp, powerControlCollections](
32 const std::optional<std::string>& chassisPath) mutable {
zhanghch058d1b46d2021-04-01 11:18:24 +080033 if (!chassisPath)
34 {
35 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
36 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
37 "Chassis",
38 sensorsAsyncResp->chassisId);
39 return;
40 }
Carol Wang4bb3dc32019-10-17 18:15:02 +080041
zhanghch058d1b46d2021-04-01 11:18:24 +080042 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,
64 sensorsAsyncResp->asyncResp->res,
65 "LimitInWatts", value))
66 {
67 return;
68 }
69 if (!value)
70 {
71 return;
72 }
73 auto valueHandler = [value, sensorsAsyncResp](
74 const boost::system::error_code ec,
75 const SensorVariant& powerCapEnable) {
76 if (ec)
Carol Wang4bb3dc32019-10-17 18:15:02 +080077 {
zhanghch058d1b46d2021-04-01 11:18:24 +080078 messages::internalError(sensorsAsyncResp->asyncResp->res);
Carol Wang4bb3dc32019-10-17 18:15:02 +080079 BMCWEB_LOG_ERROR
zhanghch058d1b46d2021-04-01 11:18:24 +080080 << "powerCapEnable Get handler: Dbus error " << 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 "
96 "feature is disabled");
97 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
Carol Wang4bb3dc32019-10-17 18:15:02 +080098 return;
99 }
100
Carol Wang4bb3dc32019-10-17 18:15:02 +0800101 crow::connections::systemBus->async_method_call(
zhanghch058d1b46d2021-04-01 11:18:24 +0800102 [sensorsAsyncResp](const boost::system::error_code ec2) {
103 if (ec2)
104 {
105 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: "
106 << ec2;
107 messages::internalError(
108 sensorsAsyncResp->asyncResp->res);
109 return;
110 }
111 sensorsAsyncResp->asyncResp->res.result(
112 boost::beast::http::status::no_content);
113 },
114 "xyz.openbmc_project.Settings",
Carol Wang4bb3dc32019-10-17 18:15:02 +0800115 "/xyz/openbmc_project/control/host0/power_cap",
zhanghch058d1b46d2021-04-01 11:18:24 +0800116 "org.freedesktop.DBus.Properties", "Set",
117 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
118 std::variant<uint32_t>(*value));
Carol Wang4bb3dc32019-10-17 18:15:02 +0800119 };
zhanghch058d1b46d2021-04-01 11:18:24 +0800120 crow::connections::systemBus->async_method_call(
121 std::move(valueHandler), "xyz.openbmc_project.Settings",
122 "/xyz/openbmc_project/control/host0/power_cap",
123 "org.freedesktop.DBus.Properties", "Get",
124 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
125 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700126 getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath));
127}
128inline void requestRoutesPower(App& app)
129{
Ed Tanous2474adf2018-09-05 16:31:16 -0700130
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700131 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700132 .privileges(redfish::privileges::getPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700133 .methods(boost::beast::http::verb::get)(
134 [](const crow::Request&,
135 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
136 const std::string& chassisName) {
137 asyncResp->res.jsonValue["PowerControl"] =
138 nlohmann::json::array();
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800139
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700140 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
141 asyncResp, chassisName,
142 sensors::dbus::paths.at(sensors::node::power),
143 sensors::node::power);
Eddie James028f7eb2019-05-17 21:24:36 +0000144
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700145 getChassisData(sensorAsyncResp);
Eddie James028f7eb2019-05-17 21:24:36 +0000146
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700147 // This callback verifies that the power limit is only provided
148 // for the chassis that implements the Chassis inventory item.
149 // This prevents things like power supplies providing the
150 // chassis power limit
151 auto chassisHandler = [sensorAsyncResp](
152 const boost::system::error_code e,
153 const std::vector<std::string>&
154 chassisPaths) {
155 if (e)
Eddie James028f7eb2019-05-17 21:24:36 +0000156 {
Eddie James028f7eb2019-05-17 21:24:36 +0000157 BMCWEB_LOG_ERROR
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700158 << "Power Limit GetSubTreePaths handler Dbus error "
159 << e;
Eddie James028f7eb2019-05-17 21:24:36 +0000160 return;
161 }
162
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700163 bool found = false;
164 for (const std::string& chassis : chassisPaths)
Eddie James028f7eb2019-05-17 21:24:36 +0000165 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700166 size_t len = std::string::npos;
167 size_t lastPos = chassis.rfind('/');
168 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000169 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700170 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000171 }
Eddie James028f7eb2019-05-17 21:24:36 +0000172
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700173 if (lastPos == chassis.size() - 1)
174 {
175 size_t end = lastPos;
176 lastPos = chassis.rfind('/', lastPos - 1);
177 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000178 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700179 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000180 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700181
182 len = end - (lastPos + 1);
Eddie James028f7eb2019-05-17 21:24:36 +0000183 }
Eddie James028f7eb2019-05-17 21:24:36 +0000184
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700185 std::string interfaceChassisName =
186 chassis.substr(lastPos + 1, len);
187 if (!interfaceChassisName.compare(
188 sensorAsyncResp->chassisId))
189 {
190 found = true;
191 break;
Eddie James028f7eb2019-05-17 21:24:36 +0000192 }
193 }
194
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700195 if (!found)
Eddie James028f7eb2019-05-17 21:24:36 +0000196 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700197 BMCWEB_LOG_DEBUG << "Power Limit not present for "
198 << sensorAsyncResp->chassisId;
199 return;
Eddie James028f7eb2019-05-17 21:24:36 +0000200 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700201
202 auto valueHandler = [sensorAsyncResp](
203 const boost::system::error_code ec,
204 const std::vector<std::pair<
205 std::string, SensorVariant>>&
206 properties) {
207 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
241 for (const std::pair<std::string, SensorVariant>&
242 property : properties)
243 {
244 if (!property.first.compare("Scale"))
245 {
246 const int64_t* i =
247 std::get_if<int64_t>(&property.second);
248
249 if (i)
250 {
251 scale = *i;
252 }
253 }
254 else if (!property.first.compare("PowerCap"))
255 {
256 const double* d =
257 std::get_if<double>(&property.second);
258 const int64_t* i =
259 std::get_if<int64_t>(&property.second);
260 const uint32_t* u =
261 std::get_if<uint32_t>(&property.second);
262
263 if (d)
264 {
265 powerCap = *d;
266 }
267 else if (i)
268 {
269 powerCap = static_cast<double>(*i);
270 }
271 else if (u)
272 {
273 powerCap = *u;
274 }
275 }
276 else if (!property.first.compare("PowerCapEnable"))
277 {
278 const bool* b =
279 std::get_if<bool>(&property.second);
280
281 if (b)
282 {
283 enabled = *b;
284 }
285 }
286 }
287
288 nlohmann::json& value =
289 sensorJson["PowerLimit"]["LimitInWatts"];
290
291 // LimitException is Mandatory attribute as per OCP
292 // Baseline Profile – v1.0.0, so currently making it
293 // "NoAction" as default value to make it OCP Compliant.
294 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
295
296 if (enabled)
297 {
298 // Redfish specification indicates PowerLimit should
299 // be null if the limit is not enabled.
300 value = powerCap * std::pow(10, scale);
301 }
302 };
303
304 crow::connections::systemBus->async_method_call(
305 std::move(valueHandler), "xyz.openbmc_project.Settings",
306 "/xyz/openbmc_project/control/host0/power_cap",
307 "org.freedesktop.DBus.Properties", "GetAll",
308 "xyz.openbmc_project.Control.Power.Cap");
Eddie James028f7eb2019-05-17 21:24:36 +0000309 };
310
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700311 crow::connections::systemBus->async_method_call(
312 std::move(chassisHandler),
313 "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