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, {{"ConfigureComponents"}}}, 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 = 101 sdbusplus::message::variant_ns::get_if<bool>( 102 &powerCapEnable); 103 if (b == nullptr) 104 { 105 messages::internalError(asyncResp->res); 106 BMCWEB_LOG_ERROR 107 << "Fail to get PowerCapEnable status "; 108 return; 109 } 110 if (!(*b)) 111 { 112 messages::actionNotSupported( 113 asyncResp->res, 114 "Setting LimitInWatts when PowerLimit " 115 "feature is disabled"); 116 BMCWEB_LOG_ERROR << "PowerLimit feature is disabled "; 117 return; 118 } 119 120 crow::connections::systemBus->async_method_call( 121 [asyncResp](const boost::system::error_code ec) { 122 if (ec) 123 { 124 BMCWEB_LOG_DEBUG 125 << "Power Limit Set: Dbus error: " << ec; 126 messages::internalError(asyncResp->res); 127 return; 128 } 129 asyncResp->res.result( 130 boost::beast::http::status::no_content); 131 }, 132 "xyz.openbmc_project.Settings", 133 "/xyz/openbmc_project/control/host0/power_cap", 134 "org.freedesktop.DBus.Properties", "Set", 135 "xyz.openbmc_project.Control.Power.Cap", "PowerCap", 136 sdbusplus::message::variant<uint32_t>(*value)); 137 }; 138 crow::connections::systemBus->async_method_call( 139 std::move(valueHandler), "xyz.openbmc_project.Settings", 140 "/xyz/openbmc_project/control/host0/power_cap", 141 "org.freedesktop.DBus.Properties", "Get", 142 "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable"); 143 }; 144 getValidChassisPath(asyncResp, std::move(getChassisPath)); 145 } 146 void doGet(crow::Response& res, const crow::Request& req, 147 const std::vector<std::string>& params) override 148 { 149 if (params.size() != 1) 150 { 151 res.result(boost::beast::http::status::internal_server_error); 152 res.end(); 153 return; 154 } 155 const std::string& chassis_name = params[0]; 156 157 res.jsonValue["PowerControl"] = nlohmann::json::array(); 158 159 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 160 res, chassis_name, typeList, "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(sensorAsyncResp->res); 224 BMCWEB_LOG_ERROR 225 << "Power Limit GetAll handler: Dbus error " << ec; 226 return; 227 } 228 229 nlohmann::json& tempArray = 230 sensorAsyncResp->res.jsonValue["PowerControl"]; 231 232 // Put multiple "sensors" into a single PowerControl, 0, so 233 // only create the first one 234 if (tempArray.empty()) 235 { 236 // Mandatory properties odata.id and MemberId 237 // A warning without a odata.type 238 tempArray.push_back( 239 {{"@odata.type", "#Power.v1_0_0.PowerControl"}, 240 {"@odata.id", "/redfish/v1/Chassis/" + 241 sensorAsyncResp->chassisId + 242 "/Power#/PowerControl/0"}, 243 {"Name", "Chassis Power Control"}, 244 {"MemberId", "0"}}); 245 } 246 247 nlohmann::json& sensorJson = tempArray.back(); 248 bool enabled = false; 249 double powerCap = 0.0; 250 int64_t scale = 0; 251 252 for (const std::pair<std::string, SensorVariant>& property : 253 properties) 254 { 255 if (!property.first.compare("Scale")) 256 { 257 const int64_t* i = 258 sdbusplus::message::variant_ns::get_if<int64_t>( 259 &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 sdbusplus::message::variant_ns::get_if<double>( 270 &property.second); 271 const int64_t* i = 272 sdbusplus::message::variant_ns::get_if<int64_t>( 273 &property.second); 274 const uint32_t* u = 275 sdbusplus::message::variant_ns::get_if< 276 uint32_t>(&property.second); 277 278 if (d) 279 { 280 powerCap = *d; 281 } 282 else if (i) 283 { 284 powerCap = static_cast<double>(*i); 285 } 286 else if (u) 287 { 288 powerCap = *u; 289 } 290 } 291 else if (!property.first.compare("PowerCapEnable")) 292 { 293 const bool* b = 294 sdbusplus::message::variant_ns::get_if<bool>( 295 &property.second); 296 297 if (b) 298 { 299 enabled = *b; 300 } 301 } 302 } 303 304 nlohmann::json& value = 305 sensorJson["PowerLimit"]["LimitInWatts"]; 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*, 1>{ 328 "xyz.openbmc_project.Inventory.Item.Chassis"}); 329 } 330 void doPatch(crow::Response& res, const crow::Request& req, 331 const std::vector<std::string>& params) override 332 { 333 if (params.size() != 1) 334 { 335 messages::internalError(res); 336 res.end(); 337 return; 338 } 339 340 const std::string& chassisName = params[0]; 341 auto asyncResp = std::make_shared<SensorsAsyncResp>(res, chassisName, 342 typeList, "Power"); 343 344 std::optional<std::vector<nlohmann::json>> voltageCollections; 345 std::optional<std::vector<nlohmann::json>> powerCtlCollections; 346 347 if (!json_util::readJson(req, asyncResp->res, "PowerControl", 348 powerCtlCollections, "Voltages", 349 voltageCollections)) 350 { 351 return; 352 } 353 354 if (powerCtlCollections) 355 { 356 setPowerCapOverride(asyncResp, *powerCtlCollections); 357 } 358 if (voltageCollections) 359 { 360 std::unordered_map<std::string, std::vector<nlohmann::json>> 361 allCollections; 362 allCollections.emplace("Voltages", *std::move(voltageCollections)); 363 setSensorOverride(asyncResp, allCollections, chassisName, typeList); 364 } 365 } 366 }; 367 368 } // namespace redfish 369