xref: /openbmc/bmcweb/features/redfish/lib/power.hpp (revision 0885057cf9e0a155eecaa8c15cb1076eaa432057)
12474adfaSEd Tanous /*
22474adfaSEd Tanous // Copyright (c) 2018 Intel Corporation
32474adfaSEd Tanous // Copyright (c) 2018 Ampere Computing LLC
42474adfaSEd Tanous /
52474adfaSEd Tanous // Licensed under the Apache License, Version 2.0 (the "License");
62474adfaSEd Tanous // you may not use this file except in compliance with the License.
72474adfaSEd Tanous // You may obtain a copy of the License at
82474adfaSEd Tanous //
92474adfaSEd Tanous //      http://www.apache.org/licenses/LICENSE-2.0
102474adfaSEd Tanous //
112474adfaSEd Tanous // Unless required by applicable law or agreed to in writing, software
122474adfaSEd Tanous // distributed under the License is distributed on an "AS IS" BASIS,
132474adfaSEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
142474adfaSEd Tanous // See the License for the specific language governing permissions and
152474adfaSEd Tanous // limitations under the License.
162474adfaSEd Tanous */
172474adfaSEd Tanous #pragma once
182474adfaSEd Tanous 
193ccb3adbSEd Tanous #include "app.hpp"
207a1dbc48SGeorge Liu #include "dbus_utility.hpp"
213ccb3adbSEd Tanous #include "query.hpp"
223ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
232474adfaSEd Tanous #include "sensors.hpp"
240d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp"
252474adfaSEd Tanous 
26d1bde9e5SKrzysztof Grobelny #include <sdbusplus/asio/property.hpp>
277e860f15SJohn Edward Broadbent 
287a1dbc48SGeorge Liu #include <array>
29*0885057cSEd Tanous #include <string>
307a1dbc48SGeorge Liu #include <string_view>
31*0885057cSEd Tanous #include <vector>
327a1dbc48SGeorge Liu 
332474adfaSEd Tanous namespace redfish
342474adfaSEd Tanous {
3553b00f5dSEd Tanous 
3653b00f5dSEd Tanous inline void afterGetPowerCapEnable(
378d1b46d7Szhanghch05     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
3853b00f5dSEd Tanous     uint32_t valueToSet, const boost::system::error_code& ec,
3953b00f5dSEd Tanous     bool powerCapEnable)
404bb3dc34SCarol Wang {
4153b00f5dSEd Tanous     if (ec)
4253b00f5dSEd Tanous     {
4353b00f5dSEd Tanous         messages::internalError(sensorsAsyncResp->asyncResp->res);
4453b00f5dSEd Tanous         BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
4553b00f5dSEd Tanous         return;
4653b00f5dSEd Tanous     }
4753b00f5dSEd Tanous     if (!powerCapEnable)
4853b00f5dSEd Tanous     {
4953b00f5dSEd Tanous         messages::actionNotSupported(
5053b00f5dSEd Tanous             sensorsAsyncResp->asyncResp->res,
5153b00f5dSEd Tanous             "Setting LimitInWatts when PowerLimit feature is disabled");
5253b00f5dSEd Tanous         BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
5353b00f5dSEd Tanous         return;
5453b00f5dSEd Tanous     }
5553b00f5dSEd Tanous 
5653b00f5dSEd Tanous     sdbusplus::asio::setProperty(
5753b00f5dSEd Tanous         *crow::connections::systemBus, "xyz.openbmc_project.Settings",
5853b00f5dSEd Tanous         "/xyz/openbmc_project/control/host0/power_cap",
5953b00f5dSEd Tanous         "xyz.openbmc_project.Control.Power.Cap", "PowerCap", valueToSet,
6053b00f5dSEd Tanous         [sensorsAsyncResp](const boost::system::error_code& ec2) {
6153b00f5dSEd Tanous         if (ec2)
6253b00f5dSEd Tanous         {
6353b00f5dSEd Tanous             BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2);
6453b00f5dSEd Tanous             messages::internalError(sensorsAsyncResp->asyncResp->res);
6553b00f5dSEd Tanous             return;
6653b00f5dSEd Tanous         }
6753b00f5dSEd Tanous         sensorsAsyncResp->asyncResp->res.result(
6853b00f5dSEd Tanous             boost::beast::http::status::no_content);
6953b00f5dSEd Tanous     });
7053b00f5dSEd Tanous }
7153b00f5dSEd Tanous 
7253b00f5dSEd Tanous inline void afterGetChassisPath(
7353b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
74*0885057cSEd Tanous     std::vector<nlohmann::json::object_t>& powerControlCollections,
7553b00f5dSEd Tanous     const std::optional<std::string>& chassisPath)
7653b00f5dSEd Tanous {
774bb3dc34SCarol Wang     if (!chassisPath)
784bb3dc34SCarol Wang     {
7962598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't find valid chassis path ");
8053b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
8153b00f5dSEd Tanous                                    sensorsAsyncResp->chassisId);
824bb3dc34SCarol Wang         return;
834bb3dc34SCarol Wang     }
844bb3dc34SCarol Wang 
854bb3dc34SCarol Wang     if (powerControlCollections.size() != 1)
864bb3dc34SCarol Wang     {
8762598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
8853b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
8953b00f5dSEd Tanous                                    "PowerControl");
904bb3dc34SCarol Wang         return;
914bb3dc34SCarol Wang     }
924bb3dc34SCarol Wang 
934bb3dc34SCarol Wang     auto& item = powerControlCollections[0];
944bb3dc34SCarol Wang 
954bb3dc34SCarol Wang     std::optional<uint32_t> value;
96*0885057cSEd Tanous     if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res,
97*0885057cSEd Tanous                                    "PowerLimit/LimitInWatts", value))
984bb3dc34SCarol Wang     {
994bb3dc34SCarol Wang         return;
1004bb3dc34SCarol Wang     }
1014bb3dc34SCarol Wang     if (!value)
1024bb3dc34SCarol Wang     {
1034bb3dc34SCarol Wang         return;
1044bb3dc34SCarol Wang     }
1051e1e598dSJonathan Doman     sdbusplus::asio::getProperty<bool>(
1061e1e598dSJonathan Doman         *crow::connections::systemBus, "xyz.openbmc_project.Settings",
1071e1e598dSJonathan Doman         "/xyz/openbmc_project/control/host0/power_cap",
1081e1e598dSJonathan Doman         "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
10953b00f5dSEd Tanous         std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
1104bb3dc34SCarol Wang }
1114bb3dc34SCarol Wang 
11253b00f5dSEd Tanous inline void afterPowerCapSettingGet(
11353b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
1145e7e2dc5SEd Tanous     const boost::system::error_code& ec,
11553b00f5dSEd Tanous     const dbus::utility::DBusPropertiesMap& properties)
11653b00f5dSEd Tanous {
117028f7ebcSEddie James     if (ec)
118028f7ebcSEddie James     {
119002d39b4SEd Tanous         messages::internalError(sensorAsyncResp->asyncResp->res);
12053b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
121028f7ebcSEddie James         return;
122028f7ebcSEddie James     }
123028f7ebcSEddie James 
1247e860f15SJohn Edward Broadbent     nlohmann::json& tempArray =
125002d39b4SEd Tanous         sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
126028f7ebcSEddie James 
1277e860f15SJohn Edward Broadbent     // Put multiple "sensors" into a single PowerControl, 0,
1287e860f15SJohn Edward Broadbent     // so only create the first one
129028f7ebcSEddie James     if (tempArray.empty())
130028f7ebcSEddie James     {
1317ab06f49SGunnar Mills         // Mandatory properties odata.id and MemberId
1327ab06f49SGunnar Mills         // A warning without a odata.type
1331476687dSEd Tanous         nlohmann::json::object_t powerControl;
134002d39b4SEd Tanous         powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
135002d39b4SEd Tanous         powerControl["@odata.id"] = "/redfish/v1/Chassis/" +
1367ab06f49SGunnar Mills                                     sensorAsyncResp->chassisId +
1371476687dSEd Tanous                                     "/Power#/PowerControl/0";
1381476687dSEd Tanous         powerControl["Name"] = "Chassis Power Control";
1391476687dSEd Tanous         powerControl["MemberId"] = "0";
140b2ba3072SPatrick Williams         tempArray.emplace_back(std::move(powerControl));
141028f7ebcSEddie James     }
142028f7ebcSEddie James 
143028f7ebcSEddie James     nlohmann::json& sensorJson = tempArray.back();
144028f7ebcSEddie James     bool enabled = false;
145028f7ebcSEddie James     double powerCap = 0.0;
146028f7ebcSEddie James     int64_t scale = 0;
147028f7ebcSEddie James 
14853b00f5dSEd Tanous     for (const std::pair<std::string, dbus::utility::DbusVariantType>&
14953b00f5dSEd Tanous              property : properties)
150028f7ebcSEddie James     {
15155f79e6fSEd Tanous         if (property.first == "Scale")
152028f7ebcSEddie James         {
15353b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
154028f7ebcSEddie James 
155e662eae8SEd Tanous             if (i != nullptr)
156028f7ebcSEddie James             {
157028f7ebcSEddie James                 scale = *i;
158028f7ebcSEddie James             }
159028f7ebcSEddie James         }
16055f79e6fSEd Tanous         else if (property.first == "PowerCap")
161028f7ebcSEddie James         {
162002d39b4SEd Tanous             const double* d = std::get_if<double>(&property.second);
16353b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
16453b00f5dSEd Tanous             const uint32_t* u = std::get_if<uint32_t>(&property.second);
165028f7ebcSEddie James 
166e662eae8SEd Tanous             if (d != nullptr)
167028f7ebcSEddie James             {
168028f7ebcSEddie James                 powerCap = *d;
169028f7ebcSEddie James             }
170e662eae8SEd Tanous             else if (i != nullptr)
171028f7ebcSEddie James             {
172271584abSEd Tanous                 powerCap = static_cast<double>(*i);
173028f7ebcSEddie James             }
174e662eae8SEd Tanous             else if (u != nullptr)
175028f7ebcSEddie James             {
176028f7ebcSEddie James                 powerCap = *u;
177028f7ebcSEddie James             }
178028f7ebcSEddie James         }
17955f79e6fSEd Tanous         else if (property.first == "PowerCapEnable")
180028f7ebcSEddie James         {
181002d39b4SEd Tanous             const bool* b = std::get_if<bool>(&property.second);
182028f7ebcSEddie James 
183e662eae8SEd Tanous             if (b != nullptr)
184028f7ebcSEddie James             {
185028f7ebcSEddie James                 enabled = *b;
186028f7ebcSEddie James             }
187028f7ebcSEddie James         }
188028f7ebcSEddie James     }
189028f7ebcSEddie James 
1907e860f15SJohn Edward Broadbent     // LimitException is Mandatory attribute as per OCP
1917e860f15SJohn Edward Broadbent     // Baseline Profile – v1.0.0, so currently making it
1927e860f15SJohn Edward Broadbent     // "NoAction" as default value to make it OCP Compliant.
1935a64a6f3SJoshi-Mansi     sensorJson["PowerLimit"]["LimitException"] = "NoAction";
1945a64a6f3SJoshi-Mansi 
195028f7ebcSEddie James     if (enabled)
196028f7ebcSEddie James     {
1977e860f15SJohn Edward Broadbent         // Redfish specification indicates PowerLimit should
1987e860f15SJohn Edward Broadbent         // be null if the limit is not enabled.
19953b00f5dSEd Tanous         sensorJson["PowerLimit"]["LimitInWatts"] = powerCap *
20053b00f5dSEd Tanous                                                    std::pow(10, scale);
201028f7ebcSEddie James     }
20253b00f5dSEd Tanous }
20353b00f5dSEd Tanous 
20453b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
20553b00f5dSEd Tanous inline void
20653b00f5dSEd Tanous     afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
20753b00f5dSEd Tanous                     const boost::system::error_code& ec2,
20853b00f5dSEd Tanous                     const Mapper& chassisPaths)
20953b00f5dSEd Tanous {
21053b00f5dSEd Tanous     if (ec2)
21153b00f5dSEd Tanous     {
21253b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
21353b00f5dSEd Tanous                          ec2);
21453b00f5dSEd Tanous         return;
21553b00f5dSEd Tanous     }
21653b00f5dSEd Tanous 
21753b00f5dSEd Tanous     bool found = false;
21853b00f5dSEd Tanous     for (const std::string& chassis : chassisPaths)
21953b00f5dSEd Tanous     {
22053b00f5dSEd Tanous         size_t len = std::string::npos;
22153b00f5dSEd Tanous         size_t lastPos = chassis.rfind('/');
22253b00f5dSEd Tanous         if (lastPos == std::string::npos)
22353b00f5dSEd Tanous         {
22453b00f5dSEd Tanous             continue;
22553b00f5dSEd Tanous         }
22653b00f5dSEd Tanous 
22753b00f5dSEd Tanous         if (lastPos == chassis.size() - 1)
22853b00f5dSEd Tanous         {
22953b00f5dSEd Tanous             size_t end = lastPos;
23053b00f5dSEd Tanous             lastPos = chassis.rfind('/', lastPos - 1);
23153b00f5dSEd Tanous             if (lastPos == std::string::npos)
23253b00f5dSEd Tanous             {
23353b00f5dSEd Tanous                 continue;
23453b00f5dSEd Tanous             }
23553b00f5dSEd Tanous 
23653b00f5dSEd Tanous             len = end - (lastPos + 1);
23753b00f5dSEd Tanous         }
23853b00f5dSEd Tanous 
23953b00f5dSEd Tanous         std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
24053b00f5dSEd Tanous         if (interfaceChassisName == sensorAsyncResp->chassisId)
24153b00f5dSEd Tanous         {
24253b00f5dSEd Tanous             found = true;
24353b00f5dSEd Tanous             break;
24453b00f5dSEd Tanous         }
24553b00f5dSEd Tanous     }
24653b00f5dSEd Tanous 
24753b00f5dSEd Tanous     if (!found)
24853b00f5dSEd Tanous     {
24953b00f5dSEd Tanous         BMCWEB_LOG_DEBUG("Power Limit not present for {}",
25053b00f5dSEd Tanous                          sensorAsyncResp->chassisId);
25153b00f5dSEd Tanous         return;
25253b00f5dSEd Tanous     }
253028f7ebcSEddie James 
254d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
255d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, "xyz.openbmc_project.Settings",
256028f7ebcSEddie James         "/xyz/openbmc_project/control/host0/power_cap",
257d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Control.Power.Cap",
25853b00f5dSEd Tanous         [sensorAsyncResp](const boost::system::error_code& ec,
25953b00f5dSEd Tanous                           const dbus::utility::DBusPropertiesMap& properties
26053b00f5dSEd Tanous 
26153b00f5dSEd Tanous         ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
26253b00f5dSEd Tanous }
26353b00f5dSEd Tanous 
26453b00f5dSEd Tanous inline void
26553b00f5dSEd Tanous     handleChassisPowerGet(App& app, const crow::Request& req,
26653b00f5dSEd Tanous                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26753b00f5dSEd Tanous                           const std::string& chassisName)
26853b00f5dSEd Tanous {
26953b00f5dSEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
27053b00f5dSEd Tanous     {
27153b00f5dSEd Tanous         return;
27253b00f5dSEd Tanous     }
27353b00f5dSEd Tanous     asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
27453b00f5dSEd Tanous 
27553b00f5dSEd Tanous     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
27653b00f5dSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
27753b00f5dSEd Tanous         sensors::node::power);
27853b00f5dSEd Tanous 
27953b00f5dSEd Tanous     getChassisData(sensorAsyncResp);
28053b00f5dSEd Tanous 
28153b00f5dSEd Tanous     // This callback verifies that the power limit is only provided
28253b00f5dSEd Tanous     // for the chassis that implements the Chassis inventory item.
28353b00f5dSEd Tanous     // This prevents things like power supplies providing the
28453b00f5dSEd Tanous     // chassis power limit
285028f7ebcSEddie James 
2867a1dbc48SGeorge Liu     constexpr std::array<std::string_view, 2> interfaces = {
287f857e9aeSAppaRao Puli         "xyz.openbmc_project.Inventory.Item.Board",
2887a1dbc48SGeorge Liu         "xyz.openbmc_project.Inventory.Item.Chassis"};
2897a1dbc48SGeorge Liu 
29053b00f5dSEd Tanous     dbus::utility::getSubTreePaths(
29153b00f5dSEd Tanous         "/xyz/openbmc_project/inventory", 0, interfaces,
29253b00f5dSEd Tanous         std::bind_front(afterGetChassis, sensorAsyncResp));
29353b00f5dSEd Tanous }
2944bb3dc34SCarol Wang 
29553b00f5dSEd Tanous inline void
29653b00f5dSEd Tanous     handleChassisPowerPatch(App& app, const crow::Request& req,
2977e860f15SJohn Edward Broadbent                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
29853b00f5dSEd Tanous                             const std::string& chassisName)
29953b00f5dSEd Tanous {
3003ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
30145ca1b86SEd Tanous     {
30245ca1b86SEd Tanous         return;
30345ca1b86SEd Tanous     }
3048d1b46d7Szhanghch05     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
30502da7c5aSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
306a0ec28b6SAdrian Ambrożewicz         sensors::node::power);
3074bb3dc34SCarol Wang 
308*0885057cSEd Tanous     std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
309*0885057cSEd Tanous     std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
3104bb3dc34SCarol Wang 
311002d39b4SEd Tanous     if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res,
312002d39b4SEd Tanous                                   "PowerControl", powerCtlCollections,
313002d39b4SEd Tanous                                   "Voltages", voltageCollections))
3144bb3dc34SCarol Wang     {
3154bb3dc34SCarol Wang         return;
3164bb3dc34SCarol Wang     }
3174bb3dc34SCarol Wang 
3184bb3dc34SCarol Wang     if (powerCtlCollections)
3194bb3dc34SCarol Wang     {
32053b00f5dSEd Tanous         redfish::chassis_utils::getValidChassisPath(
32153b00f5dSEd Tanous             sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
32253b00f5dSEd Tanous             std::bind_front(afterGetChassisPath, sensorAsyncResp,
32353b00f5dSEd Tanous                             *powerCtlCollections));
3244bb3dc34SCarol Wang     }
3254bb3dc34SCarol Wang     if (voltageCollections)
3264bb3dc34SCarol Wang     {
327*0885057cSEd Tanous         std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
3284bb3dc34SCarol Wang             allCollections;
329*0885057cSEd Tanous         allCollections.emplace("Voltages", std::move(*voltageCollections));
33080ac4024SBruce Lee         setSensorsOverride(sensorAsyncResp, allCollections);
3314bb3dc34SCarol Wang     }
33253b00f5dSEd Tanous }
33353b00f5dSEd Tanous 
33453b00f5dSEd Tanous inline void requestRoutesPower(App& app)
33553b00f5dSEd Tanous {
33653b00f5dSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
33753b00f5dSEd Tanous         .privileges(redfish::privileges::getPower)
33853b00f5dSEd Tanous         .methods(boost::beast::http::verb::get)(
33953b00f5dSEd Tanous             std::bind_front(handleChassisPowerGet, std::ref(app)));
34053b00f5dSEd Tanous 
34153b00f5dSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
34253b00f5dSEd Tanous         .privileges(redfish::privileges::patchPower)
34353b00f5dSEd Tanous         .methods(boost::beast::http::verb::patch)(
34453b00f5dSEd Tanous             std::bind_front(handleChassisPowerPatch, std::ref(app)));
345413961deSRichard Marian Thomaiyar }
3462474adfaSEd Tanous 
3472474adfaSEd Tanous } // namespace redfish
348