1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // Copyright (c) 2018 Ampere Computing LLC 4 / 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 */ 17 #pragma once 18 19 #include "app.hpp" 20 #include "dbus_utility.hpp" 21 #include "query.hpp" 22 #include "registries/privilege_registry.hpp" 23 #include "sensors.hpp" 24 #include "utils/chassis_utils.hpp" 25 26 #include <sdbusplus/asio/property.hpp> 27 28 #include <array> 29 #include <string> 30 #include <string_view> 31 #include <vector> 32 33 namespace redfish 34 { 35 36 inline void afterGetPowerCapEnable( 37 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 38 uint32_t valueToSet, const boost::system::error_code& ec, 39 bool powerCapEnable) 40 { 41 if (ec) 42 { 43 messages::internalError(sensorsAsyncResp->asyncResp->res); 44 BMCWEB_LOG_ERROR("powerCapEnable Get handler: Dbus error {}", ec); 45 return; 46 } 47 if (!powerCapEnable) 48 { 49 messages::actionNotSupported( 50 sensorsAsyncResp->asyncResp->res, 51 "Setting LimitInWatts when PowerLimit feature is disabled"); 52 BMCWEB_LOG_ERROR("PowerLimit feature is disabled "); 53 return; 54 } 55 56 sdbusplus::asio::setProperty( 57 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 58 "/xyz/openbmc_project/control/host0/power_cap", 59 "xyz.openbmc_project.Control.Power.Cap", "PowerCap", valueToSet, 60 [sensorsAsyncResp](const boost::system::error_code& ec2) { 61 if (ec2) 62 { 63 BMCWEB_LOG_DEBUG("Power Limit Set: Dbus error: {}", ec2); 64 messages::internalError(sensorsAsyncResp->asyncResp->res); 65 return; 66 } 67 sensorsAsyncResp->asyncResp->res.result( 68 boost::beast::http::status::no_content); 69 }); 70 } 71 72 inline void afterGetChassisPath( 73 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 74 std::vector<nlohmann::json::object_t>& powerControlCollections, 75 const std::optional<std::string>& chassisPath) 76 { 77 if (!chassisPath) 78 { 79 BMCWEB_LOG_WARNING("Don't find valid chassis path "); 80 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Chassis", 81 sensorsAsyncResp->chassisId); 82 return; 83 } 84 85 if (powerControlCollections.size() != 1) 86 { 87 BMCWEB_LOG_WARNING("Don't support multiple hosts at present "); 88 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, "Power", 89 "PowerControl"); 90 return; 91 } 92 93 auto& item = powerControlCollections[0]; 94 95 std::optional<uint32_t> value; 96 if (!json_util::readJsonObject(item, sensorsAsyncResp->asyncResp->res, 97 "PowerLimit/LimitInWatts", value)) 98 { 99 return; 100 } 101 if (!value) 102 { 103 return; 104 } 105 sdbusplus::asio::getProperty<bool>( 106 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 107 "/xyz/openbmc_project/control/host0/power_cap", 108 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable", 109 std::bind_front(afterGetPowerCapEnable, sensorsAsyncResp, *value)); 110 } 111 112 inline void afterPowerCapSettingGet( 113 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 114 const boost::system::error_code& ec, 115 const dbus::utility::DBusPropertiesMap& properties) 116 { 117 if (ec) 118 { 119 messages::internalError(sensorAsyncResp->asyncResp->res); 120 BMCWEB_LOG_ERROR("Power Limit GetAll handler: Dbus error {}", ec); 121 return; 122 } 123 124 nlohmann::json& tempArray = 125 sensorAsyncResp->asyncResp->res.jsonValue["PowerControl"]; 126 127 // Put multiple "sensors" into a single PowerControl, 0, 128 // so only create the first one 129 if (tempArray.empty()) 130 { 131 // Mandatory properties odata.id and MemberId 132 // A warning without a odata.type 133 nlohmann::json::object_t powerControl; 134 powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; 135 powerControl["@odata.id"] = "/redfish/v1/Chassis/" + 136 sensorAsyncResp->chassisId + 137 "/Power#/PowerControl/0"; 138 powerControl["Name"] = "Chassis Power Control"; 139 powerControl["MemberId"] = "0"; 140 tempArray.emplace_back(std::move(powerControl)); 141 } 142 143 nlohmann::json& sensorJson = tempArray.back(); 144 bool enabled = false; 145 double powerCap = 0.0; 146 int64_t scale = 0; 147 148 for (const std::pair<std::string, dbus::utility::DbusVariantType>& 149 property : properties) 150 { 151 if (property.first == "Scale") 152 { 153 const int64_t* i = std::get_if<int64_t>(&property.second); 154 155 if (i != nullptr) 156 { 157 scale = *i; 158 } 159 } 160 else if (property.first == "PowerCap") 161 { 162 const double* d = std::get_if<double>(&property.second); 163 const int64_t* i = std::get_if<int64_t>(&property.second); 164 const uint32_t* u = std::get_if<uint32_t>(&property.second); 165 166 if (d != nullptr) 167 { 168 powerCap = *d; 169 } 170 else if (i != nullptr) 171 { 172 powerCap = static_cast<double>(*i); 173 } 174 else if (u != nullptr) 175 { 176 powerCap = *u; 177 } 178 } 179 else if (property.first == "PowerCapEnable") 180 { 181 const bool* b = std::get_if<bool>(&property.second); 182 183 if (b != nullptr) 184 { 185 enabled = *b; 186 } 187 } 188 } 189 190 // LimitException is Mandatory attribute as per OCP 191 // Baseline Profile – v1.0.0, so currently making it 192 // "NoAction" as default value to make it OCP Compliant. 193 sensorJson["PowerLimit"]["LimitException"] = "NoAction"; 194 195 if (enabled) 196 { 197 // Redfish specification indicates PowerLimit should 198 // be null if the limit is not enabled. 199 sensorJson["PowerLimit"]["LimitInWatts"] = powerCap * 200 std::pow(10, scale); 201 } 202 } 203 204 using Mapper = dbus::utility::MapperGetSubTreePathsResponse; 205 inline void 206 afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 207 const boost::system::error_code& ec2, 208 const Mapper& chassisPaths) 209 { 210 if (ec2) 211 { 212 BMCWEB_LOG_ERROR("Power Limit GetSubTreePaths handler Dbus error {}", 213 ec2); 214 return; 215 } 216 217 bool found = false; 218 for (const std::string& chassis : chassisPaths) 219 { 220 size_t len = std::string::npos; 221 size_t lastPos = chassis.rfind('/'); 222 if (lastPos == std::string::npos) 223 { 224 continue; 225 } 226 227 if (lastPos == chassis.size() - 1) 228 { 229 size_t end = lastPos; 230 lastPos = chassis.rfind('/', lastPos - 1); 231 if (lastPos == std::string::npos) 232 { 233 continue; 234 } 235 236 len = end - (lastPos + 1); 237 } 238 239 std::string interfaceChassisName = chassis.substr(lastPos + 1, len); 240 if (interfaceChassisName == sensorAsyncResp->chassisId) 241 { 242 found = true; 243 break; 244 } 245 } 246 247 if (!found) 248 { 249 BMCWEB_LOG_DEBUG("Power Limit not present for {}", 250 sensorAsyncResp->chassisId); 251 return; 252 } 253 254 sdbusplus::asio::getAllProperties( 255 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 256 "/xyz/openbmc_project/control/host0/power_cap", 257 "xyz.openbmc_project.Control.Power.Cap", 258 [sensorAsyncResp](const boost::system::error_code& ec, 259 const dbus::utility::DBusPropertiesMap& properties 260 261 ) { afterPowerCapSettingGet(sensorAsyncResp, ec, properties); }); 262 } 263 264 inline void 265 handleChassisPowerGet(App& app, const crow::Request& req, 266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 267 const std::string& chassisName) 268 { 269 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 270 { 271 return; 272 } 273 asyncResp->res.jsonValue["PowerControl"] = nlohmann::json::array(); 274 275 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 276 asyncResp, chassisName, sensors::dbus::powerPaths, 277 sensors::node::power); 278 279 getChassisData(sensorAsyncResp); 280 281 // This callback verifies that the power limit is only provided 282 // for the chassis that implements the Chassis inventory item. 283 // This prevents things like power supplies providing the 284 // chassis power limit 285 286 constexpr std::array<std::string_view, 2> interfaces = { 287 "xyz.openbmc_project.Inventory.Item.Board", 288 "xyz.openbmc_project.Inventory.Item.Chassis"}; 289 290 dbus::utility::getSubTreePaths( 291 "/xyz/openbmc_project/inventory", 0, interfaces, 292 std::bind_front(afterGetChassis, sensorAsyncResp)); 293 } 294 295 inline void 296 handleChassisPowerPatch(App& app, const crow::Request& req, 297 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 298 const std::string& chassisName) 299 { 300 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 301 { 302 return; 303 } 304 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 305 asyncResp, chassisName, sensors::dbus::powerPaths, 306 sensors::node::power); 307 308 std::optional<std::vector<nlohmann::json::object_t>> voltageCollections; 309 std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections; 310 311 if (!json_util::readJsonPatch(req, sensorAsyncResp->asyncResp->res, 312 "PowerControl", powerCtlCollections, 313 "Voltages", voltageCollections)) 314 { 315 return; 316 } 317 318 if (powerCtlCollections) 319 { 320 redfish::chassis_utils::getValidChassisPath( 321 sensorAsyncResp->asyncResp, sensorAsyncResp->chassisId, 322 std::bind_front(afterGetChassisPath, sensorAsyncResp, 323 *powerCtlCollections)); 324 } 325 if (voltageCollections) 326 { 327 std::unordered_map<std::string, std::vector<nlohmann::json::object_t>> 328 allCollections; 329 allCollections.emplace("Voltages", std::move(*voltageCollections)); 330 setSensorsOverride(sensorAsyncResp, allCollections); 331 } 332 } 333 334 inline void requestRoutesPower(App& app) 335 { 336 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 337 .privileges(redfish::privileges::getPower) 338 .methods(boost::beast::http::verb::get)( 339 std::bind_front(handleChassisPowerGet, std::ref(app))); 340 341 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Power/") 342 .privileges(redfish::privileges::patchPower) 343 .methods(boost::beast::http::verb::patch)( 344 std::bind_front(handleChassisPowerPatch, std::ref(app))); 345 } 346 347 } // namespace redfish 348