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