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" 9*d7857201SEd Tanous #include "async_resp.hpp" 107a1dbc48SGeorge Liu #include "dbus_utility.hpp" 11*d7857201SEd Tanous #include "error_messages.hpp" 12539d8c6bSEd Tanous #include "generated/enums/power.hpp" 13*d7857201SEd Tanous #include "http_request.hpp" 14*d7857201SEd 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" 19*d7857201SEd Tanous #include "utils/dbus_utils.hpp" 205b90429aSEd Tanous #include "utils/json_utils.hpp" 21c9563608SJanet Adkins #include "utils/sensor_utils.hpp" 222474adfaSEd Tanous 23*d7857201SEd Tanous #include <boost/beast/http/verb.hpp> 24*d7857201SEd Tanous #include <nlohmann/json.hpp> 25*d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp> 267e860f15SJohn Edward Broadbent 277a1dbc48SGeorge Liu #include <array> 28*d7857201SEd Tanous #include <cmath> 29*d7857201SEd Tanous #include <cstddef> 30*d7857201SEd Tanous #include <cstdint> 31*d7857201SEd Tanous #include <functional> 32*d7857201SEd Tanous #include <memory> 33*d7857201SEd Tanous #include <optional> 340885057cSEd Tanous #include <string> 357a1dbc48SGeorge Liu #include <string_view> 36*d7857201SEd Tanous #include <unordered_map> 37*d7857201SEd Tanous #include <utility> 38*d7857201SEd Tanous #include <variant> 390885057cSEd Tanous #include <vector> 407a1dbc48SGeorge Liu 412474adfaSEd Tanous namespace redfish 422474adfaSEd Tanous { 4353b00f5dSEd Tanous 4453b00f5dSEd Tanous inline void afterGetPowerCapEnable( 458d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 4653b00f5dSEd Tanous uint32_t valueToSet, const boost::system::error_code& ec, 4753b00f5dSEd Tanous bool powerCapEnable) 484bb3dc34SCarol Wang { 4953b00f5dSEd Tanous if (ec) 5053b00f5dSEd Tanous { 5153b00f5dSEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 5253b00f5dSEd Tanous BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec); 5353b00f5dSEd Tanous return; 5453b00f5dSEd Tanous } 5553b00f5dSEd Tanous if (!powerCapEnable) 5653b00f5dSEd Tanous { 5753b00f5dSEd Tanous messages::actionNotSupported( 5853b00f5dSEd Tanous sensorsAsyncResp->asyncResp->res, 5953b00f5dSEd Tanous "Setting LimitInWatts when PowerLimit feature is disabled"); 6053b00f5dSEd Tanous BMCWEB_LOG_ERROR("PowerLimit feature is disabled "); 6153b00f5dSEd Tanous return; 6253b00f5dSEd Tanous } 6353b00f5dSEd Tanous 64e93abac6SGinu George setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl", 65e93abac6SGinu George "xyz.openbmc_project.Settings", 6687c44966SAsmitha Karunanithi sdbusplus::message::object_path( 6787c44966SAsmitha Karunanithi "/xyz/openbmc_project/control/host0/power_cap"), 6887c44966SAsmitha Karunanithi "xyz.openbmc_project.Control.Power.Cap", "PowerCap", 69e93abac6SGinu George valueToSet); 7053b00f5dSEd Tanous } 7153b00f5dSEd Tanous 7253b00f5dSEd Tanous inline void afterGetChassisPath( 7353b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 740885057cSEd 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; 96afc474aeSMyung Bae if (!json_util::readJsonObject( // 97afc474aeSMyung Bae item, sensorsAsyncResp->asyncResp->res, // 98afc474aeSMyung Bae "PowerLimit/LimitInWatts", value // 99afc474aeSMyung Bae )) 1004bb3dc34SCarol Wang { 1014bb3dc34SCarol Wang return; 1024bb3dc34SCarol Wang } 1034bb3dc34SCarol Wang if (!value) 1044bb3dc34SCarol Wang { 1054bb3dc34SCarol Wang return; 1064bb3dc34SCarol Wang } 107deae6a78SEd Tanous dbus::utility::getProperty<bool>( 108deae6a78SEd Tanous "xyz.openbmc_project.Settings", 1091e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 1101e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 11153b00f5dSEd Tanous std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value)); 1124bb3dc34SCarol Wang } 1134bb3dc34SCarol Wang 11453b00f5dSEd Tanous inline void afterPowerCapSettingGet( 11553b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 1165e7e2dc5SEd Tanous const boost::system::error_code& ec, 11753b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties) 11853b00f5dSEd Tanous { 119028f7ebcSEddie James if (ec) 120028f7ebcSEddie James { 121002d39b4SEd Tanous messages::internalError(sensorAsyncResp->asyncResp->res); 12253b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec); 123028f7ebcSEddie James return; 124028f7ebcSEddie James } 125028f7ebcSEddie James 1267e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 127002d39b4SEd Tanous sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 128028f7ebcSEddie James 1297e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 1307e860f15SJohn Edward Broadbent // so only create the first one 131028f7ebcSEddie James if (tempArray.empty()) 132028f7ebcSEddie James { 1337ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 1347ab06f49SGunnar Mills // A warning without a odata.type 1351476687dSEd Tanous nlohmann::json::object_t powerControl; 136002d39b4SEd Tanous powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 137bd79bce8SPatrick Williams powerControl["@odata.id"] = 138bd79bce8SPatrick Williams "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId + 1391476687dSEd Tanous "/Power#/PowerControl/0"; 1401476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 1411476687dSEd Tanous powerControl["MemberId"] = "0"; 142b2ba3072SPatrick Williams tempArray.emplace_back(std::move(powerControl)); 143028f7ebcSEddie James } 144028f7ebcSEddie James 145028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 146028f7ebcSEddie James bool enabled = false; 147028f7ebcSEddie James double powerCap = 0.0; 148028f7ebcSEddie James int64_t scale = 0; 149028f7ebcSEddie James 15053b00f5dSEd Tanous for (const std::pair<std::string, dbus::utility::DbusVariantType>& 15153b00f5dSEd Tanous property : properties) 152028f7ebcSEddie James { 15355f79e6fSEd Tanous if (property.first == "Scale") 154028f7ebcSEddie James { 15553b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 156028f7ebcSEddie James 157e662eae8SEd Tanous if (i != nullptr) 158028f7ebcSEddie James { 159028f7ebcSEddie James scale = *i; 160028f7ebcSEddie James } 161028f7ebcSEddie James } 16255f79e6fSEd Tanous else if (property.first == "PowerCap") 163028f7ebcSEddie James { 164002d39b4SEd Tanous const double* d = std::get_if<double>(&property.second); 16553b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 16653b00f5dSEd Tanous const uint32_t* u = std::get_if<uint32_t>(&property.second); 167028f7ebcSEddie James 168e662eae8SEd Tanous if (d != nullptr) 169028f7ebcSEddie James { 170028f7ebcSEddie James powerCap = *d; 171028f7ebcSEddie James } 172e662eae8SEd Tanous else if (i != nullptr) 173028f7ebcSEddie James { 174271584abSEd Tanous powerCap = static_cast<double>(*i); 175028f7ebcSEddie James } 176e662eae8SEd Tanous else if (u != nullptr) 177028f7ebcSEddie James { 178028f7ebcSEddie James powerCap = *u; 179028f7ebcSEddie James } 180028f7ebcSEddie James } 18155f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 182028f7ebcSEddie James { 183002d39b4SEd Tanous const bool* b = std::get_if<bool>(&property.second); 184028f7ebcSEddie James 185e662eae8SEd Tanous if (b != nullptr) 186028f7ebcSEddie James { 187028f7ebcSEddie James enabled = *b; 188028f7ebcSEddie James } 189028f7ebcSEddie James } 190028f7ebcSEddie James } 191028f7ebcSEddie James 1927e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 193c9563608SJanet Adkins // Baseline Profile - v1.0.0, so currently making it 1947e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 195539d8c6bSEd Tanous sensorJson["PowerLimit"]["LimitException"] = 196539d8c6bSEd Tanous power::PowerLimitException::NoAction; 1975a64a6f3SJoshi-Mansi 198028f7ebcSEddie James if (enabled) 199028f7ebcSEddie James { 2007e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 2017e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 202bd79bce8SPatrick Williams sensorJson["PowerLimit"]["LimitInWatts"] = 203bd79bce8SPatrick Williams powerCap * std::pow(10, scale); 204028f7ebcSEddie James } 20553b00f5dSEd Tanous } 20653b00f5dSEd Tanous 20753b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 208bd79bce8SPatrick Williams inline void afterGetChassis( 209bd79bce8SPatrick Williams const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 210bd79bce8SPatrick Williams const boost::system::error_code& ec2, const Mapper& chassisPaths) 21153b00f5dSEd Tanous { 21253b00f5dSEd Tanous if (ec2) 21353b00f5dSEd Tanous { 21453b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}", 21553b00f5dSEd Tanous ec2); 21653b00f5dSEd Tanous return; 21753b00f5dSEd Tanous } 21853b00f5dSEd Tanous 21953b00f5dSEd Tanous bool found = false; 22053b00f5dSEd Tanous for (const std::string& chassis : chassisPaths) 22153b00f5dSEd Tanous { 22253b00f5dSEd Tanous size_t len = std::string::npos; 22353b00f5dSEd Tanous size_t lastPos = chassis.rfind('/'); 22453b00f5dSEd Tanous if (lastPos == std::string::npos) 22553b00f5dSEd Tanous { 22653b00f5dSEd Tanous continue; 22753b00f5dSEd Tanous } 22853b00f5dSEd Tanous 22953b00f5dSEd Tanous if (lastPos == chassis.size() - 1) 23053b00f5dSEd Tanous { 23153b00f5dSEd Tanous size_t end = lastPos; 23253b00f5dSEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 23353b00f5dSEd Tanous if (lastPos == std::string::npos) 23453b00f5dSEd Tanous { 23553b00f5dSEd Tanous continue; 23653b00f5dSEd Tanous } 23753b00f5dSEd Tanous 23853b00f5dSEd Tanous len = end - (lastPos + 1); 23953b00f5dSEd Tanous } 24053b00f5dSEd Tanous 24153b00f5dSEd Tanous std::string interfaceChassisName = chassis.substr(lastPos + 1, len); 24253b00f5dSEd Tanous if (interfaceChassisName == sensorAsyncResp->chassisId) 24353b00f5dSEd Tanous { 24453b00f5dSEd Tanous found = true; 24553b00f5dSEd Tanous break; 24653b00f5dSEd Tanous } 24753b00f5dSEd Tanous } 24853b00f5dSEd Tanous 24953b00f5dSEd Tanous if (!found) 25053b00f5dSEd Tanous { 25153b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit not present for {}", 25253b00f5dSEd Tanous sensorAsyncResp->chassisId); 25353b00f5dSEd Tanous return; 25453b00f5dSEd Tanous } 255028f7ebcSEddie James 256deae6a78SEd Tanous dbus::utility::getAllProperties( 257deae6a78SEd Tanous "xyz.openbmc_project.Settings", 258028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 259d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Control.Power.Cap", 26053b00f5dSEd Tanous [sensorAsyncResp](const boost::system::error_code& ec, 26153b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties 26253b00f5dSEd Tanous 26353b00f5dSEd Tanous ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); }); 26453b00f5dSEd Tanous } 26553b00f5dSEd Tanous 26653b00f5dSEd Tanous inline void 26753b00f5dSEd Tanous handleChassisPowerGet(App& app, const crow::Request& req, 26853b00f5dSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26953b00f5dSEd Tanous const std::string& chassisName) 27053b00f5dSEd Tanous { 27153b00f5dSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 27253b00f5dSEd Tanous { 27353b00f5dSEd Tanous return; 27453b00f5dSEd Tanous } 27553b00f5dSEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 27653b00f5dSEd Tanous 27753b00f5dSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 27853b00f5dSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 2790c728b42SJanet Adkins sensor_utils::chassisSubNodeToString( 2800c728b42SJanet Adkins sensor_utils::ChassisSubNode::powerNode)); 28153b00f5dSEd Tanous 28253b00f5dSEd Tanous getChassisData(sensorAsyncResp); 28353b00f5dSEd Tanous 28453b00f5dSEd Tanous // This callback verifies that the power limit is only provided 28553b00f5dSEd Tanous // for the chassis that implements the Chassis inventory item. 28653b00f5dSEd Tanous // This prevents things like power supplies providing the 28753b00f5dSEd Tanous // chassis power limit 288028f7ebcSEddie James 2897a1dbc48SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 290f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 2917a1dbc48SGeorge Liu "xyz.openbmc_project.Inventory.Item.Chassis"}; 2927a1dbc48SGeorge Liu 29353b00f5dSEd Tanous dbus::utility::getSubTreePaths( 29453b00f5dSEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 29553b00f5dSEd Tanous std::bind_front(afterGetChassis, sensorAsyncResp)); 29653b00f5dSEd Tanous } 2974bb3dc34SCarol Wang 29853b00f5dSEd Tanous inline void 29953b00f5dSEd Tanous handleChassisPowerPatch(App& app, const crow::Request& req, 3007e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 30153b00f5dSEd Tanous const std::string& chassisName) 30253b00f5dSEd Tanous { 3033ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 30445ca1b86SEd Tanous { 30545ca1b86SEd Tanous return; 30645ca1b86SEd Tanous } 3078d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 30802da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 3090c728b42SJanet Adkins sensor_utils::chassisSubNodeToString( 3100c728b42SJanet Adkins sensor_utils::ChassisSubNode::powerNode)); 3114bb3dc34SCarol Wang 3120885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> voltageCollections; 3130885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections; 3144bb3dc34SCarol Wang 315afc474aeSMyung Bae if (!json_util::readJsonPatch( // 316afc474aeSMyung Bae req, sensorAsyncResp->asyncResp->res, // 317afc474aeSMyung Bae "PowerControl", powerCtlCollections, // 318afc474aeSMyung Bae "Voltages", voltageCollections // 319afc474aeSMyung Bae )) 3204bb3dc34SCarol Wang { 3214bb3dc34SCarol Wang return; 3224bb3dc34SCarol Wang } 3234bb3dc34SCarol Wang 3244bb3dc34SCarol Wang if (powerCtlCollections) 3254bb3dc34SCarol Wang { 32653b00f5dSEd Tanous redfish::chassis_utils::getValidChassisPath( 32753b00f5dSEd Tanous sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId, 32853b00f5dSEd Tanous std::bind_front(afterGetChassisPath, sensorAsyncResp, 32953b00f5dSEd Tanous *powerCtlCollections)); 3304bb3dc34SCarol Wang } 3314bb3dc34SCarol Wang if (voltageCollections) 3324bb3dc34SCarol Wang { 3330885057cSEd Tanous std::unordered_map<std::string, std::vector<nlohmann::json::object_t>> 3344bb3dc34SCarol Wang allCollections; 3350885057cSEd Tanous allCollections.emplace("Voltages", std::move(*voltageCollections)); 33680ac4024SBruce Lee setSensorsOverride(sensorAsyncResp, allCollections); 3374bb3dc34SCarol Wang } 33853b00f5dSEd Tanous } 33953b00f5dSEd Tanous 34053b00f5dSEd Tanous inline void requestRoutesPower(App& app) 34153b00f5dSEd Tanous { 34253b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 34353b00f5dSEd Tanous .privileges(redfish::privileges::getPower) 34453b00f5dSEd Tanous .methods(boost::beast::http::verb::get)( 34553b00f5dSEd Tanous std::bind_front(handleChassisPowerGet, std::ref(app))); 34653b00f5dSEd Tanous 34753b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 34853b00f5dSEd Tanous .privileges(redfish::privileges::patchPower) 34953b00f5dSEd Tanous .methods(boost::beast::http::verb::patch)( 35053b00f5dSEd Tanous std::bind_front(handleChassisPowerPatch, std::ref(app))); 351413961deSRichard Marian Thomaiyar } 3522474adfaSEd Tanous 3532474adfaSEd Tanous } // namespace redfish 354