xref: /openbmc/bmcweb/redfish-core/lib/power.hpp (revision 10f270b4)
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 "node.hpp"
20 #include "sensors.hpp"
21 
22 namespace redfish
23 {
24 
25 class Power : public Node
26 {
27   public:
28     Power(App& app) :
29         Node((app), "/redfish/v1/Chassis/<str>/Power/", std::string())
30     {
31         entityPrivileges = {
32             {boost::beast::http::verb::get, {{"Login"}}},
33             {boost::beast::http::verb::head, {{"Login"}}},
34             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
35             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
36             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
37             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
38     }
39 
40   private:
41     void setPowerCapOverride(
42         const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
43         std::vector<nlohmann::json>& powerControlCollections)
44     {
45         auto getChassisPath = [sensorsAsyncResp, powerControlCollections](
46                                   const std::optional<std::string>&
47                                       chassisPath) mutable {
48             if (!chassisPath)
49             {
50                 BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
51                 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
52                                            "Chassis",
53                                            sensorsAsyncResp->chassisId);
54                 return;
55             }
56 
57             if (powerControlCollections.size() != 1)
58             {
59                 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present ";
60                 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res,
61                                            "Power", "PowerControl");
62                 return;
63             }
64 
65             auto& item = powerControlCollections[0];
66 
67             std::optional<nlohmann::json> powerLimit;
68             if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res,
69                                      "PowerLimit", powerLimit))
70             {
71                 return;
72             }
73             if (!powerLimit)
74             {
75                 return;
76             }
77             std::optional<uint32_t> value;
78             if (!json_util::readJson(*powerLimit,
79                                      sensorsAsyncResp->asyncResp->res,
80                                      "LimitInWatts", value))
81             {
82                 return;
83             }
84             if (!value)
85             {
86                 return;
87             }
88             auto valueHandler = [value, sensorsAsyncResp](
89                                     const boost::system::error_code ec,
90                                     const SensorVariant& powerCapEnable) {
91                 if (ec)
92                 {
93                     messages::internalError(sensorsAsyncResp->asyncResp->res);
94                     BMCWEB_LOG_ERROR
95                         << "powerCapEnable Get handler: Dbus error " << ec;
96                     return;
97                 }
98                 // Check PowerCapEnable
99                 const bool* b = std::get_if<bool>(&powerCapEnable);
100                 if (b == nullptr)
101                 {
102                     messages::internalError(sensorsAsyncResp->asyncResp->res);
103                     BMCWEB_LOG_ERROR << "Fail to get PowerCapEnable status ";
104                     return;
105                 }
106                 if (!(*b))
107                 {
108                     messages::actionNotSupported(
109                         sensorsAsyncResp->asyncResp->res,
110                         "Setting LimitInWatts when PowerLimit "
111                         "feature is disabled");
112                     BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
113                     return;
114                 }
115 
116                 crow::connections::systemBus->async_method_call(
117                     [sensorsAsyncResp](const boost::system::error_code ec2) {
118                         if (ec2)
119                         {
120                             BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: "
121                                              << ec2;
122                             messages::internalError(
123                                 sensorsAsyncResp->asyncResp->res);
124                             return;
125                         }
126                         sensorsAsyncResp->asyncResp->res.result(
127                             boost::beast::http::status::no_content);
128                     },
129                     "xyz.openbmc_project.Settings",
130                     "/xyz/openbmc_project/control/host0/power_cap",
131                     "org.freedesktop.DBus.Properties", "Set",
132                     "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
133                     std::variant<uint32_t>(*value));
134             };
135             crow::connections::systemBus->async_method_call(
136                 std::move(valueHandler), "xyz.openbmc_project.Settings",
137                 "/xyz/openbmc_project/control/host0/power_cap",
138                 "org.freedesktop.DBus.Properties", "Get",
139                 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
140         };
141         getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath));
142     }
143     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
144                const crow::Request&,
145                const std::vector<std::string>& params) override
146     {
147         if (params.size() != 1)
148         {
149             asyncResp->res.result(
150                 boost::beast::http::status::internal_server_error);
151             return;
152         }
153         const std::string& chassisName = params[0];
154 
155         asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
156 
157         auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
158             asyncResp, chassisName,
159             sensors::dbus::paths.at(sensors::node::power),
160             sensors::node::power);
161 
162         getChassisData(sensorAsyncResp);
163 
164         // This callback verifies that the power limit is only provided for the
165         // chassis that implements the Chassis inventory item. This prevents
166         // things like power supplies providing the chassis power limit
167         auto chassisHandler = [sensorAsyncResp](
168                                   const boost::system::error_code e,
169                                   const std::vector<std::string>&
170                                       chassisPaths) {
171             if (e)
172             {
173                 BMCWEB_LOG_ERROR
174                     << "Power Limit GetSubTreePaths handler Dbus error " << e;
175                 return;
176             }
177 
178             bool found = false;
179             for (const std::string& chassis : chassisPaths)
180             {
181                 size_t len = std::string::npos;
182                 size_t lastPos = chassis.rfind('/');
183                 if (lastPos == std::string::npos)
184                 {
185                     continue;
186                 }
187 
188                 if (lastPos == chassis.size() - 1)
189                 {
190                     size_t end = lastPos;
191                     lastPos = chassis.rfind('/', lastPos - 1);
192                     if (lastPos == std::string::npos)
193                     {
194                         continue;
195                     }
196 
197                     len = end - (lastPos + 1);
198                 }
199 
200                 std::string interfaceChassisName =
201                     chassis.substr(lastPos + 1, len);
202                 if (!interfaceChassisName.compare(sensorAsyncResp->chassisId))
203                 {
204                     found = true;
205                     break;
206                 }
207             }
208 
209             if (!found)
210             {
211                 BMCWEB_LOG_DEBUG << "Power Limit not present for "
212                                  << sensorAsyncResp->chassisId;
213                 return;
214             }
215 
216             auto valueHandler =
217                 [sensorAsyncResp](
218                     const boost::system::error_code ec,
219                     const std::vector<std::pair<std::string, SensorVariant>>&
220                         properties) {
221                     if (ec)
222                     {
223                         messages::internalError(
224                             sensorAsyncResp->asyncResp->res);
225                         BMCWEB_LOG_ERROR
226                             << "Power Limit GetAll handler: Dbus error " << ec;
227                         return;
228                     }
229 
230                     nlohmann::json& tempArray = sensorAsyncResp->asyncResp->res
231                                                     .jsonValue["PowerControl"];
232 
233                     // Put multiple "sensors" into a single PowerControl, 0, so
234                     // only create the first one
235                     if (tempArray.empty())
236                     {
237                         // Mandatory properties odata.id and MemberId
238                         // A warning without a odata.type
239                         tempArray.push_back(
240                             {{"@odata.type", "#Power.v1_0_0.PowerControl"},
241                              {"@odata.id", "/redfish/v1/Chassis/" +
242                                                sensorAsyncResp->chassisId +
243                                                "/Power#/PowerControl/0"},
244                              {"Name", "Chassis Power Control"},
245                              {"MemberId", "0"}});
246                     }
247 
248                     nlohmann::json& sensorJson = tempArray.back();
249                     bool enabled = false;
250                     double powerCap = 0.0;
251                     int64_t scale = 0;
252 
253                     for (const std::pair<std::string, SensorVariant>& property :
254                          properties)
255                     {
256                         if (!property.first.compare("Scale"))
257                         {
258                             const int64_t* i =
259                                 std::get_if<int64_t>(&property.second);
260 
261                             if (i)
262                             {
263                                 scale = *i;
264                             }
265                         }
266                         else if (!property.first.compare("PowerCap"))
267                         {
268                             const double* d =
269                                 std::get_if<double>(&property.second);
270                             const int64_t* i =
271                                 std::get_if<int64_t>(&property.second);
272                             const uint32_t* u =
273                                 std::get_if<uint32_t>(&property.second);
274 
275                             if (d)
276                             {
277                                 powerCap = *d;
278                             }
279                             else if (i)
280                             {
281                                 powerCap = static_cast<double>(*i);
282                             }
283                             else if (u)
284                             {
285                                 powerCap = *u;
286                             }
287                         }
288                         else if (!property.first.compare("PowerCapEnable"))
289                         {
290                             const bool* b = std::get_if<bool>(&property.second);
291 
292                             if (b)
293                             {
294                                 enabled = *b;
295                             }
296                         }
297                     }
298 
299                     nlohmann::json& value =
300                         sensorJson["PowerLimit"]["LimitInWatts"];
301 
302                     // LimitException is Mandatory attribute as per OCP Baseline
303                     // Profile – v1.0.0, so currently making it "NoAction"
304                     // as default value to make it OCP Compliant.
305                     sensorJson["PowerLimit"]["LimitException"] = "NoAction";
306 
307                     if (enabled)
308                     {
309                         // Redfish specification indicates PowerLimit should be
310                         // null if the limit is not enabled.
311                         value = powerCap * std::pow(10, scale);
312                     }
313                 };
314 
315             crow::connections::systemBus->async_method_call(
316                 std::move(valueHandler), "xyz.openbmc_project.Settings",
317                 "/xyz/openbmc_project/control/host0/power_cap",
318                 "org.freedesktop.DBus.Properties", "GetAll",
319                 "xyz.openbmc_project.Control.Power.Cap");
320         };
321 
322         crow::connections::systemBus->async_method_call(
323             std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
324             "/xyz/openbmc_project/object_mapper",
325             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
326             "/xyz/openbmc_project/inventory", 0,
327             std::array<const char*, 2>{
328                 "xyz.openbmc_project.Inventory.Item.Board",
329                 "xyz.openbmc_project.Inventory.Item.Chassis"});
330     }
331     void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
332                  const crow::Request& req,
333                  const std::vector<std::string>& params) override
334     {
335         if (params.size() != 1)
336         {
337             messages::internalError(asyncResp->res);
338             return;
339         }
340 
341         const std::string& chassisName = params[0];
342 
343         auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
344             asyncResp, chassisName,
345             sensors::dbus::paths.at(sensors::node::power),
346             sensors::node::power);
347 
348         std::optional<std::vector<nlohmann::json>> voltageCollections;
349         std::optional<std::vector<nlohmann::json>> powerCtlCollections;
350 
351         if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res,
352                                  "PowerControl", powerCtlCollections,
353                                  "Voltages", voltageCollections))
354         {
355             return;
356         }
357 
358         if (powerCtlCollections)
359         {
360             setPowerCapOverride(sensorAsyncResp, *powerCtlCollections);
361         }
362         if (voltageCollections)
363         {
364             std::unordered_map<std::string, std::vector<nlohmann::json>>
365                 allCollections;
366             allCollections.emplace("Voltages", *std::move(voltageCollections));
367             checkAndDoSensorsOverride(sensorAsyncResp, allCollections);
368         }
369     }
370 };
371 
372 } // namespace redfish
373