12474adfaSEd Tanous /* 2*6be832e2SEd Tanous Copyright (c) 2018 Intel Corporation 3*6be832e2SEd Tanous Copyright (c) 2018 Ampere Computing LLC 4*6be832e2SEd Tanous 5*6be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License"); 6*6be832e2SEd Tanous you may not use this file except in compliance with the License. 7*6be832e2SEd Tanous You may obtain a copy of the License at 8*6be832e2SEd Tanous 9*6be832e2SEd Tanous http://www.apache.org/licenses/LICENSE-2.0 10*6be832e2SEd Tanous 11*6be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software 12*6be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS, 13*6be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*6be832e2SEd Tanous See the License for the specific language governing permissions and 15*6be832e2SEd Tanous limitations under the License. 162474adfaSEd Tanous */ 172474adfaSEd Tanous #pragma once 182474adfaSEd Tanous 193ccb3adbSEd Tanous #include "app.hpp" 207a1dbc48SGeorge Liu #include "dbus_utility.hpp" 21539d8c6bSEd Tanous #include "generated/enums/power.hpp" 223ccb3adbSEd Tanous #include "query.hpp" 233ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 242474adfaSEd Tanous #include "sensors.hpp" 250d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp" 265b90429aSEd Tanous #include "utils/json_utils.hpp" 27c9563608SJanet Adkins #include "utils/sensor_utils.hpp" 282474adfaSEd Tanous 29d1bde9e5SKrzysztof Grobelny #include <sdbusplus/asio/property.hpp> 307e860f15SJohn Edward Broadbent 317a1dbc48SGeorge Liu #include <array> 320885057cSEd Tanous #include <string> 337a1dbc48SGeorge Liu #include <string_view> 340885057cSEd Tanous #include <vector> 357a1dbc48SGeorge Liu 362474adfaSEd Tanous namespace redfish 372474adfaSEd Tanous { 3853b00f5dSEd Tanous 3953b00f5dSEd Tanous inline void afterGetPowerCapEnable( 408d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 4153b00f5dSEd Tanous uint32_t valueToSet, const boost::system::error_code& ec, 4253b00f5dSEd Tanous bool powerCapEnable) 434bb3dc34SCarol Wang { 4453b00f5dSEd Tanous if (ec) 4553b00f5dSEd Tanous { 4653b00f5dSEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 4753b00f5dSEd Tanous BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec); 4853b00f5dSEd Tanous return; 4953b00f5dSEd Tanous } 5053b00f5dSEd Tanous if (!powerCapEnable) 5153b00f5dSEd Tanous { 5253b00f5dSEd Tanous messages::actionNotSupported( 5353b00f5dSEd Tanous sensorsAsyncResp->asyncResp->res, 5453b00f5dSEd Tanous "Setting LimitInWatts when PowerLimit feature is disabled"); 5553b00f5dSEd Tanous BMCWEB_LOG_ERROR("PowerLimit feature is disabled "); 5653b00f5dSEd Tanous return; 5753b00f5dSEd Tanous } 5853b00f5dSEd Tanous 59e93abac6SGinu George setDbusProperty(sensorsAsyncResp->asyncResp, "PowerControl", 60e93abac6SGinu George "xyz.openbmc_project.Settings", 6187c44966SAsmitha Karunanithi sdbusplus::message::object_path( 6287c44966SAsmitha Karunanithi "/xyz/openbmc_project/control/host0/power_cap"), 6387c44966SAsmitha Karunanithi "xyz.openbmc_project.Control.Power.Cap", "PowerCap", 64e93abac6SGinu George valueToSet); 6553b00f5dSEd Tanous } 6653b00f5dSEd Tanous 6753b00f5dSEd Tanous inline void afterGetChassisPath( 6853b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 690885057cSEd Tanous std::vector<nlohmann::json::object_t>& powerControlCollections, 7053b00f5dSEd Tanous const std::optional<std::string>& chassisPath) 7153b00f5dSEd Tanous { 724bb3dc34SCarol Wang if (!chassisPath) 734bb3dc34SCarol Wang { 7462598e31SEd Tanous BMCWEB_LOG_WARNING("Don't find valid chassis path "); 7553b00f5dSEd Tanous messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis", 7653b00f5dSEd Tanous sensorsAsyncResp->chassisId); 774bb3dc34SCarol Wang return; 784bb3dc34SCarol Wang } 794bb3dc34SCarol Wang 804bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 814bb3dc34SCarol Wang { 8262598e31SEd Tanous BMCWEB_LOG_WARNING("Don't support multiple hosts at present "); 8353b00f5dSEd Tanous messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power", 8453b00f5dSEd Tanous "PowerControl"); 854bb3dc34SCarol Wang return; 864bb3dc34SCarol Wang } 874bb3dc34SCarol Wang 884bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 894bb3dc34SCarol Wang 904bb3dc34SCarol Wang std::optional<uint32_t> value; 910885057cSEd Tanous if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res, 920885057cSEd Tanous "PowerLimit/LimitInWatts", value)) 934bb3dc34SCarol Wang { 944bb3dc34SCarol Wang return; 954bb3dc34SCarol Wang } 964bb3dc34SCarol Wang if (!value) 974bb3dc34SCarol Wang { 984bb3dc34SCarol Wang return; 994bb3dc34SCarol Wang } 1001e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 1011e1e598dSJonathan Doman *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1021e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 1031e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 10453b00f5dSEd Tanous std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value)); 1054bb3dc34SCarol Wang } 1064bb3dc34SCarol Wang 10753b00f5dSEd Tanous inline void afterPowerCapSettingGet( 10853b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 1095e7e2dc5SEd Tanous const boost::system::error_code& ec, 11053b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties) 11153b00f5dSEd Tanous { 112028f7ebcSEddie James if (ec) 113028f7ebcSEddie James { 114002d39b4SEd Tanous messages::internalError(sensorAsyncResp->asyncResp->res); 11553b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec); 116028f7ebcSEddie James return; 117028f7ebcSEddie James } 118028f7ebcSEddie James 1197e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 120002d39b4SEd Tanous sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 121028f7ebcSEddie James 1227e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 1237e860f15SJohn Edward Broadbent // so only create the first one 124028f7ebcSEddie James if (tempArray.empty()) 125028f7ebcSEddie James { 1267ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 1277ab06f49SGunnar Mills // A warning without a odata.type 1281476687dSEd Tanous nlohmann::json::object_t powerControl; 129002d39b4SEd Tanous powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 130bd79bce8SPatrick Williams powerControl["@odata.id"] = 131bd79bce8SPatrick Williams "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId + 1321476687dSEd Tanous "/Power#/PowerControl/0"; 1331476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 1341476687dSEd Tanous powerControl["MemberId"] = "0"; 135b2ba3072SPatrick Williams tempArray.emplace_back(std::move(powerControl)); 136028f7ebcSEddie James } 137028f7ebcSEddie James 138028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 139028f7ebcSEddie James bool enabled = false; 140028f7ebcSEddie James double powerCap = 0.0; 141028f7ebcSEddie James int64_t scale = 0; 142028f7ebcSEddie James 14353b00f5dSEd Tanous for (const std::pair<std::string, dbus::utility::DbusVariantType>& 14453b00f5dSEd Tanous property : properties) 145028f7ebcSEddie James { 14655f79e6fSEd Tanous if (property.first == "Scale") 147028f7ebcSEddie James { 14853b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 149028f7ebcSEddie James 150e662eae8SEd Tanous if (i != nullptr) 151028f7ebcSEddie James { 152028f7ebcSEddie James scale = *i; 153028f7ebcSEddie James } 154028f7ebcSEddie James } 15555f79e6fSEd Tanous else if (property.first == "PowerCap") 156028f7ebcSEddie James { 157002d39b4SEd Tanous const double* d = std::get_if<double>(&property.second); 15853b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 15953b00f5dSEd Tanous const uint32_t* u = std::get_if<uint32_t>(&property.second); 160028f7ebcSEddie James 161e662eae8SEd Tanous if (d != nullptr) 162028f7ebcSEddie James { 163028f7ebcSEddie James powerCap = *d; 164028f7ebcSEddie James } 165e662eae8SEd Tanous else if (i != nullptr) 166028f7ebcSEddie James { 167271584abSEd Tanous powerCap = static_cast<double>(*i); 168028f7ebcSEddie James } 169e662eae8SEd Tanous else if (u != nullptr) 170028f7ebcSEddie James { 171028f7ebcSEddie James powerCap = *u; 172028f7ebcSEddie James } 173028f7ebcSEddie James } 17455f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 175028f7ebcSEddie James { 176002d39b4SEd Tanous const bool* b = std::get_if<bool>(&property.second); 177028f7ebcSEddie James 178e662eae8SEd Tanous if (b != nullptr) 179028f7ebcSEddie James { 180028f7ebcSEddie James enabled = *b; 181028f7ebcSEddie James } 182028f7ebcSEddie James } 183028f7ebcSEddie James } 184028f7ebcSEddie James 1857e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 186c9563608SJanet Adkins // Baseline Profile - v1.0.0, so currently making it 1877e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 188539d8c6bSEd Tanous sensorJson["PowerLimit"]["LimitException"] = 189539d8c6bSEd Tanous power::PowerLimitException::NoAction; 1905a64a6f3SJoshi-Mansi 191028f7ebcSEddie James if (enabled) 192028f7ebcSEddie James { 1937e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 1947e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 195bd79bce8SPatrick Williams sensorJson["PowerLimit"]["LimitInWatts"] = 196bd79bce8SPatrick Williams powerCap * std::pow(10, scale); 197028f7ebcSEddie James } 19853b00f5dSEd Tanous } 19953b00f5dSEd Tanous 20053b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 201bd79bce8SPatrick Williams inline void afterGetChassis( 202bd79bce8SPatrick Williams const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 203bd79bce8SPatrick Williams const boost::system::error_code& ec2, const Mapper& chassisPaths) 20453b00f5dSEd Tanous { 20553b00f5dSEd Tanous if (ec2) 20653b00f5dSEd Tanous { 20753b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}", 20853b00f5dSEd Tanous ec2); 20953b00f5dSEd Tanous return; 21053b00f5dSEd Tanous } 21153b00f5dSEd Tanous 21253b00f5dSEd Tanous bool found = false; 21353b00f5dSEd Tanous for (const std::string& chassis : chassisPaths) 21453b00f5dSEd Tanous { 21553b00f5dSEd Tanous size_t len = std::string::npos; 21653b00f5dSEd Tanous size_t lastPos = chassis.rfind('/'); 21753b00f5dSEd Tanous if (lastPos == std::string::npos) 21853b00f5dSEd Tanous { 21953b00f5dSEd Tanous continue; 22053b00f5dSEd Tanous } 22153b00f5dSEd Tanous 22253b00f5dSEd Tanous if (lastPos == chassis.size() - 1) 22353b00f5dSEd Tanous { 22453b00f5dSEd Tanous size_t end = lastPos; 22553b00f5dSEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 22653b00f5dSEd Tanous if (lastPos == std::string::npos) 22753b00f5dSEd Tanous { 22853b00f5dSEd Tanous continue; 22953b00f5dSEd Tanous } 23053b00f5dSEd Tanous 23153b00f5dSEd Tanous len = end - (lastPos + 1); 23253b00f5dSEd Tanous } 23353b00f5dSEd Tanous 23453b00f5dSEd Tanous std::string interfaceChassisName = chassis.substr(lastPos + 1, len); 23553b00f5dSEd Tanous if (interfaceChassisName == sensorAsyncResp->chassisId) 23653b00f5dSEd Tanous { 23753b00f5dSEd Tanous found = true; 23853b00f5dSEd Tanous break; 23953b00f5dSEd Tanous } 24053b00f5dSEd Tanous } 24153b00f5dSEd Tanous 24253b00f5dSEd Tanous if (!found) 24353b00f5dSEd Tanous { 24453b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit not present for {}", 24553b00f5dSEd Tanous sensorAsyncResp->chassisId); 24653b00f5dSEd Tanous return; 24753b00f5dSEd Tanous } 248028f7ebcSEddie James 249d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 250d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, "xyz.openbmc_project.Settings", 251028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 252d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Control.Power.Cap", 25353b00f5dSEd Tanous [sensorAsyncResp](const boost::system::error_code& ec, 25453b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties 25553b00f5dSEd Tanous 25653b00f5dSEd Tanous ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); }); 25753b00f5dSEd Tanous } 25853b00f5dSEd Tanous 25953b00f5dSEd Tanous inline void 26053b00f5dSEd Tanous handleChassisPowerGet(App& app, const crow::Request& req, 26153b00f5dSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26253b00f5dSEd Tanous const std::string& chassisName) 26353b00f5dSEd Tanous { 26453b00f5dSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 26553b00f5dSEd Tanous { 26653b00f5dSEd Tanous return; 26753b00f5dSEd Tanous } 26853b00f5dSEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 26953b00f5dSEd Tanous 27053b00f5dSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 27153b00f5dSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 2720c728b42SJanet Adkins sensor_utils::chassisSubNodeToString( 2730c728b42SJanet Adkins sensor_utils::ChassisSubNode::powerNode)); 27453b00f5dSEd Tanous 27553b00f5dSEd Tanous getChassisData(sensorAsyncResp); 27653b00f5dSEd Tanous 27753b00f5dSEd Tanous // This callback verifies that the power limit is only provided 27853b00f5dSEd Tanous // for the chassis that implements the Chassis inventory item. 27953b00f5dSEd Tanous // This prevents things like power supplies providing the 28053b00f5dSEd Tanous // chassis power limit 281028f7ebcSEddie James 2827a1dbc48SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 283f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 2847a1dbc48SGeorge Liu "xyz.openbmc_project.Inventory.Item.Chassis"}; 2857a1dbc48SGeorge Liu 28653b00f5dSEd Tanous dbus::utility::getSubTreePaths( 28753b00f5dSEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 28853b00f5dSEd Tanous std::bind_front(afterGetChassis, sensorAsyncResp)); 28953b00f5dSEd Tanous } 2904bb3dc34SCarol Wang 29153b00f5dSEd Tanous inline void 29253b00f5dSEd Tanous handleChassisPowerPatch(App& app, const crow::Request& req, 2937e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 29453b00f5dSEd Tanous const std::string& chassisName) 29553b00f5dSEd Tanous { 2963ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 29745ca1b86SEd Tanous { 29845ca1b86SEd Tanous return; 29945ca1b86SEd Tanous } 3008d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 30102da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 3020c728b42SJanet Adkins sensor_utils::chassisSubNodeToString( 3030c728b42SJanet Adkins sensor_utils::ChassisSubNode::powerNode)); 3044bb3dc34SCarol Wang 3050885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> voltageCollections; 3060885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections; 3074bb3dc34SCarol Wang 308002d39b4SEd Tanous if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res, 309002d39b4SEd Tanous "PowerControl", powerCtlCollections, 310002d39b4SEd Tanous "Voltages", voltageCollections)) 3114bb3dc34SCarol Wang { 3124bb3dc34SCarol Wang return; 3134bb3dc34SCarol Wang } 3144bb3dc34SCarol Wang 3154bb3dc34SCarol Wang if (powerCtlCollections) 3164bb3dc34SCarol Wang { 31753b00f5dSEd Tanous redfish::chassis_utils::getValidChassisPath( 31853b00f5dSEd Tanous sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId, 31953b00f5dSEd Tanous std::bind_front(afterGetChassisPath, sensorAsyncResp, 32053b00f5dSEd Tanous *powerCtlCollections)); 3214bb3dc34SCarol Wang } 3224bb3dc34SCarol Wang if (voltageCollections) 3234bb3dc34SCarol Wang { 3240885057cSEd Tanous std::unordered_map<std::string, std::vector<nlohmann::json::object_t>> 3254bb3dc34SCarol Wang allCollections; 3260885057cSEd Tanous allCollections.emplace("Voltages", std::move(*voltageCollections)); 32780ac4024SBruce Lee setSensorsOverride(sensorAsyncResp, allCollections); 3284bb3dc34SCarol Wang } 32953b00f5dSEd Tanous } 33053b00f5dSEd Tanous 33153b00f5dSEd Tanous inline void requestRoutesPower(App& app) 33253b00f5dSEd Tanous { 33353b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 33453b00f5dSEd Tanous .privileges(redfish::privileges::getPower) 33553b00f5dSEd Tanous .methods(boost::beast::http::verb::get)( 33653b00f5dSEd Tanous std::bind_front(handleChassisPowerGet, std::ref(app))); 33753b00f5dSEd Tanous 33853b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 33953b00f5dSEd Tanous .privileges(redfish::privileges::patchPower) 34053b00f5dSEd Tanous .methods(boost::beast::http::verb::patch)( 34153b00f5dSEd Tanous std::bind_front(handleChassisPowerPatch, std::ref(app))); 342413961deSRichard Marian Thomaiyar } 3432474adfaSEd Tanous 3442474adfaSEd Tanous } // namespace redfish 345