xref: /openbmc/bmcweb/features/redfish/lib/power.hpp (revision deae6a789444debc4724fb6902fc5def299afbee)
12474adfaSEd Tanous /*
26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation
36be832e2SEd Tanous Copyright (c) 2018 Ampere Computing LLC
46be832e2SEd Tanous 
56be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
66be832e2SEd Tanous you may not use this file except in compliance with the License.
76be832e2SEd Tanous You may obtain a copy of the License at
86be832e2SEd Tanous 
96be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
106be832e2SEd Tanous 
116be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
126be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
136be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
146be832e2SEd Tanous See the License for the specific language governing permissions and
156be832e2SEd Tanous limitations under the License.
162474adfaSEd Tanous */
172474adfaSEd Tanous #pragma once
182474adfaSEd Tanous 
193ccb3adbSEd Tanous #include "app.hpp"
207a1dbc48SGeorge Liu #include "dbus_utility.hpp"
21539d8c6bSEd Tanous #include "generated/enums/power.hpp"
223ccb3adbSEd Tanous #include "query.hpp"
233ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
242474adfaSEd Tanous #include "sensors.hpp"
250d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp"
265b90429aSEd Tanous #include "utils/json_utils.hpp"
27c9563608SJanet Adkins #include "utils/sensor_utils.hpp"
282474adfaSEd Tanous 
29d1bde9e5SKrzysztof Grobelny #include <sdbusplus/asio/property.hpp>
307e860f15SJohn Edward Broadbent 
317a1dbc48SGeorge Liu #include <array>
320885057cSEd Tanous #include <string>
337a1dbc48SGeorge Liu #include <string_view>
340885057cSEd Tanous #include <vector>
357a1dbc48SGeorge Liu 
362474adfaSEd Tanous namespace redfish
372474adfaSEd Tanous {
3853b00f5dSEd Tanous 
3953b00f5dSEd Tanous inline void afterGetPowerCapEnable(
408d1b46d7Szhanghch05     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
4153b00f5dSEd Tanous     uint32_t valueToSet, const boost::system::error_code& ec,
4253b00f5dSEd Tanous     bool powerCapEnable)
434bb3dc34SCarol Wang {
4453b00f5dSEd Tanous     if (ec)
4553b00f5dSEd Tanous     {
4653b00f5dSEd Tanous         messages::internalError(sensorsAsyncResp->asyncResp->res);
4753b00f5dSEd Tanous         BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
4853b00f5dSEd Tanous         return;
4953b00f5dSEd Tanous     }
5053b00f5dSEd Tanous     if (!powerCapEnable)
5153b00f5dSEd Tanous     {
5253b00f5dSEd Tanous         messages::actionNotSupported(
5353b00f5dSEd Tanous             sensorsAsyncResp->asyncResp->res,
5453b00f5dSEd Tanous             "Setting LimitInWatts when PowerLimit feature is disabled");
5553b00f5dSEd Tanous         BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
5653b00f5dSEd Tanous         return;
5753b00f5dSEd Tanous     }
5853b00f5dSEd Tanous 
59e93abac6SGinu George     setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
60e93abac6SGinu George                     "xyz.openbmc_project.Settings",
6187c44966SAsmitha Karunanithi                     sdbusplus::message::object_path(
6287c44966SAsmitha Karunanithi                         "/xyz/openbmc_project/control/host0/power_cap"),
6387c44966SAsmitha Karunanithi                     "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
64e93abac6SGinu George                     valueToSet);
6553b00f5dSEd Tanous }
6653b00f5dSEd Tanous 
6753b00f5dSEd Tanous inline void afterGetChassisPath(
6853b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
690885057cSEd Tanous     std::vector<nlohmann::json::object_t>& powerControlCollections,
7053b00f5dSEd Tanous     const std::optional<std::string>& chassisPath)
7153b00f5dSEd Tanous {
724bb3dc34SCarol Wang     if (!chassisPath)
734bb3dc34SCarol Wang     {
7462598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't find valid chassis path ");
7553b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
7653b00f5dSEd Tanous                                    sensorsAsyncResp->chassisId);
774bb3dc34SCarol Wang         return;
784bb3dc34SCarol Wang     }
794bb3dc34SCarol Wang 
804bb3dc34SCarol Wang     if (powerControlCollections.size() != 1)
814bb3dc34SCarol Wang     {
8262598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
8353b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
8453b00f5dSEd Tanous                                    "PowerControl");
854bb3dc34SCarol Wang         return;
864bb3dc34SCarol Wang     }
874bb3dc34SCarol Wang 
884bb3dc34SCarol Wang     auto& item = powerControlCollections[0];
894bb3dc34SCarol Wang 
904bb3dc34SCarol Wang     std::optional<uint32_t> value;
91afc474aeSMyung Bae     if (!json_util::readJsonObject( //
92afc474aeSMyung Bae             item, sensorsAsyncResp->asyncResp->res, //
93afc474aeSMyung Bae             "PowerLimit/LimitInWatts", value //
94afc474aeSMyung Bae             ))
954bb3dc34SCarol Wang     {
964bb3dc34SCarol Wang         return;
974bb3dc34SCarol Wang     }
984bb3dc34SCarol Wang     if (!value)
994bb3dc34SCarol Wang     {
1004bb3dc34SCarol Wang         return;
1014bb3dc34SCarol Wang     }
102*deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
103*deae6a78SEd Tanous         "xyz.openbmc_project.Settings",
1041e1e598dSJonathan Doman         "/xyz/openbmc_project/control/host0/power_cap",
1051e1e598dSJonathan Doman         "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
10653b00f5dSEd Tanous         std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
1074bb3dc34SCarol Wang }
1084bb3dc34SCarol Wang 
10953b00f5dSEd Tanous inline void afterPowerCapSettingGet(
11053b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
1115e7e2dc5SEd Tanous     const boost::system::error_code& ec,
11253b00f5dSEd Tanous     const dbus::utility::DBusPropertiesMap& properties)
11353b00f5dSEd Tanous {
114028f7ebcSEddie James     if (ec)
115028f7ebcSEddie James     {
116002d39b4SEd Tanous         messages::internalError(sensorAsyncResp->asyncResp->res);
11753b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
118028f7ebcSEddie James         return;
119028f7ebcSEddie James     }
120028f7ebcSEddie James 
1217e860f15SJohn Edward Broadbent     nlohmann::json& tempArray =
122002d39b4SEd Tanous         sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
123028f7ebcSEddie James 
1247e860f15SJohn Edward Broadbent     // Put multiple "sensors" into a single PowerControl, 0,
1257e860f15SJohn Edward Broadbent     // so only create the first one
126028f7ebcSEddie James     if (tempArray.empty())
127028f7ebcSEddie James     {
1287ab06f49SGunnar Mills         // Mandatory properties odata.id and MemberId
1297ab06f49SGunnar Mills         // A warning without a odata.type
1301476687dSEd Tanous         nlohmann::json::object_t powerControl;
131002d39b4SEd Tanous         powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
132bd79bce8SPatrick Williams         powerControl["@odata.id"] =
133bd79bce8SPatrick Williams             "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
1341476687dSEd Tanous             "/Power#/PowerControl/0";
1351476687dSEd Tanous         powerControl["Name"] = "Chassis Power Control";
1361476687dSEd Tanous         powerControl["MemberId"] = "0";
137b2ba3072SPatrick Williams         tempArray.emplace_back(std::move(powerControl));
138028f7ebcSEddie James     }
139028f7ebcSEddie James 
140028f7ebcSEddie James     nlohmann::json& sensorJson = tempArray.back();
141028f7ebcSEddie James     bool enabled = false;
142028f7ebcSEddie James     double powerCap = 0.0;
143028f7ebcSEddie James     int64_t scale = 0;
144028f7ebcSEddie James 
14553b00f5dSEd Tanous     for (const std::pair<std::string, dbus::utility::DbusVariantType>&
14653b00f5dSEd Tanous              property : properties)
147028f7ebcSEddie James     {
14855f79e6fSEd Tanous         if (property.first == "Scale")
149028f7ebcSEddie James         {
15053b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
151028f7ebcSEddie James 
152e662eae8SEd Tanous             if (i != nullptr)
153028f7ebcSEddie James             {
154028f7ebcSEddie James                 scale = *i;
155028f7ebcSEddie James             }
156028f7ebcSEddie James         }
15755f79e6fSEd Tanous         else if (property.first == "PowerCap")
158028f7ebcSEddie James         {
159002d39b4SEd Tanous             const double* d = std::get_if<double>(&property.second);
16053b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
16153b00f5dSEd Tanous             const uint32_t* u = std::get_if<uint32_t>(&property.second);
162028f7ebcSEddie James 
163e662eae8SEd Tanous             if (d != nullptr)
164028f7ebcSEddie James             {
165028f7ebcSEddie James                 powerCap = *d;
166028f7ebcSEddie James             }
167e662eae8SEd Tanous             else if (i != nullptr)
168028f7ebcSEddie James             {
169271584abSEd Tanous                 powerCap = static_cast<double>(*i);
170028f7ebcSEddie James             }
171e662eae8SEd Tanous             else if (u != nullptr)
172028f7ebcSEddie James             {
173028f7ebcSEddie James                 powerCap = *u;
174028f7ebcSEddie James             }
175028f7ebcSEddie James         }
17655f79e6fSEd Tanous         else if (property.first == "PowerCapEnable")
177028f7ebcSEddie James         {
178002d39b4SEd Tanous             const bool* b = std::get_if<bool>(&property.second);
179028f7ebcSEddie James 
180e662eae8SEd Tanous             if (b != nullptr)
181028f7ebcSEddie James             {
182028f7ebcSEddie James                 enabled = *b;
183028f7ebcSEddie James             }
184028f7ebcSEddie James         }
185028f7ebcSEddie James     }
186028f7ebcSEddie James 
1877e860f15SJohn Edward Broadbent     // LimitException is Mandatory attribute as per OCP
188c9563608SJanet Adkins     // Baseline Profile - v1.0.0, so currently making it
1897e860f15SJohn Edward Broadbent     // "NoAction" as default value to make it OCP Compliant.
190539d8c6bSEd Tanous     sensorJson["PowerLimit"]["LimitException"] =
191539d8c6bSEd Tanous         power::PowerLimitException::NoAction;
1925a64a6f3SJoshi-Mansi 
193028f7ebcSEddie James     if (enabled)
194028f7ebcSEddie James     {
1957e860f15SJohn Edward Broadbent         // Redfish specification indicates PowerLimit should
1967e860f15SJohn Edward Broadbent         // be null if the limit is not enabled.
197bd79bce8SPatrick Williams         sensorJson["PowerLimit"]["LimitInWatts"] =
198bd79bce8SPatrick Williams             powerCap * std::pow(10, scale);
199028f7ebcSEddie James     }
20053b00f5dSEd Tanous }
20153b00f5dSEd Tanous 
20253b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
203bd79bce8SPatrick Williams inline void afterGetChassis(
204bd79bce8SPatrick Williams     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
205bd79bce8SPatrick Williams     const boost::system::error_code& ec2, const Mapper& chassisPaths)
20653b00f5dSEd Tanous {
20753b00f5dSEd Tanous     if (ec2)
20853b00f5dSEd Tanous     {
20953b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
21053b00f5dSEd Tanous                          ec2);
21153b00f5dSEd Tanous         return;
21253b00f5dSEd Tanous     }
21353b00f5dSEd Tanous 
21453b00f5dSEd Tanous     bool found = false;
21553b00f5dSEd Tanous     for (const std::string& chassis : chassisPaths)
21653b00f5dSEd Tanous     {
21753b00f5dSEd Tanous         size_t len = std::string::npos;
21853b00f5dSEd Tanous         size_t lastPos = chassis.rfind('/');
21953b00f5dSEd Tanous         if (lastPos == std::string::npos)
22053b00f5dSEd Tanous         {
22153b00f5dSEd Tanous             continue;
22253b00f5dSEd Tanous         }
22353b00f5dSEd Tanous 
22453b00f5dSEd Tanous         if (lastPos == chassis.size() - 1)
22553b00f5dSEd Tanous         {
22653b00f5dSEd Tanous             size_t end = lastPos;
22753b00f5dSEd Tanous             lastPos = chassis.rfind('/', lastPos - 1);
22853b00f5dSEd Tanous             if (lastPos == std::string::npos)
22953b00f5dSEd Tanous             {
23053b00f5dSEd Tanous                 continue;
23153b00f5dSEd Tanous             }
23253b00f5dSEd Tanous 
23353b00f5dSEd Tanous             len = end - (lastPos + 1);
23453b00f5dSEd Tanous         }
23553b00f5dSEd Tanous 
23653b00f5dSEd Tanous         std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
23753b00f5dSEd Tanous         if (interfaceChassisName == sensorAsyncResp->chassisId)
23853b00f5dSEd Tanous         {
23953b00f5dSEd Tanous             found = true;
24053b00f5dSEd Tanous             break;
24153b00f5dSEd Tanous         }
24253b00f5dSEd Tanous     }
24353b00f5dSEd Tanous 
24453b00f5dSEd Tanous     if (!found)
24553b00f5dSEd Tanous     {
24653b00f5dSEd Tanous         BMCWEB_LOG_DEBUG("Power Limit not present for {}",
24753b00f5dSEd Tanous                          sensorAsyncResp->chassisId);
24853b00f5dSEd Tanous         return;
24953b00f5dSEd Tanous     }
250028f7ebcSEddie James 
251*deae6a78SEd Tanous     dbus::utility::getAllProperties(
252*deae6a78SEd Tanous         "xyz.openbmc_project.Settings",
253028f7ebcSEddie James         "/xyz/openbmc_project/control/host0/power_cap",
254d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Control.Power.Cap",
25553b00f5dSEd Tanous         [sensorAsyncResp](const boost::system::error_code& ec,
25653b00f5dSEd Tanous                           const dbus::utility::DBusPropertiesMap& properties
25753b00f5dSEd Tanous 
25853b00f5dSEd Tanous         ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
25953b00f5dSEd Tanous }
26053b00f5dSEd Tanous 
26153b00f5dSEd Tanous inline void
26253b00f5dSEd Tanous     handleChassisPowerGet(App& app, const crow::Request& req,
26353b00f5dSEd Tanous                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26453b00f5dSEd Tanous                           const std::string& chassisName)
26553b00f5dSEd Tanous {
26653b00f5dSEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
26753b00f5dSEd Tanous     {
26853b00f5dSEd Tanous         return;
26953b00f5dSEd Tanous     }
27053b00f5dSEd Tanous     asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
27153b00f5dSEd Tanous 
27253b00f5dSEd Tanous     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
27353b00f5dSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
2740c728b42SJanet Adkins         sensor_utils::chassisSubNodeToString(
2750c728b42SJanet Adkins             sensor_utils::ChassisSubNode::powerNode));
27653b00f5dSEd Tanous 
27753b00f5dSEd Tanous     getChassisData(sensorAsyncResp);
27853b00f5dSEd Tanous 
27953b00f5dSEd Tanous     // This callback verifies that the power limit is only provided
28053b00f5dSEd Tanous     // for the chassis that implements the Chassis inventory item.
28153b00f5dSEd Tanous     // This prevents things like power supplies providing the
28253b00f5dSEd Tanous     // chassis power limit
283028f7ebcSEddie James 
2847a1dbc48SGeorge Liu     constexpr std::array<std::string_view, 2> interfaces = {
285f857e9aeSAppaRao Puli         "xyz.openbmc_project.Inventory.Item.Board",
2867a1dbc48SGeorge Liu         "xyz.openbmc_project.Inventory.Item.Chassis"};
2877a1dbc48SGeorge Liu 
28853b00f5dSEd Tanous     dbus::utility::getSubTreePaths(
28953b00f5dSEd Tanous         "/xyz/openbmc_project/inventory", 0, interfaces,
29053b00f5dSEd Tanous         std::bind_front(afterGetChassis, sensorAsyncResp));
29153b00f5dSEd Tanous }
2924bb3dc34SCarol Wang 
29353b00f5dSEd Tanous inline void
29453b00f5dSEd Tanous     handleChassisPowerPatch(App& app, const crow::Request& req,
2957e860f15SJohn Edward Broadbent                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
29653b00f5dSEd Tanous                             const std::string& chassisName)
29753b00f5dSEd Tanous {
2983ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
29945ca1b86SEd Tanous     {
30045ca1b86SEd Tanous         return;
30145ca1b86SEd Tanous     }
3028d1b46d7Szhanghch05     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
30302da7c5aSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
3040c728b42SJanet Adkins         sensor_utils::chassisSubNodeToString(
3050c728b42SJanet Adkins             sensor_utils::ChassisSubNode::powerNode));
3064bb3dc34SCarol Wang 
3070885057cSEd Tanous     std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
3080885057cSEd Tanous     std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
3094bb3dc34SCarol Wang 
310afc474aeSMyung Bae     if (!json_util::readJsonPatch( //
311afc474aeSMyung Bae             req, sensorAsyncResp->asyncResp->res, //
312afc474aeSMyung Bae             "PowerControl", powerCtlCollections, //
313afc474aeSMyung Bae             "Voltages", voltageCollections //
314afc474aeSMyung Bae             ))
3154bb3dc34SCarol Wang     {
3164bb3dc34SCarol Wang         return;
3174bb3dc34SCarol Wang     }
3184bb3dc34SCarol Wang 
3194bb3dc34SCarol Wang     if (powerCtlCollections)
3204bb3dc34SCarol Wang     {
32153b00f5dSEd Tanous         redfish::chassis_utils::getValidChassisPath(
32253b00f5dSEd Tanous             sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
32353b00f5dSEd Tanous             std::bind_front(afterGetChassisPath, sensorAsyncResp,
32453b00f5dSEd Tanous                             *powerCtlCollections));
3254bb3dc34SCarol Wang     }
3264bb3dc34SCarol Wang     if (voltageCollections)
3274bb3dc34SCarol Wang     {
3280885057cSEd Tanous         std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
3294bb3dc34SCarol Wang             allCollections;
3300885057cSEd Tanous         allCollections.emplace("Voltages", std::move(*voltageCollections));
33180ac4024SBruce Lee         setSensorsOverride(sensorAsyncResp, allCollections);
3324bb3dc34SCarol Wang     }
33353b00f5dSEd Tanous }
33453b00f5dSEd Tanous 
33553b00f5dSEd Tanous inline void requestRoutesPower(App& app)
33653b00f5dSEd Tanous {
33753b00f5dSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
33853b00f5dSEd Tanous         .privileges(redfish::privileges::getPower)
33953b00f5dSEd Tanous         .methods(boost::beast::http::verb::get)(
34053b00f5dSEd Tanous             std::bind_front(handleChassisPowerGet, std::ref(app)));
34153b00f5dSEd Tanous 
34253b00f5dSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
34353b00f5dSEd Tanous         .privileges(redfish::privileges::patchPower)
34453b00f5dSEd Tanous         .methods(boost::beast::http::verb::patch)(
34553b00f5dSEd Tanous             std::bind_front(handleChassisPowerPatch, std::ref(app)));
346413961deSRichard Marian Thomaiyar }
3472474adfaSEd Tanous 
3482474adfaSEd Tanous } // namespace redfish
349