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 "node.hpp" 202474adfaSEd Tanous #include "sensors.hpp" 212474adfaSEd Tanous 222474adfaSEd Tanous namespace redfish 232474adfaSEd Tanous { 242474adfaSEd Tanous 252474adfaSEd Tanous class Power : public Node 262474adfaSEd Tanous { 272474adfaSEd Tanous public: 2852cc112dSEd Tanous Power(App& app) : 292474adfaSEd Tanous Node((app), "/redfish/v1/Chassis/<str>/Power/", std::string()) 302474adfaSEd Tanous { 312474adfaSEd Tanous entityPrivileges = { 322474adfaSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 332474adfaSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 341b1b43f2Sjayaprakash Mutyala {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 352474adfaSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 362474adfaSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 372474adfaSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 382474adfaSEd Tanous } 392474adfaSEd Tanous 402474adfaSEd Tanous private: 414bb3dc34SCarol Wang void setPowerCapOverride( 42*8d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 434bb3dc34SCarol Wang std::vector<nlohmann::json>& powerControlCollections) 444bb3dc34SCarol Wang { 45*8d1b46d7Szhanghch05 auto getChassisPath = [sensorsAsyncResp, powerControlCollections]( 46*8d1b46d7Szhanghch05 const std::optional<std::string>& 47*8d1b46d7Szhanghch05 chassisPath) mutable { 484bb3dc34SCarol Wang if (!chassisPath) 494bb3dc34SCarol Wang { 504bb3dc34SCarol Wang BMCWEB_LOG_ERROR << "Don't find valid chassis path "; 51*8d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 52*8d1b46d7Szhanghch05 "Chassis", 53*8d1b46d7Szhanghch05 sensorsAsyncResp->chassisId); 544bb3dc34SCarol Wang return; 554bb3dc34SCarol Wang } 564bb3dc34SCarol Wang 574bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 584bb3dc34SCarol Wang { 59*8d1b46d7Szhanghch05 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present "; 60*8d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 61*8d1b46d7Szhanghch05 "Power", "PowerControl"); 624bb3dc34SCarol Wang return; 634bb3dc34SCarol Wang } 644bb3dc34SCarol Wang 654bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 664bb3dc34SCarol Wang 674bb3dc34SCarol Wang std::optional<nlohmann::json> powerLimit; 68*8d1b46d7Szhanghch05 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res, 69*8d1b46d7Szhanghch05 "PowerLimit", powerLimit)) 704bb3dc34SCarol Wang { 714bb3dc34SCarol Wang return; 724bb3dc34SCarol Wang } 734bb3dc34SCarol Wang if (!powerLimit) 744bb3dc34SCarol Wang { 754bb3dc34SCarol Wang return; 764bb3dc34SCarol Wang } 774bb3dc34SCarol Wang std::optional<uint32_t> value; 78*8d1b46d7Szhanghch05 if (!json_util::readJson(*powerLimit, 79*8d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res, 804bb3dc34SCarol Wang "LimitInWatts", value)) 814bb3dc34SCarol Wang { 824bb3dc34SCarol Wang return; 834bb3dc34SCarol Wang } 844bb3dc34SCarol Wang if (!value) 854bb3dc34SCarol Wang { 864bb3dc34SCarol Wang return; 874bb3dc34SCarol Wang } 88*8d1b46d7Szhanghch05 auto valueHandler = [value, sensorsAsyncResp]( 894bb3dc34SCarol Wang const boost::system::error_code ec, 904bb3dc34SCarol Wang const SensorVariant& powerCapEnable) { 914bb3dc34SCarol Wang if (ec) 924bb3dc34SCarol Wang { 93*8d1b46d7Szhanghch05 messages::internalError(sensorsAsyncResp->asyncResp->res); 944bb3dc34SCarol Wang BMCWEB_LOG_ERROR 954bb3dc34SCarol Wang << "powerCapEnable Get handler: Dbus error " << ec; 964bb3dc34SCarol Wang return; 974bb3dc34SCarol Wang } 984bb3dc34SCarol Wang // Check PowerCapEnable 998d78b7a9SPatrick Williams const bool* b = std::get_if<bool>(&powerCapEnable); 1004bb3dc34SCarol Wang if (b == nullptr) 1014bb3dc34SCarol Wang { 102*8d1b46d7Szhanghch05 messages::internalError(sensorsAsyncResp->asyncResp->res); 103*8d1b46d7Szhanghch05 BMCWEB_LOG_ERROR << "Fail to get PowerCapEnable status "; 1044bb3dc34SCarol Wang return; 1054bb3dc34SCarol Wang } 1064bb3dc34SCarol Wang if (!(*b)) 1074bb3dc34SCarol Wang { 1084bb3dc34SCarol Wang messages::actionNotSupported( 109*8d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res, 1104bb3dc34SCarol Wang "Setting LimitInWatts when PowerLimit " 1114bb3dc34SCarol Wang "feature is disabled"); 1124bb3dc34SCarol Wang BMCWEB_LOG_ERROR << "PowerLimit feature is disabled "; 1134bb3dc34SCarol Wang return; 1144bb3dc34SCarol Wang } 1154bb3dc34SCarol Wang 1164bb3dc34SCarol Wang crow::connections::systemBus->async_method_call( 117*8d1b46d7Szhanghch05 [sensorsAsyncResp](const boost::system::error_code ec2) { 11823a21a1cSEd Tanous if (ec2) 1194bb3dc34SCarol Wang { 120*8d1b46d7Szhanghch05 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: " 121*8d1b46d7Szhanghch05 << ec2; 122*8d1b46d7Szhanghch05 messages::internalError( 123*8d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res); 1244bb3dc34SCarol Wang return; 1254bb3dc34SCarol Wang } 126*8d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res.result( 1274bb3dc34SCarol Wang boost::beast::http::status::no_content); 1284bb3dc34SCarol Wang }, 1294bb3dc34SCarol Wang "xyz.openbmc_project.Settings", 1304bb3dc34SCarol Wang "/xyz/openbmc_project/control/host0/power_cap", 1314bb3dc34SCarol Wang "org.freedesktop.DBus.Properties", "Set", 1324bb3dc34SCarol Wang "xyz.openbmc_project.Control.Power.Cap", "PowerCap", 13319bd78d9SPatrick Williams std::variant<uint32_t>(*value)); 1344bb3dc34SCarol Wang }; 1354bb3dc34SCarol Wang crow::connections::systemBus->async_method_call( 1364bb3dc34SCarol Wang std::move(valueHandler), "xyz.openbmc_project.Settings", 1374bb3dc34SCarol Wang "/xyz/openbmc_project/control/host0/power_cap", 1384bb3dc34SCarol Wang "org.freedesktop.DBus.Properties", "Get", 1394bb3dc34SCarol Wang "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable"); 1404bb3dc34SCarol Wang }; 141*8d1b46d7Szhanghch05 getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath)); 1424bb3dc34SCarol Wang } 143*8d1b46d7Szhanghch05 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 144*8d1b46d7Szhanghch05 const crow::Request&, 1452474adfaSEd Tanous const std::vector<std::string>& params) override 1462474adfaSEd Tanous { 1472474adfaSEd Tanous if (params.size() != 1) 1482474adfaSEd Tanous { 149*8d1b46d7Szhanghch05 asyncResp->res.result( 150*8d1b46d7Szhanghch05 boost::beast::http::status::internal_server_error); 1512474adfaSEd Tanous return; 1522474adfaSEd Tanous } 1532c70f800SEd Tanous const std::string& chassisName = params[0]; 1542474adfaSEd Tanous 155*8d1b46d7Szhanghch05 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 156c5d03ff4SJennifer Lee 1572474adfaSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 158*8d1b46d7Szhanghch05 asyncResp, chassisName, 159*8d1b46d7Szhanghch05 sensors::dbus::paths.at(sensors::node::power), 160a0ec28b6SAdrian Ambrożewicz sensors::node::power); 161028f7ebcSEddie James 1622474adfaSEd Tanous getChassisData(sensorAsyncResp); 163028f7ebcSEddie James 164028f7ebcSEddie James // This callback verifies that the power limit is only provided for the 165028f7ebcSEddie James // chassis that implements the Chassis inventory item. This prevents 166028f7ebcSEddie James // things like power supplies providing the chassis power limit 167028f7ebcSEddie James auto chassisHandler = [sensorAsyncResp]( 168271584abSEd Tanous const boost::system::error_code e, 169028f7ebcSEddie James const std::vector<std::string>& 170028f7ebcSEddie James chassisPaths) { 171271584abSEd Tanous if (e) 172028f7ebcSEddie James { 173028f7ebcSEddie James BMCWEB_LOG_ERROR 174271584abSEd Tanous << "Power Limit GetSubTreePaths handler Dbus error " << e; 175028f7ebcSEddie James return; 176028f7ebcSEddie James } 177028f7ebcSEddie James 178028f7ebcSEddie James bool found = false; 179028f7ebcSEddie James for (const std::string& chassis : chassisPaths) 180028f7ebcSEddie James { 181028f7ebcSEddie James size_t len = std::string::npos; 182f23b7296SEd Tanous size_t lastPos = chassis.rfind('/'); 183028f7ebcSEddie James if (lastPos == std::string::npos) 184028f7ebcSEddie James { 185028f7ebcSEddie James continue; 186028f7ebcSEddie James } 187028f7ebcSEddie James 188028f7ebcSEddie James if (lastPos == chassis.size() - 1) 189028f7ebcSEddie James { 190028f7ebcSEddie James size_t end = lastPos; 191f23b7296SEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 192028f7ebcSEddie James if (lastPos == std::string::npos) 193028f7ebcSEddie James { 194028f7ebcSEddie James continue; 195028f7ebcSEddie James } 196028f7ebcSEddie James 197028f7ebcSEddie James len = end - (lastPos + 1); 198028f7ebcSEddie James } 199028f7ebcSEddie James 200028f7ebcSEddie James std::string interfaceChassisName = 201028f7ebcSEddie James chassis.substr(lastPos + 1, len); 202028f7ebcSEddie James if (!interfaceChassisName.compare(sensorAsyncResp->chassisId)) 203028f7ebcSEddie James { 204028f7ebcSEddie James found = true; 205028f7ebcSEddie James break; 206028f7ebcSEddie James } 207028f7ebcSEddie James } 208028f7ebcSEddie James 209028f7ebcSEddie James if (!found) 210028f7ebcSEddie James { 211028f7ebcSEddie James BMCWEB_LOG_DEBUG << "Power Limit not present for " 212028f7ebcSEddie James << sensorAsyncResp->chassisId; 213028f7ebcSEddie James return; 214028f7ebcSEddie James } 215028f7ebcSEddie James 216028f7ebcSEddie James auto valueHandler = 217028f7ebcSEddie James [sensorAsyncResp]( 218028f7ebcSEddie James const boost::system::error_code ec, 219028f7ebcSEddie James const std::vector<std::pair<std::string, SensorVariant>>& 220028f7ebcSEddie James properties) { 221028f7ebcSEddie James if (ec) 222028f7ebcSEddie James { 223*8d1b46d7Szhanghch05 messages::internalError( 224*8d1b46d7Szhanghch05 sensorAsyncResp->asyncResp->res); 225028f7ebcSEddie James BMCWEB_LOG_ERROR 226028f7ebcSEddie James << "Power Limit GetAll handler: Dbus error " << ec; 227028f7ebcSEddie James return; 228028f7ebcSEddie James } 229028f7ebcSEddie James 230*8d1b46d7Szhanghch05 nlohmann::json& tempArray = sensorAsyncResp->asyncResp->res 231*8d1b46d7Szhanghch05 .jsonValue["PowerControl"]; 232028f7ebcSEddie James 2337ab06f49SGunnar Mills // Put multiple "sensors" into a single PowerControl, 0, so 2347ab06f49SGunnar Mills // only create the first one 235028f7ebcSEddie James if (tempArray.empty()) 236028f7ebcSEddie James { 2377ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 2387ab06f49SGunnar Mills // A warning without a odata.type 2397ab06f49SGunnar Mills tempArray.push_back( 2407ab06f49SGunnar Mills {{"@odata.type", "#Power.v1_0_0.PowerControl"}, 2417ab06f49SGunnar Mills {"@odata.id", "/redfish/v1/Chassis/" + 2427ab06f49SGunnar Mills sensorAsyncResp->chassisId + 2437ab06f49SGunnar Mills "/Power#/PowerControl/0"}, 2447ab06f49SGunnar Mills {"Name", "Chassis Power Control"}, 2457ab06f49SGunnar Mills {"MemberId", "0"}}); 246028f7ebcSEddie James } 247028f7ebcSEddie James 248028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 249028f7ebcSEddie James bool enabled = false; 250028f7ebcSEddie James double powerCap = 0.0; 251028f7ebcSEddie James int64_t scale = 0; 252028f7ebcSEddie James 253028f7ebcSEddie James for (const std::pair<std::string, SensorVariant>& property : 254028f7ebcSEddie James properties) 255028f7ebcSEddie James { 256028f7ebcSEddie James if (!property.first.compare("Scale")) 257028f7ebcSEddie James { 258028f7ebcSEddie James const int64_t* i = 2598d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 260028f7ebcSEddie James 261028f7ebcSEddie James if (i) 262028f7ebcSEddie James { 263028f7ebcSEddie James scale = *i; 264028f7ebcSEddie James } 265028f7ebcSEddie James } 266028f7ebcSEddie James else if (!property.first.compare("PowerCap")) 267028f7ebcSEddie James { 268028f7ebcSEddie James const double* d = 2698d78b7a9SPatrick Williams std::get_if<double>(&property.second); 270028f7ebcSEddie James const int64_t* i = 2718d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 272028f7ebcSEddie James const uint32_t* u = 2738d78b7a9SPatrick Williams std::get_if<uint32_t>(&property.second); 274028f7ebcSEddie James 275028f7ebcSEddie James if (d) 276028f7ebcSEddie James { 277028f7ebcSEddie James powerCap = *d; 278028f7ebcSEddie James } 279028f7ebcSEddie James else if (i) 280028f7ebcSEddie James { 281271584abSEd Tanous powerCap = static_cast<double>(*i); 282028f7ebcSEddie James } 283028f7ebcSEddie James else if (u) 284028f7ebcSEddie James { 285028f7ebcSEddie James powerCap = *u; 286028f7ebcSEddie James } 287028f7ebcSEddie James } 288028f7ebcSEddie James else if (!property.first.compare("PowerCapEnable")) 289028f7ebcSEddie James { 2908d78b7a9SPatrick Williams const bool* b = std::get_if<bool>(&property.second); 291028f7ebcSEddie James 292028f7ebcSEddie James if (b) 293028f7ebcSEddie James { 294028f7ebcSEddie James enabled = *b; 295028f7ebcSEddie James } 296028f7ebcSEddie James } 297028f7ebcSEddie James } 298028f7ebcSEddie James 2997ab06f49SGunnar Mills nlohmann::json& value = 3007ab06f49SGunnar Mills sensorJson["PowerLimit"]["LimitInWatts"]; 301028f7ebcSEddie James 3025a64a6f3SJoshi-Mansi // LimitException is Mandatory attribute as per OCP Baseline 3035a64a6f3SJoshi-Mansi // Profile – v1.0.0, so currently making it "NoAction" 3045a64a6f3SJoshi-Mansi // as default value to make it OCP Compliant. 3055a64a6f3SJoshi-Mansi sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 3065a64a6f3SJoshi-Mansi 307028f7ebcSEddie James if (enabled) 308028f7ebcSEddie James { 309028f7ebcSEddie James // Redfish specification indicates PowerLimit should be 310028f7ebcSEddie James // null if the limit is not enabled. 311028f7ebcSEddie James value = powerCap * std::pow(10, scale); 312028f7ebcSEddie James } 313028f7ebcSEddie James }; 314028f7ebcSEddie James 315028f7ebcSEddie James crow::connections::systemBus->async_method_call( 316028f7ebcSEddie James std::move(valueHandler), "xyz.openbmc_project.Settings", 317028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 318028f7ebcSEddie James "org.freedesktop.DBus.Properties", "GetAll", 319028f7ebcSEddie James "xyz.openbmc_project.Control.Power.Cap"); 320028f7ebcSEddie James }; 321028f7ebcSEddie James 322028f7ebcSEddie James crow::connections::systemBus->async_method_call( 323028f7ebcSEddie James std::move(chassisHandler), "xyz.openbmc_project.ObjectMapper", 324028f7ebcSEddie James "/xyz/openbmc_project/object_mapper", 325028f7ebcSEddie James "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 326271584abSEd Tanous "/xyz/openbmc_project/inventory", 0, 327f857e9aeSAppaRao Puli std::array<const char*, 2>{ 328f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 329028f7ebcSEddie James "xyz.openbmc_project.Inventory.Item.Chassis"}); 3302474adfaSEd Tanous } 331*8d1b46d7Szhanghch05 void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 332*8d1b46d7Szhanghch05 const crow::Request& req, 333413961deSRichard Marian Thomaiyar const std::vector<std::string>& params) override 334413961deSRichard Marian Thomaiyar { 3354bb3dc34SCarol Wang if (params.size() != 1) 3364bb3dc34SCarol Wang { 337*8d1b46d7Szhanghch05 messages::internalError(asyncResp->res); 3384bb3dc34SCarol Wang return; 3394bb3dc34SCarol Wang } 3404bb3dc34SCarol Wang 3414bb3dc34SCarol Wang const std::string& chassisName = params[0]; 342*8d1b46d7Szhanghch05 343*8d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 344*8d1b46d7Szhanghch05 asyncResp, chassisName, 345*8d1b46d7Szhanghch05 sensors::dbus::paths.at(sensors::node::power), 346a0ec28b6SAdrian Ambrożewicz sensors::node::power); 3474bb3dc34SCarol Wang 3484bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> voltageCollections; 3494bb3dc34SCarol Wang std::optional<std::vector<nlohmann::json>> powerCtlCollections; 3504bb3dc34SCarol Wang 351*8d1b46d7Szhanghch05 if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res, 352*8d1b46d7Szhanghch05 "PowerControl", powerCtlCollections, 353*8d1b46d7Szhanghch05 "Voltages", voltageCollections)) 3544bb3dc34SCarol Wang { 3554bb3dc34SCarol Wang return; 3564bb3dc34SCarol Wang } 3574bb3dc34SCarol Wang 3584bb3dc34SCarol Wang if (powerCtlCollections) 3594bb3dc34SCarol Wang { 360*8d1b46d7Szhanghch05 setPowerCapOverride(sensorAsyncResp, *powerCtlCollections); 3614bb3dc34SCarol Wang } 3624bb3dc34SCarol Wang if (voltageCollections) 3634bb3dc34SCarol Wang { 3644bb3dc34SCarol Wang std::unordered_map<std::string, std::vector<nlohmann::json>> 3654bb3dc34SCarol Wang allCollections; 3664bb3dc34SCarol Wang allCollections.emplace("Voltages", *std::move(voltageCollections)); 367*8d1b46d7Szhanghch05 checkAndDoSensorsOverride(sensorAsyncResp, allCollections); 3684bb3dc34SCarol Wang } 369413961deSRichard Marian Thomaiyar } 3702474adfaSEd Tanous }; 3712474adfaSEd Tanous 3722474adfaSEd Tanous } // namespace redfish 373