xref: /openbmc/bmcweb/redfish-core/lib/power.hpp (revision ffb05364)
1 /*
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 
19 #include "sensors.hpp"
20 
21 #include <app.hpp>
22 #include <registries/privilege_registry.hpp>
23 
24 namespace redfish
25 {
26 inline void setPowerCapOverride(
27     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
28     std::vector<nlohmann::json>& powerControlCollections)
29 {
30     auto getChassisPath =
31         [sensorsAsyncResp, powerControlCollections](
32             const std::optional<std::string>& chassisPath) mutable {
33             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             }
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,
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)
77                 {
78                     messages::internalError(sensorsAsyncResp->asyncResp->res);
79                     BMCWEB_LOG_ERROR
80                         << "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 ";
98                     return;
99                 }
100 
101                 crow::connections::systemBus->async_method_call(
102                     [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",
115                     "/xyz/openbmc_project/control/host0/power_cap",
116                     "org.freedesktop.DBus.Properties", "Set",
117                     "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
118                     std::variant<uint32_t>(*value));
119             };
120             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         };
126     getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath));
127 }
128 inline void requestRoutesPower(App& app)
129 {
130 
131     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
132         .privileges(redfish::privileges::getPower)
133         .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();
139 
140                 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
141                     asyncResp, chassisName,
142                     sensors::dbus::paths.at(sensors::node::power),
143                     sensors::node::power);
144 
145                 getChassisData(sensorAsyncResp);
146 
147                 // 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)
156                     {
157                         BMCWEB_LOG_ERROR
158                             << "Power Limit GetSubTreePaths handler Dbus error "
159                             << e;
160                         return;
161                     }
162 
163                     bool found = false;
164                     for (const std::string& chassis : chassisPaths)
165                     {
166                         size_t len = std::string::npos;
167                         size_t lastPos = chassis.rfind('/');
168                         if (lastPos == std::string::npos)
169                         {
170                             continue;
171                         }
172 
173                         if (lastPos == chassis.size() - 1)
174                         {
175                             size_t end = lastPos;
176                             lastPos = chassis.rfind('/', lastPos - 1);
177                             if (lastPos == std::string::npos)
178                             {
179                                 continue;
180                             }
181 
182                             len = end - (lastPos + 1);
183                         }
184 
185                         std::string interfaceChassisName =
186                             chassis.substr(lastPos + 1, len);
187                         if (!interfaceChassisName.compare(
188                                 sensorAsyncResp->chassisId))
189                         {
190                             found = true;
191                             break;
192                         }
193                     }
194 
195                     if (!found)
196                     {
197                         BMCWEB_LOG_DEBUG << "Power Limit not present for "
198                                          << sensorAsyncResp->chassisId;
199                         return;
200                     }
201 
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");
309                 };
310 
311                 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             });
321 
322     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
323         .privileges(redfish::privileges::patchPower)
324         .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);
332 
333                 std::optional<std::vector<nlohmann::json>> voltageCollections;
334                 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
335 
336                 if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res,
337                                          "PowerControl", powerCtlCollections,
338                                          "Voltages", voltageCollections))
339                 {
340                     return;
341                 }
342 
343                 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));
353                     setSensorsOverride(sensorAsyncResp, allCollections);
354                 }
355             });
356 }
357 
358 } // namespace redfish
359