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