12474adfaSEd Tanous /* 26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation 36be832e2SEd Tanous Copyright (c) 2018 Ampere Computing LLC 46be832e2SEd Tanous 56be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License"); 66be832e2SEd Tanous you may not use this file except in compliance with the License. 76be832e2SEd Tanous You may obtain a copy of the License at 86be832e2SEd Tanous 96be832e2SEd Tanous http://www.apache.org/licenses/LICENSE-2.0 106be832e2SEd Tanous 116be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software 126be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS, 136be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 146be832e2SEd Tanous See the License for the specific language governing permissions and 156be832e2SEd 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; 91afc474aeSMyung Bae if (!json_util::readJsonObject( // 92afc474aeSMyung Bae item, sensorsAsyncResp->asyncResp->res, // 93afc474aeSMyung Bae "PowerLimit/LimitInWatts", value // 94afc474aeSMyung Bae )) 954bb3dc34SCarol Wang { 964bb3dc34SCarol Wang return; 974bb3dc34SCarol Wang } 984bb3dc34SCarol Wang if (!value) 994bb3dc34SCarol Wang { 1004bb3dc34SCarol Wang return; 1014bb3dc34SCarol Wang } 102*deae6a78SEd Tanous dbus::utility::getProperty<bool>( 103*deae6a78SEd Tanous "xyz.openbmc_project.Settings", 1041e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 1051e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 10653b00f5dSEd Tanous std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value)); 1074bb3dc34SCarol Wang } 1084bb3dc34SCarol Wang 10953b00f5dSEd Tanous inline void afterPowerCapSettingGet( 11053b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 1115e7e2dc5SEd Tanous const boost::system::error_code& ec, 11253b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties) 11353b00f5dSEd Tanous { 114028f7ebcSEddie James if (ec) 115028f7ebcSEddie James { 116002d39b4SEd Tanous messages::internalError(sensorAsyncResp->asyncResp->res); 11753b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec); 118028f7ebcSEddie James return; 119028f7ebcSEddie James } 120028f7ebcSEddie James 1217e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 122002d39b4SEd Tanous sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 123028f7ebcSEddie James 1247e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 1257e860f15SJohn Edward Broadbent // so only create the first one 126028f7ebcSEddie James if (tempArray.empty()) 127028f7ebcSEddie James { 1287ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 1297ab06f49SGunnar Mills // A warning without a odata.type 1301476687dSEd Tanous nlohmann::json::object_t powerControl; 131002d39b4SEd Tanous powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 132bd79bce8SPatrick Williams powerControl["@odata.id"] = 133bd79bce8SPatrick Williams "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId + 1341476687dSEd Tanous "/Power#/PowerControl/0"; 1351476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 1361476687dSEd Tanous powerControl["MemberId"] = "0"; 137b2ba3072SPatrick Williams tempArray.emplace_back(std::move(powerControl)); 138028f7ebcSEddie James } 139028f7ebcSEddie James 140028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 141028f7ebcSEddie James bool enabled = false; 142028f7ebcSEddie James double powerCap = 0.0; 143028f7ebcSEddie James int64_t scale = 0; 144028f7ebcSEddie James 14553b00f5dSEd Tanous for (const std::pair<std::string, dbus::utility::DbusVariantType>& 14653b00f5dSEd Tanous property : properties) 147028f7ebcSEddie James { 14855f79e6fSEd Tanous if (property.first == "Scale") 149028f7ebcSEddie James { 15053b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 151028f7ebcSEddie James 152e662eae8SEd Tanous if (i != nullptr) 153028f7ebcSEddie James { 154028f7ebcSEddie James scale = *i; 155028f7ebcSEddie James } 156028f7ebcSEddie James } 15755f79e6fSEd Tanous else if (property.first == "PowerCap") 158028f7ebcSEddie James { 159002d39b4SEd Tanous const double* d = std::get_if<double>(&property.second); 16053b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 16153b00f5dSEd Tanous const uint32_t* u = std::get_if<uint32_t>(&property.second); 162028f7ebcSEddie James 163e662eae8SEd Tanous if (d != nullptr) 164028f7ebcSEddie James { 165028f7ebcSEddie James powerCap = *d; 166028f7ebcSEddie James } 167e662eae8SEd Tanous else if (i != nullptr) 168028f7ebcSEddie James { 169271584abSEd Tanous powerCap = static_cast<double>(*i); 170028f7ebcSEddie James } 171e662eae8SEd Tanous else if (u != nullptr) 172028f7ebcSEddie James { 173028f7ebcSEddie James powerCap = *u; 174028f7ebcSEddie James } 175028f7ebcSEddie James } 17655f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 177028f7ebcSEddie James { 178002d39b4SEd Tanous const bool* b = std::get_if<bool>(&property.second); 179028f7ebcSEddie James 180e662eae8SEd Tanous if (b != nullptr) 181028f7ebcSEddie James { 182028f7ebcSEddie James enabled = *b; 183028f7ebcSEddie James } 184028f7ebcSEddie James } 185028f7ebcSEddie James } 186028f7ebcSEddie James 1877e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 188c9563608SJanet Adkins // Baseline Profile - v1.0.0, so currently making it 1897e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 190539d8c6bSEd Tanous sensorJson["PowerLimit"]["LimitException"] = 191539d8c6bSEd Tanous power::PowerLimitException::NoAction; 1925a64a6f3SJoshi-Mansi 193028f7ebcSEddie James if (enabled) 194028f7ebcSEddie James { 1957e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 1967e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 197bd79bce8SPatrick Williams sensorJson["PowerLimit"]["LimitInWatts"] = 198bd79bce8SPatrick Williams powerCap * std::pow(10, scale); 199028f7ebcSEddie James } 20053b00f5dSEd Tanous } 20153b00f5dSEd Tanous 20253b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 203bd79bce8SPatrick Williams inline void afterGetChassis( 204bd79bce8SPatrick Williams const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 205bd79bce8SPatrick Williams const boost::system::error_code& ec2, const Mapper& chassisPaths) 20653b00f5dSEd Tanous { 20753b00f5dSEd Tanous if (ec2) 20853b00f5dSEd Tanous { 20953b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}", 21053b00f5dSEd Tanous ec2); 21153b00f5dSEd Tanous return; 21253b00f5dSEd Tanous } 21353b00f5dSEd Tanous 21453b00f5dSEd Tanous bool found = false; 21553b00f5dSEd Tanous for (const std::string& chassis : chassisPaths) 21653b00f5dSEd Tanous { 21753b00f5dSEd Tanous size_t len = std::string::npos; 21853b00f5dSEd Tanous size_t lastPos = chassis.rfind('/'); 21953b00f5dSEd Tanous if (lastPos == std::string::npos) 22053b00f5dSEd Tanous { 22153b00f5dSEd Tanous continue; 22253b00f5dSEd Tanous } 22353b00f5dSEd Tanous 22453b00f5dSEd Tanous if (lastPos == chassis.size() - 1) 22553b00f5dSEd Tanous { 22653b00f5dSEd Tanous size_t end = lastPos; 22753b00f5dSEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 22853b00f5dSEd Tanous if (lastPos == std::string::npos) 22953b00f5dSEd Tanous { 23053b00f5dSEd Tanous continue; 23153b00f5dSEd Tanous } 23253b00f5dSEd Tanous 23353b00f5dSEd Tanous len = end - (lastPos + 1); 23453b00f5dSEd Tanous } 23553b00f5dSEd Tanous 23653b00f5dSEd Tanous std::string interfaceChassisName = chassis.substr(lastPos + 1, len); 23753b00f5dSEd Tanous if (interfaceChassisName == sensorAsyncResp->chassisId) 23853b00f5dSEd Tanous { 23953b00f5dSEd Tanous found = true; 24053b00f5dSEd Tanous break; 24153b00f5dSEd Tanous } 24253b00f5dSEd Tanous } 24353b00f5dSEd Tanous 24453b00f5dSEd Tanous if (!found) 24553b00f5dSEd Tanous { 24653b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit not present for {}", 24753b00f5dSEd Tanous sensorAsyncResp->chassisId); 24853b00f5dSEd Tanous return; 24953b00f5dSEd Tanous } 250028f7ebcSEddie James 251*deae6a78SEd Tanous dbus::utility::getAllProperties( 252*deae6a78SEd Tanous "xyz.openbmc_project.Settings", 253028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 254d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Control.Power.Cap", 25553b00f5dSEd Tanous [sensorAsyncResp](const boost::system::error_code& ec, 25653b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties 25753b00f5dSEd Tanous 25853b00f5dSEd Tanous ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); }); 25953b00f5dSEd Tanous } 26053b00f5dSEd Tanous 26153b00f5dSEd Tanous inline void 26253b00f5dSEd Tanous handleChassisPowerGet(App& app, const crow::Request& req, 26353b00f5dSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26453b00f5dSEd Tanous const std::string& chassisName) 26553b00f5dSEd Tanous { 26653b00f5dSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 26753b00f5dSEd Tanous { 26853b00f5dSEd Tanous return; 26953b00f5dSEd Tanous } 27053b00f5dSEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 27153b00f5dSEd Tanous 27253b00f5dSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 27353b00f5dSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 2740c728b42SJanet Adkins sensor_utils::chassisSubNodeToString( 2750c728b42SJanet Adkins sensor_utils::ChassisSubNode::powerNode)); 27653b00f5dSEd Tanous 27753b00f5dSEd Tanous getChassisData(sensorAsyncResp); 27853b00f5dSEd Tanous 27953b00f5dSEd Tanous // This callback verifies that the power limit is only provided 28053b00f5dSEd Tanous // for the chassis that implements the Chassis inventory item. 28153b00f5dSEd Tanous // This prevents things like power supplies providing the 28253b00f5dSEd Tanous // chassis power limit 283028f7ebcSEddie James 2847a1dbc48SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 285f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 2867a1dbc48SGeorge Liu "xyz.openbmc_project.Inventory.Item.Chassis"}; 2877a1dbc48SGeorge Liu 28853b00f5dSEd Tanous dbus::utility::getSubTreePaths( 28953b00f5dSEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 29053b00f5dSEd Tanous std::bind_front(afterGetChassis, sensorAsyncResp)); 29153b00f5dSEd Tanous } 2924bb3dc34SCarol Wang 29353b00f5dSEd Tanous inline void 29453b00f5dSEd Tanous handleChassisPowerPatch(App& app, const crow::Request& req, 2957e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 29653b00f5dSEd Tanous const std::string& chassisName) 29753b00f5dSEd Tanous { 2983ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 29945ca1b86SEd Tanous { 30045ca1b86SEd Tanous return; 30145ca1b86SEd Tanous } 3028d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 30302da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 3040c728b42SJanet Adkins sensor_utils::chassisSubNodeToString( 3050c728b42SJanet Adkins sensor_utils::ChassisSubNode::powerNode)); 3064bb3dc34SCarol Wang 3070885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> voltageCollections; 3080885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections; 3094bb3dc34SCarol Wang 310afc474aeSMyung Bae if (!json_util::readJsonPatch( // 311afc474aeSMyung Bae req, sensorAsyncResp->asyncResp->res, // 312afc474aeSMyung Bae "PowerControl", powerCtlCollections, // 313afc474aeSMyung Bae "Voltages", voltageCollections // 314afc474aeSMyung Bae )) 3154bb3dc34SCarol Wang { 3164bb3dc34SCarol Wang return; 3174bb3dc34SCarol Wang } 3184bb3dc34SCarol Wang 3194bb3dc34SCarol Wang if (powerCtlCollections) 3204bb3dc34SCarol Wang { 32153b00f5dSEd Tanous redfish::chassis_utils::getValidChassisPath( 32253b00f5dSEd Tanous sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId, 32353b00f5dSEd Tanous std::bind_front(afterGetChassisPath, sensorAsyncResp, 32453b00f5dSEd Tanous *powerCtlCollections)); 3254bb3dc34SCarol Wang } 3264bb3dc34SCarol Wang if (voltageCollections) 3274bb3dc34SCarol Wang { 3280885057cSEd Tanous std::unordered_map<std::string, std::vector<nlohmann::json::object_t>> 3294bb3dc34SCarol Wang allCollections; 3300885057cSEd Tanous allCollections.emplace("Voltages", std::move(*voltageCollections)); 33180ac4024SBruce Lee setSensorsOverride(sensorAsyncResp, allCollections); 3324bb3dc34SCarol Wang } 33353b00f5dSEd Tanous } 33453b00f5dSEd Tanous 33553b00f5dSEd Tanous inline void requestRoutesPower(App& app) 33653b00f5dSEd Tanous { 33753b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 33853b00f5dSEd Tanous .privileges(redfish::privileges::getPower) 33953b00f5dSEd Tanous .methods(boost::beast::http::verb::get)( 34053b00f5dSEd Tanous std::bind_front(handleChassisPowerGet, std::ref(app))); 34153b00f5dSEd Tanous 34253b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 34353b00f5dSEd Tanous .privileges(redfish::privileges::patchPower) 34453b00f5dSEd Tanous .methods(boost::beast::http::verb::patch)( 34553b00f5dSEd Tanous std::bind_front(handleChassisPowerPatch, std::ref(app))); 346413961deSRichard Marian Thomaiyar } 3472474adfaSEd Tanous 3482474adfaSEd Tanous } // namespace redfish 349