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