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