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