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 192474adfaSEd Tanous #include "sensors.hpp" 202474adfaSEd Tanous 217e860f15SJohn Edward Broadbent #include <app.hpp> 22168e20c1SEd Tanous #include <dbus_utility.hpp> 2345ca1b86SEd Tanous #include <query.hpp> 24ed398213SEd Tanous #include <registries/privilege_registry.hpp> 257e860f15SJohn Edward Broadbent 262474adfaSEd Tanous namespace redfish 272474adfaSEd Tanous { 284f48d5f6SEd Tanous inline void setPowerCapOverride( 298d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 304bb3dc34SCarol Wang std::vector<nlohmann::json>& powerControlCollections) 314bb3dc34SCarol Wang { 320fda0f12SGeorge Liu auto getChassisPath = [sensorsAsyncResp, powerControlCollections]( 330fda0f12SGeorge Liu const std::optional<std::string>& 340fda0f12SGeorge Liu chassisPath) mutable { 354bb3dc34SCarol Wang if (!chassisPath) 364bb3dc34SCarol Wang { 374bb3dc34SCarol Wang BMCWEB_LOG_ERROR << "Don't find valid chassis path "; 388d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 390fda0f12SGeorge Liu "Chassis", sensorsAsyncResp->chassisId); 404bb3dc34SCarol Wang return; 414bb3dc34SCarol Wang } 424bb3dc34SCarol Wang 434bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 444bb3dc34SCarol Wang { 458d1b46d7Szhanghch05 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present "; 468d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 478d1b46d7Szhanghch05 "Power", "PowerControl"); 484bb3dc34SCarol Wang return; 494bb3dc34SCarol Wang } 504bb3dc34SCarol Wang 514bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 524bb3dc34SCarol Wang 534bb3dc34SCarol Wang std::optional<nlohmann::json> powerLimit; 548d1b46d7Szhanghch05 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res, 558d1b46d7Szhanghch05 "PowerLimit", powerLimit)) 564bb3dc34SCarol Wang { 574bb3dc34SCarol Wang return; 584bb3dc34SCarol Wang } 594bb3dc34SCarol Wang if (!powerLimit) 604bb3dc34SCarol Wang { 614bb3dc34SCarol Wang return; 624bb3dc34SCarol Wang } 634bb3dc34SCarol Wang std::optional<uint32_t> value; 640fda0f12SGeorge Liu if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res, 654bb3dc34SCarol Wang "LimitInWatts", value)) 664bb3dc34SCarol Wang { 674bb3dc34SCarol Wang return; 684bb3dc34SCarol Wang } 694bb3dc34SCarol Wang if (!value) 704bb3dc34SCarol Wang { 714bb3dc34SCarol Wang return; 724bb3dc34SCarol Wang } 731e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 741e1e598dSJonathan Doman *crow::connections::systemBus, "xyz.openbmc_project.Settings", 751e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 761e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 771e1e598dSJonathan Doman [value, sensorsAsyncResp](const boost::system::error_code ec, 781e1e598dSJonathan Doman bool powerCapEnable) { 794bb3dc34SCarol Wang if (ec) 804bb3dc34SCarol Wang { 818d1b46d7Szhanghch05 messages::internalError(sensorsAsyncResp->asyncResp->res); 821e1e598dSJonathan Doman BMCWEB_LOG_ERROR 831e1e598dSJonathan Doman << "powerCapEnable Get handler: Dbus error " << ec; 844bb3dc34SCarol Wang return; 854bb3dc34SCarol Wang } 861e1e598dSJonathan Doman if (!powerCapEnable) 874bb3dc34SCarol Wang { 884bb3dc34SCarol Wang messages::actionNotSupported( 898d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res, 900fda0f12SGeorge Liu "Setting LimitInWatts when PowerLimit feature is disabled"); 914bb3dc34SCarol Wang BMCWEB_LOG_ERROR << "PowerLimit feature is disabled "; 924bb3dc34SCarol Wang return; 934bb3dc34SCarol Wang } 944bb3dc34SCarol Wang 954bb3dc34SCarol Wang crow::connections::systemBus->async_method_call( 968d1b46d7Szhanghch05 [sensorsAsyncResp](const boost::system::error_code ec2) { 9723a21a1cSEd Tanous if (ec2) 984bb3dc34SCarol Wang { 998d1b46d7Szhanghch05 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: " 1008d1b46d7Szhanghch05 << ec2; 1018d1b46d7Szhanghch05 messages::internalError( 1028d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res); 1034bb3dc34SCarol Wang return; 1044bb3dc34SCarol Wang } 1058d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res.result( 1064bb3dc34SCarol Wang boost::beast::http::status::no_content); 1074bb3dc34SCarol Wang }, 1084bb3dc34SCarol Wang "xyz.openbmc_project.Settings", 1094bb3dc34SCarol Wang "/xyz/openbmc_project/control/host0/power_cap", 1104bb3dc34SCarol Wang "org.freedesktop.DBus.Properties", "Set", 1114bb3dc34SCarol Wang "xyz.openbmc_project.Control.Power.Cap", "PowerCap", 1121e1e598dSJonathan Doman std::variant<uint32_t>(*value)); 1131e1e598dSJonathan Doman }); 1144bb3dc34SCarol Wang }; 1158d1b46d7Szhanghch05 getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath)); 1164bb3dc34SCarol Wang } 1177e860f15SJohn Edward Broadbent inline void requestRoutesPower(App& app) 1182474adfaSEd Tanous { 1192474adfaSEd Tanous 1207e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 121ed398213SEd Tanous .privileges(redfish::privileges::getPower) 122168e20c1SEd Tanous .methods( 12345ca1b86SEd Tanous boost::beast::http::verb:: 12445ca1b86SEd Tanous get)([&app](const crow::Request& req, 12545ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1267e860f15SJohn Edward Broadbent const std::string& chassisName) { 12745ca1b86SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 12845ca1b86SEd Tanous { 12945ca1b86SEd Tanous return; 13045ca1b86SEd Tanous } 131168e20c1SEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 132c5d03ff4SJennifer Lee 1332474adfaSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 13402da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 135a0ec28b6SAdrian Ambrożewicz sensors::node::power); 136028f7ebcSEddie James 1372474adfaSEd Tanous getChassisData(sensorAsyncResp); 138028f7ebcSEddie James 1397e860f15SJohn Edward Broadbent // This callback verifies that the power limit is only provided 1407e860f15SJohn Edward Broadbent // for the chassis that implements the Chassis inventory item. 1417e860f15SJohn Edward Broadbent // This prevents things like power supplies providing the 1427e860f15SJohn Edward Broadbent // chassis power limit 143b9d36b47SEd Tanous 144b9d36b47SEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 145028f7ebcSEddie James auto chassisHandler = [sensorAsyncResp]( 146271584abSEd Tanous const boost::system::error_code e, 147b9d36b47SEd Tanous const Mapper& chassisPaths) { 148271584abSEd Tanous if (e) 149028f7ebcSEddie James { 150028f7ebcSEddie James BMCWEB_LOG_ERROR 1517e860f15SJohn Edward Broadbent << "Power Limit GetSubTreePaths handler Dbus error " 1527e860f15SJohn Edward Broadbent << e; 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 178028f7ebcSEddie James std::string interfaceChassisName = 179028f7ebcSEddie James chassis.substr(lastPos + 1, 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 { 189028f7ebcSEddie James BMCWEB_LOG_DEBUG << "Power Limit not present for " 190028f7ebcSEddie James << sensorAsyncResp->chassisId; 191028f7ebcSEddie James return; 192028f7ebcSEddie James } 193028f7ebcSEddie James 194168e20c1SEd Tanous auto valueHandler = 195168e20c1SEd Tanous [sensorAsyncResp]( 196028f7ebcSEddie James const boost::system::error_code ec, 197b9d36b47SEd Tanous const dbus::utility::DBusPropertiesMap& properties) { 198028f7ebcSEddie James if (ec) 199028f7ebcSEddie James { 2008d1b46d7Szhanghch05 messages::internalError( 2018d1b46d7Szhanghch05 sensorAsyncResp->asyncResp->res); 202028f7ebcSEddie James BMCWEB_LOG_ERROR 2037e860f15SJohn Edward Broadbent << "Power Limit GetAll handler: Dbus error " 2047e860f15SJohn Edward Broadbent << ec; 205028f7ebcSEddie James return; 206028f7ebcSEddie James } 207028f7ebcSEddie James 2087e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 2097e860f15SJohn Edward Broadbent sensorAsyncResp->asyncResp->res 2108d1b46d7Szhanghch05 .jsonValue["PowerControl"]; 211028f7ebcSEddie James 2127e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 2137e860f15SJohn Edward Broadbent // so only create the first one 214028f7ebcSEddie James if (tempArray.empty()) 215028f7ebcSEddie James { 2167ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 2177ab06f49SGunnar Mills // A warning without a odata.type 218*1476687dSEd Tanous nlohmann::json::object_t powerControl; 219*1476687dSEd Tanous powerControl["@odata.type"] = 220*1476687dSEd Tanous "#Power.v1_0_0.PowerControl"; 221*1476687dSEd Tanous powerControl["@odata.id"] = 222*1476687dSEd Tanous "/redfish/v1/Chassis/" + 2237ab06f49SGunnar Mills sensorAsyncResp->chassisId + 224*1476687dSEd Tanous "/Power#/PowerControl/0"; 225*1476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 226*1476687dSEd Tanous powerControl["MemberId"] = "0"; 227*1476687dSEd Tanous tempArray.push_back(std::move(powerControl)); 228028f7ebcSEddie James } 229028f7ebcSEddie James 230028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 231028f7ebcSEddie James bool enabled = false; 232028f7ebcSEddie James double powerCap = 0.0; 233028f7ebcSEddie James int64_t scale = 0; 234028f7ebcSEddie James 235168e20c1SEd Tanous for (const std::pair<std::string, 236168e20c1SEd Tanous dbus::utility::DbusVariantType>& 2377e860f15SJohn Edward Broadbent property : properties) 238028f7ebcSEddie James { 23955f79e6fSEd Tanous if (property.first == "Scale") 240028f7ebcSEddie James { 241028f7ebcSEddie James const int64_t* i = 2428d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 243028f7ebcSEddie James 244e662eae8SEd Tanous if (i != nullptr) 245028f7ebcSEddie James { 246028f7ebcSEddie James scale = *i; 247028f7ebcSEddie James } 248028f7ebcSEddie James } 24955f79e6fSEd Tanous else if (property.first == "PowerCap") 250028f7ebcSEddie James { 251028f7ebcSEddie James const double* d = 2528d78b7a9SPatrick Williams std::get_if<double>(&property.second); 253028f7ebcSEddie James const int64_t* i = 2548d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 255028f7ebcSEddie James const uint32_t* u = 2568d78b7a9SPatrick Williams std::get_if<uint32_t>(&property.second); 257028f7ebcSEddie James 258e662eae8SEd Tanous if (d != nullptr) 259028f7ebcSEddie James { 260028f7ebcSEddie James powerCap = *d; 261028f7ebcSEddie James } 262e662eae8SEd Tanous else if (i != nullptr) 263028f7ebcSEddie James { 264271584abSEd Tanous powerCap = static_cast<double>(*i); 265028f7ebcSEddie James } 266e662eae8SEd Tanous else if (u != nullptr) 267028f7ebcSEddie James { 268028f7ebcSEddie James powerCap = *u; 269028f7ebcSEddie James } 270028f7ebcSEddie James } 27155f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 272028f7ebcSEddie James { 2737e860f15SJohn Edward Broadbent const bool* b = 2747e860f15SJohn Edward Broadbent std::get_if<bool>(&property.second); 275028f7ebcSEddie James 276e662eae8SEd Tanous if (b != nullptr) 277028f7ebcSEddie James { 278028f7ebcSEddie James enabled = *b; 279028f7ebcSEddie James } 280028f7ebcSEddie James } 281028f7ebcSEddie James } 282028f7ebcSEddie James 2837ab06f49SGunnar Mills nlohmann::json& value = 2847ab06f49SGunnar Mills sensorJson["PowerLimit"]["LimitInWatts"]; 285028f7ebcSEddie James 2867e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 2877e860f15SJohn Edward Broadbent // Baseline Profile – v1.0.0, so currently making it 2887e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 2895a64a6f3SJoshi-Mansi sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 2905a64a6f3SJoshi-Mansi 291028f7ebcSEddie James if (enabled) 292028f7ebcSEddie James { 2937e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 2947e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 295028f7ebcSEddie James value = powerCap * std::pow(10, scale); 296028f7ebcSEddie James } 297028f7ebcSEddie James }; 298028f7ebcSEddie James 299028f7ebcSEddie James crow::connections::systemBus->async_method_call( 300028f7ebcSEddie James std::move(valueHandler), "xyz.openbmc_project.Settings", 301028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 302028f7ebcSEddie James "org.freedesktop.DBus.Properties", "GetAll", 303028f7ebcSEddie James "xyz.openbmc_project.Control.Power.Cap"); 304028f7ebcSEddie James }; 305028f7ebcSEddie James 306028f7ebcSEddie James crow::connections::systemBus->async_method_call( 307168e20c1SEd Tanous std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper", 308028f7ebcSEddie James "/xyz/openbmc_project/object_mapper", 309028f7ebcSEddie James "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 310271584abSEd Tanous "/xyz/openbmc_project/inventory", 0, 311f857e9aeSAppaRao Puli std::array<const char*, 2>{ 312f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 313028f7ebcSEddie James "xyz.openbmc_project.Inventory.Item.Chassis"}); 3147e860f15SJohn Edward Broadbent }); 3154bb3dc34SCarol Wang 3167e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 317ed398213SEd Tanous .privileges(redfish::privileges::patchPower) 3187e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::patch)( 31945ca1b86SEd Tanous [&app](const crow::Request& req, 3207e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3217e860f15SJohn Edward Broadbent const std::string& chassisName) { 32245ca1b86SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 32345ca1b86SEd Tanous { 32445ca1b86SEd Tanous return; 32545ca1b86SEd Tanous } 3268d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 32702da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 328a0ec28b6SAdrian Ambrożewicz sensors::node::power); 3294bb3dc34SCarol Wang 3304bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> voltageCollections; 3314bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> powerCtlCollections; 3324bb3dc34SCarol Wang 33315ed6780SWilly Tu if (!json_util::readJsonPatch( 33415ed6780SWilly Tu req, sensorAsyncResp->asyncResp->res, "PowerControl", 33515ed6780SWilly Tu powerCtlCollections, "Voltages", voltageCollections)) 3364bb3dc34SCarol Wang { 3374bb3dc34SCarol Wang return; 3384bb3dc34SCarol Wang } 3394bb3dc34SCarol Wang 3404bb3dc34SCarol Wang if (powerCtlCollections) 3414bb3dc34SCarol Wang { 3428d1b46d7Szhanghch05 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections); 3434bb3dc34SCarol Wang } 3444bb3dc34SCarol Wang if (voltageCollections) 3454bb3dc34SCarol Wang { 3464bb3dc34SCarol Wang std::unordered_map<std::string, std::vector<nlohmann::json>> 3474bb3dc34SCarol Wang allCollections; 3487e860f15SJohn Edward Broadbent allCollections.emplace("Voltages", 3497e860f15SJohn Edward Broadbent *std::move(voltageCollections)); 35080ac4024SBruce Lee setSensorsOverride(sensorAsyncResp, allCollections); 3514bb3dc34SCarol Wang } 3527e860f15SJohn Edward Broadbent }); 353413961deSRichard Marian Thomaiyar } 3542474adfaSEd Tanous 3552474adfaSEd Tanous } // namespace redfish 356