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