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