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