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 193ccb3adbSEd Tanous #include "app.hpp" 207a1dbc48SGeorge Liu #include "dbus_utility.hpp" 213ccb3adbSEd Tanous #include "query.hpp" 223ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 232474adfaSEd Tanous #include "sensors.hpp" 240d7702c0SZhenwei Chen #include "utils/chassis_utils.hpp" 252474adfaSEd Tanous 26d1bde9e5SKrzysztof Grobelny #include <sdbusplus/asio/property.hpp> 277e860f15SJohn Edward Broadbent 287a1dbc48SGeorge Liu #include <array> 29*0885057cSEd Tanous #include <string> 307a1dbc48SGeorge Liu #include <string_view> 31*0885057cSEd Tanous #include <vector> 327a1dbc48SGeorge Liu 332474adfaSEd Tanous namespace redfish 342474adfaSEd Tanous { 3553b00f5dSEd Tanous 3653b00f5dSEd Tanous inline void afterGetPowerCapEnable( 378d1b46d7Szhanghch05 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 3853b00f5dSEd Tanous uint32_t valueToSet, const boost::system::error_code& ec, 3953b00f5dSEd Tanous bool powerCapEnable) 404bb3dc34SCarol Wang { 4153b00f5dSEd Tanous if (ec) 4253b00f5dSEd Tanous { 4353b00f5dSEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 4453b00f5dSEd Tanous BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec); 4553b00f5dSEd Tanous return; 4653b00f5dSEd Tanous } 4753b00f5dSEd Tanous if (!powerCapEnable) 4853b00f5dSEd Tanous { 4953b00f5dSEd Tanous messages::actionNotSupported( 5053b00f5dSEd Tanous sensorsAsyncResp->asyncResp->res, 5153b00f5dSEd Tanous "Setting LimitInWatts when PowerLimit feature is disabled"); 5253b00f5dSEd Tanous BMCWEB_LOG_ERROR("PowerLimit feature is disabled "); 5353b00f5dSEd Tanous return; 5453b00f5dSEd Tanous } 5553b00f5dSEd Tanous 5653b00f5dSEd Tanous sdbusplus::asio::setProperty( 5753b00f5dSEd Tanous *crow::connections::systemBus, "xyz.openbmc_project.Settings", 5853b00f5dSEd Tanous "/xyz/openbmc_project/control/host0/power_cap", 5953b00f5dSEd Tanous "xyz.openbmc_project.Control.Power.Cap", "PowerCap", valueToSet, 6053b00f5dSEd Tanous [sensorsAsyncResp](const boost::system::error_code& ec2) { 6153b00f5dSEd Tanous if (ec2) 6253b00f5dSEd Tanous { 6353b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2); 6453b00f5dSEd Tanous messages::internalError(sensorsAsyncResp->asyncResp->res); 6553b00f5dSEd Tanous return; 6653b00f5dSEd Tanous } 6753b00f5dSEd Tanous sensorsAsyncResp->asyncResp->res.result( 6853b00f5dSEd Tanous boost::beast::http::status::no_content); 6953b00f5dSEd Tanous }); 7053b00f5dSEd Tanous } 7153b00f5dSEd Tanous 7253b00f5dSEd Tanous inline void afterGetChassisPath( 7353b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 74*0885057cSEd Tanous std::vector<nlohmann::json::object_t>& powerControlCollections, 7553b00f5dSEd Tanous const std::optional<std::string>& chassisPath) 7653b00f5dSEd Tanous { 774bb3dc34SCarol Wang if (!chassisPath) 784bb3dc34SCarol Wang { 7962598e31SEd Tanous BMCWEB_LOG_WARNING("Don't find valid chassis path "); 8053b00f5dSEd Tanous messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis", 8153b00f5dSEd Tanous sensorsAsyncResp->chassisId); 824bb3dc34SCarol Wang return; 834bb3dc34SCarol Wang } 844bb3dc34SCarol Wang 854bb3dc34SCarol Wang if (powerControlCollections.size() != 1) 864bb3dc34SCarol Wang { 8762598e31SEd Tanous BMCWEB_LOG_WARNING("Don't support multiple hosts at present "); 8853b00f5dSEd Tanous messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power", 8953b00f5dSEd Tanous "PowerControl"); 904bb3dc34SCarol Wang return; 914bb3dc34SCarol Wang } 924bb3dc34SCarol Wang 934bb3dc34SCarol Wang auto& item = powerControlCollections[0]; 944bb3dc34SCarol Wang 954bb3dc34SCarol Wang std::optional<uint32_t> value; 96*0885057cSEd Tanous if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res, 97*0885057cSEd Tanous "PowerLimit/LimitInWatts", value)) 984bb3dc34SCarol Wang { 994bb3dc34SCarol Wang return; 1004bb3dc34SCarol Wang } 1014bb3dc34SCarol Wang if (!value) 1024bb3dc34SCarol Wang { 1034bb3dc34SCarol Wang return; 1044bb3dc34SCarol Wang } 1051e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 1061e1e598dSJonathan Doman *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1071e1e598dSJonathan Doman "/xyz/openbmc_project/control/host0/power_cap", 1081e1e598dSJonathan Doman "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 10953b00f5dSEd Tanous std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value)); 1104bb3dc34SCarol Wang } 1114bb3dc34SCarol Wang 11253b00f5dSEd Tanous inline void afterPowerCapSettingGet( 11353b00f5dSEd Tanous const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 1145e7e2dc5SEd Tanous const boost::system::error_code& ec, 11553b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties) 11653b00f5dSEd Tanous { 117028f7ebcSEddie James if (ec) 118028f7ebcSEddie James { 119002d39b4SEd Tanous messages::internalError(sensorAsyncResp->asyncResp->res); 12053b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec); 121028f7ebcSEddie James return; 122028f7ebcSEddie James } 123028f7ebcSEddie James 1247e860f15SJohn Edward Broadbent nlohmann::json& tempArray = 125002d39b4SEd Tanous sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 126028f7ebcSEddie James 1277e860f15SJohn Edward Broadbent // Put multiple "sensors" into a single PowerControl, 0, 1287e860f15SJohn Edward Broadbent // so only create the first one 129028f7ebcSEddie James if (tempArray.empty()) 130028f7ebcSEddie James { 1317ab06f49SGunnar Mills // Mandatory properties odata.id and MemberId 1327ab06f49SGunnar Mills // A warning without a odata.type 1331476687dSEd Tanous nlohmann::json::object_t powerControl; 134002d39b4SEd Tanous powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 135002d39b4SEd Tanous powerControl["@odata.id"] = "/redfish/v1/Chassis/" + 1367ab06f49SGunnar Mills sensorAsyncResp->chassisId + 1371476687dSEd Tanous "/Power#/PowerControl/0"; 1381476687dSEd Tanous powerControl["Name"] = "Chassis Power Control"; 1391476687dSEd Tanous powerControl["MemberId"] = "0"; 140b2ba3072SPatrick Williams tempArray.emplace_back(std::move(powerControl)); 141028f7ebcSEddie James } 142028f7ebcSEddie James 143028f7ebcSEddie James nlohmann::json& sensorJson = tempArray.back(); 144028f7ebcSEddie James bool enabled = false; 145028f7ebcSEddie James double powerCap = 0.0; 146028f7ebcSEddie James int64_t scale = 0; 147028f7ebcSEddie James 14853b00f5dSEd Tanous for (const std::pair<std::string, dbus::utility::DbusVariantType>& 14953b00f5dSEd Tanous property : properties) 150028f7ebcSEddie James { 15155f79e6fSEd Tanous if (property.first == "Scale") 152028f7ebcSEddie James { 15353b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 154028f7ebcSEddie James 155e662eae8SEd Tanous if (i != nullptr) 156028f7ebcSEddie James { 157028f7ebcSEddie James scale = *i; 158028f7ebcSEddie James } 159028f7ebcSEddie James } 16055f79e6fSEd Tanous else if (property.first == "PowerCap") 161028f7ebcSEddie James { 162002d39b4SEd Tanous const double* d = std::get_if<double>(&property.second); 16353b00f5dSEd Tanous const int64_t* i = std::get_if<int64_t>(&property.second); 16453b00f5dSEd Tanous const uint32_t* u = std::get_if<uint32_t>(&property.second); 165028f7ebcSEddie James 166e662eae8SEd Tanous if (d != nullptr) 167028f7ebcSEddie James { 168028f7ebcSEddie James powerCap = *d; 169028f7ebcSEddie James } 170e662eae8SEd Tanous else if (i != nullptr) 171028f7ebcSEddie James { 172271584abSEd Tanous powerCap = static_cast<double>(*i); 173028f7ebcSEddie James } 174e662eae8SEd Tanous else if (u != nullptr) 175028f7ebcSEddie James { 176028f7ebcSEddie James powerCap = *u; 177028f7ebcSEddie James } 178028f7ebcSEddie James } 17955f79e6fSEd Tanous else if (property.first == "PowerCapEnable") 180028f7ebcSEddie James { 181002d39b4SEd Tanous const bool* b = std::get_if<bool>(&property.second); 182028f7ebcSEddie James 183e662eae8SEd Tanous if (b != nullptr) 184028f7ebcSEddie James { 185028f7ebcSEddie James enabled = *b; 186028f7ebcSEddie James } 187028f7ebcSEddie James } 188028f7ebcSEddie James } 189028f7ebcSEddie James 1907e860f15SJohn Edward Broadbent // LimitException is Mandatory attribute as per OCP 1917e860f15SJohn Edward Broadbent // Baseline Profile – v1.0.0, so currently making it 1927e860f15SJohn Edward Broadbent // "NoAction" as default value to make it OCP Compliant. 1935a64a6f3SJoshi-Mansi sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 1945a64a6f3SJoshi-Mansi 195028f7ebcSEddie James if (enabled) 196028f7ebcSEddie James { 1977e860f15SJohn Edward Broadbent // Redfish specification indicates PowerLimit should 1987e860f15SJohn Edward Broadbent // be null if the limit is not enabled. 19953b00f5dSEd Tanous sensorJson["PowerLimit"]["LimitInWatts"] = powerCap * 20053b00f5dSEd Tanous std::pow(10, scale); 201028f7ebcSEddie James } 20253b00f5dSEd Tanous } 20353b00f5dSEd Tanous 20453b00f5dSEd Tanous using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 20553b00f5dSEd Tanous inline void 20653b00f5dSEd Tanous afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 20753b00f5dSEd Tanous const boost::system::error_code& ec2, 20853b00f5dSEd Tanous const Mapper& chassisPaths) 20953b00f5dSEd Tanous { 21053b00f5dSEd Tanous if (ec2) 21153b00f5dSEd Tanous { 21253b00f5dSEd Tanous BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}", 21353b00f5dSEd Tanous ec2); 21453b00f5dSEd Tanous return; 21553b00f5dSEd Tanous } 21653b00f5dSEd Tanous 21753b00f5dSEd Tanous bool found = false; 21853b00f5dSEd Tanous for (const std::string& chassis : chassisPaths) 21953b00f5dSEd Tanous { 22053b00f5dSEd Tanous size_t len = std::string::npos; 22153b00f5dSEd Tanous size_t lastPos = chassis.rfind('/'); 22253b00f5dSEd Tanous if (lastPos == std::string::npos) 22353b00f5dSEd Tanous { 22453b00f5dSEd Tanous continue; 22553b00f5dSEd Tanous } 22653b00f5dSEd Tanous 22753b00f5dSEd Tanous if (lastPos == chassis.size() - 1) 22853b00f5dSEd Tanous { 22953b00f5dSEd Tanous size_t end = lastPos; 23053b00f5dSEd Tanous lastPos = chassis.rfind('/', lastPos - 1); 23153b00f5dSEd Tanous if (lastPos == std::string::npos) 23253b00f5dSEd Tanous { 23353b00f5dSEd Tanous continue; 23453b00f5dSEd Tanous } 23553b00f5dSEd Tanous 23653b00f5dSEd Tanous len = end - (lastPos + 1); 23753b00f5dSEd Tanous } 23853b00f5dSEd Tanous 23953b00f5dSEd Tanous std::string interfaceChassisName = chassis.substr(lastPos + 1, len); 24053b00f5dSEd Tanous if (interfaceChassisName == sensorAsyncResp->chassisId) 24153b00f5dSEd Tanous { 24253b00f5dSEd Tanous found = true; 24353b00f5dSEd Tanous break; 24453b00f5dSEd Tanous } 24553b00f5dSEd Tanous } 24653b00f5dSEd Tanous 24753b00f5dSEd Tanous if (!found) 24853b00f5dSEd Tanous { 24953b00f5dSEd Tanous BMCWEB_LOG_DEBUG("Power Limit not present for {}", 25053b00f5dSEd Tanous sensorAsyncResp->chassisId); 25153b00f5dSEd Tanous return; 25253b00f5dSEd Tanous } 253028f7ebcSEddie James 254d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 255d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, "xyz.openbmc_project.Settings", 256028f7ebcSEddie James "/xyz/openbmc_project/control/host0/power_cap", 257d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Control.Power.Cap", 25853b00f5dSEd Tanous [sensorAsyncResp](const boost::system::error_code& ec, 25953b00f5dSEd Tanous const dbus::utility::DBusPropertiesMap& properties 26053b00f5dSEd Tanous 26153b00f5dSEd Tanous ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); }); 26253b00f5dSEd Tanous } 26353b00f5dSEd Tanous 26453b00f5dSEd Tanous inline void 26553b00f5dSEd Tanous handleChassisPowerGet(App& app, const crow::Request& req, 26653b00f5dSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26753b00f5dSEd Tanous const std::string& chassisName) 26853b00f5dSEd Tanous { 26953b00f5dSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 27053b00f5dSEd Tanous { 27153b00f5dSEd Tanous return; 27253b00f5dSEd Tanous } 27353b00f5dSEd Tanous asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 27453b00f5dSEd Tanous 27553b00f5dSEd Tanous auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 27653b00f5dSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 27753b00f5dSEd Tanous sensors::node::power); 27853b00f5dSEd Tanous 27953b00f5dSEd Tanous getChassisData(sensorAsyncResp); 28053b00f5dSEd Tanous 28153b00f5dSEd Tanous // This callback verifies that the power limit is only provided 28253b00f5dSEd Tanous // for the chassis that implements the Chassis inventory item. 28353b00f5dSEd Tanous // This prevents things like power supplies providing the 28453b00f5dSEd Tanous // chassis power limit 285028f7ebcSEddie James 2867a1dbc48SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 287f857e9aeSAppaRao Puli "xyz.openbmc_project.Inventory.Item.Board", 2887a1dbc48SGeorge Liu "xyz.openbmc_project.Inventory.Item.Chassis"}; 2897a1dbc48SGeorge Liu 29053b00f5dSEd Tanous dbus::utility::getSubTreePaths( 29153b00f5dSEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 29253b00f5dSEd Tanous std::bind_front(afterGetChassis, sensorAsyncResp)); 29353b00f5dSEd Tanous } 2944bb3dc34SCarol Wang 29553b00f5dSEd Tanous inline void 29653b00f5dSEd Tanous handleChassisPowerPatch(App& app, const crow::Request& req, 2977e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 29853b00f5dSEd Tanous const std::string& chassisName) 29953b00f5dSEd Tanous { 3003ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 30145ca1b86SEd Tanous { 30245ca1b86SEd Tanous return; 30345ca1b86SEd Tanous } 3048d1b46d7Szhanghch05 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 30502da7c5aSEd Tanous asyncResp, chassisName, sensors::dbus::powerPaths, 306a0ec28b6SAdrian Ambrożewicz sensors::node::power); 3074bb3dc34SCarol Wang 308*0885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> voltageCollections; 309*0885057cSEd Tanous std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections; 3104bb3dc34SCarol Wang 311002d39b4SEd Tanous if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res, 312002d39b4SEd Tanous "PowerControl", powerCtlCollections, 313002d39b4SEd Tanous "Voltages", voltageCollections)) 3144bb3dc34SCarol Wang { 3154bb3dc34SCarol Wang return; 3164bb3dc34SCarol Wang } 3174bb3dc34SCarol Wang 3184bb3dc34SCarol Wang if (powerCtlCollections) 3194bb3dc34SCarol Wang { 32053b00f5dSEd Tanous redfish::chassis_utils::getValidChassisPath( 32153b00f5dSEd Tanous sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId, 32253b00f5dSEd Tanous std::bind_front(afterGetChassisPath, sensorAsyncResp, 32353b00f5dSEd Tanous *powerCtlCollections)); 3244bb3dc34SCarol Wang } 3254bb3dc34SCarol Wang if (voltageCollections) 3264bb3dc34SCarol Wang { 327*0885057cSEd Tanous std::unordered_map<std::string, std::vector<nlohmann::json::object_t>> 3284bb3dc34SCarol Wang allCollections; 329*0885057cSEd Tanous allCollections.emplace("Voltages", std::move(*voltageCollections)); 33080ac4024SBruce Lee setSensorsOverride(sensorAsyncResp, allCollections); 3314bb3dc34SCarol Wang } 33253b00f5dSEd Tanous } 33353b00f5dSEd Tanous 33453b00f5dSEd Tanous inline void requestRoutesPower(App& app) 33553b00f5dSEd Tanous { 33653b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 33753b00f5dSEd Tanous .privileges(redfish::privileges::getPower) 33853b00f5dSEd Tanous .methods(boost::beast::http::verb::get)( 33953b00f5dSEd Tanous std::bind_front(handleChassisPowerGet, std::ref(app))); 34053b00f5dSEd Tanous 34153b00f5dSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 34253b00f5dSEd Tanous .privileges(redfish::privileges::patchPower) 34353b00f5dSEd Tanous .methods(boost::beast::http::verb::patch)( 34453b00f5dSEd Tanous std::bind_front(handleChassisPowerPatch, std::ref(app))); 345413961deSRichard Marian Thomaiyar } 3462474adfaSEd Tanous 3472474adfaSEd Tanous } // namespace redfish 348