blob: cecaeab4bb7873fc9411be4bf13c306f63ab2c8a [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"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000020#include "utils/chassis_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070021
John Edward Broadbent7e860f12021-04-08 15:57:16 -070022#include <app.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080023#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070024#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070025#include <registries/privilege_registry.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070026
Ed Tanous2474adf2018-09-05 16:31:16 -070027namespace redfish
28{
Ed Tanous4f48d5f2021-06-21 08:27:45 -070029inline void setPowerCapOverride(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070030 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
31 std::vector<nlohmann::json>& powerControlCollections)
Ed Tanous2474adf2018-09-05 16:31:16 -070032{
Ed Tanous002d39b2022-05-31 08:59:27 -070033 auto getChassisPath =
34 [sensorsAsyncResp, powerControlCollections](
35 const std::optional<std::string>& chassisPath) mutable {
George Liu0fda0f12021-11-16 10:06:17 +080036 if (!chassisPath)
37 {
38 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
39 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
40 "Chassis", sensorsAsyncResp->chassisId);
41 return;
42 }
43
44 if (powerControlCollections.size() != 1)
45 {
46 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present ";
47 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
48 "Power", "PowerControl");
49 return;
50 }
51
52 auto& item = powerControlCollections[0];
53
54 std::optional<nlohmann::json> powerLimit;
55 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res,
56 "PowerLimit", powerLimit))
57 {
58 return;
59 }
60 if (!powerLimit)
61 {
62 return;
63 }
64 std::optional<uint32_t> value;
65 if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res,
66 "LimitInWatts", value))
67 {
68 return;
69 }
70 if (!value)
71 {
72 return;
73 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070074 sdbusplus::asio::getProperty<bool>(
75 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0fda0f12021-11-16 10:06:17 +080076 "/xyz/openbmc_project/control/host0/power_cap",
Jonathan Doman1e1e5982021-06-11 09:36:17 -070077 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
78 [value, sensorsAsyncResp](const boost::system::error_code ec,
79 bool powerCapEnable) {
Ed Tanous002d39b2022-05-31 08:59:27 -070080 if (ec)
81 {
82 messages::internalError(sensorsAsyncResp->asyncResp->res);
83 BMCWEB_LOG_ERROR << "powerCapEnable Get handler: Dbus error "
84 << ec;
85 return;
86 }
87 if (!powerCapEnable)
88 {
89 messages::actionNotSupported(
90 sensorsAsyncResp->asyncResp->res,
91 "Setting LimitInWatts when PowerLimit feature is disabled");
92 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
93 return;
94 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -070095
Ed Tanous002d39b2022-05-31 08:59:27 -070096 crow::connections::systemBus->async_method_call(
97 [sensorsAsyncResp](const boost::system::error_code ec2) {
98 if (ec2)
99 {
100 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: " << ec2;
101 messages::internalError(sensorsAsyncResp->asyncResp->res);
102 return;
103 }
104 sensorsAsyncResp->asyncResp->res.result(
105 boost::beast::http::status::no_content);
106 },
107 "xyz.openbmc_project.Settings",
108 "/xyz/openbmc_project/control/host0/power_cap",
109 "org.freedesktop.DBus.Properties", "Set",
110 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
111 std::variant<uint32_t>(*value));
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700112 });
George Liu0fda0f12021-11-16 10:06:17 +0800113 };
Zhenwei Chen0d7702c2022-07-12 16:42:08 +0000114 redfish::chassis_utils::getValidChassisPath(sensorsAsyncResp->asyncResp,
115 sensorsAsyncResp->chassisId,
116 std::move(getChassisPath));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700117}
118inline void requestRoutesPower(App& app)
119{
Ed Tanous2474adf2018-09-05 16:31:16 -0700120
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700121 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700122 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700123 .methods(boost::beast::http::verb::get)(
124 [&app](const crow::Request& req,
125 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
126 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000127 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700128 {
129 return;
130 }
131 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
132
133 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
134 asyncResp, chassisName, sensors::dbus::powerPaths,
135 sensors::node::power);
136
137 getChassisData(sensorAsyncResp);
138
139 // This callback verifies that the power limit is only provided
140 // for the chassis that implements the Chassis inventory item.
141 // This prevents things like power supplies providing the
142 // chassis power limit
143
144 using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
145 auto chassisHandler =
146 [sensorAsyncResp](const boost::system::error_code e,
147 const Mapper& chassisPaths) {
148 if (e)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700149 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700150 BMCWEB_LOG_ERROR
151 << "Power Limit GetSubTreePaths handler Dbus error " << e;
Ed Tanous45ca1b82022-03-25 13:07:27 -0700152 return;
153 }
Jennifer Leec5d03ff2019-03-08 15:42:58 -0800154
Ed Tanous002d39b2022-05-31 08:59:27 -0700155 bool found = false;
156 for (const std::string& chassis : chassisPaths)
157 {
158 size_t len = std::string::npos;
159 size_t lastPos = chassis.rfind('/');
160 if (lastPos == std::string::npos)
Ed Tanous168e20c2021-12-13 14:39:53 -0800161 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700162 continue;
Ed Tanous168e20c2021-12-13 14:39:53 -0800163 }
164
Ed Tanous002d39b2022-05-31 08:59:27 -0700165 if (lastPos == chassis.size() - 1)
Ed Tanous168e20c2021-12-13 14:39:53 -0800166 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700167 size_t end = lastPos;
168 lastPos = chassis.rfind('/', lastPos - 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800169 if (lastPos == std::string::npos)
Eddie James028f7eb2019-05-17 21:24:36 +0000170 {
Ed Tanous168e20c2021-12-13 14:39:53 -0800171 continue;
Eddie James028f7eb2019-05-17 21:24:36 +0000172 }
173
Ed Tanous002d39b2022-05-31 08:59:27 -0700174 len = end - (lastPos + 1);
Ed Tanous168e20c2021-12-13 14:39:53 -0800175 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700176
Ed Tanous002d39b2022-05-31 08:59:27 -0700177 std::string interfaceChassisName =
178 chassis.substr(lastPos + 1, len);
179 if (interfaceChassisName == sensorAsyncResp->chassisId)
Ed Tanous168e20c2021-12-13 14:39:53 -0800180 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700181 found = true;
182 break;
183 }
184 }
185
186 if (!found)
187 {
188 BMCWEB_LOG_DEBUG << "Power Limit not present for "
189 << sensorAsyncResp->chassisId;
190 return;
191 }
192
193 auto valueHandler =
194 [sensorAsyncResp](
195 const boost::system::error_code ec,
196 const dbus::utility::DBusPropertiesMap& properties) {
197 if (ec)
198 {
199 messages::internalError(sensorAsyncResp->asyncResp->res);
200 BMCWEB_LOG_ERROR
201 << "Power Limit GetAll handler: Dbus error " << ec;
Ed Tanous168e20c2021-12-13 14:39:53 -0800202 return;
203 }
204
Ed Tanous002d39b2022-05-31 08:59:27 -0700205 nlohmann::json& tempArray =
206 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
207
208 // Put multiple "sensors" into a single PowerControl, 0,
209 // so only create the first one
210 if (tempArray.empty())
211 {
212 // Mandatory properties odata.id and MemberId
213 // A warning without a odata.type
214 nlohmann::json::object_t powerControl;
215 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
216 powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
217 sensorAsyncResp->chassisId +
218 "/Power#/PowerControl/0";
219 powerControl["Name"] = "Chassis Power Control";
220 powerControl["MemberId"] = "0";
221 tempArray.push_back(std::move(powerControl));
222 }
223
224 nlohmann::json& sensorJson = tempArray.back();
225 bool enabled = false;
226 double powerCap = 0.0;
227 int64_t scale = 0;
228
229 for (const std::pair<std::string,
230 dbus::utility::DbusVariantType>& property :
231 properties)
232 {
233 if (property.first == "Scale")
234 {
235 const int64_t* i =
236 std::get_if<int64_t>(&property.second);
237
238 if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700239 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700240 scale = *i;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700241 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700242 }
243 else if (property.first == "PowerCap")
244 {
245 const double* d = std::get_if<double>(&property.second);
246 const int64_t* i =
247 std::get_if<int64_t>(&property.second);
248 const uint32_t* u =
249 std::get_if<uint32_t>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700250
Ed Tanous002d39b2022-05-31 08:59:27 -0700251 if (d != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700252 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700253 powerCap = *d;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700254 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700255 else if (i != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700256 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700257 powerCap = static_cast<double>(*i);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700258 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700259 else if (u != nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700260 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700261 powerCap = *u;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700262 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700263 }
264 else if (property.first == "PowerCapEnable")
265 {
266 const bool* b = std::get_if<bool>(&property.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700267
Ed Tanous002d39b2022-05-31 08:59:27 -0700268 if (b != nullptr)
269 {
270 enabled = *b;
271 }
272 }
273 }
274
275 nlohmann::json& value =
276 sensorJson["PowerLimit"]["LimitInWatts"];
277
278 // LimitException is Mandatory attribute as per OCP
279 // Baseline Profile – v1.0.0, so currently making it
280 // "NoAction" as default value to make it OCP Compliant.
281 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
282
283 if (enabled)
284 {
285 // Redfish specification indicates PowerLimit should
286 // be null if the limit is not enabled.
287 value = powerCap * std::pow(10, scale);
288 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800289 };
290
291 crow::connections::systemBus->async_method_call(
Ed Tanous002d39b2022-05-31 08:59:27 -0700292 std::move(valueHandler), "xyz.openbmc_project.Settings",
293 "/xyz/openbmc_project/control/host0/power_cap",
294 "org.freedesktop.DBus.Properties", "GetAll",
295 "xyz.openbmc_project.Control.Power.Cap");
296 };
297
298 crow::connections::systemBus->async_method_call(
299 std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
300 "/xyz/openbmc_project/object_mapper",
301 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
302 "/xyz/openbmc_project/inventory", 0,
303 std::array<const char*, 2>{
304 "xyz.openbmc_project.Inventory.Item.Board",
305 "xyz.openbmc_project.Inventory.Item.Chassis"});
Ed Tanous168e20c2021-12-13 14:39:53 -0800306 });
Eddie James028f7eb2019-05-17 21:24:36 +0000307
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700308 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700309 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700310 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700311 [&app](const crow::Request& req,
312 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
313 const std::string& chassisName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000314 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700315 {
316 return;
317 }
318 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
319 asyncResp, chassisName, sensors::dbus::powerPaths,
320 sensors::node::power);
Carol Wang4bb3dc32019-10-17 18:15:02 +0800321
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 std::optional<std::vector<nlohmann::json>> voltageCollections;
323 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
zhanghch058d1b46d2021-04-01 11:18:24 +0800324
Ed Tanous002d39b2022-05-31 08:59:27 -0700325 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
326 "PowerControl", powerCtlCollections,
327 "Voltages", voltageCollections))
328 {
329 return;
330 }
Carol Wang4bb3dc32019-10-17 18:15:02 +0800331
Ed Tanous002d39b2022-05-31 08:59:27 -0700332 if (powerCtlCollections)
333 {
334 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
335 }
336 if (voltageCollections)
337 {
338 std::unordered_map<std::string, std::vector<nlohmann::json>>
339 allCollections;
340 allCollections.emplace("Voltages", *std::move(voltageCollections));
341 setSensorsOverride(sensorAsyncResp, allCollections);
342 }
343 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700344}
Ed Tanous2474adf2018-09-05 16:31:16 -0700345
346} // namespace redfish