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