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> 22ed398213SEd Tanous #include <registries/privilege_registry.hpp> 237e860f15SJohn Edward Broadbent 242474adfaSEd Tanous namespace redfish 252474adfaSEd Tanous { 264f48d5f6SEd Tanous inline void setPowerCapOverride( 278d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 284bb3dc34SCarol Wang std::vector<nlohmann::json>& powerControlCollections) 294bb3dc34SCarol Wang { 30*0fda0f12SGeorge Liu auto getChassisPath = [sensorsAsyncResp, powerControlCollections]( 31*0fda0f12SGeorge Liu const std::optional<std::string>& 32*0fda0f12SGeorge Liu chassisPath) mutable { 334bb3dc34SCarol Wang if (!chassisPath) 344bb3dc34SCarol Wang { 354bb3dc34SCarol Wang BMCWEB_LOG_ERROR << "Don't find valid chassis path "; 368d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 37*0fda0f12SGeorge Liu "Chassis", sensorsAsyncResp->chassisId); 384bb3dc34SCarol Wang return; 394bb3dc34SCarol Wang } 404bb3dc34SCarol Wang 414bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 424bb3dc34SCarol Wang { 438d1b46d7Szhanghch05 BMCWEB_LOG_ERROR << "Don't support multiple hosts at present "; 448d1b46d7Szhanghch05 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 458d1b46d7Szhanghch05 "Power", "PowerControl"); 464bb3dc34SCarol Wang return; 474bb3dc34SCarol Wang } 484bb3dc34SCarol Wang 494bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 504bb3dc34SCarol Wang 514bb3dc34SCarol Wang std::optional<nlohmann::json> powerLimit; 528d1b46d7Szhanghch05 if (!json_util::readJson(item, sensorsAsyncResp->asyncResp->res, 538d1b46d7Szhanghch05 "PowerLimit", powerLimit)) 544bb3dc34SCarol Wang { 554bb3dc34SCarol Wang return; 564bb3dc34SCarol Wang } 574bb3dc34SCarol Wang if (!powerLimit) 584bb3dc34SCarol Wang { 594bb3dc34SCarol Wang return; 604bb3dc34SCarol Wang } 614bb3dc34SCarol Wang std::optional<uint32_t> value; 62*0fda0f12SGeorge Liu if (!json_util::readJson(*powerLimit, sensorsAsyncResp->asyncResp->res, 634bb3dc34SCarol Wang "LimitInWatts", value)) 644bb3dc34SCarol Wang { 654bb3dc34SCarol Wang return; 664bb3dc34SCarol Wang } 674bb3dc34SCarol Wang if (!value) 684bb3dc34SCarol Wang { 694bb3dc34SCarol Wang return; 704bb3dc34SCarol Wang } 718d1b46d7Szhanghch05 auto valueHandler = [value, sensorsAsyncResp]( 724bb3dc34SCarol Wang const boost::system::error_code ec, 734bb3dc34SCarol Wang const SensorVariant& powerCapEnable) { 744bb3dc34SCarol Wang if (ec) 754bb3dc34SCarol Wang { 768d1b46d7Szhanghch05 messages::internalError(sensorsAsyncResp->asyncResp->res); 77*0fda0f12SGeorge Liu BMCWEB_LOG_ERROR << "powerCapEnable Get handler: Dbus error " 78*0fda0f12SGeorge Liu << ec; 794bb3dc34SCarol Wang return; 804bb3dc34SCarol Wang } 814bb3dc34SCarol Wang // Check PowerCapEnable 828d78b7a9SPatrick Williams const bool* b = std::get_if<bool>(&powerCapEnable); 834bb3dc34SCarol Wang if (b == nullptr) 844bb3dc34SCarol Wang { 858d1b46d7Szhanghch05 messages::internalError(sensorsAsyncResp->asyncResp->res); 868d1b46d7Szhanghch05 BMCWEB_LOG_ERROR << "Fail to get PowerCapEnable status "; 874bb3dc34SCarol Wang return; 884bb3dc34SCarol Wang } 894bb3dc34SCarol Wang if (!(*b)) 904bb3dc34SCarol Wang { 914bb3dc34SCarol Wang messages::actionNotSupported( 928d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res, 93*0fda0f12SGeorge Liu "Setting LimitInWatts when PowerLimit feature is disabled"); 944bb3dc34SCarol Wang BMCWEB_LOG_ERROR << "PowerLimit feature is disabled "; 954bb3dc34SCarol Wang return; 964bb3dc34SCarol Wang } 974bb3dc34SCarol Wang 984bb3dc34SCarol Wang crow::connections::systemBus->async_method_call( 998d1b46d7Szhanghch05 [sensorsAsyncResp](const boost::system::error_code ec2) { 10023a21a1cSEd Tanous if (ec2) 1014bb3dc34SCarol Wang { 1028d1b46d7Szhanghch05 BMCWEB_LOG_DEBUG << "Power Limit Set: Dbus error: " 1038d1b46d7Szhanghch05 << ec2; 1048d1b46d7Szhanghch05 messages::internalError( 1058d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res); 1064bb3dc34SCarol Wang return; 1074bb3dc34SCarol Wang } 1088d1b46d7Szhanghch05 sensorsAsyncResp->asyncResp->res.result( 1094bb3dc34SCarol Wang boost::beast::http::status::no_content); 1104bb3dc34SCarol Wang }, 1114bb3dc34SCarol Wang "xyz.openbmc_project.Settings", 1124bb3dc34SCarol Wang "/xyz/openbmc_project/control/host0/power_cap", 1134bb3dc34SCarol Wang "org.freedesktop.DBus.Properties", "Set", 1144bb3dc34SCarol Wang "xyz.openbmc_project.Control.Power.Cap", "PowerCap", 11519bd78d9SPatrick Williams std::variant<uint32_t>(*value)); 1164bb3dc34SCarol Wang }; 1174bb3dc34SCarol Wang crow::connections::systemBus->async_method_call( 1184bb3dc34SCarol Wang std::move(valueHandler), "xyz.openbmc_project.Settings", 1194bb3dc34SCarol Wang "/xyz/openbmc_project/control/host0/power_cap", 1204bb3dc34SCarol Wang "org.freedesktop.DBus.Properties", "Get", 1214bb3dc34SCarol Wang "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable"); 1224bb3dc34SCarol Wang }; 1238d1b46d7Szhanghch05 getValidChassisPath(sensorsAsyncResp, std::move(getChassisPath)); 1244bb3dc34SCarol Wang } 1257e860f15SJohn Edward Broadbent inline void requestRoutesPower(App& app) 1262474adfaSEd Tanous { 1272474adfaSEd Tanous 1287e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 129ed398213SEd Tanous .privileges(redfish::privileges::getPower) 1307e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 1317e860f15SJohn Edward Broadbent [](const crow::Request&, 1327e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1337e860f15SJohn Edward Broadbent const std::string& chassisName) { 1347e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["PowerControl"] = 1357e860f15SJohn Edward Broadbent nlohmann::json::array(); 136c5d03ff4SJennifer Lee 1372474adfaSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 1388d1b46d7Szhanghch05 asyncResp, chassisName, 1398d1b46d7Szhanghch05 sensors::dbus::paths.at(sensors::node::power), 140a0ec28b6SAdrian Ambrożewicz sensors::node::power); 141028f7ebcSEddie James 1422474adfaSEd Tanous getChassisData(sensorAsyncResp); 143028f7ebcSEddie James 1447e860f15SJohn Edward Broadbent // This callback verifies that the power limit is only provided 1457e860f15SJohn Edward Broadbent // for the chassis that implements the Chassis inventory item. 1467e860f15SJohn Edward Broadbent // This prevents things like power supplies providing the 1477e860f15SJohn Edward Broadbent // chassis power limit 148028f7ebcSEddie James auto chassisHandler = [sensorAsyncResp]( 149271584abSEd Tanous const boost::system::error_code e, 150028f7ebcSEddie James const std::vector<std::string>& 151028f7ebcSEddie James chassisPaths) { 152271584abSEd Tanous if (e) 153028f7ebcSEddie James { 154028f7ebcSEddie James BMCWEB_LOG_ERROR 1557e860f15SJohn Edward Broadbent << "Power Limit GetSubTreePaths handler Dbus error " 1567e860f15SJohn Edward Broadbent << e; 157028f7ebcSEddie James return; 158028f7ebcSEddie James } 159028f7ebcSEddie James 160028f7ebcSEddie James bool found = false; 161028f7ebcSEddie James for (const std::string& chassis : chassisPaths) 162028f7ebcSEddie James { 163028f7ebcSEddie James size_t len = std::string::npos; 164f23b7296SEd Tanous size_t lastPos = chassis.rfind('/'); 165028f7ebcSEddie James if (lastPos == std::string::npos) 166028f7ebcSEddie James { 167028f7ebcSEddie James continue; 168028f7ebcSEddie James } 169028f7ebcSEddie James 170028f7ebcSEddie James if (lastPos == chassis.size() - 1) 171028f7ebcSEddie James { 172028f7ebcSEddie James size_t end = lastPos; 173f23b7296SEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 174028f7ebcSEddie James if (lastPos == std::string::npos) 175028f7ebcSEddie James { 176028f7ebcSEddie James continue; 177028f7ebcSEddie James } 178028f7ebcSEddie James 179028f7ebcSEddie James len = end - (lastPos + 1); 180028f7ebcSEddie James } 181028f7ebcSEddie James 182028f7ebcSEddie James std::string interfaceChassisName = 183028f7ebcSEddie James chassis.substr(lastPos + 1, len); 1847e860f15SJohn Edward Broadbent if (!interfaceChassisName.compare( 1857e860f15SJohn Edward Broadbent sensorAsyncResp->chassisId)) 186028f7ebcSEddie James { 187028f7ebcSEddie James found = true; 188028f7ebcSEddie James break; 189028f7ebcSEddie James } 190028f7ebcSEddie James } 191028f7ebcSEddie James 192028f7ebcSEddie James if (!found) 193028f7ebcSEddie James { 194028f7ebcSEddie James BMCWEB_LOG_DEBUG << "Power Limit not present for " 195028f7ebcSEddie James << sensorAsyncResp->chassisId; 196028f7ebcSEddie James return; 197028f7ebcSEddie James } 198028f7ebcSEddie James 1997e860f15SJohn Edward Broadbent auto valueHandler = [sensorAsyncResp]( 200028f7ebcSEddie James const boost::system::error_code ec, 2017e860f15SJohn Edward Broadbent const std::vector<std::pair< 2027e860f15SJohn Edward Broadbent std::string, SensorVariant>>& 203028f7ebcSEddie James properties) { 204028f7ebcSEddie James if (ec) 205028f7ebcSEddie James { 2068d1b46d7Szhanghch05 messages::internalError( 2078d1b46d7Szhanghch05 sensorAsyncResp->asyncResp->res); 208028f7ebcSEddie James BMCWEB_LOG_ERROR 2097e860f15SJohn Edward Broadbent << "Power Limit GetAll handler: Dbus error " 2107e860f15SJohn Edward Broadbent << ec; 211028f7ebcSEddie James return; 212028f7ebcSEddie James } 213028f7ebcSEddie James 2147e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 2157e860f15SJohn Edward Broadbent sensorAsyncResp->asyncResp->res 2168d1b46d7Szhanghch05 .jsonValue["PowerControl"]; 217028f7ebcSEddie James 2187e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 2197e860f15SJohn Edward Broadbent // so only create the first one 220028f7ebcSEddie James if (tempArray.empty()) 221028f7ebcSEddie James { 2227ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 2237ab06f49SGunnar Mills // A warning without a odata.type 2247ab06f49SGunnar Mills tempArray.push_back( 2257ab06f49SGunnar Mills {{"@odata.type", "#Power.v1_0_0.PowerControl"}, 2267ab06f49SGunnar Mills {"@odata.id", "/redfish/v1/Chassis/" + 2277ab06f49SGunnar Mills sensorAsyncResp->chassisId + 2287ab06f49SGunnar Mills "/Power#/PowerControl/0"}, 2297ab06f49SGunnar Mills {"Name", "Chassis Power Control"}, 2307ab06f49SGunnar Mills {"MemberId", "0"}}); 231028f7ebcSEddie James } 232028f7ebcSEddie James 233028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 234028f7ebcSEddie James bool enabled = false; 235028f7ebcSEddie James double powerCap = 0.0; 236028f7ebcSEddie James int64_t scale = 0; 237028f7ebcSEddie James 2387e860f15SJohn Edward Broadbent for (const std::pair<std::string, SensorVariant>& 2397e860f15SJohn Edward Broadbent property : properties) 240028f7ebcSEddie James { 241028f7ebcSEddie James if (!property.first.compare("Scale")) 242028f7ebcSEddie James { 243028f7ebcSEddie James const int64_t* i = 2448d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 245028f7ebcSEddie James 246028f7ebcSEddie James if (i) 247028f7ebcSEddie James { 248028f7ebcSEddie James scale = *i; 249028f7ebcSEddie James } 250028f7ebcSEddie James } 251028f7ebcSEddie James else if (!property.first.compare("PowerCap")) 252028f7ebcSEddie James { 253028f7ebcSEddie James const double* d = 2548d78b7a9SPatrick Williams std::get_if<double>(&property.second); 255028f7ebcSEddie James const int64_t* i = 2568d78b7a9SPatrick Williams std::get_if<int64_t>(&property.second); 257028f7ebcSEddie James const uint32_t* u = 2588d78b7a9SPatrick Williams std::get_if<uint32_t>(&property.second); 259028f7ebcSEddie James 260028f7ebcSEddie James if (d) 261028f7ebcSEddie James { 262028f7ebcSEddie James powerCap = *d; 263028f7ebcSEddie James } 264028f7ebcSEddie James else if (i) 265028f7ebcSEddie James { 266271584abSEd Tanous powerCap = static_cast<double>(*i); 267028f7ebcSEddie James } 268028f7ebcSEddie James else if (u) 269028f7ebcSEddie James { 270028f7ebcSEddie James powerCap = *u; 271028f7ebcSEddie James } 272028f7ebcSEddie James } 273028f7ebcSEddie James else if (!property.first.compare("PowerCapEnable")) 274028f7ebcSEddie James { 2757e860f15SJohn Edward Broadbent const bool* b = 2767e860f15SJohn Edward Broadbent std::get_if<bool>(&property.second); 277028f7ebcSEddie James 278028f7ebcSEddie James if (b) 279028f7ebcSEddie James { 280028f7ebcSEddie James enabled = *b; 281028f7ebcSEddie James } 282028f7ebcSEddie James } 283028f7ebcSEddie James } 284028f7ebcSEddie James 2857ab06f49SGunnar Mills nlohmann::json& value = 2867ab06f49SGunnar Mills sensorJson["PowerLimit"]["LimitInWatts"]; 287028f7ebcSEddie James 2887e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 2897e860f15SJohn Edward Broadbent // Baseline Profile – v1.0.0, so currently making it 2907e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 2915a64a6f3SJoshi-Mansi sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 2925a64a6f3SJoshi-Mansi 293028f7ebcSEddie James if (enabled) 294028f7ebcSEddie James { 2957e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 2967e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 297028f7ebcSEddie James value = powerCap * std::pow(10, scale); 298028f7ebcSEddie James } 299028f7ebcSEddie James }; 300028f7ebcSEddie James 301028f7ebcSEddie James crow::connections::systemBus->async_method_call( 302028f7ebcSEddie James std::move(valueHandler), "xyz.openbmc_project.Settings", 303028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 304028f7ebcSEddie James "org.freedesktop.DBus.Properties", "GetAll", 305028f7ebcSEddie James "xyz.openbmc_project.Control.Power.Cap"); 306028f7ebcSEddie James }; 307028f7ebcSEddie James 308028f7ebcSEddie James crow::connections::systemBus->async_method_call( 3097e860f15SJohn Edward Broadbent std::move(chassisHandler), 3107e860f15SJohn Edward Broadbent "xyz.openbmc_project.ObjectMapper", 311028f7ebcSEddie James "/xyz/openbmc_project/object_mapper", 312028f7ebcSEddie James "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 313271584abSEd Tanous "/xyz/openbmc_project/inventory", 0, 314f857e9aeSAppaRao Puli std::array<const char*, 2>{ 315f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 316028f7ebcSEddie James "xyz.openbmc_project.Inventory.Item.Chassis"}); 3177e860f15SJohn Edward Broadbent }); 3184bb3dc34SCarol Wang 3197e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 320ed398213SEd Tanous .privileges(redfish::privileges::patchPower) 3217e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::patch)( 3227e860f15SJohn Edward Broadbent [](const crow::Request& req, 3237e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3247e860f15SJohn Edward Broadbent const std::string& chassisName) { 3258d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 3268d1b46d7Szhanghch05 asyncResp, chassisName, 3278d1b46d7Szhanghch05 sensors::dbus::paths.at(sensors::node::power), 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 3338d1b46d7Szhanghch05 if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res, 3348d1b46d7Szhanghch05 "PowerControl", powerCtlCollections, 3358d1b46d7Szhanghch05 "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