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 { 334f48d5f6SEd Tanous inline void setPowerCapOverride( 348d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 354bb3dc34SCarol Wang std::vector<nlohmann::json>& powerControlCollections) 364bb3dc34SCarol Wang { 37002d39b4SEd Tanous auto getChassisPath = 38002d39b4SEd Tanous [sensorsAsyncResp, powerControlCollections]( 39002d39b4SEd Tanous const std::optional<std::string>& chassisPath) mutable { 404bb3dc34SCarol Wang if (!chassisPath) 414bb3dc34SCarol Wang { 42*62598e31SEd Tanous BMCWEB_LOG_WARNING("Don't find valid chassis path "); 438d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 440fda0f12SGeorge Liu "Chassis", sensorsAsyncResp->chassisId); 454bb3dc34SCarol Wang return; 464bb3dc34SCarol Wang } 474bb3dc34SCarol Wang 484bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 494bb3dc34SCarol Wang { 50*62598e31SEd Tanous BMCWEB_LOG_WARNING("Don't support multiple hosts at present "); 518d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 528d1b46d7Szhanghch05 "Power", "PowerControl"); 534bb3dc34SCarol Wang return; 544bb3dc34SCarol Wang } 554bb3dc34SCarol Wang 564bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 574bb3dc34SCarol Wang 584bb3dc34SCarol Wang std::optional<nlohmann::json> powerLimit; 598d1b46d7Szhanghch05 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res, 608d1b46d7Szhanghch05 "PowerLimit", powerLimit)) 614bb3dc34SCarol Wang { 624bb3dc34SCarol Wang return; 634bb3dc34SCarol Wang } 644bb3dc34SCarol Wang if (!powerLimit) 654bb3dc34SCarol Wang { 664bb3dc34SCarol Wang return; 674bb3dc34SCarol Wang } 684bb3dc34SCarol Wang std::optional<uint32_t> value; 690fda0f12SGeorge Liu if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res, 704bb3dc34SCarol Wang "LimitInWatts", value)) 714bb3dc34SCarol Wang { 724bb3dc34SCarol Wang return; 734bb3dc34SCarol Wang } 744bb3dc34SCarol Wang if (!value) 754bb3dc34SCarol Wang { 764bb3dc34SCarol Wang return; 774bb3dc34SCarol Wang } 781e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 791e1e598dSJonathan Doman *crow::connections::systemBus, "xyz.openbmc_project.Settings", 801e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 811e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 825e7e2dc5SEd Tanous [value, sensorsAsyncResp](const boost::system::error_code& ec, 831e1e598dSJonathan Doman bool powerCapEnable) { 844bb3dc34SCarol Wang if (ec) 854bb3dc34SCarol Wang { 868d1b46d7Szhanghch05 messages::internalError(sensorsAsyncResp->asyncResp->res); 87*62598e31SEd Tanous BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", 88*62598e31SEd Tanous ec); 894bb3dc34SCarol Wang return; 904bb3dc34SCarol Wang } 911e1e598dSJonathan Doman if (!powerCapEnable) 924bb3dc34SCarol Wang { 934bb3dc34SCarol Wang messages::actionNotSupported( 948d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res, 950fda0f12SGeorge Liu "Setting LimitInWatts when PowerLimit feature is disabled"); 96*62598e31SEd Tanous BMCWEB_LOG_ERROR("PowerLimit feature is disabled "); 974bb3dc34SCarol Wang return; 984bb3dc34SCarol Wang } 994bb3dc34SCarol Wang 1009ae226faSGeorge Liu sdbusplus::asio::setProperty( 1019ae226faSGeorge Liu *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1029ae226faSGeorge Liu "/xyz/openbmc_project/control/host0/power_cap", 1039ae226faSGeorge Liu "xyz.openbmc_project.Control.Power.Cap", "PowerCap", *value, 1045e7e2dc5SEd Tanous [sensorsAsyncResp](const boost::system::error_code& ec2) { 10523a21a1cSEd Tanous if (ec2) 1064bb3dc34SCarol Wang { 107*62598e31SEd Tanous BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2); 108002d39b4SEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 1094bb3dc34SCarol Wang return; 1104bb3dc34SCarol Wang } 1118d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res.result( 1124bb3dc34SCarol Wang boost::beast::http::status::no_content); 1139ae226faSGeorge Liu }); 1141e1e598dSJonathan Doman }); 1154bb3dc34SCarol Wang }; 1160d7702c0SZhenwei Chen redfish::chassis_utils::getValidChassisPath(sensorsAsyncResp->asyncResp, 1170d7702c0SZhenwei Chen sensorsAsyncResp->chassisId, 1180d7702c0SZhenwei Chen std::move(getChassisPath)); 1194bb3dc34SCarol Wang } 1207e860f15SJohn Edward Broadbent inline void requestRoutesPower(App& app) 1212474adfaSEd Tanous { 1227e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 123ed398213SEd Tanous .privileges(redfish::privileges::getPower) 124002d39b4SEd Tanous .methods(boost::beast::http::verb::get)( 125002d39b4SEd Tanous [&app](const crow::Request& req, 12645ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1277e860f15SJohn Edward Broadbent const std::string& chassisName) { 1283ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 12945ca1b86SEd Tanous { 13045ca1b86SEd Tanous return; 13145ca1b86SEd Tanous } 132168e20c1SEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 133c5d03ff4SJennifer Lee 1342474adfaSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 13502da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 136a0ec28b6SAdrian Ambrożewicz sensors::node::power); 137028f7ebcSEddie James 1382474adfaSEd Tanous getChassisData(sensorAsyncResp); 139028f7ebcSEddie James 1407e860f15SJohn Edward Broadbent // This callback verifies that the power limit is only provided 1417e860f15SJohn Edward Broadbent // for the chassis that implements the Chassis inventory item. 1427e860f15SJohn Edward Broadbent // This prevents things like power supplies providing the 1437e860f15SJohn Edward Broadbent // chassis power limit 144b9d36b47SEd Tanous 145b9d36b47SEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 146002d39b4SEd Tanous auto chassisHandler = 1478b24275dSEd Tanous [sensorAsyncResp](const boost::system::error_code& ec2, 148b9d36b47SEd Tanous const Mapper& chassisPaths) { 1498b24275dSEd Tanous if (ec2) 150028f7ebcSEddie James { 151*62598e31SEd Tanous BMCWEB_LOG_ERROR( 152*62598e31SEd Tanous "Power Limit GetSubTreePaths handler Dbus error {}", ec2); 153028f7ebcSEddie James return; 154028f7ebcSEddie James } 155028f7ebcSEddie James 156028f7ebcSEddie James bool found = false; 157028f7ebcSEddie James for (const std::string& chassis : chassisPaths) 158028f7ebcSEddie James { 159028f7ebcSEddie James size_t len = std::string::npos; 160f23b7296SEd Tanous size_t lastPos = chassis.rfind('/'); 161028f7ebcSEddie James if (lastPos == std::string::npos) 162028f7ebcSEddie James { 163028f7ebcSEddie James continue; 164028f7ebcSEddie James } 165028f7ebcSEddie James 166028f7ebcSEddie James if (lastPos == chassis.size() - 1) 167028f7ebcSEddie James { 168028f7ebcSEddie James size_t end = lastPos; 169f23b7296SEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 170028f7ebcSEddie James if (lastPos == std::string::npos) 171028f7ebcSEddie James { 172028f7ebcSEddie James continue; 173028f7ebcSEddie James } 174028f7ebcSEddie James 175028f7ebcSEddie James len = end - (lastPos + 1); 176028f7ebcSEddie James } 177028f7ebcSEddie James 17889492a15SPatrick Williams std::string interfaceChassisName = chassis.substr(lastPos + 1, 17989492a15SPatrick Williams len); 18055f79e6fSEd Tanous if (interfaceChassisName == sensorAsyncResp->chassisId) 181028f7ebcSEddie James { 182028f7ebcSEddie James found = true; 183028f7ebcSEddie James break; 184028f7ebcSEddie James } 185028f7ebcSEddie James } 186028f7ebcSEddie James 187028f7ebcSEddie James if (!found) 188028f7ebcSEddie James { 189*62598e31SEd Tanous BMCWEB_LOG_DEBUG("Power Limit not present for {}", 190*62598e31SEd Tanous sensorAsyncResp->chassisId); 191028f7ebcSEddie James return; 192028f7ebcSEddie James } 193028f7ebcSEddie James 194168e20c1SEd Tanous auto valueHandler = 195168e20c1SEd Tanous [sensorAsyncResp]( 1965e7e2dc5SEd Tanous const boost::system::error_code& ec, 197b9d36b47SEd Tanous const dbus::utility::DBusPropertiesMap& properties) { 198028f7ebcSEddie James if (ec) 199028f7ebcSEddie James { 200002d39b4SEd Tanous messages::internalError(sensorAsyncResp->asyncResp->res); 201*62598e31SEd Tanous BMCWEB_LOG_ERROR( 202*62598e31SEd Tanous "Power Limit GetAll handler: Dbus error {}", ec); 203028f7ebcSEddie James return; 204028f7ebcSEddie James } 205028f7ebcSEddie James 2067e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 207002d39b4SEd Tanous sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 208028f7ebcSEddie James 2097e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 2107e860f15SJohn Edward Broadbent // so only create the first one 211028f7ebcSEddie James if (tempArray.empty()) 212028f7ebcSEddie James { 2137ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 2147ab06f49SGunnar Mills // A warning without a odata.type 2151476687dSEd Tanous nlohmann::json::object_t powerControl; 216002d39b4SEd Tanous powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 217002d39b4SEd Tanous powerControl["@odata.id"] = "/redfish/v1/Chassis/" + 2187ab06f49SGunnar Mills sensorAsyncResp->chassisId + 2191476687dSEd Tanous "/Power#/PowerControl/0"; 2201476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 2211476687dSEd Tanous powerControl["MemberId"] = "0"; 222b2ba3072SPatrick Williams tempArray.emplace_back(std::move(powerControl)); 223028f7ebcSEddie James } 224028f7ebcSEddie James 225028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 226028f7ebcSEddie James bool enabled = false; 227028f7ebcSEddie James double powerCap = 0.0; 228028f7ebcSEddie James int64_t scale = 0; 229028f7ebcSEddie James 230168e20c1SEd Tanous for (const std::pair<std::string, 231002d39b4SEd Tanous dbus::utility::DbusVariantType>& property : 232002d39b4SEd Tanous properties) 233028f7ebcSEddie James { 23455f79e6fSEd Tanous if (property.first == "Scale") 235028f7ebcSEddie James { 236028f7ebcSEddie James const int64_t* i = 2378d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 238028f7ebcSEddie James 239e662eae8SEd Tanous if (i != nullptr) 240028f7ebcSEddie James { 241028f7ebcSEddie James scale = *i; 242028f7ebcSEddie James } 243028f7ebcSEddie James } 24455f79e6fSEd Tanous else if (property.first == "PowerCap") 245028f7ebcSEddie James { 246002d39b4SEd Tanous const double* d = std::get_if<double>(&property.second); 247028f7ebcSEddie James const int64_t* i = 2488d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 249028f7ebcSEddie James const uint32_t* u = 2508d78b7a9SPatrick Williams std::get_if<uint32_t>(&property.second); 251028f7ebcSEddie James 252e662eae8SEd Tanous if (d != nullptr) 253028f7ebcSEddie James { 254028f7ebcSEddie James powerCap = *d; 255028f7ebcSEddie James } 256e662eae8SEd Tanous else if (i != nullptr) 257028f7ebcSEddie James { 258271584abSEd Tanous powerCap = static_cast<double>(*i); 259028f7ebcSEddie James } 260e662eae8SEd Tanous else if (u != nullptr) 261028f7ebcSEddie James { 262028f7ebcSEddie James powerCap = *u; 263028f7ebcSEddie James } 264028f7ebcSEddie James } 26555f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 266028f7ebcSEddie James { 267002d39b4SEd Tanous const bool* b = std::get_if<bool>(&property.second); 268028f7ebcSEddie James 269e662eae8SEd Tanous if (b != nullptr) 270028f7ebcSEddie James { 271028f7ebcSEddie James enabled = *b; 272028f7ebcSEddie James } 273028f7ebcSEddie James } 274028f7ebcSEddie James } 275028f7ebcSEddie James 2767e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 2777e860f15SJohn Edward Broadbent // Baseline Profile – v1.0.0, so currently making it 2787e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 2795a64a6f3SJoshi-Mansi sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 2805a64a6f3SJoshi-Mansi 281028f7ebcSEddie James if (enabled) 282028f7ebcSEddie James { 2837e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 2847e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 285d4413c5bSGeorge Liu sensorJson["PowerLimit"]["LimitInWatts"] = 286d4413c5bSGeorge Liu powerCap * std::pow(10, scale); 287028f7ebcSEddie James } 288028f7ebcSEddie James }; 289028f7ebcSEddie James 290d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 291d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, "xyz.openbmc_project.Settings", 292028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 293d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Control.Power.Cap", 294d1bde9e5SKrzysztof Grobelny std::move(valueHandler)); 295028f7ebcSEddie James }; 296028f7ebcSEddie James 2977a1dbc48SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 298f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 2997a1dbc48SGeorge Liu "xyz.openbmc_project.Inventory.Item.Chassis"}; 3007a1dbc48SGeorge Liu 3017a1dbc48SGeorge Liu dbus::utility::getSubTreePaths("/xyz/openbmc_project/inventory", 0, 3027a1dbc48SGeorge Liu interfaces, std::move(chassisHandler)); 3037e860f15SJohn Edward Broadbent }); 3044bb3dc34SCarol Wang 3057e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 306ed398213SEd Tanous .privileges(redfish::privileges::patchPower) 3077e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::patch)( 30845ca1b86SEd Tanous [&app](const crow::Request& req, 3097e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3107e860f15SJohn Edward Broadbent const std::string& chassisName) { 3113ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 31245ca1b86SEd Tanous { 31345ca1b86SEd Tanous return; 31445ca1b86SEd Tanous } 3158d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 31602da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 317a0ec28b6SAdrian Ambrożewicz sensors::node::power); 3184bb3dc34SCarol Wang 3194bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> voltageCollections; 3204bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> powerCtlCollections; 3214bb3dc34SCarol Wang 322002d39b4SEd Tanous if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res, 323002d39b4SEd Tanous "PowerControl", powerCtlCollections, 324002d39b4SEd Tanous "Voltages", voltageCollections)) 3254bb3dc34SCarol Wang { 3264bb3dc34SCarol Wang return; 3274bb3dc34SCarol Wang } 3284bb3dc34SCarol Wang 3294bb3dc34SCarol Wang if (powerCtlCollections) 3304bb3dc34SCarol Wang { 3318d1b46d7Szhanghch05 setPowerCapOverride(sensorAsyncResp, *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 } 3407e860f15SJohn Edward Broadbent }); 341413961deSRichard Marian Thomaiyar } 3422474adfaSEd Tanous 3432474adfaSEd Tanous } // namespace redfish 344