xref: /openbmc/bmcweb/features/redfish/lib/power.hpp (revision 3f95a2772fc4fd57459fdfffbee52d8831fc4f35)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
440e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Ampere Computing LLC
56be832e2SEd Tanous 
62474adfaSEd Tanous #pragma once
72474adfaSEd Tanous 
83ccb3adbSEd Tanous #include "app.hpp"
9d7857201SEd Tanous #include "async_resp.hpp"
107a1dbc48SGeorge Liu #include "dbus_utility.hpp"
11d7857201SEd Tanous #include "error_messages.hpp"
12539d8c6bSEd Tanous #include "generated/enums/power.hpp"
13d7857201SEd Tanous #include "http_request.hpp"
14d7857201SEd Tanous #include "logging.hpp"
153ccb3adbSEd Tanous #include "query.hpp"
163ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
172474adfaSEd Tanous #include "sensors.hpp"
180d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp"
19d7857201SEd Tanous #include "utils/dbus_utils.hpp"
205b90429aSEd Tanous #include "utils/json_utils.hpp"
21c9563608SJanet Adkins #include "utils/sensor_utils.hpp"
222474adfaSEd Tanous 
23d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
24d7857201SEd Tanous #include <nlohmann/json.hpp>
25d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
267e860f15SJohn Edward Broadbent 
27d7857201SEd Tanous #include <cmath>
28d7857201SEd Tanous #include <cstddef>
29d7857201SEd Tanous #include <cstdint>
30d7857201SEd Tanous #include <functional>
31d7857201SEd Tanous #include <memory>
32d7857201SEd Tanous #include <optional>
330885057cSEd Tanous #include <string>
347a1dbc48SGeorge Liu #include <string_view>
35d7857201SEd Tanous #include <unordered_map>
36d7857201SEd Tanous #include <utility>
37d7857201SEd Tanous #include <variant>
380885057cSEd Tanous #include <vector>
397a1dbc48SGeorge Liu 
402474adfaSEd Tanous namespace redfish
412474adfaSEd Tanous {
4253b00f5dSEd Tanous 
4353b00f5dSEd Tanous inline void afterGetPowerCapEnable(
448d1b46d7Szhanghch05     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
4553b00f5dSEd Tanous     uint32_t valueToSet, const boost::system::error_code& ec,
4653b00f5dSEd Tanous     bool powerCapEnable)
474bb3dc34SCarol Wang {
4853b00f5dSEd Tanous     if (ec)
4953b00f5dSEd Tanous     {
5053b00f5dSEd Tanous         messages::internalError(sensorsAsyncResp->asyncResp->res);
5153b00f5dSEd Tanous         BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec);
5253b00f5dSEd Tanous         return;
5353b00f5dSEd Tanous     }
5453b00f5dSEd Tanous     if (!powerCapEnable)
5553b00f5dSEd Tanous     {
5653b00f5dSEd Tanous         messages::actionNotSupported(
5753b00f5dSEd Tanous             sensorsAsyncResp->asyncResp->res,
5853b00f5dSEd Tanous             "Setting LimitInWatts when PowerLimit feature is disabled");
5953b00f5dSEd Tanous         BMCWEB_LOG_ERROR("PowerLimit feature is disabled ");
6053b00f5dSEd Tanous         return;
6153b00f5dSEd Tanous     }
6253b00f5dSEd Tanous 
63e93abac6SGinu George     setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl",
64e93abac6SGinu George                     "xyz.openbmc_project.Settings",
6587c44966SAsmitha Karunanithi                     sdbusplus::message::object_path(
6687c44966SAsmitha Karunanithi                         "/xyz/openbmc_project/control/host0/power_cap"),
6787c44966SAsmitha Karunanithi                     "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
68e93abac6SGinu George                     valueToSet);
6953b00f5dSEd Tanous }
7053b00f5dSEd Tanous 
7153b00f5dSEd Tanous inline void afterGetChassisPath(
7253b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp,
730885057cSEd Tanous     std::vector<nlohmann::json::object_t>& powerControlCollections,
7453b00f5dSEd Tanous     const std::optional<std::string>& chassisPath)
7553b00f5dSEd Tanous {
764bb3dc34SCarol Wang     if (!chassisPath)
774bb3dc34SCarol Wang     {
7862598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't find valid chassis path ");
7953b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis",
8053b00f5dSEd Tanous                                    sensorsAsyncResp->chassisId);
814bb3dc34SCarol Wang         return;
824bb3dc34SCarol Wang     }
834bb3dc34SCarol Wang 
844bb3dc34SCarol Wang     if (powerControlCollections.size() != 1)
854bb3dc34SCarol Wang     {
8662598e31SEd Tanous         BMCWEB_LOG_WARNING("Don't support multiple hosts at present ");
8753b00f5dSEd Tanous         messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power",
8853b00f5dSEd Tanous                                    "PowerControl");
894bb3dc34SCarol Wang         return;
904bb3dc34SCarol Wang     }
914bb3dc34SCarol Wang 
924bb3dc34SCarol Wang     auto& item = powerControlCollections[0];
934bb3dc34SCarol Wang 
944bb3dc34SCarol Wang     std::optional<uint32_t> value;
95afc474aeSMyung Bae     if (!json_util::readJsonObject(                 //
96afc474aeSMyung Bae             item, sensorsAsyncResp->asyncResp->res, //
97afc474aeSMyung Bae             "PowerLimit/LimitInWatts", value        //
98afc474aeSMyung Bae             ))
994bb3dc34SCarol Wang     {
1004bb3dc34SCarol Wang         return;
1014bb3dc34SCarol Wang     }
1024bb3dc34SCarol Wang     if (!value)
1034bb3dc34SCarol Wang     {
1044bb3dc34SCarol Wang         return;
1054bb3dc34SCarol Wang     }
106deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
107deae6a78SEd Tanous         "xyz.openbmc_project.Settings",
1081e1e598dSJonathan Doman         "/xyz/openbmc_project/control/host0/power_cap",
1091e1e598dSJonathan Doman         "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable",
11053b00f5dSEd Tanous         std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value));
1114bb3dc34SCarol Wang }
1124bb3dc34SCarol Wang 
11353b00f5dSEd Tanous inline void afterPowerCapSettingGet(
11453b00f5dSEd Tanous     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
1155e7e2dc5SEd Tanous     const boost::system::error_code& ec,
11653b00f5dSEd Tanous     const dbus::utility::DBusPropertiesMap& properties)
11753b00f5dSEd Tanous {
118028f7ebcSEddie James     if (ec)
119028f7ebcSEddie James     {
120002d39b4SEd Tanous         messages::internalError(sensorAsyncResp->asyncResp->res);
12153b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec);
122028f7ebcSEddie James         return;
123028f7ebcSEddie James     }
124028f7ebcSEddie James 
1257e860f15SJohn Edward Broadbent     nlohmann::json& tempArray =
126002d39b4SEd Tanous         sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"];
127028f7ebcSEddie James 
1287e860f15SJohn Edward Broadbent     // Put multiple "sensors" into a single PowerControl, 0,
1297e860f15SJohn Edward Broadbent     // so only create the first one
130028f7ebcSEddie James     if (tempArray.empty())
131028f7ebcSEddie James     {
1327ab06f49SGunnar Mills         // Mandatory properties odata.id and MemberId
1337ab06f49SGunnar Mills         // A warning without a odata.type
1341476687dSEd Tanous         nlohmann::json::object_t powerControl;
135002d39b4SEd Tanous         powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl";
136bd79bce8SPatrick Williams         powerControl["@odata.id"] =
137bd79bce8SPatrick Williams             "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId +
1381476687dSEd Tanous             "/Power#/PowerControl/0";
1391476687dSEd Tanous         powerControl["Name"] = "Chassis Power Control";
1401476687dSEd Tanous         powerControl["MemberId"] = "0";
141b2ba3072SPatrick Williams         tempArray.emplace_back(std::move(powerControl));
142028f7ebcSEddie James     }
143028f7ebcSEddie James 
144028f7ebcSEddie James     nlohmann::json& sensorJson = tempArray.back();
145028f7ebcSEddie James     bool enabled = false;
146028f7ebcSEddie James     double powerCap = 0.0;
147028f7ebcSEddie James     int64_t scale = 0;
148028f7ebcSEddie James 
14953b00f5dSEd Tanous     for (const std::pair<std::string, dbus::utility::DbusVariantType>&
15053b00f5dSEd Tanous              property : properties)
151028f7ebcSEddie James     {
15255f79e6fSEd Tanous         if (property.first == "Scale")
153028f7ebcSEddie James         {
15453b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
155028f7ebcSEddie James 
156e662eae8SEd Tanous             if (i != nullptr)
157028f7ebcSEddie James             {
158028f7ebcSEddie James                 scale = *i;
159028f7ebcSEddie James             }
160028f7ebcSEddie James         }
16155f79e6fSEd Tanous         else if (property.first == "PowerCap")
162028f7ebcSEddie James         {
163002d39b4SEd Tanous             const double* d = std::get_if<double>(&property.second);
16453b00f5dSEd Tanous             const int64_t* i = std::get_if<int64_t>(&property.second);
16553b00f5dSEd Tanous             const uint32_t* u = std::get_if<uint32_t>(&property.second);
166028f7ebcSEddie James 
167e662eae8SEd Tanous             if (d != nullptr)
168028f7ebcSEddie James             {
169028f7ebcSEddie James                 powerCap = *d;
170028f7ebcSEddie James             }
171e662eae8SEd Tanous             else if (i != nullptr)
172028f7ebcSEddie James             {
173271584abSEd Tanous                 powerCap = static_cast<double>(*i);
174028f7ebcSEddie James             }
175e662eae8SEd Tanous             else if (u != nullptr)
176028f7ebcSEddie James             {
177028f7ebcSEddie James                 powerCap = *u;
178028f7ebcSEddie James             }
179028f7ebcSEddie James         }
18055f79e6fSEd Tanous         else if (property.first == "PowerCapEnable")
181028f7ebcSEddie James         {
182002d39b4SEd Tanous             const bool* b = std::get_if<bool>(&property.second);
183028f7ebcSEddie James 
184e662eae8SEd Tanous             if (b != nullptr)
185028f7ebcSEddie James             {
186028f7ebcSEddie James                 enabled = *b;
187028f7ebcSEddie James             }
188028f7ebcSEddie James         }
189028f7ebcSEddie James     }
190028f7ebcSEddie James 
1917e860f15SJohn Edward Broadbent     // LimitException is Mandatory attribute as per OCP
192c9563608SJanet Adkins     // Baseline Profile - v1.0.0, so currently making it
1937e860f15SJohn Edward Broadbent     // "NoAction" as default value to make it OCP Compliant.
194539d8c6bSEd Tanous     sensorJson["PowerLimit"]["LimitException"] =
195539d8c6bSEd Tanous         power::PowerLimitException::NoAction;
1965a64a6f3SJoshi-Mansi 
197028f7ebcSEddie James     if (enabled)
198028f7ebcSEddie James     {
1997e860f15SJohn Edward Broadbent         // Redfish specification indicates PowerLimit should
2007e860f15SJohn Edward Broadbent         // be null if the limit is not enabled.
201bd79bce8SPatrick Williams         sensorJson["PowerLimit"]["LimitInWatts"] =
202bd79bce8SPatrick Williams             powerCap * std::pow(10, scale);
203028f7ebcSEddie James     }
20453b00f5dSEd Tanous }
20553b00f5dSEd Tanous 
20653b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse;
207bd79bce8SPatrick Williams inline void afterGetChassis(
208bd79bce8SPatrick Williams     const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp,
209bd79bce8SPatrick Williams     const boost::system::error_code& ec2, const Mapper& chassisPaths)
21053b00f5dSEd Tanous {
21153b00f5dSEd Tanous     if (ec2)
21253b00f5dSEd Tanous     {
21353b00f5dSEd Tanous         BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}",
21453b00f5dSEd Tanous                          ec2);
21553b00f5dSEd Tanous         return;
21653b00f5dSEd Tanous     }
21753b00f5dSEd Tanous 
21853b00f5dSEd Tanous     bool found = false;
21953b00f5dSEd Tanous     for (const std::string& chassis : chassisPaths)
22053b00f5dSEd Tanous     {
22153b00f5dSEd Tanous         size_t len = std::string::npos;
22253b00f5dSEd Tanous         size_t lastPos = chassis.rfind('/');
22353b00f5dSEd Tanous         if (lastPos == std::string::npos)
22453b00f5dSEd Tanous         {
22553b00f5dSEd Tanous             continue;
22653b00f5dSEd Tanous         }
22753b00f5dSEd Tanous 
22853b00f5dSEd Tanous         if (lastPos == chassis.size() - 1)
22953b00f5dSEd Tanous         {
23053b00f5dSEd Tanous             size_t end = lastPos;
23153b00f5dSEd Tanous             lastPos = chassis.rfind('/', lastPos - 1);
23253b00f5dSEd Tanous             if (lastPos == std::string::npos)
23353b00f5dSEd Tanous             {
23453b00f5dSEd Tanous                 continue;
23553b00f5dSEd Tanous             }
23653b00f5dSEd Tanous 
23753b00f5dSEd Tanous             len = end - (lastPos + 1);
23853b00f5dSEd Tanous         }
23953b00f5dSEd Tanous 
24053b00f5dSEd Tanous         std::string interfaceChassisName = chassis.substr(lastPos + 1, len);
24153b00f5dSEd Tanous         if (interfaceChassisName == sensorAsyncResp->chassisId)
24253b00f5dSEd Tanous         {
24353b00f5dSEd Tanous             found = true;
24453b00f5dSEd Tanous             break;
24553b00f5dSEd Tanous         }
24653b00f5dSEd Tanous     }
24753b00f5dSEd Tanous 
24853b00f5dSEd Tanous     if (!found)
24953b00f5dSEd Tanous     {
25053b00f5dSEd Tanous         BMCWEB_LOG_DEBUG("Power Limit not present for {}",
25153b00f5dSEd Tanous                          sensorAsyncResp->chassisId);
25253b00f5dSEd Tanous         return;
25353b00f5dSEd Tanous     }
254028f7ebcSEddie James 
255deae6a78SEd Tanous     dbus::utility::getAllProperties(
256deae6a78SEd Tanous         "xyz.openbmc_project.Settings",
257028f7ebcSEddie James         "/xyz/openbmc_project/control/host0/power_cap",
258d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Control.Power.Cap",
25953b00f5dSEd Tanous         [sensorAsyncResp](const boost::system::error_code& ec,
26053b00f5dSEd Tanous                           const dbus::utility::DBusPropertiesMap& properties
26153b00f5dSEd Tanous 
26253b00f5dSEd Tanous         ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); });
26353b00f5dSEd Tanous }
26453b00f5dSEd Tanous 
265504af5a0SPatrick Williams inline void handleChassisPowerGet(
266504af5a0SPatrick Williams     App& app, const crow::Request& req,
26753b00f5dSEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26853b00f5dSEd Tanous     const std::string& chassisName)
26953b00f5dSEd Tanous {
27053b00f5dSEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
27153b00f5dSEd Tanous     {
27253b00f5dSEd Tanous         return;
27353b00f5dSEd Tanous     }
27453b00f5dSEd Tanous     asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array();
27553b00f5dSEd Tanous 
27653b00f5dSEd Tanous     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
27753b00f5dSEd Tanous         asyncResp, chassisName, sensors::dbus::powerPaths,
2780c728b42SJanet Adkins         sensor_utils::chassisSubNodeToString(
2790c728b42SJanet Adkins             sensor_utils::ChassisSubNode::powerNode));
28053b00f5dSEd Tanous 
28153b00f5dSEd Tanous     getChassisData(sensorAsyncResp);
28253b00f5dSEd Tanous 
28353b00f5dSEd Tanous     // This callback verifies that the power limit is only provided
28453b00f5dSEd Tanous     // for the chassis that implements the Chassis inventory item.
28553b00f5dSEd Tanous     // This prevents things like power supplies providing the
28653b00f5dSEd Tanous     // chassis power limit
287028f7ebcSEddie James 
28853b00f5dSEd Tanous     dbus::utility::getSubTreePaths(
289*3f95a277SMyung Bae         "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
29053b00f5dSEd Tanous         std::bind_front(afterGetChassis, sensorAsyncResp));
29153b00f5dSEd Tanous }
2924bb3dc34SCarol Wang 
293504af5a0SPatrick Williams inline void handleChassisPowerPatch(
294504af5a0SPatrick Williams     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