xref: /openbmc/bmcweb/redfish-core/lib/power.hpp (revision a778c026)
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, {{"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     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 = std::get_if<bool>(&powerCapEnable);
101                     if (b == nullptr)
102                     {
103                         messages::internalError(asyncResp->res);
104                         BMCWEB_LOG_ERROR
105                             << "Fail to get PowerCapEnable status ";
106                         return;
107                     }
108                     if (!(*b))
109                     {
110                         messages::actionNotSupported(
111                             asyncResp->res,
112                             "Setting LimitInWatts when PowerLimit "
113                             "feature is disabled");
114                         BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
115                         return;
116                     }
117 
118                     crow::connections::systemBus->async_method_call(
119                         [asyncResp](const boost::system::error_code ec) {
120                             if (ec)
121                             {
122                                 BMCWEB_LOG_DEBUG
123                                     << "Power Limit Set: Dbus error: " << ec;
124                                 messages::internalError(asyncResp->res);
125                                 return;
126                             }
127                             asyncResp->res.result(
128                                 boost::beast::http::status::no_content);
129                         },
130                         "xyz.openbmc_project.Settings",
131                         "/xyz/openbmc_project/control/host0/power_cap",
132                         "org.freedesktop.DBus.Properties", "Set",
133                         "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
134                         std::variant<uint32_t>(*value));
135                 };
136                 crow::connections::systemBus->async_method_call(
137                     std::move(valueHandler), "xyz.openbmc_project.Settings",
138                     "/xyz/openbmc_project/control/host0/power_cap",
139                     "org.freedesktop.DBus.Properties", "Get",
140                     "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
141             };
142         getValidChassisPath(asyncResp, std::move(getChassisPath));
143     }
144     void doGet(crow::Response& res, const crow::Request& req,
145                const std::vector<std::string>& params) override
146     {
147         if (params.size() != 1)
148         {
149             res.result(boost::beast::http::status::internal_server_error);
150             res.end();
151             return;
152         }
153         const std::string& chassis_name = params[0];
154 
155         res.jsonValue["PowerControl"] = nlohmann::json::array();
156 
157         auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
158             res, chassis_name, typeList, "Power");
159 
160         getChassisData(sensorAsyncResp);
161 
162         // This callback verifies that the power limit is only provided for the
163         // chassis that implements the Chassis inventory item. This prevents
164         // things like power supplies providing the chassis power limit
165         auto chassisHandler = [sensorAsyncResp](
166                                   const boost::system::error_code e,
167                                   const std::vector<std::string>&
168                                       chassisPaths) {
169             if (e)
170             {
171                 BMCWEB_LOG_ERROR
172                     << "Power Limit GetSubTreePaths handler Dbus error " << e;
173                 return;
174             }
175 
176             bool found = false;
177             for (const std::string& chassis : chassisPaths)
178             {
179                 size_t len = std::string::npos;
180                 size_t lastPos = chassis.rfind("/");
181                 if (lastPos == std::string::npos)
182                 {
183                     continue;
184                 }
185 
186                 if (lastPos == chassis.size() - 1)
187                 {
188                     size_t end = lastPos;
189                     lastPos = chassis.rfind("/", lastPos - 1);
190                     if (lastPos == std::string::npos)
191                     {
192                         continue;
193                     }
194 
195                     len = end - (lastPos + 1);
196                 }
197 
198                 std::string interfaceChassisName =
199                     chassis.substr(lastPos + 1, len);
200                 if (!interfaceChassisName.compare(sensorAsyncResp->chassisId))
201                 {
202                     found = true;
203                     break;
204                 }
205             }
206 
207             if (!found)
208             {
209                 BMCWEB_LOG_DEBUG << "Power Limit not present for "
210                                  << sensorAsyncResp->chassisId;
211                 return;
212             }
213 
214             auto valueHandler =
215                 [sensorAsyncResp](
216                     const boost::system::error_code ec,
217                     const std::vector<std::pair<std::string, SensorVariant>>&
218                         properties) {
219                     if (ec)
220                     {
221                         messages::internalError(sensorAsyncResp->res);
222                         BMCWEB_LOG_ERROR
223                             << "Power Limit GetAll handler: Dbus error " << ec;
224                         return;
225                     }
226 
227                     nlohmann::json& tempArray =
228                         sensorAsyncResp->res.jsonValue["PowerControl"];
229 
230                     // Put multiple "sensors" into a single PowerControl, 0, so
231                     // only create the first one
232                     if (tempArray.empty())
233                     {
234                         // Mandatory properties odata.id and MemberId
235                         // A warning without a odata.type
236                         tempArray.push_back(
237                             {{"@odata.type", "#Power.v1_0_0.PowerControl"},
238                              {"@odata.id", "/redfish/v1/Chassis/" +
239                                                sensorAsyncResp->chassisId +
240                                                "/Power#/PowerControl/0"},
241                              {"Name", "Chassis Power Control"},
242                              {"MemberId", "0"}});
243                     }
244 
245                     nlohmann::json& sensorJson = tempArray.back();
246                     bool enabled = false;
247                     double powerCap = 0.0;
248                     int64_t scale = 0;
249 
250                     for (const std::pair<std::string, SensorVariant>& property :
251                          properties)
252                     {
253                         if (!property.first.compare("Scale"))
254                         {
255                             const int64_t* i =
256                                 std::get_if<int64_t>(&property.second);
257 
258                             if (i)
259                             {
260                                 scale = *i;
261                             }
262                         }
263                         else if (!property.first.compare("PowerCap"))
264                         {
265                             const double* d =
266                                 std::get_if<double>(&property.second);
267                             const int64_t* i =
268                                 std::get_if<int64_t>(&property.second);
269                             const uint32_t* u =
270                                 std::get_if<uint32_t>(&property.second);
271 
272                             if (d)
273                             {
274                                 powerCap = *d;
275                             }
276                             else if (i)
277                             {
278                                 powerCap = static_cast<double>(*i);
279                             }
280                             else if (u)
281                             {
282                                 powerCap = *u;
283                             }
284                         }
285                         else if (!property.first.compare("PowerCapEnable"))
286                         {
287                             const bool* b = std::get_if<bool>(&property.second);
288 
289                             if (b)
290                             {
291                                 enabled = *b;
292                             }
293                         }
294                     }
295 
296                     nlohmann::json& value =
297                         sensorJson["PowerLimit"]["LimitInWatts"];
298 
299                     // LimitException is Mandatory attribute as per OCP Baseline
300                     // Profile – v1.0.0, so currently making it "NoAction"
301                     // as default value to make it OCP Compliant.
302                     sensorJson["PowerLimit"]["LimitException"] = "NoAction";
303 
304                     if (enabled)
305                     {
306                         // Redfish specification indicates PowerLimit should be
307                         // null if the limit is not enabled.
308                         value = powerCap * std::pow(10, scale);
309                     }
310                 };
311 
312             crow::connections::systemBus->async_method_call(
313                 std::move(valueHandler), "xyz.openbmc_project.Settings",
314                 "/xyz/openbmc_project/control/host0/power_cap",
315                 "org.freedesktop.DBus.Properties", "GetAll",
316                 "xyz.openbmc_project.Control.Power.Cap");
317         };
318 
319         crow::connections::systemBus->async_method_call(
320             std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper",
321             "/xyz/openbmc_project/object_mapper",
322             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
323             "/xyz/openbmc_project/inventory", 0,
324             std::array<const char*, 2>{
325                 "xyz.openbmc_project.Inventory.Item.Board",
326                 "xyz.openbmc_project.Inventory.Item.Chassis"});
327     }
328     void doPatch(crow::Response& res, const crow::Request& req,
329                  const std::vector<std::string>& params) override
330     {
331         if (params.size() != 1)
332         {
333             messages::internalError(res);
334             res.end();
335             return;
336         }
337 
338         const std::string& chassisName = params[0];
339         auto asyncResp = std::make_shared<SensorsAsyncResp>(res, chassisName,
340                                                             typeList, "Power");
341 
342         std::optional<std::vector<nlohmann::json>> voltageCollections;
343         std::optional<std::vector<nlohmann::json>> powerCtlCollections;
344 
345         if (!json_util::readJson(req, asyncResp->res, "PowerControl",
346                                  powerCtlCollections, "Voltages",
347                                  voltageCollections))
348         {
349             return;
350         }
351 
352         if (powerCtlCollections)
353         {
354             setPowerCapOverride(asyncResp, *powerCtlCollections);
355         }
356         if (voltageCollections)
357         {
358             std::unordered_map<std::string, std::vector<nlohmann::json>>
359                 allCollections;
360             allCollections.emplace("Voltages", *std::move(voltageCollections));
361             checkAndDoSensorsOverride(asyncResp, allCollections);
362         }
363     }
364 };
365 
366 } // namespace redfish
367