xref: /openbmc/bmcweb/redfish-core/lib/power.hpp (revision 09b9d45e)
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(CrowApp& 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, {{"ConfigureComponents"}}},
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     std::vector<const char*> typeList = {"/xyz/openbmc_project/sensors/voltage",
42                                          "/xyz/openbmc_project/sensors/power"};
43     void setPowerCapOverride(
44         std::shared_ptr<SensorsAsyncResp> asyncResp,
45         std::vector<nlohmann::json>& powerControlCollections)
46     {
47         auto getChassisPath =
48             [asyncResp, powerControlCollections](
49                 const std::optional<std::string>& chassisPath) mutable {
50                 if (!chassisPath)
51                 {
52                     BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
53                     messages::resourceNotFound(asyncResp->res, "Chassis",
54                                                asyncResp->chassisId);
55                     return;
56                 }
57 
58                 if (powerControlCollections.size() != 1)
59                 {
60                     BMCWEB_LOG_ERROR
61                         << "Don't support multiple hosts at present ";
62                     messages::resourceNotFound(asyncResp->res, "Power",
63                                                "PowerControl");
64                     return;
65                 }
66 
67                 auto& item = powerControlCollections[0];
68 
69                 std::optional<nlohmann::json> powerLimit;
70                 if (!json_util::readJson(item, asyncResp->res, "PowerLimit",
71                                          powerLimit))
72                 {
73                     return;
74                 }
75                 if (!powerLimit)
76                 {
77                     return;
78                 }
79                 std::optional<uint32_t> value;
80                 if (!json_util::readJson(*powerLimit, asyncResp->res,
81                                          "LimitInWatts", value))
82                 {
83                     return;
84                 }
85                 if (!value)
86                 {
87                     return;
88                 }
89                 auto valueHandler = [value, asyncResp](
90                                         const boost::system::error_code ec,
91                                         const SensorVariant& powerCapEnable) {
92                     if (ec)
93                     {
94                         messages::internalError(asyncResp->res);
95                         BMCWEB_LOG_ERROR
96                             << "powerCapEnable Get handler: Dbus error " << ec;
97                         return;
98                     }
99                     // Check PowerCapEnable
100                     const bool* b =
101                         sdbusplus::message::variant_ns::get_if<bool>(
102                             &powerCapEnable);
103                     if (b == nullptr)
104                     {
105                         messages::internalError(asyncResp->res);
106                         BMCWEB_LOG_ERROR
107                             << "Fail to get PowerCapEnable status ";
108                         return;
109                     }
110                     if (!(*b))
111                     {
112                         messages::actionNotSupported(
113                             asyncResp->res,
114                             "Setting LimitInWatts when PowerLimit "
115                             "feature is disabled");
116                         BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
117                         return;
118                     }
119 
120                     crow::connections::systemBus->async_method_call(
121                         [asyncResp](const boost::system::error_code ec) {
122                             if (ec)
123                             {
124                                 BMCWEB_LOG_DEBUG
125                                     << "Power Limit Set: Dbus error: " << ec;
126                                 messages::internalError(asyncResp->res);
127                                 return;
128                             }
129                             asyncResp->res.result(
130                                 boost::beast::http::status::no_content);
131                         },
132                         "xyz.openbmc_project.Settings",
133                         "/xyz/openbmc_project/control/host0/power_cap",
134                         "org.freedesktop.DBus.Properties", "Set",
135                         "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
136                         sdbusplus::message::variant<uint32_t>(*value));
137                 };
138                 crow::connections::systemBus->async_method_call(
139                     std::move(valueHandler), "xyz.openbmc_project.Settings",
140                     "/xyz/openbmc_project/control/host0/power_cap",
141                     "org.freedesktop.DBus.Properties", "Get",
142                     "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
143             };
144         getValidChassisPath(asyncResp, std::move(getChassisPath));
145     }
146     void doGet(crow::Response& res, const crow::Request& req,
147                const std::vector<std::string>& params) override
148     {
149         if (params.size() != 1)
150         {
151             res.result(boost::beast::http::status::internal_server_error);
152             res.end();
153             return;
154         }
155         const std::string& chassis_name = params[0];
156 
157         res.jsonValue["PowerControl"] = nlohmann::json::array();
158 
159         auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
160             res, chassis_name, typeList, "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(sensorAsyncResp->res);
224                         BMCWEB_LOG_ERROR
225                             << "Power Limit GetAll handler: Dbus error " << ec;
226                         return;
227                     }
228 
229                     nlohmann::json& tempArray =
230                         sensorAsyncResp->res.jsonValue["PowerControl"];
231 
232                     // Put multiple "sensors" into a single PowerControl, 0, so
233                     // only create the first one
234                     if (tempArray.empty())
235                     {
236                         // Mandatory properties odata.id and MemberId
237                         // A warning without a odata.type
238                         tempArray.push_back(
239                             {{"@odata.type", "#Power.v1_0_0.PowerControl"},
240                              {"@odata.id", "/redfish/v1/Chassis/" +
241                                                sensorAsyncResp->chassisId +
242                                                "/Power#/PowerControl/0"},
243                              {"Name", "Chassis Power Control"},
244                              {"MemberId", "0"}});
245                     }
246 
247                     nlohmann::json& sensorJson = tempArray.back();
248                     bool enabled = false;
249                     double powerCap = 0.0;
250                     int64_t scale = 0;
251 
252                     for (const std::pair<std::string, SensorVariant>& property :
253                          properties)
254                     {
255                         if (!property.first.compare("Scale"))
256                         {
257                             const int64_t* i =
258                                 sdbusplus::message::variant_ns::get_if<int64_t>(
259                                     &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                                 sdbusplus::message::variant_ns::get_if<double>(
270                                     &property.second);
271                             const int64_t* i =
272                                 sdbusplus::message::variant_ns::get_if<int64_t>(
273                                     &property.second);
274                             const uint32_t* u =
275                                 sdbusplus::message::variant_ns::get_if<
276                                     uint32_t>(&property.second);
277 
278                             if (d)
279                             {
280                                 powerCap = *d;
281                             }
282                             else if (i)
283                             {
284                                 powerCap = static_cast<double>(*i);
285                             }
286                             else if (u)
287                             {
288                                 powerCap = *u;
289                             }
290                         }
291                         else if (!property.first.compare("PowerCapEnable"))
292                         {
293                             const bool* b =
294                                 sdbusplus::message::variant_ns::get_if<bool>(
295                                     &property.second);
296 
297                             if (b)
298                             {
299                                 enabled = *b;
300                             }
301                         }
302                     }
303 
304                     nlohmann::json& value =
305                         sensorJson["PowerLimit"]["LimitInWatts"];
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*, 1>{
328                 "xyz.openbmc_project.Inventory.Item.Chassis"});
329     }
330     void doPatch(crow::Response& res, const crow::Request& req,
331                  const std::vector<std::string>& params) override
332     {
333         if (params.size() != 1)
334         {
335             messages::internalError(res);
336             res.end();
337             return;
338         }
339 
340         const std::string& chassisName = params[0];
341         auto asyncResp = std::make_shared<SensorsAsyncResp>(res, chassisName,
342                                                             typeList, "Power");
343 
344         std::optional<std::vector<nlohmann::json>> voltageCollections;
345         std::optional<std::vector<nlohmann::json>> powerCtlCollections;
346 
347         if (!json_util::readJson(req, asyncResp->res, "PowerControl",
348                                  powerCtlCollections, "Voltages",
349                                  voltageCollections))
350         {
351             return;
352         }
353 
354         if (powerCtlCollections)
355         {
356             setPowerCapOverride(asyncResp, *powerCtlCollections);
357         }
358         if (voltageCollections)
359         {
360             std::unordered_map<std::string, std::vector<nlohmann::json>>
361                 allCollections;
362             allCollections.emplace("Voltages", *std::move(voltageCollections));
363             setSensorOverride(asyncResp, allCollections, chassisName, typeList);
364         }
365     }
366 };
367 
368 } // namespace redfish
369