1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "health.hpp" 19 20 #include <boost/container/flat_map.hpp> 21 #include <node.hpp> 22 #include <utils/collection.hpp> 23 #include <utils/json_utils.hpp> 24 25 namespace redfish 26 { 27 28 using InterfacesProperties = boost::container::flat_map< 29 std::string, 30 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>; 31 32 inline void 33 getCpuDataByInterface(const std::shared_ptr<AsyncResp>& aResp, 34 const InterfacesProperties& cpuInterfacesProperties) 35 { 36 BMCWEB_LOG_DEBUG << "Get CPU resources by interface."; 37 38 // Added for future purpose. Once present and functional attributes added 39 // in busctl call, need to add actual logic to fetch original values. 40 bool present = false; 41 const bool functional = true; 42 auto health = std::make_shared<HealthPopulate>(aResp); 43 health->populate(); 44 45 for (const auto& interface : cpuInterfacesProperties) 46 { 47 for (const auto& property : interface.second) 48 { 49 if (property.first == "CoreCount") 50 { 51 const uint16_t* coresCount = 52 std::get_if<uint16_t>(&property.second); 53 if (coresCount == nullptr) 54 { 55 // Important property not in desired type 56 messages::internalError(aResp->res); 57 return; 58 } 59 if (*coresCount == 0) 60 { 61 // Slot is not populated, set status end return 62 aResp->res.jsonValue["Status"]["State"] = "Absent"; 63 // HTTP Code will be set up automatically, just return 64 return; 65 } 66 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 67 present = true; 68 aResp->res.jsonValue["TotalCores"] = *coresCount; 69 } 70 else if (property.first == "MaxSpeedInMhz") 71 { 72 const uint32_t* value = std::get_if<uint32_t>(&property.second); 73 if (value != nullptr) 74 { 75 aResp->res.jsonValue["MaxSpeedMHz"] = *value; 76 } 77 } 78 else if (property.first == "Socket") 79 { 80 const std::string* value = 81 std::get_if<std::string>(&property.second); 82 if (value != nullptr) 83 { 84 aResp->res.jsonValue["Socket"] = *value; 85 } 86 } 87 else if (property.first == "ThreadCount") 88 { 89 const uint16_t* value = std::get_if<uint16_t>(&property.second); 90 if (value != nullptr) 91 { 92 aResp->res.jsonValue["TotalThreads"] = *value; 93 } 94 } 95 else if (property.first == "Family") 96 { 97 const std::string* value = 98 std::get_if<std::string>(&property.second); 99 if (value != nullptr) 100 { 101 aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 102 *value; 103 } 104 } 105 else if (property.first == "Id") 106 { 107 const uint64_t* value = std::get_if<uint64_t>(&property.second); 108 if (value != nullptr && *value != 0) 109 { 110 present = true; 111 aResp->res 112 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 113 boost::lexical_cast<std::string>(*value); 114 } 115 } 116 } 117 } 118 119 if (present == false) 120 { 121 aResp->res.jsonValue["Status"]["State"] = "Absent"; 122 aResp->res.jsonValue["Status"]["Health"] = "OK"; 123 } 124 else 125 { 126 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 127 if (functional) 128 { 129 aResp->res.jsonValue["Status"]["Health"] = "OK"; 130 } 131 else 132 { 133 aResp->res.jsonValue["Status"]["Health"] = "Critical"; 134 } 135 } 136 137 return; 138 } 139 140 inline void getCpuDataByService(std::shared_ptr<AsyncResp> aResp, 141 const std::string& cpuId, 142 const std::string& service, 143 const std::string& objPath) 144 { 145 BMCWEB_LOG_DEBUG << "Get available system cpu resources by service."; 146 147 crow::connections::systemBus->async_method_call( 148 [cpuId, service, objPath, aResp{std::move(aResp)}]( 149 const boost::system::error_code ec, 150 const dbus::utility::ManagedObjectType& dbusData) { 151 if (ec) 152 { 153 BMCWEB_LOG_DEBUG << "DBUS response error"; 154 messages::internalError(aResp->res); 155 return; 156 } 157 aResp->res.jsonValue["Id"] = cpuId; 158 aResp->res.jsonValue["Name"] = "Processor"; 159 aResp->res.jsonValue["ProcessorType"] = "CPU"; 160 161 bool slotPresent = false; 162 std::string corePath = objPath + "/core"; 163 size_t totalCores = 0; 164 for (const auto& object : dbusData) 165 { 166 if (object.first.str == objPath) 167 { 168 getCpuDataByInterface(aResp, object.second); 169 } 170 else if (boost::starts_with(object.first.str, corePath)) 171 { 172 for (const auto& interface : object.second) 173 { 174 if (interface.first == 175 "xyz.openbmc_project.Inventory.Item") 176 { 177 for (const auto& property : interface.second) 178 { 179 if (property.first == "Present") 180 { 181 const bool* present = 182 std::get_if<bool>(&property.second); 183 if (present != nullptr) 184 { 185 if (*present == true) 186 { 187 slotPresent = true; 188 totalCores++; 189 } 190 } 191 } 192 } 193 } 194 } 195 } 196 } 197 // In getCpuDataByInterface(), state and health are set 198 // based on the present and functional status. If core 199 // count is zero, then it has a higher precedence. 200 if (slotPresent) 201 { 202 if (totalCores == 0) 203 { 204 // Slot is not populated, set status end return 205 aResp->res.jsonValue["Status"]["State"] = "Absent"; 206 aResp->res.jsonValue["Status"]["Health"] = "OK"; 207 } 208 aResp->res.jsonValue["TotalCores"] = totalCores; 209 } 210 return; 211 }, 212 service, "/xyz/openbmc_project/inventory", 213 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 214 } 215 216 inline void getCpuAssetData(std::shared_ptr<AsyncResp> aResp, 217 const std::string& service, 218 const std::string& objPath) 219 { 220 BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; 221 crow::connections::systemBus->async_method_call( 222 [objPath, aResp{std::move(aResp)}]( 223 const boost::system::error_code ec, 224 const boost::container::flat_map< 225 std::string, std::variant<std::string, uint32_t, uint16_t, 226 bool>>& properties) { 227 if (ec) 228 { 229 BMCWEB_LOG_DEBUG << "DBUS response error"; 230 messages::internalError(aResp->res); 231 return; 232 } 233 234 for (const auto& property : properties) 235 { 236 if (property.first == "SerialNumber") 237 { 238 const std::string* sn = 239 std::get_if<std::string>(&property.second); 240 if (sn != nullptr && !sn->empty()) 241 { 242 aResp->res.jsonValue["SerialNumber"] = *sn; 243 } 244 } 245 else if (property.first == "Model") 246 { 247 const std::string* model = 248 std::get_if<std::string>(&property.second); 249 if (model != nullptr && !model->empty()) 250 { 251 aResp->res.jsonValue["Model"] = *model; 252 } 253 } 254 else if (property.first == "Manufacturer") 255 { 256 257 const std::string* mfg = 258 std::get_if<std::string>(&property.second); 259 if (mfg != nullptr) 260 { 261 aResp->res.jsonValue["Manufacturer"] = *mfg; 262 263 // Otherwise would be unexpected. 264 if (mfg->find("Intel") != std::string::npos) 265 { 266 aResp->res.jsonValue["ProcessorArchitecture"] = 267 "x86"; 268 aResp->res.jsonValue["InstructionSet"] = "x86-64"; 269 } 270 else if (mfg->find("IBM") != std::string::npos) 271 { 272 aResp->res.jsonValue["ProcessorArchitecture"] = 273 "Power"; 274 aResp->res.jsonValue["InstructionSet"] = "PowerISA"; 275 } 276 } 277 } 278 } 279 }, 280 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 281 "xyz.openbmc_project.Inventory.Decorator.Asset"); 282 } 283 284 inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp, 285 const std::string& service, 286 const std::string& objPath) 287 { 288 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; 289 crow::connections::systemBus->async_method_call( 290 [objPath, aResp{std::move(aResp)}]( 291 const boost::system::error_code ec, 292 const boost::container::flat_map< 293 std::string, std::variant<std::string, uint32_t, uint16_t, 294 bool>>& properties) { 295 if (ec) 296 { 297 BMCWEB_LOG_DEBUG << "DBUS response error"; 298 messages::internalError(aResp->res); 299 return; 300 } 301 302 for (const auto& property : properties) 303 { 304 if (property.first == "Version") 305 { 306 const std::string* ver = 307 std::get_if<std::string>(&property.second); 308 if (ver != nullptr) 309 { 310 aResp->res.jsonValue["Version"] = *ver; 311 } 312 break; 313 } 314 } 315 }, 316 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 317 "xyz.openbmc_project.Inventory.Decorator.Revision"); 318 } 319 320 inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp, 321 const std::string& acclrtrId, 322 const std::string& service, 323 const std::string& objPath) 324 { 325 BMCWEB_LOG_DEBUG 326 << "Get available system Accelerator resources by service."; 327 crow::connections::systemBus->async_method_call( 328 [acclrtrId, aResp{std::move(aResp)}]( 329 const boost::system::error_code ec, 330 const boost::container::flat_map< 331 std::string, std::variant<std::string, uint32_t, uint16_t, 332 bool>>& properties) { 333 if (ec) 334 { 335 BMCWEB_LOG_DEBUG << "DBUS response error"; 336 messages::internalError(aResp->res); 337 return; 338 } 339 aResp->res.jsonValue["Id"] = acclrtrId; 340 aResp->res.jsonValue["Name"] = "Processor"; 341 const bool* accPresent = nullptr; 342 const bool* accFunctional = nullptr; 343 344 for (const auto& property : properties) 345 { 346 if (property.first == "Functional") 347 { 348 accFunctional = std::get_if<bool>(&property.second); 349 } 350 else if (property.first == "Present") 351 { 352 accPresent = std::get_if<bool>(&property.second); 353 } 354 } 355 356 std::string state = "Enabled"; 357 std::string health = "OK"; 358 359 if (accPresent != nullptr && *accPresent == false) 360 { 361 state = "Absent"; 362 } 363 364 if ((accFunctional != nullptr) && (*accFunctional == false)) 365 { 366 if (state == "Enabled") 367 { 368 health = "Critical"; 369 } 370 } 371 372 aResp->res.jsonValue["Status"]["State"] = state; 373 aResp->res.jsonValue["Status"]["Health"] = health; 374 aResp->res.jsonValue["ProcessorType"] = "Accelerator"; 375 }, 376 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); 377 } 378 379 inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, 380 const std::string& processorId, 381 const std::vector<const char*>& inventoryItems) 382 { 383 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 384 385 crow::connections::systemBus->async_method_call( 386 [processorId, aResp{std::move(aResp)}]( 387 const boost::system::error_code ec, 388 const boost::container::flat_map< 389 std::string, boost::container::flat_map< 390 std::string, std::vector<std::string>>>& 391 subtree) { 392 if (ec) 393 { 394 BMCWEB_LOG_DEBUG << "DBUS response error"; 395 messages::internalError(aResp->res); 396 return; 397 } 398 for (const auto& object : subtree) 399 { 400 if (boost::ends_with(object.first, processorId)) 401 { 402 for (const auto& service : object.second) 403 { 404 for (const auto& inventory : service.second) 405 { 406 if (inventory == "xyz.openbmc_project." 407 "Inventory.Decorator.Asset") 408 { 409 getCpuAssetData(aResp, service.first, 410 object.first); 411 } 412 else if (inventory == 413 "xyz.openbmc_project." 414 "Inventory.Decorator.Revision") 415 { 416 getCpuRevisionData(aResp, service.first, 417 object.first); 418 } 419 else if (inventory == "xyz.openbmc_project." 420 "Inventory.Item.Cpu") 421 { 422 getCpuDataByService(aResp, processorId, 423 service.first, 424 object.first); 425 } 426 else if (inventory == "xyz.openbmc_project." 427 "Inventory.Item.Accelerator") 428 { 429 getAcceleratorDataByService(aResp, processorId, 430 service.first, 431 object.first); 432 } 433 } 434 } 435 return; 436 } 437 } 438 // Object not found 439 messages::resourceNotFound(aResp->res, "Processor", processorId); 440 return; 441 }, 442 "xyz.openbmc_project.ObjectMapper", 443 "/xyz/openbmc_project/object_mapper", 444 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 445 "/xyz/openbmc_project/inventory", 0, inventoryItems); 446 } 447 448 class ProcessorCollection : public Node 449 { 450 public: 451 /* 452 * Default Constructor 453 */ 454 ProcessorCollection(App& app) : 455 Node(app, "/redfish/v1/Systems/system/Processors/") 456 { 457 entityPrivileges = { 458 {boost::beast::http::verb::get, {{"Login"}}}, 459 {boost::beast::http::verb::head, {{"Login"}}}, 460 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 461 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 462 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 463 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 464 } 465 466 private: 467 /** 468 * Functions triggers appropriate requests on DBus 469 */ 470 void doGet(crow::Response& res, const crow::Request&, 471 const std::vector<std::string>&) override 472 { 473 res.jsonValue["@odata.type"] = 474 "#ProcessorCollection.ProcessorCollection"; 475 res.jsonValue["Name"] = "Processor Collection"; 476 477 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors"; 478 auto asyncResp = std::make_shared<AsyncResp>(res); 479 480 collection_util::getCollectionMembers( 481 asyncResp, "/redfish/v1/Systems/system/Processors", 482 {"xyz.openbmc_project.Inventory.Item.Cpu", 483 "xyz.openbmc_project.Inventory.Item.Accelerator"}); 484 } 485 }; 486 487 class Processor : public Node 488 { 489 public: 490 /* 491 * Default Constructor 492 */ 493 Processor(App& app) : 494 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string()) 495 { 496 entityPrivileges = { 497 {boost::beast::http::verb::get, {{"Login"}}}, 498 {boost::beast::http::verb::head, {{"Login"}}}, 499 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 500 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 501 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 502 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 503 } 504 505 private: 506 /** 507 * Functions triggers appropriate requests on DBus 508 */ 509 void doGet(crow::Response& res, const crow::Request&, 510 const std::vector<std::string>& params) override 511 { 512 // Check if there is required param, truly entering this shall be 513 // impossible 514 if (params.size() != 1) 515 { 516 messages::internalError(res); 517 518 res.end(); 519 return; 520 } 521 const std::string& processorId = params[0]; 522 res.jsonValue["@odata.type"] = "#Processor.v1_9_0.Processor"; 523 res.jsonValue["@odata.id"] = 524 "/redfish/v1/Systems/system/Processors/" + processorId; 525 526 auto asyncResp = std::make_shared<AsyncResp>(res); 527 528 getProcessorData(asyncResp, processorId, 529 {"xyz.openbmc_project.Inventory.Item.Cpu", 530 "xyz.openbmc_project.Inventory.Decorator.Asset", 531 "xyz.openbmc_project.Inventory.Item.Accelerator"}); 532 } 533 }; 534 535 } // namespace redfish 536