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