xref: /openbmc/bmcweb/features/redfish/lib/power.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Ampere Computing LLC
56be832e2SEd Tanous 
62474adfaSEd Tanous #pragma once
72474adfaSEd Tanous 
83ccb3adbSEd Tanous #include "app.hpp"
97a1dbc48SGeorge Liu #include "dbus_utility.hpp"
10539d8c6bSEd Tanous #include "generated/enums/power.hpp"
113ccb3adbSEd Tanous #include "query.hpp"
123ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
132474adfaSEd Tanous #include "sensors.hpp"
140d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp"
155b90429aSEd Tanous #include "utils/json_utils.hpp"
16c9563608SJanet Adkins #include "utils/sensor_utils.hpp"
172474adfaSEd Tanous 
18d1bde9e5SKrzysztof Grobelny #include <sdbusplus/asio/property.hpp>
197e860f15SJohn Edward Broadbent 
207a1dbc48SGeorge Liu #include <array>
210885057cSEd Tanous #include <string>
227a1dbc48SGeorge Liu #include <string_view>
230885057cSEd Tanous #include <vector>
247a1dbc48SGeorge Liu 
252474adfaSEd Tanous namespace redfish
262474adfaSEd Tanous {
2753b00f5dSEd Tanous 
2853b00f5dSEd Tanous inline void afterGetPowerCapEnable(
298d1b46d7Szhanghch05     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
3053b00f5dSEd Tanous     uint32_t valueToSet, const boost::system::error_code& ec,
3153b00f5dSEd Tanous     bool powerCapEnable)
324bb3dc34SCarol Wang {
3353b00f5dSEd Tanous     if (ec)
3453b00f5dSEd Tanous     {
3553b00f5dSEd Tanous         messages::internalError(sensorsAsyncResp->asyncResp->res);
3653b00f5dSEd Tanous         BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
3753b00f5dSEd Tanous         return;
3853b00f5dSEd Tanous     }
3953b00f5dSEd Tanous     if (!powerCapEnable)
4053b00f5dSEd Tanous     {
4153b00f5dSEd Tanous         messages::actionNotSupported(
4253b00f5dSEd Tanous             sensorsAsyncResp->asyncResp->res,
4353b00f5dSEd Tanous             "Setting LimitInWatts when PowerLimit feature is disabled");
4453b00f5dSEd Tanous         BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
4553b00f5dSEd Tanous         return;
4653b00f5dSEd Tanous     }
4753b00f5dSEd Tanous 
48e93abac6SGinu George     setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
49e93abac6SGinu George                     "xyz.openbmc_project.Settings",
5087c44966SAsmitha Karunanithi                     sdbusplus::message::object_path(
5187c44966SAsmitha Karunanithi                         "/xyz/openbmc_project/control/host0/power_cap"),
5287c44966SAsmitha Karunanithi                     "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
53e93abac6SGinu George                     valueToSet);
5453b00f5dSEd Tanous }
5553b00f5dSEd Tanous 
5653b00f5dSEd Tanous inline void afterGetChassisPath(
5753b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
580885057cSEd Tanous     std::vector<nlohmann::json::object_t>& powerControlCollections,
5953b00f5dSEd Tanous     const std::optional<std::string>& chassisPath)
6053b00f5dSEd Tanous {
614bb3dc34SCarol Wang     if (!chassisPath)
624bb3dc34SCarol Wang     {
6362598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't find valid chassis path ");
6453b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
6553b00f5dSEd Tanous                                    sensorsAsyncResp->chassisId);
664bb3dc34SCarol Wang         return;
674bb3dc34SCarol Wang     }
684bb3dc34SCarol Wang 
694bb3dc34SCarol Wang     if (powerControlCollections.size() != 1)
704bb3dc34SCarol Wang     {
7162598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
7253b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
7353b00f5dSEd Tanous                                    "PowerControl");
744bb3dc34SCarol Wang         return;
754bb3dc34SCarol Wang     }
764bb3dc34SCarol Wang 
774bb3dc34SCarol Wang     auto& item = powerControlCollections[0];
784bb3dc34SCarol Wang 
794bb3dc34SCarol Wang     std::optional<uint32_t> value;
80afc474aeSMyung Bae     if (!json_util::readJsonObject( //
81afc474aeSMyung Bae             item, sensorsAsyncResp->asyncResp->res, //
82afc474aeSMyung Bae             "PowerLimit/LimitInWatts", value //
83afc474aeSMyung Bae             ))
844bb3dc34SCarol Wang     {
854bb3dc34SCarol Wang         return;
864bb3dc34SCarol Wang     }
874bb3dc34SCarol Wang     if (!value)
884bb3dc34SCarol Wang     {
894bb3dc34SCarol Wang         return;
904bb3dc34SCarol Wang     }
91deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
92deae6a78SEd Tanous         "xyz.openbmc_project.Settings",
931e1e598dSJonathan Doman         "/xyz/openbmc_project/control/host0/power_cap",
941e1e598dSJonathan Doman         "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
9553b00f5dSEd Tanous         std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
964bb3dc34SCarol Wang }
974bb3dc34SCarol Wang 
9853b00f5dSEd Tanous inline void afterPowerCapSettingGet(
9953b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
1005e7e2dc5SEd Tanous     const boost::system::error_code& ec,
10153b00f5dSEd Tanous     const dbus::utility::DBusPropertiesMap& properties)
10253b00f5dSEd Tanous {
103028f7ebcSEddie James     if (ec)
104028f7ebcSEddie James     {
105002d39b4SEd Tanous         messages::internalError(sensorAsyncResp->asyncResp->res);
10653b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
107028f7ebcSEddie James         return;
108028f7ebcSEddie James     }
109028f7ebcSEddie James 
1107e860f15SJohn Edward Broadbent     nlohmann::json& tempArray =
111002d39b4SEd Tanous         sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
112028f7ebcSEddie James 
1137e860f15SJohn Edward Broadbent     // Put multiple "sensors" into a single PowerControl, 0,
1147e860f15SJohn Edward Broadbent     // so only create the first one
115028f7ebcSEddie James     if (tempArray.empty())
116028f7ebcSEddie James     {
1177ab06f49SGunnar Mills         // Mandatory properties odata.id and MemberId
1187ab06f49SGunnar Mills         // A warning without a odata.type
1191476687dSEd Tanous         nlohmann::json::object_t powerControl;
120002d39b4SEd Tanous         powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
121bd79bce8SPatrick Williams         powerControl["@odata.id"] =
122bd79bce8SPatrick Williams             "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
1231476687dSEd Tanous             "/Power#/PowerControl/0";
1241476687dSEd Tanous         powerControl["Name"] = "Chassis Power Control";
1251476687dSEd Tanous         powerControl["MemberId"] = "0";
126b2ba3072SPatrick Williams         tempArray.emplace_back(std::move(powerControl));
127028f7ebcSEddie James     }
128028f7ebcSEddie James 
129028f7ebcSEddie James     nlohmann::json& sensorJson = tempArray.back();
130028f7ebcSEddie James     bool enabled = false;
131028f7ebcSEddie James     double powerCap = 0.0;
132028f7ebcSEddie James     int64_t scale = 0;
133028f7ebcSEddie James 
13453b00f5dSEd Tanous     for (const std::pair<std::string, dbus::utility::DbusVariantType>&
13553b00f5dSEd Tanous              property : properties)
136028f7ebcSEddie James     {
13755f79e6fSEd Tanous         if (property.first == "Scale")
138028f7ebcSEddie James         {
13953b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
140028f7ebcSEddie James 
141e662eae8SEd Tanous             if (i != nullptr)
142028f7ebcSEddie James             {
143028f7ebcSEddie James                 scale = *i;
144028f7ebcSEddie James             }
145028f7ebcSEddie James         }
14655f79e6fSEd Tanous         else if (property.first == "PowerCap")
147028f7ebcSEddie James         {
148002d39b4SEd Tanous             const double* d = std::get_if<double>(&property.second);
14953b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
15053b00f5dSEd Tanous             const uint32_t* u = std::get_if<uint32_t>(&property.second);
151028f7ebcSEddie James 
152e662eae8SEd Tanous             if (d != nullptr)
153028f7ebcSEddie James             {
154028f7ebcSEddie James                 powerCap = *d;
155028f7ebcSEddie James             }
156e662eae8SEd Tanous             else if (i != nullptr)
157028f7ebcSEddie James             {
158271584abSEd Tanous                 powerCap = static_cast<double>(*i);
159028f7ebcSEddie James             }
160e662eae8SEd Tanous             else if (u != nullptr)
161028f7ebcSEddie James             {
162028f7ebcSEddie James                 powerCap = *u;
163028f7ebcSEddie James             }
164028f7ebcSEddie James         }
16555f79e6fSEd Tanous         else if (property.first == "PowerCapEnable")
166028f7ebcSEddie James         {
167002d39b4SEd Tanous             const bool* b = std::get_if<bool>(&property.second);
168028f7ebcSEddie James 
169e662eae8SEd Tanous             if (b != nullptr)
170028f7ebcSEddie James             {
171028f7ebcSEddie James                 enabled = *b;
172028f7ebcSEddie James             }
173028f7ebcSEddie James         }
174028f7ebcSEddie James     }
175028f7ebcSEddie James 
1767e860f15SJohn Edward Broadbent     // LimitException is Mandatory attribute as per OCP
177c9563608SJanet Adkins     // Baseline Profile - v1.0.0, so currently making it
1787e860f15SJohn Edward Broadbent     // "NoAction" as default value to make it OCP Compliant.
179539d8c6bSEd Tanous     sensorJson["PowerLimit"]["LimitException"] =
180539d8c6bSEd Tanous         power::PowerLimitException::NoAction;
1815a64a6f3SJoshi-Mansi 
182028f7ebcSEddie James     if (enabled)
183028f7ebcSEddie James     {
1847e860f15SJohn Edward Broadbent         // Redfish specification indicates PowerLimit should
1857e860f15SJohn Edward Broadbent         // be null if the limit is not enabled.
186bd79bce8SPatrick Williams         sensorJson["PowerLimit"]["LimitInWatts"] =
187bd79bce8SPatrick Williams             powerCap * std::pow(10, scale);
188028f7ebcSEddie James     }
18953b00f5dSEd Tanous }
19053b00f5dSEd Tanous 
19153b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
192bd79bce8SPatrick Williams inline void afterGetChassis(
193bd79bce8SPatrick Williams     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
194bd79bce8SPatrick Williams     const boost::system::error_code& ec2, const Mapper& chassisPaths)
19553b00f5dSEd Tanous {
19653b00f5dSEd Tanous     if (ec2)
19753b00f5dSEd Tanous     {
19853b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
19953b00f5dSEd Tanous                          ec2);
20053b00f5dSEd Tanous         return;
20153b00f5dSEd Tanous     }
20253b00f5dSEd Tanous 
20353b00f5dSEd Tanous     bool found = false;
20453b00f5dSEd Tanous     for (const std::string& chassis : chassisPaths)
20553b00f5dSEd Tanous     {
20653b00f5dSEd Tanous         size_t len = std::string::npos;
20753b00f5dSEd Tanous         size_t lastPos = chassis.rfind('/');
20853b00f5dSEd Tanous         if (lastPos == std::string::npos)
20953b00f5dSEd Tanous         {
21053b00f5dSEd Tanous             continue;
21153b00f5dSEd Tanous         }
21253b00f5dSEd Tanous 
21353b00f5dSEd Tanous         if (lastPos == chassis.size() - 1)
21453b00f5dSEd Tanous         {
21553b00f5dSEd Tanous             size_t end = lastPos;
21653b00f5dSEd Tanous             lastPos = chassis.rfind('/', lastPos - 1);
21753b00f5dSEd Tanous             if (lastPos == std::string::npos)
21853b00f5dSEd Tanous             {
21953b00f5dSEd Tanous                 continue;
22053b00f5dSEd Tanous             }
22153b00f5dSEd Tanous 
22253b00f5dSEd Tanous             len = end - (lastPos + 1);
22353b00f5dSEd Tanous         }
22453b00f5dSEd Tanous 
22553b00f5dSEd Tanous         std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
22653b00f5dSEd Tanous         if (interfaceChassisName == sensorAsyncResp->chassisId)
22753b00f5dSEd Tanous         {
22853b00f5dSEd Tanous             found = true;
22953b00f5dSEd Tanous             break;
23053b00f5dSEd Tanous         }
23153b00f5dSEd Tanous     }
23253b00f5dSEd Tanous 
23353b00f5dSEd Tanous     if (!found)
23453b00f5dSEd Tanous     {
23553b00f5dSEd Tanous         BMCWEB_LOG_DEBUG("Power Limit not present for {}",
23653b00f5dSEd Tanous                          sensorAsyncResp->chassisId);
23753b00f5dSEd Tanous         return;
23853b00f5dSEd Tanous     }
239028f7ebcSEddie James 
240deae6a78SEd Tanous     dbus::utility::getAllProperties(
241deae6a78SEd Tanous         "xyz.openbmc_project.Settings",
242028f7ebcSEddie James         "/xyz/openbmc_project/control/host0/power_cap",
243d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Control.Power.Cap",
24453b00f5dSEd Tanous         [sensorAsyncResp](const boost::system::error_code& ec,
24553b00f5dSEd Tanous                           const dbus::utility::DBusPropertiesMap& properties
24653b00f5dSEd Tanous 
24753b00f5dSEd Tanous         ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
24853b00f5dSEd Tanous }
24953b00f5dSEd Tanous 
25053b00f5dSEd Tanous inline void
25153b00f5dSEd Tanous     handleChassisPowerGet(App& app, const crow::Request& req,
25253b00f5dSEd Tanous                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
25353b00f5dSEd Tanous                           const std::string& chassisName)
25453b00f5dSEd Tanous {
25553b00f5dSEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
25653b00f5dSEd Tanous     {
25753b00f5dSEd Tanous         return;
25853b00f5dSEd Tanous     }
25953b00f5dSEd Tanous     asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
26053b00f5dSEd Tanous 
26153b00f5dSEd Tanous     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
26253b00f5dSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
2630c728b42SJanet Adkins         sensor_utils::chassisSubNodeToString(
2640c728b42SJanet Adkins             sensor_utils::ChassisSubNode::powerNode));
26553b00f5dSEd Tanous 
26653b00f5dSEd Tanous     getChassisData(sensorAsyncResp);
26753b00f5dSEd Tanous 
26853b00f5dSEd Tanous     // This callback verifies that the power limit is only provided
26953b00f5dSEd Tanous     // for the chassis that implements the Chassis inventory item.
27053b00f5dSEd Tanous     // This prevents things like power supplies providing the
27153b00f5dSEd Tanous     // chassis power limit
272028f7ebcSEddie James 
2737a1dbc48SGeorge Liu     constexpr std::array<std::string_view, 2> interfaces = {
274f857e9aeSAppaRao Puli         "xyz.openbmc_project.Inventory.Item.Board",
2757a1dbc48SGeorge Liu         "xyz.openbmc_project.Inventory.Item.Chassis"};
2767a1dbc48SGeorge Liu 
27753b00f5dSEd Tanous     dbus::utility::getSubTreePaths(
27853b00f5dSEd Tanous         "/xyz/openbmc_project/inventory", 0, interfaces,
27953b00f5dSEd Tanous         std::bind_front(afterGetChassis, sensorAsyncResp));
28053b00f5dSEd Tanous }
2814bb3dc34SCarol Wang 
28253b00f5dSEd Tanous inline void
28353b00f5dSEd Tanous     handleChassisPowerPatch(App& app, const crow::Request& req,
2847e860f15SJohn Edward Broadbent                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
28553b00f5dSEd Tanous                             const std::string& chassisName)
28653b00f5dSEd Tanous {
2873ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
28845ca1b86SEd Tanous     {
28945ca1b86SEd Tanous         return;
29045ca1b86SEd Tanous     }
2918d1b46d7Szhanghch05     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
29202da7c5aSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
2930c728b42SJanet Adkins         sensor_utils::chassisSubNodeToString(
2940c728b42SJanet Adkins             sensor_utils::ChassisSubNode::powerNode));
2954bb3dc34SCarol Wang 
2960885057cSEd Tanous     std::optional<std::vector<nlohmann::json::object_t>> voltageCollections;
2970885057cSEd Tanous     std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections;
2984bb3dc34SCarol Wang 
299afc474aeSMyung Bae     if (!json_util::readJsonPatch( //
300afc474aeSMyung Bae             req, sensorAsyncResp->asyncResp->res, //
301afc474aeSMyung Bae             "PowerControl", powerCtlCollections, //
302afc474aeSMyung Bae             "Voltages", voltageCollections //
303afc474aeSMyung Bae             ))
3044bb3dc34SCarol Wang     {
3054bb3dc34SCarol Wang         return;
3064bb3dc34SCarol Wang     }
3074bb3dc34SCarol Wang 
3084bb3dc34SCarol Wang     if (powerCtlCollections)
3094bb3dc34SCarol Wang     {
31053b00f5dSEd Tanous         redfish::chassis_utils::getValidChassisPath(
31153b00f5dSEd Tanous             sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId,
31253b00f5dSEd Tanous             std::bind_front(afterGetChassisPath, sensorAsyncResp,
31353b00f5dSEd Tanous                             *powerCtlCollections));
3144bb3dc34SCarol Wang     }
3154bb3dc34SCarol Wang     if (voltageCollections)
3164bb3dc34SCarol Wang     {
3170885057cSEd Tanous         std::unordered_map<std::string, std::vector<nlohmann::json::object_t>>
3184bb3dc34SCarol Wang             allCollections;
3190885057cSEd Tanous         allCollections.emplace("Voltages", std::move(*voltageCollections));
32080ac4024SBruce Lee         setSensorsOverride(sensorAsyncResp, allCollections);
3214bb3dc34SCarol Wang     }
32253b00f5dSEd Tanous }
32353b00f5dSEd Tanous 
32453b00f5dSEd Tanous inline void requestRoutesPower(App& app)
32553b00f5dSEd Tanous {
32653b00f5dSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
32753b00f5dSEd Tanous         .privileges(redfish::privileges::getPower)
32853b00f5dSEd Tanous         .methods(boost::beast::http::verb::get)(
32953b00f5dSEd Tanous             std::bind_front(handleChassisPowerGet, std::ref(app)));
33053b00f5dSEd Tanous 
33153b00f5dSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/")
33253b00f5dSEd Tanous         .privileges(redfish::privileges::patchPower)
33353b00f5dSEd Tanous         .methods(boost::beast::http::verb::patch)(
33453b00f5dSEd Tanous             std::bind_front(handleChassisPowerPatch, std::ref(app)));
335413961deSRichard Marian Thomaiyar }
3362474adfaSEd Tanous 
3372474adfaSEd Tanous } // namespace redfish
338