blob: cd7fa0f08ae4f131f5ee5a0082b84245576a1798 [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 Tanous3ccb3ad2023-01-13 17:40:03 -080019#include "app.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080020#include "dbus_utility.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "query.hpp"
22#include "registries/privilege_registry.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070023#include "sensors.hpp"
Zhenwei Chen0d7702c2022-07-12 16:42:08 +000024#include "utils/chassis_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070025#include "utils/json_utils.hpp"
Ed Tanous2474adf2018-09-05 16:31:16 -070026
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020027#include <sdbusplus/asio/property.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070028
George Liu7a1dbc42022-12-07 16:03:22 +080029#include <array>
Ed Tanous08850572024-03-06 15:09:17 -080030#include <string>
George Liu7a1dbc42022-12-07 16:03:22 +080031#include <string_view>
Ed Tanous08850572024-03-06 15:09:17 -080032#include <vector>
George Liu7a1dbc42022-12-07 16:03:22 +080033
Ed Tanous2474adf2018-09-05 16:31:16 -070034namespace redfish
35{
Ed Tanous53b00f52024-03-08 09:05:10 -080036
37inline void afterGetPowerCapEnable(
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous53b00f52024-03-08 09:05:10 -080039 uint32_t valueToSet, const boost::system::error_code& ec,
40 bool powerCapEnable)
Ed Tanous2474adf2018-09-05 16:31:16 -070041{
Ed Tanous53b00f52024-03-08 09:05:10 -080042 if (ec)
43 {
44 messages::internalError(sensorsAsyncResp->asyncResp->res);
45 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
46 return;
47 }
48 if (!powerCapEnable)
49 {
50 messages::actionNotSupported(
51 sensorsAsyncResp->asyncResp->res,
52 "Setting LimitInWatts when PowerLimit feature is disabled");
53 BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
54 return;
55 }
George Liu0fda0f12021-11-16 10:06:17 +080056
Ginu Georgee93abac2024-06-14 17:35:27 +053057 setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
58 "xyz.openbmc_project.Settings",
Asmitha Karunanithi87c44962024-04-04 18:28:33 +000059 sdbusplus::message::object_path(
60 "/xyz/openbmc_project/control/host0/power_cap"),
61 "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
Ginu Georgee93abac2024-06-14 17:35:27 +053062 valueToSet);
John Edward Broadbent7e860f12021-04-08 15:57:16 -070063}
Ed Tanous53b00f52024-03-08 09:05:10 -080064
65inline void afterGetChassisPath(
66 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
Ed Tanous08850572024-03-06 15:09:17 -080067 std::vector<nlohmann::json::object_t>& powerControlCollections,
Ed Tanous53b00f52024-03-08 09:05:10 -080068 const std::optional<std::string>& chassisPath)
69{
70 if (!chassisPath)
71 {
72 BMCWEB_LOG_WARNING("Don't find valid chassis path ");
73 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
74 sensorsAsyncResp->chassisId);
75 return;
76 }
77
78 if (powerControlCollections.size() != 1)
79 {
80 BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
81 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
82 "PowerControl");
83 return;
84 }
85
86 auto& item = powerControlCollections[0];
87
Ed Tanous53b00f52024-03-08 09:05:10 -080088 std::optional<uint32_t> value;
Ed Tanous08850572024-03-06 15:09:17 -080089 if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res,
90 "PowerLimit/LimitInWatts", value))
Ed Tanous53b00f52024-03-08 09:05:10 -080091 {
92 return;
93 }
94 if (!value)
95 {
96 return;
97 }
98 sdbusplus::asio::getProperty<bool>(
99 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
100 "/xyz/openbmc_project/control/host0/power_cap",
101 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
102 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
103}
104
105inline void afterPowerCapSettingGet(
106 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
107 const boost::system::error_code& ec,
108 const dbus::utility::DBusPropertiesMap& properties)
109{
110 if (ec)
111 {
112 messages::internalError(sensorAsyncResp->asyncResp->res);
113 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
114 return;
115 }
116
117 nlohmann::json& tempArray =
118 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
119
120 // Put multiple "sensors" into a single PowerControl, 0,
121 // so only create the first one
122 if (tempArray.empty())
123 {
124 // Mandatory properties odata.id and MemberId
125 // A warning without a odata.type
126 nlohmann::json::object_t powerControl;
127 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
128 powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
129 sensorAsyncResp->chassisId +
130 "/Power#/PowerControl/0";
131 powerControl["Name"] = "Chassis Power Control";
132 powerControl["MemberId"] = "0";
133 tempArray.emplace_back(std::move(powerControl));
134 }
135
136 nlohmann::json& sensorJson = tempArray.back();
137 bool enabled = false;
138 double powerCap = 0.0;
139 int64_t scale = 0;
140
141 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
142 property : properties)
143 {
144 if (property.first == "Scale")
145 {
146 const int64_t* i = std::get_if<int64_t>(&property.second);
147
148 if (i != nullptr)
149 {
150 scale = *i;
151 }
152 }
153 else if (property.first == "PowerCap")
154 {
155 const double* d = std::get_if<double>(&property.second);
156 const int64_t* i = std::get_if<int64_t>(&property.second);
157 const uint32_t* u = std::get_if<uint32_t>(&property.second);
158
159 if (d != nullptr)
160 {
161 powerCap = *d;
162 }
163 else if (i != nullptr)
164 {
165 powerCap = static_cast<double>(*i);
166 }
167 else if (u != nullptr)
168 {
169 powerCap = *u;
170 }
171 }
172 else if (property.first == "PowerCapEnable")
173 {
174 const bool* b = std::get_if<bool>(&property.second);
175
176 if (b != nullptr)
177 {
178 enabled = *b;
179 }
180 }
181 }
182
183 // LimitException is Mandatory attribute as per OCP
184 // Baseline Profile – v1.0.0, so currently making it
185 // "NoAction" as default value to make it OCP Compliant.
186 sensorJson["PowerLimit"]["LimitException"] = "NoAction";
187
188 if (enabled)
189 {
190 // Redfish specification indicates PowerLimit should
191 // be null if the limit is not enabled.
192 sensorJson["PowerLimit"]["LimitInWatts"] = powerCap *
193 std::pow(10, scale);
194 }
195}
196
197using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
198inline void
199 afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
200 const boost::system::error_code& ec2,
201 const Mapper& chassisPaths)
202{
203 if (ec2)
204 {
205 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
206 ec2);
207 return;
208 }
209
210 bool found = false;
211 for (const std::string& chassis : chassisPaths)
212 {
213 size_t len = std::string::npos;
214 size_t lastPos = chassis.rfind('/');
215 if (lastPos == std::string::npos)
216 {
217 continue;
218 }
219
220 if (lastPos == chassis.size() - 1)
221 {
222 size_t end = lastPos;
223 lastPos = chassis.rfind('/', lastPos - 1);
224 if (lastPos == std::string::npos)
225 {
226 continue;
227 }
228
229 len = end - (lastPos + 1);
230 }
231
232 std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
233 if (interfaceChassisName == sensorAsyncResp->chassisId)
234 {
235 found = true;
236 break;
237 }
238 }
239
240 if (!found)
241 {
242 BMCWEB_LOG_DEBUG("Power Limit not present for {}",
243 sensorAsyncResp->chassisId);
244 return;
245 }
246
247 sdbusplus::asio::getAllProperties(
248 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
249 "/xyz/openbmc_project/control/host0/power_cap",
250 "xyz.openbmc_project.Control.Power.Cap",
251 [sensorAsyncResp](const boost::system::error_code& ec,
252 const dbus::utility::DBusPropertiesMap& properties
253
254 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
255}
256
257inline void
258 handleChassisPowerGet(App& app, const crow::Request& req,
259 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
260 const std::string& chassisName)
261{
262 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
263 {
264 return;
265 }
266 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
267
268 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
269 asyncResp, chassisName, sensors::dbus::powerPaths,
270 sensors::node::power);
271
272 getChassisData(sensorAsyncResp);
273
274 // This callback verifies that the power limit is only provided
275 // for the chassis that implements the Chassis inventory item.
276 // This prevents things like power supplies providing the
277 // chassis power limit
278
279 constexpr std::array<std::string_view, 2> interfaces = {
280 "xyz.openbmc_project.Inventory.Item.Board",
281 "xyz.openbmc_project.Inventory.Item.Chassis"};
282
283 dbus::utility::getSubTreePaths(
284 "/xyz/openbmc_project/inventory", 0, interfaces,
285 std::bind_front(afterGetChassis, sensorAsyncResp));
286}
287
288inline void
289 handleChassisPowerPatch(App& app, const crow::Request& req,
290 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
291 const std::string& chassisName)
292{
293 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
294 {
295 return;
296 }
297 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
298 asyncResp, chassisName, sensors::dbus::powerPaths,
299 sensors::node::power);
300
Ed Tanous08850572024-03-06 15:09:17 -0800301 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
302 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
Ed Tanous53b00f52024-03-08 09:05:10 -0800303
304 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
305 "PowerControl", powerCtlCollections,
306 "Voltages", voltageCollections))
307 {
308 return;
309 }
310
311 if (powerCtlCollections)
312 {
313 redfish::chassis_utils::getValidChassisPath(
314 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
315 std::bind_front(afterGetChassisPath, sensorAsyncResp,
316 *powerCtlCollections));
317 }
318 if (voltageCollections)
319 {
Ed Tanous08850572024-03-06 15:09:17 -0800320 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
Ed Tanous53b00f52024-03-08 09:05:10 -0800321 allCollections;
Ed Tanous08850572024-03-06 15:09:17 -0800322 allCollections.emplace("Voltages", std::move(*voltageCollections));
Ed Tanous53b00f52024-03-08 09:05:10 -0800323 setSensorsOverride(sensorAsyncResp, allCollections);
324 }
325}
326
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700327inline void requestRoutesPower(App& app)
328{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700329 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700330 .privileges(redfish::privileges::getPower)
Ed Tanous002d39b2022-05-31 08:59:27 -0700331 .methods(boost::beast::http::verb::get)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800332 std::bind_front(handleChassisPowerGet, std::ref(app)));
Eddie James028f7eb2019-05-17 21:24:36 +0000333
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700334 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
Ed Tanoused398212021-06-09 17:05:54 -0700335 .privileges(redfish::privileges::patchPower)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700336 .methods(boost::beast::http::verb::patch)(
Ed Tanous53b00f52024-03-08 09:05:10 -0800337 std::bind_front(handleChassisPowerPatch, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700338}
Ed Tanous2474adf2018-09-05 16:31:16 -0700339
340} // namespace redfish