12474adfaSEd Tanous /* 22474adfaSEd Tanous // Copyright (c) 2018 Intel Corporation 32474adfaSEd Tanous // Copyright (c) 2018 Ampere Computing LLC 42474adfaSEd Tanous / 52474adfaSEd Tanous // Licensed under the Apache License, Version 2.0 (the "License"); 62474adfaSEd Tanous // you may not use this file except in compliance with the License. 72474adfaSEd Tanous // You may obtain a copy of the License at 82474adfaSEd Tanous // 92474adfaSEd Tanous // http://www.apache.org/licenses/LICENSE-2.0 102474adfaSEd Tanous // 112474adfaSEd Tanous // Unless required by applicable law or agreed to in writing, software 122474adfaSEd Tanous // distributed under the License is distributed on an "AS IS" BASIS, 132474adfaSEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 142474adfaSEd Tanous // See the License for the specific language governing permissions and 152474adfaSEd Tanous // limitations under the License. 162474adfaSEd Tanous */ 172474adfaSEd Tanous #pragma once 182474adfaSEd Tanous 193ccb3adbSEd Tanous #include "app.hpp" 207a1dbc48SGeorge Liu #include "dbus_utility.hpp" 213ccb3adbSEd Tanous #include "query.hpp" 223ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 232474adfaSEd Tanous #include "sensors.hpp" 240d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp" 252474adfaSEd Tanous 26d1bde9e5SKrzysztof Grobelny #include <sdbusplus/asio/property.hpp> 277e860f15SJohn Edward Broadbent 287a1dbc48SGeorge Liu #include <array> 297a1dbc48SGeorge Liu #include <string_view> 307a1dbc48SGeorge Liu 312474adfaSEd Tanous namespace redfish 322474adfaSEd Tanous { 33*53b00f5dSEd Tanous 34*53b00f5dSEd Tanous inline void afterGetPowerCapEnable( 358d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 36*53b00f5dSEd Tanous uint32_t valueToSet, const boost::system::error_code& ec, 37*53b00f5dSEd Tanous bool powerCapEnable) 384bb3dc34SCarol Wang { 39*53b00f5dSEd Tanous if (ec) 40*53b00f5dSEd Tanous { 41*53b00f5dSEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 42*53b00f5dSEd Tanous BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec); 43*53b00f5dSEd Tanous return; 44*53b00f5dSEd Tanous } 45*53b00f5dSEd Tanous if (!powerCapEnable) 46*53b00f5dSEd Tanous { 47*53b00f5dSEd Tanous messages::actionNotSupported( 48*53b00f5dSEd Tanous sensorsAsyncResp->asyncResp->res, 49*53b00f5dSEd Tanous "Setting LimitInWatts when PowerLimit feature is disabled"); 50*53b00f5dSEd Tanous BMCWEB_LOG_ERROR("PowerLimit feature is disabled "); 51*53b00f5dSEd Tanous return; 52*53b00f5dSEd Tanous } 53*53b00f5dSEd Tanous 54*53b00f5dSEd Tanous sdbusplus::asio::setProperty( 55*53b00f5dSEd Tanous *crow::connections::systemBus, "xyz.openbmc_project.Settings", 56*53b00f5dSEd Tanous "/xyz/openbmc_project/control/host0/power_cap", 57*53b00f5dSEd Tanous "xyz.openbmc_project.Control.Power.Cap", "PowerCap", valueToSet, 58*53b00f5dSEd Tanous [sensorsAsyncResp](const boost::system::error_code& ec2) { 59*53b00f5dSEd Tanous if (ec2) 60*53b00f5dSEd Tanous { 61*53b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2); 62*53b00f5dSEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 63*53b00f5dSEd Tanous return; 64*53b00f5dSEd Tanous } 65*53b00f5dSEd Tanous sensorsAsyncResp->asyncResp->res.result( 66*53b00f5dSEd Tanous boost::beast::http::status::no_content); 67*53b00f5dSEd Tanous }); 68*53b00f5dSEd Tanous } 69*53b00f5dSEd Tanous 70*53b00f5dSEd Tanous inline void afterGetChassisPath( 71*53b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 72*53b00f5dSEd Tanous std::vector<nlohmann::json>& powerControlCollections, 73*53b00f5dSEd Tanous const std::optional<std::string>& chassisPath) 74*53b00f5dSEd Tanous { 754bb3dc34SCarol Wang if (!chassisPath) 764bb3dc34SCarol Wang { 7762598e31SEd Tanous BMCWEB_LOG_WARNING("Don't find valid chassis path "); 78*53b00f5dSEd Tanous messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis", 79*53b00f5dSEd Tanous sensorsAsyncResp->chassisId); 804bb3dc34SCarol Wang return; 814bb3dc34SCarol Wang } 824bb3dc34SCarol Wang 834bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 844bb3dc34SCarol Wang { 8562598e31SEd Tanous BMCWEB_LOG_WARNING("Don't support multiple hosts at present "); 86*53b00f5dSEd Tanous messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power", 87*53b00f5dSEd Tanous "PowerControl"); 884bb3dc34SCarol Wang return; 894bb3dc34SCarol Wang } 904bb3dc34SCarol Wang 914bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 924bb3dc34SCarol Wang 934bb3dc34SCarol Wang std::optional<nlohmann::json> powerLimit; 948d1b46d7Szhanghch05 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res, 958d1b46d7Szhanghch05 "PowerLimit", powerLimit)) 964bb3dc34SCarol Wang { 974bb3dc34SCarol Wang return; 984bb3dc34SCarol Wang } 994bb3dc34SCarol Wang if (!powerLimit) 1004bb3dc34SCarol Wang { 1014bb3dc34SCarol Wang return; 1024bb3dc34SCarol Wang } 1034bb3dc34SCarol Wang std::optional<uint32_t> value; 1040fda0f12SGeorge Liu if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res, 1054bb3dc34SCarol Wang "LimitInWatts", value)) 1064bb3dc34SCarol Wang { 1074bb3dc34SCarol Wang return; 1084bb3dc34SCarol Wang } 1094bb3dc34SCarol Wang if (!value) 1104bb3dc34SCarol Wang { 1114bb3dc34SCarol Wang return; 1124bb3dc34SCarol Wang } 1131e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 1141e1e598dSJonathan Doman *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1151e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 1161e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 117*53b00f5dSEd Tanous std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value)); 1184bb3dc34SCarol Wang } 1194bb3dc34SCarol Wang 120*53b00f5dSEd Tanous inline void afterPowerCapSettingGet( 121*53b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 1225e7e2dc5SEd Tanous const boost::system::error_code& ec, 123*53b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties) 124*53b00f5dSEd Tanous { 125028f7ebcSEddie James if (ec) 126028f7ebcSEddie James { 127002d39b4SEd Tanous messages::internalError(sensorAsyncResp->asyncResp->res); 128*53b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec); 129028f7ebcSEddie James return; 130028f7ebcSEddie James } 131028f7ebcSEddie James 1327e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 133002d39b4SEd Tanous sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 134028f7ebcSEddie James 1357e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 1367e860f15SJohn Edward Broadbent // so only create the first one 137028f7ebcSEddie James if (tempArray.empty()) 138028f7ebcSEddie James { 1397ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 1407ab06f49SGunnar Mills // A warning without a odata.type 1411476687dSEd Tanous nlohmann::json::object_t powerControl; 142002d39b4SEd Tanous powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 143002d39b4SEd Tanous powerControl["@odata.id"] = "/redfish/v1/Chassis/" + 1447ab06f49SGunnar Mills sensorAsyncResp->chassisId + 1451476687dSEd Tanous "/Power#/PowerControl/0"; 1461476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 1471476687dSEd Tanous powerControl["MemberId"] = "0"; 148b2ba3072SPatrick Williams tempArray.emplace_back(std::move(powerControl)); 149028f7ebcSEddie James } 150028f7ebcSEddie James 151028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 152028f7ebcSEddie James bool enabled = false; 153028f7ebcSEddie James double powerCap = 0.0; 154028f7ebcSEddie James int64_t scale = 0; 155028f7ebcSEddie James 156*53b00f5dSEd Tanous for (const std::pair<std::string, dbus::utility::DbusVariantType>& 157*53b00f5dSEd Tanous property : properties) 158028f7ebcSEddie James { 15955f79e6fSEd Tanous if (property.first == "Scale") 160028f7ebcSEddie James { 161*53b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 162028f7ebcSEddie James 163e662eae8SEd Tanous if (i != nullptr) 164028f7ebcSEddie James { 165028f7ebcSEddie James scale = *i; 166028f7ebcSEddie James } 167028f7ebcSEddie James } 16855f79e6fSEd Tanous else if (property.first == "PowerCap") 169028f7ebcSEddie James { 170002d39b4SEd Tanous const double* d = std::get_if<double>(&property.second); 171*53b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 172*53b00f5dSEd Tanous const uint32_t* u = std::get_if<uint32_t>(&property.second); 173028f7ebcSEddie James 174e662eae8SEd Tanous if (d != nullptr) 175028f7ebcSEddie James { 176028f7ebcSEddie James powerCap = *d; 177028f7ebcSEddie James } 178e662eae8SEd Tanous else if (i != nullptr) 179028f7ebcSEddie James { 180271584abSEd Tanous powerCap = static_cast<double>(*i); 181028f7ebcSEddie James } 182e662eae8SEd Tanous else if (u != nullptr) 183028f7ebcSEddie James { 184028f7ebcSEddie James powerCap = *u; 185028f7ebcSEddie James } 186028f7ebcSEddie James } 18755f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 188028f7ebcSEddie James { 189002d39b4SEd Tanous const bool* b = std::get_if<bool>(&property.second); 190028f7ebcSEddie James 191e662eae8SEd Tanous if (b != nullptr) 192028f7ebcSEddie James { 193028f7ebcSEddie James enabled = *b; 194028f7ebcSEddie James } 195028f7ebcSEddie James } 196028f7ebcSEddie James } 197028f7ebcSEddie James 1987e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 1997e860f15SJohn Edward Broadbent // Baseline Profile – v1.0.0, so currently making it 2007e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 2015a64a6f3SJoshi-Mansi sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 2025a64a6f3SJoshi-Mansi 203028f7ebcSEddie James if (enabled) 204028f7ebcSEddie James { 2057e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 2067e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 207*53b00f5dSEd Tanous sensorJson["PowerLimit"]["LimitInWatts"] = powerCap * 208*53b00f5dSEd Tanous std::pow(10, scale); 209028f7ebcSEddie James } 210*53b00f5dSEd Tanous } 211*53b00f5dSEd Tanous 212*53b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 213*53b00f5dSEd Tanous inline void 214*53b00f5dSEd Tanous afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 215*53b00f5dSEd Tanous const boost::system::error_code& ec2, 216*53b00f5dSEd Tanous const Mapper& chassisPaths) 217*53b00f5dSEd Tanous { 218*53b00f5dSEd Tanous if (ec2) 219*53b00f5dSEd Tanous { 220*53b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}", 221*53b00f5dSEd Tanous ec2); 222*53b00f5dSEd Tanous return; 223*53b00f5dSEd Tanous } 224*53b00f5dSEd Tanous 225*53b00f5dSEd Tanous bool found = false; 226*53b00f5dSEd Tanous for (const std::string& chassis : chassisPaths) 227*53b00f5dSEd Tanous { 228*53b00f5dSEd Tanous size_t len = std::string::npos; 229*53b00f5dSEd Tanous size_t lastPos = chassis.rfind('/'); 230*53b00f5dSEd Tanous if (lastPos == std::string::npos) 231*53b00f5dSEd Tanous { 232*53b00f5dSEd Tanous continue; 233*53b00f5dSEd Tanous } 234*53b00f5dSEd Tanous 235*53b00f5dSEd Tanous if (lastPos == chassis.size() - 1) 236*53b00f5dSEd Tanous { 237*53b00f5dSEd Tanous size_t end = lastPos; 238*53b00f5dSEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 239*53b00f5dSEd Tanous if (lastPos == std::string::npos) 240*53b00f5dSEd Tanous { 241*53b00f5dSEd Tanous continue; 242*53b00f5dSEd Tanous } 243*53b00f5dSEd Tanous 244*53b00f5dSEd Tanous len = end - (lastPos + 1); 245*53b00f5dSEd Tanous } 246*53b00f5dSEd Tanous 247*53b00f5dSEd Tanous std::string interfaceChassisName = chassis.substr(lastPos + 1, len); 248*53b00f5dSEd Tanous if (interfaceChassisName == sensorAsyncResp->chassisId) 249*53b00f5dSEd Tanous { 250*53b00f5dSEd Tanous found = true; 251*53b00f5dSEd Tanous break; 252*53b00f5dSEd Tanous } 253*53b00f5dSEd Tanous } 254*53b00f5dSEd Tanous 255*53b00f5dSEd Tanous if (!found) 256*53b00f5dSEd Tanous { 257*53b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit not present for {}", 258*53b00f5dSEd Tanous sensorAsyncResp->chassisId); 259*53b00f5dSEd Tanous return; 260*53b00f5dSEd Tanous } 261028f7ebcSEddie James 262d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 263d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, "xyz.openbmc_project.Settings", 264028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 265d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Control.Power.Cap", 266*53b00f5dSEd Tanous [sensorAsyncResp](const boost::system::error_code& ec, 267*53b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties 268*53b00f5dSEd Tanous 269*53b00f5dSEd Tanous ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); }); 270*53b00f5dSEd Tanous } 271*53b00f5dSEd Tanous 272*53b00f5dSEd Tanous inline void 273*53b00f5dSEd Tanous handleChassisPowerGet(App& app, const crow::Request& req, 274*53b00f5dSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 275*53b00f5dSEd Tanous const std::string& chassisName) 276*53b00f5dSEd Tanous { 277*53b00f5dSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 278*53b00f5dSEd Tanous { 279*53b00f5dSEd Tanous return; 280*53b00f5dSEd Tanous } 281*53b00f5dSEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 282*53b00f5dSEd Tanous 283*53b00f5dSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 284*53b00f5dSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 285*53b00f5dSEd Tanous sensors::node::power); 286*53b00f5dSEd Tanous 287*53b00f5dSEd Tanous getChassisData(sensorAsyncResp); 288*53b00f5dSEd Tanous 289*53b00f5dSEd Tanous // This callback verifies that the power limit is only provided 290*53b00f5dSEd Tanous // for the chassis that implements the Chassis inventory item. 291*53b00f5dSEd Tanous // This prevents things like power supplies providing the 292*53b00f5dSEd Tanous // chassis power limit 293028f7ebcSEddie James 2947a1dbc48SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 295f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 2967a1dbc48SGeorge Liu "xyz.openbmc_project.Inventory.Item.Chassis"}; 2977a1dbc48SGeorge Liu 298*53b00f5dSEd Tanous dbus::utility::getSubTreePaths( 299*53b00f5dSEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 300*53b00f5dSEd Tanous std::bind_front(afterGetChassis, sensorAsyncResp)); 301*53b00f5dSEd Tanous } 3024bb3dc34SCarol Wang 303*53b00f5dSEd Tanous inline void 304*53b00f5dSEd Tanous handleChassisPowerPatch(App& app, const crow::Request& req, 3057e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 306*53b00f5dSEd Tanous const std::string& chassisName) 307*53b00f5dSEd Tanous { 3083ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 30945ca1b86SEd Tanous { 31045ca1b86SEd Tanous return; 31145ca1b86SEd Tanous } 3128d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 31302da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 314a0ec28b6SAdrian Ambrożewicz sensors::node::power); 3154bb3dc34SCarol Wang 3164bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> voltageCollections; 3174bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> powerCtlCollections; 3184bb3dc34SCarol Wang 319002d39b4SEd Tanous if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res, 320002d39b4SEd Tanous "PowerControl", powerCtlCollections, 321002d39b4SEd Tanous "Voltages", voltageCollections)) 3224bb3dc34SCarol Wang { 3234bb3dc34SCarol Wang return; 3244bb3dc34SCarol Wang } 3254bb3dc34SCarol Wang 3264bb3dc34SCarol Wang if (powerCtlCollections) 3274bb3dc34SCarol Wang { 328*53b00f5dSEd Tanous redfish::chassis_utils::getValidChassisPath( 329*53b00f5dSEd Tanous sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId, 330*53b00f5dSEd Tanous std::bind_front(afterGetChassisPath, sensorAsyncResp, 331*53b00f5dSEd Tanous *powerCtlCollections)); 3324bb3dc34SCarol Wang } 3334bb3dc34SCarol Wang if (voltageCollections) 3344bb3dc34SCarol Wang { 3354bb3dc34SCarol Wang std::unordered_map<std::string, std::vector<nlohmann::json>> 3364bb3dc34SCarol Wang allCollections; 337002d39b4SEd Tanous allCollections.emplace("Voltages", *std::move(voltageCollections)); 33880ac4024SBruce Lee setSensorsOverride(sensorAsyncResp, allCollections); 3394bb3dc34SCarol Wang } 340*53b00f5dSEd Tanous } 341*53b00f5dSEd Tanous 342*53b00f5dSEd Tanous inline void requestRoutesPower(App& app) 343*53b00f5dSEd Tanous { 344*53b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 345*53b00f5dSEd Tanous .privileges(redfish::privileges::getPower) 346*53b00f5dSEd Tanous .methods(boost::beast::http::verb::get)( 347*53b00f5dSEd Tanous std::bind_front(handleChassisPowerGet, std::ref(app))); 348*53b00f5dSEd Tanous 349*53b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 350*53b00f5dSEd Tanous .privileges(redfish::privileges::patchPower) 351*53b00f5dSEd Tanous .methods(boost::beast::http::verb::patch)( 352*53b00f5dSEd Tanous std::bind_front(handleChassisPowerPatch, std::ref(app))); 353413961deSRichard Marian Thomaiyar } 3542474adfaSEd Tanous 3552474adfaSEd Tanous } // namespace redfish 356