1 #include "config.h" 2 3 #include "worker.hpp" 4 5 #include "backup_restore.hpp" 6 #include "configuration.hpp" 7 #include "constants.hpp" 8 #include "event_logger.hpp" 9 #include "exceptions.hpp" 10 #include "logger.hpp" 11 #include "parser.hpp" 12 #include "parser_factory.hpp" 13 #include "parser_interface.hpp" 14 15 #include <utility/common_utility.hpp> 16 #include <utility/dbus_utility.hpp> 17 #include <utility/json_utility.hpp> 18 #include <utility/vpd_specific_utility.hpp> 19 20 #include <filesystem> 21 #include <fstream> 22 #include <future> 23 #include <typeindex> 24 #include <unordered_set> 25 26 namespace vpd 27 { 28 29 Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount) : 30 m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount) 31 { 32 // Implies the processing is based on some config JSON 33 if (!m_configJsonPath.empty()) 34 { 35 // Check if symlink is already there to confirm fresh boot/factory 36 // reset. 37 if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK)) 38 { 39 logging::logMessage("Sym Link already present"); 40 m_configJsonPath = INVENTORY_JSON_SYM_LINK; 41 m_isSymlinkPresent = true; 42 } 43 44 try 45 { 46 uint16_t l_errCode = 0; 47 m_parsedJson = 48 jsonUtility::getParsedJson(m_configJsonPath, l_errCode); 49 50 if (l_errCode) 51 { 52 throw std::runtime_error( 53 "JSON parsing failed for file [ " + m_configJsonPath + 54 " ], error : " + commonUtility::getErrCodeMsg(l_errCode)); 55 } 56 57 // check for mandatory fields at this point itself. 58 if (!m_parsedJson.contains("frus")) 59 { 60 throw std::runtime_error("Mandatory tag(s) missing from JSON"); 61 } 62 } 63 catch (const std::exception& ex) 64 { 65 throw(JsonException(ex.what(), m_configJsonPath)); 66 } 67 } 68 else 69 { 70 logging::logMessage("Processing in not based on any config JSON"); 71 } 72 } 73 74 static std::string readFitConfigValue() 75 { 76 std::vector<std::string> output = 77 commonUtility::executeCmd("/sbin/fw_printenv"); 78 std::string fitConfigValue; 79 80 for (const auto& entry : output) 81 { 82 auto pos = entry.find("="); 83 auto key = entry.substr(0, pos); 84 if (key != "fitconfig") 85 { 86 continue; 87 } 88 89 if (pos + 1 < entry.size()) 90 { 91 fitConfigValue = entry.substr(pos + 1); 92 } 93 } 94 95 return fitConfigValue; 96 } 97 98 bool Worker::isSystemVPDOnDBus() const 99 { 100 const std::string& mboardPath = 101 m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value( 102 "inventoryPath", ""); 103 104 if (mboardPath.empty()) 105 { 106 throw JsonException("System vpd file path missing in JSON", 107 INVENTORY_JSON_SYM_LINK); 108 } 109 110 std::array<const char*, 1> interfaces = { 111 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 112 113 const types::MapperGetObject& objectMap = 114 dbusUtility::getObjectMap(mboardPath, interfaces); 115 116 if (objectMap.empty()) 117 { 118 return false; 119 } 120 return true; 121 } 122 123 std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const 124 { 125 if (parsedVpd.empty()) 126 { 127 throw std::runtime_error("Empty VPD map. Can't Extract IM value"); 128 } 129 130 const auto& itrToVSBP = parsedVpd.find("VSBP"); 131 if (itrToVSBP == parsedVpd.end()) 132 { 133 throw DataException("VSBP record missing."); 134 } 135 136 const auto& itrToIM = (itrToVSBP->second).find("IM"); 137 if (itrToIM == (itrToVSBP->second).end()) 138 { 139 throw DataException("IM keyword missing."); 140 } 141 142 types::BinaryVector imVal; 143 std::copy(itrToIM->second.begin(), itrToIM->second.end(), 144 back_inserter(imVal)); 145 146 std::ostringstream imData; 147 for (auto& aByte : imVal) 148 { 149 imData << std::setw(2) << std::setfill('0') << std::hex 150 << static_cast<int>(aByte); 151 } 152 153 return imData.str(); 154 } 155 156 std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const 157 { 158 if (parsedVpd.empty()) 159 { 160 throw std::runtime_error("Empty VPD map. Can't Extract HW value"); 161 } 162 163 const auto& itrToVINI = parsedVpd.find("VINI"); 164 if (itrToVINI == parsedVpd.end()) 165 { 166 throw DataException("VINI record missing."); 167 } 168 169 const auto& itrToHW = (itrToVINI->second).find("HW"); 170 if (itrToHW == (itrToVINI->second).end()) 171 { 172 throw DataException("HW keyword missing."); 173 } 174 175 types::BinaryVector hwVal; 176 std::copy(itrToHW->second.begin(), itrToHW->second.end(), 177 back_inserter(hwVal)); 178 179 // The planar pass only comes from the LSB of the HW keyword, 180 // where as the MSB is used for other purposes such as signifying clock 181 // termination. 182 hwVal[0] = 0x00; 183 184 std::ostringstream hwString; 185 for (auto& aByte : hwVal) 186 { 187 hwString << std::setw(2) << std::setfill('0') << std::hex 188 << static_cast<int>(aByte); 189 } 190 191 return hwString.str(); 192 } 193 194 void Worker::fillVPDMap(const std::string& vpdFilePath, 195 types::VPDMapVariant& vpdMap) 196 { 197 if (vpdFilePath.empty()) 198 { 199 throw std::runtime_error("Invalid file path passed to fillVPDMap API."); 200 } 201 202 if (!std::filesystem::exists(vpdFilePath)) 203 { 204 throw std::runtime_error("Can't Find physical file"); 205 } 206 207 std::shared_ptr<Parser> vpdParser = 208 std::make_shared<Parser>(vpdFilePath, m_parsedJson); 209 vpdMap = vpdParser->parse(); 210 } 211 212 void Worker::getSystemJson(std::string& systemJson, 213 const types::VPDMapVariant& parsedVpdMap) 214 { 215 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 216 { 217 std::string hwKWdValue = getHWVersion(*pVal); 218 if (hwKWdValue.empty()) 219 { 220 throw DataException("HW value fetched is empty."); 221 } 222 223 const std::string& imKwdValue = getIMValue(*pVal); 224 if (imKwdValue.empty()) 225 { 226 throw DataException("IM value fetched is empty."); 227 } 228 229 auto itrToIM = config::systemType.find(imKwdValue); 230 if (itrToIM == config::systemType.end()) 231 { 232 throw DataException("IM keyword does not map to any system type"); 233 } 234 235 const types::HWVerList hwVersionList = itrToIM->second.second; 236 if (!hwVersionList.empty()) 237 { 238 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(), 239 ::toupper); 240 241 auto itrToHW = 242 std::find_if(hwVersionList.begin(), hwVersionList.end(), 243 [&hwKWdValue](const auto& aPair) { 244 return aPair.first == hwKWdValue; 245 }); 246 247 if (itrToHW != hwVersionList.end()) 248 { 249 if (!(*itrToHW).second.empty()) 250 { 251 systemJson += (*itrToIM).first + "_" + (*itrToHW).second + 252 ".json"; 253 } 254 else 255 { 256 systemJson += (*itrToIM).first + ".json"; 257 } 258 return; 259 } 260 } 261 systemJson += itrToIM->second.first + ".json"; 262 return; 263 } 264 265 throw DataException( 266 "Invalid VPD type returned from Parser. Can't get system JSON."); 267 } 268 269 static void setEnvAndReboot(const std::string& key, const std::string& value) 270 { 271 // set env and reboot and break. 272 commonUtility::executeCmd("/sbin/fw_setenv", key, value); 273 logging::logMessage("Rebooting BMC to pick up new device tree"); 274 275 // make dbus call to reboot 276 auto bus = sdbusplus::bus::new_default_system(); 277 auto method = bus.new_method_call( 278 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 279 "org.freedesktop.systemd1.Manager", "Reboot"); 280 bus.call_noreply(method); 281 } 282 283 void Worker::setJsonSymbolicLink(const std::string& i_systemJson) 284 { 285 std::error_code l_ec; 286 l_ec.clear(); 287 288 // Check if symlink file path exists and if the JSON at this location is a 289 // symlink. 290 if (m_isSymlinkPresent && 291 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec)) 292 { // Don't care about exception in "is_symlink". Will continue with creation 293 // of symlink. 294 295 const auto& l_symlinkFilePth = 296 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec); 297 298 if (l_ec) 299 { 300 logging::logMessage( 301 "Can't read existing symlink. Error =" + l_ec.message() + 302 "Trying removal of symlink and creation of new symlink."); 303 } 304 305 // If currently set JSON is the required one. No further processing 306 // required. 307 if (i_systemJson == l_symlinkFilePth) 308 { 309 // Correct symlink already set. 310 return; 311 } 312 313 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec)) 314 { 315 // No point going further. If removal fails for existing symlink, 316 // create will anyways throw. 317 throw std::runtime_error( 318 "Removal of symlink failed with Error = " + l_ec.message() + 319 ". Can't proceed with create_symlink."); 320 } 321 } 322 323 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec)) 324 { 325 if (l_ec) 326 { 327 throw std::runtime_error( 328 "File system call to exist failed with error = " + 329 l_ec.message()); 330 } 331 332 // implies it is a fresh boot/factory reset. 333 // Create the directory for hosting the symlink 334 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec)) 335 { 336 if (l_ec) 337 { 338 throw std::runtime_error( 339 "File system call to create directory failed with error = " + 340 l_ec.message()); 341 } 342 } 343 } 344 345 // create a new symlink based on the system 346 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK, 347 l_ec); 348 349 if (l_ec) 350 { 351 throw std::runtime_error( 352 "create_symlink system call failed with error: " + l_ec.message()); 353 } 354 355 // If the flow is at this point implies the symlink was not present there. 356 // Considering this as factory reset. 357 m_isFactoryResetDone = true; 358 } 359 360 void Worker::setDeviceTreeAndJson() 361 { 362 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH, 363 constants::vpdCollectionInProgress); 364 365 // JSON is madatory for processing of this API. 366 if (m_parsedJson.empty()) 367 { 368 throw JsonException("System config JSON is empty", m_configJsonPath); 369 } 370 371 types::VPDMapVariant parsedVpdMap; 372 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap); 373 374 // Implies it is default JSON. 375 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX}; 376 377 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system 378 // This is required to support movement from rainier to Blue Ridge on the 379 // fly. 380 381 getSystemJson(systemJson, parsedVpdMap); 382 383 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX)) 384 { 385 throw DataException( 386 "No system JSON found corresponding to IM read from VPD."); 387 } 388 389 uint16_t l_errCode = 0; 390 391 // re-parse the JSON once appropriate JSON has been selected. 392 m_parsedJson = jsonUtility::getParsedJson(systemJson, l_errCode); 393 394 if (l_errCode) 395 { 396 throw(JsonException( 397 "JSON parsing failed for file [ " + systemJson + 398 " ], error : " + commonUtility::getErrCodeMsg(l_errCode), 399 systemJson)); 400 } 401 402 std::string devTreeFromJson; 403 if (m_parsedJson.contains("devTree")) 404 { 405 devTreeFromJson = m_parsedJson["devTree"]; 406 407 if (devTreeFromJson.empty()) 408 { 409 EventLogger::createSyncPel( 410 types::ErrorType::JsonFailure, types::SeverityType::Error, 411 __FILE__, __FUNCTION__, 0, 412 "Mandatory value for device tree missing from JSON[" + 413 systemJson + "]", 414 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 415 } 416 } 417 418 auto fitConfigVal = readFitConfigValue(); 419 420 if (devTreeFromJson.empty() || 421 fitConfigVal.find(devTreeFromJson) != std::string::npos) 422 { // Skipping setting device tree as either devtree info is missing from 423 // Json or it is rightly set. 424 425 setJsonSymbolicLink(systemJson); 426 427 if (isSystemVPDOnDBus()) 428 { 429 uint16_t l_errCode = 0; 430 if (jsonUtility::isBackupAndRestoreRequired(m_parsedJson, 431 l_errCode)) 432 { 433 performBackupAndRestore(parsedVpdMap); 434 } 435 else if (l_errCode) 436 { 437 logging::logMessage( 438 "Failed to check if backup and restore required. Reason : " + 439 commonUtility::getErrCodeMsg(l_errCode)); 440 } 441 } 442 443 // proceed to publish system VPD. 444 publishSystemVPD(parsedVpdMap); 445 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH, 446 constants::vpdCollectionCompleted); 447 return; 448 } 449 450 setEnvAndReboot("fitconfig", devTreeFromJson); 451 exit(EXIT_SUCCESS); 452 } 453 454 void Worker::populateIPZVPDpropertyMap( 455 types::InterfaceMap& interfacePropMap, 456 const types::IPZKwdValueMap& keyordValueMap, 457 const std::string& interfaceName) 458 { 459 types::PropertyMap propertyValueMap; 460 for (const auto& kwdVal : keyordValueMap) 461 { 462 auto kwd = kwdVal.first; 463 464 if (kwd[0] == '#') 465 { 466 kwd = std::string("PD_") + kwd[1]; 467 } 468 else if (isdigit(kwd[0])) 469 { 470 kwd = std::string("N_") + kwd; 471 } 472 473 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end()); 474 propertyValueMap.emplace(move(kwd), move(value)); 475 } 476 477 if (!propertyValueMap.empty()) 478 { 479 interfacePropMap.emplace(interfaceName, propertyValueMap); 480 } 481 } 482 483 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap, 484 types::InterfaceMap& interfaceMap) 485 { 486 for (const auto& kwdValMap : keyordVPDMap) 487 { 488 types::PropertyMap propertyValueMap; 489 auto kwd = kwdValMap.first; 490 491 if (kwd[0] == '#') 492 { 493 kwd = std::string("PD_") + kwd[1]; 494 } 495 else if (isdigit(kwd[0])) 496 { 497 kwd = std::string("N_") + kwd; 498 } 499 500 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second)) 501 { 502 types::BinaryVector value((*keywordValue).begin(), 503 (*keywordValue).end()); 504 propertyValueMap.emplace(move(kwd), move(value)); 505 } 506 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second)) 507 { 508 types::BinaryVector value((*keywordValue).begin(), 509 (*keywordValue).end()); 510 propertyValueMap.emplace(move(kwd), move(value)); 511 } 512 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second)) 513 { 514 if (kwd == "MemorySizeInKB") 515 { 516 types::PropertyMap memProp; 517 memProp.emplace(move(kwd), ((*keywordValue))); 518 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm", 519 move(memProp)); 520 continue; 521 } 522 else 523 { 524 logging::logMessage( 525 "Unknown Keyword =" + kwd + " found in keyword VPD map"); 526 continue; 527 } 528 } 529 else 530 { 531 logging::logMessage( 532 "Unknown variant type found in keyword VPD map."); 533 continue; 534 } 535 536 if (!propertyValueMap.empty()) 537 { 538 vpdSpecificUtility::insertOrMerge( 539 interfaceMap, constants::kwdVpdInf, move(propertyValueMap)); 540 } 541 } 542 } 543 544 void Worker::populateInterfaces(const nlohmann::json& interfaceJson, 545 types::InterfaceMap& interfaceMap, 546 const types::VPDMapVariant& parsedVpdMap) 547 { 548 for (const auto& interfacesPropPair : interfaceJson.items()) 549 { 550 const std::string& interface = interfacesPropPair.key(); 551 types::PropertyMap propertyMap; 552 553 for (const auto& propValuePair : interfacesPropPair.value().items()) 554 { 555 const std::string property = propValuePair.key(); 556 557 if (propValuePair.value().is_boolean()) 558 { 559 propertyMap.emplace(property, 560 propValuePair.value().get<bool>()); 561 } 562 else if (propValuePair.value().is_string()) 563 { 564 if (property.compare("LocationCode") == 0 && 565 interface.compare("com.ibm.ipzvpd.Location") == 0) 566 { 567 std::string value = 568 vpdSpecificUtility::getExpandedLocationCode( 569 propValuePair.value().get<std::string>(), 570 parsedVpdMap); 571 propertyMap.emplace(property, value); 572 573 auto l_locCodeProperty = propertyMap; 574 vpdSpecificUtility::insertOrMerge( 575 interfaceMap, 576 std::string(constants::xyzLocationCodeInf), 577 move(l_locCodeProperty)); 578 } 579 else 580 { 581 propertyMap.emplace( 582 property, propValuePair.value().get<std::string>()); 583 } 584 } 585 else if (propValuePair.value().is_array()) 586 { 587 try 588 { 589 propertyMap.emplace( 590 property, 591 propValuePair.value().get<types::BinaryVector>()); 592 } 593 catch (const nlohmann::detail::type_error& e) 594 { 595 std::cerr << "Type exception: " << e.what() << "\n"; 596 } 597 } 598 else if (propValuePair.value().is_number()) 599 { 600 // For now assume the value is a size_t. In the future it would 601 // be nice to come up with a way to get the type from the JSON. 602 propertyMap.emplace(property, 603 propValuePair.value().get<size_t>()); 604 } 605 else if (propValuePair.value().is_object()) 606 { 607 const std::string& record = 608 propValuePair.value().value("recordName", ""); 609 const std::string& keyword = 610 propValuePair.value().value("keywordName", ""); 611 const std::string& encoding = 612 propValuePair.value().value("encoding", ""); 613 614 if (auto ipzVpdMap = 615 std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 616 { 617 if (!record.empty() && !keyword.empty() && 618 (*ipzVpdMap).count(record) && 619 (*ipzVpdMap).at(record).count(keyword)) 620 { 621 auto encoded = vpdSpecificUtility::encodeKeyword( 622 ((*ipzVpdMap).at(record).at(keyword)), encoding); 623 propertyMap.emplace(property, encoded); 624 } 625 } 626 else if (auto kwdVpdMap = 627 std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 628 { 629 if (!keyword.empty() && (*kwdVpdMap).count(keyword)) 630 { 631 if (auto kwValue = std::get_if<types::BinaryVector>( 632 &(*kwdVpdMap).at(keyword))) 633 { 634 auto encodedValue = 635 vpdSpecificUtility::encodeKeyword( 636 std::string((*kwValue).begin(), 637 (*kwValue).end()), 638 encoding); 639 640 propertyMap.emplace(property, encodedValue); 641 } 642 else if (auto kwValue = std::get_if<std::string>( 643 &(*kwdVpdMap).at(keyword))) 644 { 645 auto encodedValue = 646 vpdSpecificUtility::encodeKeyword( 647 std::string((*kwValue).begin(), 648 (*kwValue).end()), 649 encoding); 650 651 propertyMap.emplace(property, encodedValue); 652 } 653 else if (auto uintValue = std::get_if<size_t>( 654 &(*kwdVpdMap).at(keyword))) 655 { 656 propertyMap.emplace(property, *uintValue); 657 } 658 else 659 { 660 logging::logMessage( 661 "Unknown keyword found, Keywrod = " + keyword); 662 } 663 } 664 } 665 } 666 } 667 vpdSpecificUtility::insertOrMerge(interfaceMap, interface, 668 move(propertyMap)); 669 } 670 } 671 672 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword) 673 { 674 const unsigned char l_io[] = { 675 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 676 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF}; 677 678 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0). 679 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs' 680 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as 681 // IO. 682 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG, 683 constants::SIZE_OF_8EQ_IN_PG) == 0) 684 { 685 return true; 686 } 687 688 // The CPU is not an IO 689 return false; 690 } 691 692 bool Worker::primeInventory(const std::string& i_vpdFilePath) 693 { 694 if (i_vpdFilePath.empty()) 695 { 696 logging::logMessage("Empty VPD file path given"); 697 return false; 698 } 699 700 if (m_parsedJson.empty()) 701 { 702 logging::logMessage("Empty JSON detected for " + i_vpdFilePath); 703 return false; 704 } 705 else if (!m_parsedJson["frus"].contains(i_vpdFilePath)) 706 { 707 logging::logMessage("File " + i_vpdFilePath + 708 ", is not found in the system config JSON file."); 709 return false; 710 } 711 712 types::ObjectMap l_objectInterfaceMap; 713 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath]) 714 { 715 types::InterfaceMap l_interfaces; 716 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]); 717 718 if (l_Fru.contains("ccin")) 719 { 720 continue; 721 } 722 723 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false)) 724 { 725 continue; 726 } 727 728 // Reset data under PIM for this FRU only if the FRU is not synthesized 729 // and we handle it's Present property. 730 if (isPresentPropertyHandlingRequired(l_Fru)) 731 { 732 // Clear data under PIM if already exists. 733 vpdSpecificUtility::resetDataUnderPIM( 734 std::string(l_Fru["inventoryPath"]), l_interfaces); 735 } 736 737 // Add extra interfaces mentioned in the Json config file 738 if (l_Fru.contains("extraInterfaces")) 739 { 740 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces, 741 std::monostate{}); 742 } 743 744 types::PropertyMap l_propertyValueMap; 745 746 // Update Present property for this FRU only if we handle Present 747 // property for the FRU. 748 if (isPresentPropertyHandlingRequired(l_Fru)) 749 { 750 l_propertyValueMap.emplace("Present", false); 751 752 // TODO: Present based on file will be taken care in future. 753 // By default present is set to false for FRU at the time of 754 // priming. Once collection goes through, it will be set to true in 755 // that flow. 756 /*if (std::filesystem::exists(i_vpdFilePath)) 757 { 758 l_propertyValueMap["Present"] = true; 759 }*/ 760 } 761 762 vpdSpecificUtility::insertOrMerge(l_interfaces, 763 "xyz.openbmc_project.Inventory.Item", 764 move(l_propertyValueMap)); 765 766 if (l_Fru.value("inherit", true) && 767 m_parsedJson.contains("commonInterfaces")) 768 { 769 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces, 770 std::monostate{}); 771 } 772 773 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces); 774 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces); 775 776 // Emplace the default state of FRU VPD collection 777 types::PropertyMap l_fruCollectionProperty = { 778 {"Status", constants::vpdCollectionNotStarted}}; 779 780 vpdSpecificUtility::insertOrMerge(l_interfaces, 781 constants::vpdCollectionInterface, 782 std::move(l_fruCollectionProperty)); 783 784 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 785 std::move(l_interfaces)); 786 } 787 788 // Notify PIM 789 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 790 { 791 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath); 792 return false; 793 } 794 795 return true; 796 } 797 798 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru, 799 types::InterfaceMap& interfaces) 800 { 801 // embedded property(true or false) says whether the subfru is embedded 802 // into the parent fru (or) not. VPD sets Present property only for 803 // embedded frus. If the subfru is not an embedded FRU, the subfru may 804 // or may not be physically present. Those non embedded frus will always 805 // have Present=false irrespective of its physical presence or absence. 806 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set 807 // Present to true for such sub frus. 808 // Eg: ethernet port is embedded into bmc card. So set Present to true 809 // for such sub frus. Also donot populate present property for embedded 810 // subfru which is synthesized. Currently there is no subfru which are 811 // both embedded and synthesized. But still the case is handled here. 812 813 // Check if its required to handle presence for this FRU. 814 if (singleFru.value("handlePresence", true)) 815 { 816 types::PropertyMap presProp; 817 presProp.emplace("Present", true); 818 vpdSpecificUtility::insertOrMerge( 819 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp)); 820 } 821 } 822 823 void Worker::processExtraInterfaces(const nlohmann::json& singleFru, 824 types::InterfaceMap& interfaces, 825 const types::VPDMapVariant& parsedVpdMap) 826 { 827 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap); 828 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 829 { 830 if (singleFru["extraInterfaces"].contains( 831 "xyz.openbmc_project.Inventory.Item.Cpu")) 832 { 833 auto itrToRec = (*ipzVpdMap).find("CP00"); 834 if (itrToRec == (*ipzVpdMap).end()) 835 { 836 return; 837 } 838 839 uint16_t l_errCode = 0; 840 const std::string pgKeywordValue{vpdSpecificUtility::getKwVal( 841 itrToRec->second, "PG", l_errCode)}; 842 843 if (!pgKeywordValue.empty()) 844 { 845 if (isCPUIOGoodOnly(pgKeywordValue)) 846 { 847 interfaces["xyz.openbmc_project.Inventory.Item"] 848 ["PrettyName"] = "IO Module"; 849 } 850 } 851 else 852 { 853 throw DataException( 854 std::string(__FUNCTION__) + 855 "Failed to get value for keyword PG, error : " + 856 commonUtility::getErrCodeMsg(l_errCode)); 857 } 858 } 859 } 860 } 861 862 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru, 863 const types::VPDMapVariant& parsedVpdMap, 864 types::InterfaceMap& interfaces) 865 { 866 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 867 { 868 for (const auto& record : singleFru["copyRecords"]) 869 { 870 const std::string& recordName = record; 871 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end()) 872 { 873 populateIPZVPDpropertyMap(interfaces, 874 (*ipzVpdMap).at(recordName), 875 constants::ipzVpdInf + recordName); 876 } 877 } 878 } 879 } 880 881 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap, 882 types::InterfaceMap& interfaces) 883 { 884 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 885 { 886 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap) 887 { 888 populateIPZVPDpropertyMap(interfaces, kwdValueMap, 889 constants::ipzVpdInf + recordName); 890 } 891 } 892 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 893 { 894 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces); 895 } 896 897 if (m_parsedJson.contains("commonInterfaces")) 898 { 899 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces, 900 parsedVpdMap); 901 } 902 } 903 904 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru, 905 const types::VPDMapVariant& parsedVpdMap) 906 { 907 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 908 { 909 auto itrToRec = (*ipzVPDMap).find("VINI"); 910 if (itrToRec == (*ipzVPDMap).end()) 911 { 912 return false; 913 } 914 915 uint16_t l_errCode = 0; 916 std::string ccinFromVpd{ 917 vpdSpecificUtility::getKwVal(itrToRec->second, "CC", l_errCode)}; 918 919 if (ccinFromVpd.empty()) 920 { 921 logging::logMessage("Failed to get CCIN kwd value, error : " + 922 commonUtility::getErrCodeMsg(l_errCode)); 923 return false; 924 } 925 926 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(), 927 ::toupper); 928 929 std::vector<std::string> ccinList; 930 for (std::string ccin : singleFru["ccin"]) 931 { 932 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper); 933 ccinList.push_back(ccin); 934 } 935 936 if (ccinList.empty()) 937 { 938 return false; 939 } 940 941 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) == 942 ccinList.end()) 943 { 944 return false; 945 } 946 } 947 return true; 948 } 949 950 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath, 951 types::InterfaceMap& io_interfaces) 952 { 953 if (!dbusUtility::isChassisPowerOn()) 954 { 955 std::array<const char*, 1> l_operationalStatusInf = { 956 constants::operationalStatusInf}; 957 958 auto mapperObjectMap = dbusUtility::getObjectMap( 959 i_inventoryObjPath, l_operationalStatusInf); 960 961 // If the object has been found. Check if it is under PIM. 962 if (mapperObjectMap.size() != 0) 963 { 964 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 965 { 966 if (l_serviceName == constants::pimServiceName) 967 { 968 // The object is already under PIM. No need to process 969 // again. Retain the old value. 970 return; 971 } 972 } 973 } 974 975 // Implies value is not there in D-Bus. Populate it with default 976 // value "true". 977 types::PropertyMap l_functionalProp; 978 l_functionalProp.emplace("Functional", true); 979 vpdSpecificUtility::insertOrMerge(io_interfaces, 980 constants::operationalStatusInf, 981 move(l_functionalProp)); 982 } 983 984 // if chassis is power on. Functional property should be there on D-Bus. 985 // Don't process. 986 return; 987 } 988 989 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath, 990 types::InterfaceMap& io_interfaces) 991 { 992 if (!dbusUtility::isChassisPowerOn()) 993 { 994 std::array<const char*, 1> l_enableInf = {constants::enableInf}; 995 996 auto mapperObjectMap = 997 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 998 999 // If the object has been found. Check if it is under PIM. 1000 if (mapperObjectMap.size() != 0) 1001 { 1002 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 1003 { 1004 if (l_serviceName == constants::pimServiceName) 1005 { 1006 // The object is already under PIM. No need to process 1007 // again. Retain the old value. 1008 return; 1009 } 1010 } 1011 } 1012 1013 // Implies value is not there in D-Bus. Populate it with default 1014 // value "true". 1015 types::PropertyMap l_enabledProp; 1016 l_enabledProp.emplace("Enabled", true); 1017 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf, 1018 move(l_enabledProp)); 1019 } 1020 1021 // if chassis is power on. Enabled property should be there on D-Bus. 1022 // Don't process. 1023 return; 1024 } 1025 1026 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap, 1027 types::ObjectMap& objectInterfaceMap, 1028 const std::string& vpdFilePath) 1029 { 1030 if (vpdFilePath.empty()) 1031 { 1032 throw std::runtime_error( 1033 std::string(__FUNCTION__) + 1034 "Invalid parameter passed to populateDbus API."); 1035 } 1036 1037 // JSON config is mandatory for processing of "if". Add "else" for any 1038 // processing without config JSON. 1039 if (!m_parsedJson.empty()) 1040 { 1041 types::InterfaceMap interfaces; 1042 1043 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath]) 1044 { 1045 const auto& inventoryPath = aFru["inventoryPath"]; 1046 sdbusplus::message::object_path fruObjectPath(inventoryPath); 1047 if (aFru.contains("ccin")) 1048 { 1049 if (!processFruWithCCIN(aFru, parsedVpdMap)) 1050 { 1051 continue; 1052 } 1053 } 1054 1055 if (aFru.value("inherit", true)) 1056 { 1057 processInheritFlag(parsedVpdMap, interfaces); 1058 } 1059 1060 // If specific record needs to be copied. 1061 if (aFru.contains("copyRecords")) 1062 { 1063 processCopyRecordFlag(aFru, parsedVpdMap, interfaces); 1064 } 1065 1066 if (aFru.contains("extraInterfaces")) 1067 { 1068 // Process extra interfaces w.r.t a FRU. 1069 processExtraInterfaces(aFru, interfaces, parsedVpdMap); 1070 } 1071 1072 // Process FRUS which are embedded in the parent FRU and whose VPD 1073 // will be synthesized. 1074 if ((aFru.value("embedded", true)) && 1075 (!aFru.value("synthesized", false))) 1076 { 1077 processEmbeddedAndSynthesizedFrus(aFru, interfaces); 1078 } 1079 1080 processFunctionalProperty(inventoryPath, interfaces); 1081 processEnabledProperty(inventoryPath, interfaces); 1082 1083 objectInterfaceMap.emplace(std::move(fruObjectPath), 1084 std::move(interfaces)); 1085 } 1086 } 1087 } 1088 1089 std::string Worker::createAssetTagString( 1090 const types::VPDMapVariant& i_parsedVpdMap) 1091 { 1092 std::string l_assetTag; 1093 1094 // system VPD will be in IPZ format. 1095 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap)) 1096 { 1097 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS); 1098 if (l_itrToVsys != (*l_parsedVpdMap).end()) 1099 { 1100 uint16_t l_errCode = 0; 1101 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal( 1102 l_itrToVsys->second, constants::kwdTM, l_errCode)}; 1103 1104 if (l_tmKwdValue.empty()) 1105 { 1106 throw std::runtime_error( 1107 std::string("Failed to get value for keyword [") + 1108 constants::kwdTM + 1109 std::string("] while creating Asset tag. Error : " + 1110 commonUtility::getErrCodeMsg(l_errCode))); 1111 } 1112 1113 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal( 1114 l_itrToVsys->second, constants::kwdSE, l_errCode)}; 1115 1116 if (l_seKwdValue.empty()) 1117 { 1118 throw std::runtime_error( 1119 std::string("Failed to get value for keyword [") + 1120 constants::kwdSE + 1121 std::string("] while creating Asset tag. Error : " + 1122 commonUtility::getErrCodeMsg(l_errCode))); 1123 } 1124 1125 l_assetTag = std::string{"Server-"} + l_tmKwdValue + 1126 std::string{"-"} + l_seKwdValue; 1127 } 1128 else 1129 { 1130 throw std::runtime_error( 1131 "VSYS record not found in parsed VPD map to create Asset tag."); 1132 } 1133 } 1134 else 1135 { 1136 throw std::runtime_error( 1137 "Invalid VPD type recieved to create Asset tag."); 1138 } 1139 1140 return l_assetTag; 1141 } 1142 1143 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap) 1144 { 1145 types::ObjectMap objectInterfaceMap; 1146 1147 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 1148 { 1149 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH); 1150 1151 try 1152 { 1153 if (m_isFactoryResetDone) 1154 { 1155 const auto& l_assetTag = createAssetTagString(parsedVpdMap); 1156 1157 auto l_itrToSystemPath = objectInterfaceMap.find( 1158 sdbusplus::message::object_path(constants::systemInvPath)); 1159 if (l_itrToSystemPath == objectInterfaceMap.end()) 1160 { 1161 throw std::runtime_error( 1162 "Asset tag update failed. System Path not found in object map."); 1163 } 1164 1165 types::PropertyMap l_assetTagProperty; 1166 l_assetTagProperty.emplace("AssetTag", l_assetTag); 1167 1168 (l_itrToSystemPath->second) 1169 .emplace(constants::assetTagInf, 1170 std::move(l_assetTagProperty)); 1171 } 1172 } 1173 catch (const std::exception& l_ex) 1174 { 1175 EventLogger::createSyncPel( 1176 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1177 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1178 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1179 } 1180 1181 // Notify PIM 1182 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1183 { 1184 throw std::runtime_error("Call to PIM failed for system VPD"); 1185 } 1186 } 1187 else 1188 { 1189 throw DataException("Invalid format of parsed VPD map."); 1190 } 1191 } 1192 1193 bool Worker::processPreAction(const std::string& i_vpdFilePath, 1194 const std::string& i_flagToProcess, 1195 uint16_t& i_errCode) 1196 { 1197 if (i_vpdFilePath.empty() || i_flagToProcess.empty()) 1198 { 1199 i_errCode = error_code::INVALID_INPUT_PARAMETER; 1200 return false; 1201 } 1202 1203 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction", 1204 i_vpdFilePath, i_flagToProcess, 1205 i_errCode)) && 1206 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS)) 1207 { 1208 // TODO: Need a way to delete inventory object from Dbus and persisted 1209 // data section in case any FRU is not present or there is any 1210 // problem in collecting it. Once it has been deleted, it can be 1211 // re-created in the flow of priming the inventory. This needs to be 1212 // done either here or in the exception section of "parseAndPublishVPD" 1213 // API. Any failure in the process of collecting FRU will land up in the 1214 // excpetion of "parseAndPublishVPD". 1215 1216 // If the FRU is not there, clear the VINI/CCIN data. 1217 // Enity manager probes for this keyword to look for this 1218 // FRU, now if the data is persistent on BMC and FRU is 1219 // removed this can lead to ambiguity. Hence clearing this 1220 // Keyword if FRU is absent. 1221 const auto& inventoryPath = 1222 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath", 1223 ""); 1224 1225 if (!inventoryPath.empty()) 1226 { 1227 types::ObjectMap l_pimObjMap{ 1228 {inventoryPath, 1229 {{constants::kwdVpdInf, 1230 {{constants::kwdCCIN, types::BinaryVector{}}}}}}}; 1231 1232 if (!dbusUtility::callPIM(std::move(l_pimObjMap))) 1233 { 1234 logging::logMessage( 1235 "Call to PIM failed for file " + i_vpdFilePath); 1236 } 1237 } 1238 else 1239 { 1240 logging::logMessage( 1241 "Inventory path is empty in Json for file " + i_vpdFilePath); 1242 } 1243 1244 return false; 1245 } 1246 return true; 1247 } 1248 1249 bool Worker::processPostAction( 1250 const std::string& i_vpdFruPath, const std::string& i_flagToProcess, 1251 const std::optional<types::VPDMapVariant> i_parsedVpd) 1252 { 1253 if (i_vpdFruPath.empty() || i_flagToProcess.empty()) 1254 { 1255 logging::logMessage( 1256 "Invalid input parameter. Abort processing post action"); 1257 return false; 1258 } 1259 1260 // Check if post action tag is to be triggered in the flow of collection 1261 // based on some CCIN value? 1262 if (m_parsedJson["frus"][i_vpdFruPath] 1263 .at(0)["postAction"][i_flagToProcess] 1264 .contains("ccin")) 1265 { 1266 if (!i_parsedVpd.has_value()) 1267 { 1268 logging::logMessage("Empty VPD Map"); 1269 return false; 1270 } 1271 1272 // CCIN match is required to process post action for this FRU as it 1273 // contains the flag. 1274 if (!vpdSpecificUtility::findCcinInVpd( 1275 m_parsedJson["frus"][i_vpdFruPath].at( 1276 0)["postAction"]["collection"], 1277 i_parsedVpd.value())) 1278 { 1279 // If CCIN is not found, implies post action processing is not 1280 // required for this FRU. Let the flow continue. 1281 return true; 1282 } 1283 } 1284 1285 uint16_t l_errCode = 0; 1286 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction", 1287 i_vpdFruPath, i_flagToProcess, 1288 l_errCode)) 1289 { 1290 logging::logMessage( 1291 "Execution of post action failed for path: " + i_vpdFruPath + 1292 " . Reason: " + commonUtility::getErrCodeMsg(l_errCode)); 1293 1294 // If post action was required and failed only in that case return 1295 // false. In all other case post action is considered passed. 1296 return false; 1297 } 1298 1299 return true; 1300 } 1301 1302 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath) 1303 { 1304 try 1305 { 1306 uint16_t l_errCode = 0; 1307 1308 if (i_vpdFilePath.empty()) 1309 { 1310 throw std::runtime_error( 1311 std::string(__FUNCTION__) + 1312 " Empty VPD file path passed. Abort processing"); 1313 } 1314 1315 bool isPreActionRequired = false; 1316 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1317 "preAction", "collection", l_errCode)) 1318 { 1319 l_errCode = 0; 1320 isPreActionRequired = true; 1321 if (!processPreAction(i_vpdFilePath, "collection", l_errCode)) 1322 { 1323 if (l_errCode == error_code::DEVICE_NOT_PRESENT) 1324 { 1325 logging::logMessage( 1326 commonUtility::getErrCodeMsg(l_errCode) + 1327 i_vpdFilePath); 1328 // Presence pin has been read successfully and has been read 1329 // as false, so this is not a failure case, hence returning 1330 // empty variant so that pre action is not marked as failed. 1331 return types::VPDMapVariant{}; 1332 } 1333 throw std::runtime_error( 1334 std::string(__FUNCTION__) + 1335 " Pre-Action failed with error: " + 1336 commonUtility::getErrCodeMsg(l_errCode)); 1337 } 1338 } 1339 else if (l_errCode) 1340 { 1341 logging::logMessage( 1342 "Failed to check if pre action required for FRU [" + 1343 i_vpdFilePath + 1344 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1345 } 1346 1347 if (!std::filesystem::exists(i_vpdFilePath)) 1348 { 1349 if (isPreActionRequired) 1350 { 1351 throw std::runtime_error( 1352 std::string(__FUNCTION__) + " Could not find file path " + 1353 i_vpdFilePath + "Skipping parser trigger for the EEPROM"); 1354 } 1355 return types::VPDMapVariant{}; 1356 } 1357 1358 std::shared_ptr<Parser> vpdParser = 1359 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson); 1360 1361 types::VPDMapVariant l_parsedVpd = vpdParser->parse(); 1362 1363 // Before returning, as collection is over, check if FRU qualifies for 1364 // any post action in the flow of collection. 1365 // Note: Don't change the order, post action needs to be processed only 1366 // after collection for FRU is successfully done. 1367 l_errCode = 0; 1368 1369 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1370 "postAction", "collection", 1371 l_errCode)) 1372 { 1373 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd)) 1374 { 1375 // Post action was required but failed while executing. 1376 // Behaviour can be undefined. 1377 EventLogger::createSyncPel( 1378 types::ErrorType::InternalFailure, 1379 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0, 1380 std::string("Required post action failed for path [" + 1381 i_vpdFilePath + "]"), 1382 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1383 } 1384 } 1385 else if (l_errCode) 1386 { 1387 logging::logMessage( 1388 "Error while checking if post action required for FRU [" + 1389 i_vpdFilePath + 1390 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1391 } 1392 1393 return l_parsedVpd; 1394 } 1395 catch (std::exception& l_ex) 1396 { 1397 uint16_t l_errCode = 0; 1398 std::string l_exMsg{ 1399 std::string(__FUNCTION__) + " : VPD parsing failed for " + 1400 i_vpdFilePath + " due to error: " + l_ex.what()}; 1401 1402 // If post fail action is required, execute it. 1403 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1404 "postFailAction", "collection", 1405 l_errCode)) 1406 { 1407 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath, 1408 "collection", l_errCode)) 1409 { 1410 l_exMsg += ". Post fail action also failed. Error : " + 1411 commonUtility::getErrCodeMsg(l_errCode) + 1412 " Aborting collection for this FRU."; 1413 } 1414 } 1415 else if (l_errCode) 1416 { 1417 l_exMsg += 1418 ". Failed to check if post fail action required, error : " + 1419 commonUtility::getErrCodeMsg(l_errCode); 1420 } 1421 1422 if (typeid(l_ex) == typeid(DataException)) 1423 { 1424 throw DataException(l_exMsg); 1425 } 1426 else if (typeid(l_ex) == typeid(EccException)) 1427 { 1428 throw EccException(l_exMsg); 1429 } 1430 throw std::runtime_error(l_exMsg); 1431 } 1432 } 1433 1434 std::tuple<bool, std::string> Worker::parseAndPublishVPD( 1435 const std::string& i_vpdFilePath) 1436 { 1437 std::string l_inventoryPath{}; 1438 1439 try 1440 { 1441 m_semaphore.acquire(); 1442 1443 // Thread launched. 1444 m_mutex.lock(); 1445 m_activeCollectionThreadCount++; 1446 m_mutex.unlock(); 1447 1448 setCollectionStatusProperty(i_vpdFilePath, 1449 constants::vpdCollectionInProgress); 1450 1451 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath); 1452 if (!std::holds_alternative<std::monostate>(parsedVpdMap)) 1453 { 1454 types::ObjectMap objectInterfaceMap; 1455 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath); 1456 1457 // Notify PIM 1458 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1459 { 1460 throw std::runtime_error( 1461 std::string(__FUNCTION__) + 1462 "Call to PIM failed while publishing VPD."); 1463 } 1464 } 1465 else 1466 { 1467 logging::logMessage("Empty parsedVpdMap recieved for path [" + 1468 i_vpdFilePath + "]. Check PEL for reason."); 1469 1470 // As empty parsedVpdMap recieved for some reason, but still 1471 // considered VPD collection is completed. Hence FRU collection 1472 // Status will be set as completed. 1473 } 1474 } 1475 catch (const std::exception& ex) 1476 { 1477 setCollectionStatusProperty(i_vpdFilePath, 1478 constants::vpdCollectionFailed); 1479 1480 // handle all the exceptions internally. Return only true/false 1481 // based on status of execution. 1482 if (typeid(ex) == std::type_index(typeid(DataException))) 1483 { 1484 uint16_t l_errCode = 0; 1485 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip 1486 // logging error for these cases. 1487 if (vpdSpecificUtility::isPass1Planar()) 1488 { 1489 std::string l_invPath = 1490 jsonUtility::getInventoryObjPathFromJson( 1491 m_parsedJson, i_vpdFilePath, l_errCode); 1492 1493 if (l_errCode != 0) 1494 { 1495 logging::logMessage( 1496 "Failed to get inventory object path from JSON for FRU [" + 1497 i_vpdFilePath + 1498 "], error: " + commonUtility::getErrCodeMsg(l_errCode)); 1499 } 1500 1501 const std::string& l_invPathLeafValue = 1502 sdbusplus::message::object_path(l_invPath).filename(); 1503 1504 if ((l_invPathLeafValue.find("pcie_card", 0) != 1505 std::string::npos)) 1506 { 1507 // skip logging any PEL for PCIe cards on pass 1 planar. 1508 return std::make_tuple(false, i_vpdFilePath); 1509 } 1510 } 1511 } 1512 1513 EventLogger::createSyncPel( 1514 EventLogger::getErrorType(ex), 1515 (typeid(ex) == typeid(DataException)) || 1516 (typeid(ex) == typeid(EccException)) 1517 ? types::SeverityType::Warning 1518 : types::SeverityType::Informational, 1519 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex), 1520 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1521 1522 // TODO: Figure out a way to clear data in case of any failure at 1523 // runtime. 1524 1525 // set present property to false for any error case. In future this will 1526 // be replaced by presence logic. 1527 // Update Present property for this FRU only if we handle Present 1528 // property for the FRU. 1529 if (isPresentPropertyHandlingRequired( 1530 m_parsedJson["frus"][i_vpdFilePath].at(0))) 1531 { 1532 setPresentProperty(i_vpdFilePath, false); 1533 } 1534 1535 m_semaphore.release(); 1536 return std::make_tuple(false, i_vpdFilePath); 1537 } 1538 1539 setCollectionStatusProperty(i_vpdFilePath, 1540 constants::vpdCollectionCompleted); 1541 m_semaphore.release(); 1542 return std::make_tuple(true, i_vpdFilePath); 1543 } 1544 1545 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath) 1546 { 1547 if (i_vpdFilePath.empty()) 1548 { 1549 return true; 1550 } 1551 1552 // skip processing of system VPD again as it has been already collected. 1553 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH) 1554 { 1555 return true; 1556 } 1557 1558 if (dbusUtility::isChassisPowerOn()) 1559 { 1560 // If chassis is powered on, skip collecting FRUs which are 1561 // powerOffOnly. 1562 1563 uint16_t l_errCode = 0; 1564 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath, 1565 l_errCode)) 1566 { 1567 return true; 1568 } 1569 else if (l_errCode) 1570 { 1571 logging::logMessage( 1572 "Failed to check if FRU is power off only for FRU [" + 1573 i_vpdFilePath + 1574 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1575 } 1576 1577 l_errCode = 0; 1578 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson( 1579 m_parsedJson, i_vpdFilePath, l_errCode); 1580 1581 if (l_errCode) 1582 { 1583 logging::logMessage( 1584 "Failed to get inventory path from JSON for FRU [" + 1585 i_vpdFilePath + 1586 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1587 1588 return false; 1589 } 1590 1591 const std::string& l_invPathLeafValue = 1592 sdbusplus::message::object_path(l_invPath).filename(); 1593 1594 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos)) 1595 { 1596 return true; 1597 } 1598 } 1599 1600 return false; 1601 } 1602 1603 void Worker::collectFrusFromJson() 1604 { 1605 // A parsed JSON file should be present to pick FRUs EEPROM paths 1606 if (m_parsedJson.empty()) 1607 { 1608 throw JsonException( 1609 std::string(__FUNCTION__) + 1610 ": Config JSON is mandatory for processing of FRUs through this API.", 1611 m_configJsonPath); 1612 } 1613 1614 const nlohmann::json& listOfFrus = 1615 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>(); 1616 1617 for (const auto& itemFRUS : listOfFrus.items()) 1618 { 1619 const std::string& vpdFilePath = itemFRUS.key(); 1620 1621 if (skipPathForCollection(vpdFilePath)) 1622 { 1623 continue; 1624 } 1625 1626 try 1627 { 1628 std::thread{[vpdFilePath, this]() { 1629 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath); 1630 1631 m_mutex.lock(); 1632 m_activeCollectionThreadCount--; 1633 m_mutex.unlock(); 1634 1635 if (!m_activeCollectionThreadCount) 1636 { 1637 m_isAllFruCollected = true; 1638 } 1639 }}.detach(); 1640 } 1641 catch (const std::exception& l_ex) 1642 { 1643 // add vpdFilePath(EEPROM path) to failed list 1644 m_failedEepromPaths.push_front(vpdFilePath); 1645 } 1646 } 1647 } 1648 1649 // ToDo: Move the API under IBM_SYSTEM 1650 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap) 1651 { 1652 try 1653 { 1654 uint16_t l_errCode = 0; 1655 std::string l_backupAndRestoreCfgFilePath = 1656 m_parsedJson.value("backupRestoreConfigPath", ""); 1657 1658 nlohmann::json l_backupAndRestoreCfgJsonObj = 1659 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath, 1660 l_errCode); 1661 1662 if (l_errCode) 1663 { 1664 throw JsonException( 1665 "JSON parsing failed for file [ " + 1666 l_backupAndRestoreCfgFilePath + 1667 " ], error : " + commonUtility::getErrCodeMsg(l_errCode), 1668 l_backupAndRestoreCfgFilePath); 1669 } 1670 1671 // check if either of "source" or "destination" has inventory path. 1672 // this indicates that this sytem has System VPD on hardware 1673 // and other copy on D-Bus (BMC cache). 1674 if (!l_backupAndRestoreCfgJsonObj.empty() && 1675 ((l_backupAndRestoreCfgJsonObj.contains("source") && 1676 l_backupAndRestoreCfgJsonObj["source"].contains( 1677 "inventoryPath")) || 1678 (l_backupAndRestoreCfgJsonObj.contains("destination") && 1679 l_backupAndRestoreCfgJsonObj["destination"].contains( 1680 "inventoryPath")))) 1681 { 1682 BackupAndRestore l_backupAndRestoreObj(m_parsedJson); 1683 auto [l_srcVpdVariant, 1684 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore(); 1685 1686 // ToDo: Revisit is this check is required or not. 1687 if (auto l_srcVpdMap = 1688 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant); 1689 l_srcVpdMap && !(*l_srcVpdMap).empty()) 1690 { 1691 io_srcVpdMap = std::move(l_srcVpdVariant); 1692 } 1693 } 1694 } 1695 catch (const std::exception& l_ex) 1696 { 1697 EventLogger::createSyncPel( 1698 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1699 __FILE__, __FUNCTION__, 0, 1700 std::string( 1701 "Exception caught while backup and restore VPD keyword's.") + 1702 EventLogger::getErrorMsg(l_ex), 1703 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1704 } 1705 } 1706 1707 void Worker::deleteFruVpd(const std::string& i_dbusObjPath) 1708 { 1709 if (i_dbusObjPath.empty()) 1710 { 1711 throw std::runtime_error("Given DBus object path is empty."); 1712 } 1713 1714 uint16_t l_errCode = 0; 1715 const std::string& l_fruPath = 1716 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode); 1717 1718 if (l_errCode) 1719 { 1720 logging::logMessage( 1721 "Failed to get FRU path for inventory path [" + i_dbusObjPath + 1722 "], error : " + commonUtility::getErrCodeMsg(l_errCode) + 1723 " Aborting FRU VPD deletion."); 1724 return; 1725 } 1726 1727 try 1728 { 1729 auto l_presentPropValue = dbusUtility::readDbusProperty( 1730 constants::pimServiceName, i_dbusObjPath, 1731 constants::inventoryItemInf, "Present"); 1732 1733 if (auto l_value = std::get_if<bool>(&l_presentPropValue)) 1734 { 1735 uint16_t l_errCode = 0; 1736 // check if FRU's Present property is handled by vpd-manager 1737 const auto& l_isFruPresenceHandled = 1738 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath, 1739 l_errCode); 1740 1741 if (l_errCode) 1742 { 1743 throw std::runtime_error( 1744 "Failed to check if FRU's presence is handled, reason: " + 1745 commonUtility::getErrCodeMsg(l_errCode)); 1746 } 1747 1748 if (!(*l_value) && l_isFruPresenceHandled) 1749 { 1750 throw std::runtime_error("Given FRU is not present"); 1751 } 1752 else if (*l_value && !l_isFruPresenceHandled) 1753 { 1754 throw std::runtime_error( 1755 "Given FRU is present and its presence is not handled by vpd-manager."); 1756 } 1757 else 1758 { 1759 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1760 "preAction", "deletion", 1761 l_errCode)) 1762 { 1763 if (!processPreAction(l_fruPath, "deletion", l_errCode)) 1764 { 1765 std::string l_msg = "Pre action failed"; 1766 if (l_errCode) 1767 { 1768 l_msg += " Reason: " + 1769 commonUtility::getErrCodeMsg(l_errCode); 1770 } 1771 throw std::runtime_error(l_msg); 1772 } 1773 } 1774 else if (l_errCode) 1775 { 1776 logging::logMessage( 1777 "Failed to check if pre action required for FRU [" + 1778 l_fruPath + "], error : " + 1779 commonUtility::getErrCodeMsg(l_errCode)); 1780 } 1781 1782 std::vector<std::string> l_interfaceList{ 1783 constants::operationalStatusInf}; 1784 1785 types::MapperGetSubTree l_subTreeMap = 1786 dbusUtility::getObjectSubTree(i_dbusObjPath, 0, 1787 l_interfaceList); 1788 1789 types::ObjectMap l_objectMap; 1790 1791 // Updates VPD specific interfaces property value under PIM for 1792 // sub FRUs. 1793 for (const auto& [l_objectPath, l_serviceInterfaceMap] : 1794 l_subTreeMap) 1795 { 1796 types::InterfaceMap l_interfaceMap; 1797 vpdSpecificUtility::resetDataUnderPIM(l_objectPath, 1798 l_interfaceMap); 1799 l_objectMap.emplace(l_objectPath, 1800 std::move(l_interfaceMap)); 1801 } 1802 1803 types::InterfaceMap l_interfaceMap; 1804 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath, 1805 l_interfaceMap); 1806 1807 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap)); 1808 1809 if (!dbusUtility::callPIM(std::move(l_objectMap))) 1810 { 1811 throw std::runtime_error("Call to PIM failed."); 1812 } 1813 1814 l_errCode = 0; 1815 1816 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1817 "postAction", "deletion", 1818 l_errCode)) 1819 { 1820 if (!processPostAction(l_fruPath, "deletion")) 1821 { 1822 throw std::runtime_error("Post action failed"); 1823 } 1824 } 1825 else if (l_errCode) 1826 { 1827 logging::logMessage( 1828 "Failed to check if post action required during deletion for FRU [" + 1829 l_fruPath + "], error : " + 1830 commonUtility::getErrCodeMsg(l_errCode)); 1831 } 1832 } 1833 } 1834 else 1835 { 1836 logging::logMessage( 1837 "Can't process delete VPD for FRU [" + i_dbusObjPath + 1838 "] as unable to read present property"); 1839 return; 1840 } 1841 1842 logging::logMessage( 1843 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath); 1844 } 1845 catch (const std::exception& l_ex) 1846 { 1847 uint16_t l_errCode = 0; 1848 std::string l_errMsg = 1849 "Failed to delete VPD for FRU : " + i_dbusObjPath + 1850 " error: " + std::string(l_ex.what()); 1851 1852 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1853 "postFailAction", "deletion", 1854 l_errCode)) 1855 { 1856 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath, 1857 "deletion", l_errCode)) 1858 { 1859 l_errMsg += ". Post fail action also failed, error : " + 1860 commonUtility::getErrCodeMsg(l_errCode); 1861 } 1862 } 1863 else if (l_errCode) 1864 { 1865 l_errMsg += 1866 ". Failed to check if post fail action required, error : " + 1867 commonUtility::getErrCodeMsg(l_errCode); 1868 } 1869 1870 logging::logMessage(l_errMsg); 1871 } 1872 } 1873 1874 void Worker::setPresentProperty(const std::string& i_vpdPath, 1875 const bool& i_value) 1876 { 1877 try 1878 { 1879 if (i_vpdPath.empty()) 1880 { 1881 throw std::runtime_error( 1882 "Path is empty. Can't set present property"); 1883 } 1884 1885 types::ObjectMap l_objectInterfaceMap; 1886 1887 // If the given path is EEPROM path. 1888 if (m_parsedJson["frus"].contains(i_vpdPath)) 1889 { 1890 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 1891 { 1892 sdbusplus::message::object_path l_fruObjectPath( 1893 l_Fru["inventoryPath"]); 1894 1895 types::PropertyMap l_propertyValueMap; 1896 l_propertyValueMap.emplace("Present", i_value); 1897 1898 types::InterfaceMap l_interfaces; 1899 vpdSpecificUtility::insertOrMerge(l_interfaces, 1900 constants::inventoryItemInf, 1901 move(l_propertyValueMap)); 1902 1903 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 1904 std::move(l_interfaces)); 1905 } 1906 } 1907 else 1908 { 1909 // consider it as an inventory path. 1910 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 1911 { 1912 throw std::runtime_error( 1913 "Invalid inventory path: " + i_vpdPath); 1914 } 1915 1916 types::PropertyMap l_propertyValueMap; 1917 l_propertyValueMap.emplace("Present", i_value); 1918 1919 types::InterfaceMap l_interfaces; 1920 vpdSpecificUtility::insertOrMerge(l_interfaces, 1921 constants::inventoryItemInf, 1922 move(l_propertyValueMap)); 1923 1924 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 1925 } 1926 1927 // Notify PIM 1928 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1929 { 1930 throw DbusException( 1931 std::string(__FUNCTION__) + 1932 "Call to PIM failed while setting present property for path " + 1933 i_vpdPath); 1934 } 1935 } 1936 catch (const std::exception& l_ex) 1937 { 1938 EventLogger::createSyncPel( 1939 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1940 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1941 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1942 } 1943 } 1944 1945 void Worker::performVpdRecollection() 1946 { 1947 try 1948 { 1949 // Check if system config JSON is present 1950 if (m_parsedJson.empty()) 1951 { 1952 throw std::runtime_error( 1953 "System config json object is empty, can't process recollection."); 1954 } 1955 1956 uint16_t l_errCode = 0; 1957 const auto& l_frusReplaceableAtStandby = 1958 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson, 1959 l_errCode); 1960 1961 if (l_errCode) 1962 { 1963 logging::logMessage( 1964 "Failed to get list of FRUs replaceable at runtime, error : " + 1965 commonUtility::getErrCodeMsg(l_errCode)); 1966 return; 1967 } 1968 1969 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby) 1970 { 1971 // ToDo: Add some logic/trace to know the flow to 1972 // collectSingleFruVpd has been directed via 1973 // performVpdRecollection. 1974 collectSingleFruVpd(l_fruInventoryPath); 1975 } 1976 return; 1977 } 1978 1979 catch (const std::exception& l_ex) 1980 { 1981 // TODO Log PEL 1982 logging::logMessage( 1983 "VPD recollection failed with error: " + std::string(l_ex.what())); 1984 } 1985 } 1986 1987 void Worker::collectSingleFruVpd( 1988 const sdbusplus::message::object_path& i_dbusObjPath) 1989 { 1990 std::string l_fruPath{}; 1991 uint16_t l_errCode = 0; 1992 1993 try 1994 { 1995 // Check if system config JSON is present 1996 if (m_parsedJson.empty()) 1997 { 1998 logging::logMessage( 1999 "System config JSON object not present. Single FRU VPD collection is not performed for " + 2000 std::string(i_dbusObjPath)); 2001 return; 2002 } 2003 2004 // Get FRU path for the given D-bus object path from JSON 2005 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, 2006 l_errCode); 2007 2008 if (l_fruPath.empty()) 2009 { 2010 if (l_errCode) 2011 { 2012 logging::logMessage( 2013 "Failed to get FRU path for [" + 2014 std::string(i_dbusObjPath) + 2015 "], error : " + commonUtility::getErrCodeMsg(l_errCode) + 2016 " Aborting single FRU VPD collection."); 2017 return; 2018 } 2019 2020 logging::logMessage( 2021 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " + 2022 std::string(i_dbusObjPath)); 2023 return; 2024 } 2025 2026 // Check if host is up and running 2027 if (dbusUtility::isHostRunning()) 2028 { 2029 uint16_t l_errCode = 0; 2030 bool isFruReplaceableAtRuntime = 2031 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath, 2032 l_errCode); 2033 2034 if (l_errCode) 2035 { 2036 logging::logMessage( 2037 "Failed to check if FRU is replaceable at runtime for FRU : [" + 2038 std::string(i_dbusObjPath) + 2039 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2040 return; 2041 } 2042 2043 if (!isFruReplaceableAtRuntime) 2044 { 2045 logging::logMessage( 2046 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " + 2047 std::string(i_dbusObjPath)); 2048 return; 2049 } 2050 } 2051 else if (dbusUtility::isBMCReady()) 2052 { 2053 uint16_t l_errCode = 0; 2054 bool isFruReplaceableAtStandby = 2055 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath, 2056 l_errCode); 2057 2058 if (l_errCode) 2059 { 2060 logging::logMessage( 2061 "Error while checking if FRU is replaceable at standby for FRU [" + 2062 std::string(i_dbusObjPath) + 2063 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2064 } 2065 2066 l_errCode = 0; 2067 bool isFruReplaceableAtRuntime = 2068 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath, 2069 l_errCode); 2070 2071 if (l_errCode) 2072 { 2073 logging::logMessage( 2074 "Failed to check if FRU is replaceable at runtime for FRU : [" + 2075 std::string(i_dbusObjPath) + 2076 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2077 return; 2078 } 2079 2080 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime)) 2081 { 2082 logging::logMessage( 2083 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " + 2084 std::string(i_dbusObjPath)); 2085 return; 2086 } 2087 } 2088 2089 // Set collection Status as InProgress. Since it's an intermediate state 2090 // D-bus set-property call is good enough to update the status. 2091 const std::string& l_collStatusProp = "Status"; 2092 2093 setCollectionStatusProperty(l_fruPath, 2094 constants::vpdCollectionInProgress); 2095 2096 // Parse VPD 2097 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath); 2098 2099 // If l_parsedVpd is pointing to std::monostate 2100 if (l_parsedVpd.index() == 0) 2101 { 2102 throw std::runtime_error( 2103 "VPD parsing failed for " + std::string(i_dbusObjPath)); 2104 } 2105 2106 // Get D-bus object map from worker class 2107 types::ObjectMap l_dbusObjectMap; 2108 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath); 2109 2110 if (l_dbusObjectMap.empty()) 2111 { 2112 throw std::runtime_error( 2113 "Failed to create D-bus object map. Single FRU VPD collection failed for " + 2114 std::string(i_dbusObjPath)); 2115 } 2116 2117 // Call PIM's Notify method 2118 if (!dbusUtility::callPIM(move(l_dbusObjectMap))) 2119 { 2120 throw std::runtime_error( 2121 "Notify PIM failed. Single FRU VPD collection failed for " + 2122 std::string(i_dbusObjPath)); 2123 } 2124 setCollectionStatusProperty(l_fruPath, 2125 constants::vpdCollectionCompleted); 2126 } 2127 catch (const std::exception& l_error) 2128 { 2129 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed); 2130 // TODO: Log PEL 2131 logging::logMessage(std::string(l_error.what())); 2132 } 2133 } 2134 2135 void Worker::setCollectionStatusProperty( 2136 const std::string& i_vpdPath, const std::string& i_value) const noexcept 2137 { 2138 try 2139 { 2140 if (i_vpdPath.empty()) 2141 { 2142 throw std::runtime_error( 2143 "Given path is empty. Can't set collection Status property"); 2144 } 2145 2146 types::PropertyMap l_timeStampMap; 2147 if (i_value == constants::vpdCollectionCompleted || 2148 i_value == constants::vpdCollectionFailed) 2149 { 2150 l_timeStampMap.emplace( 2151 "CompletedTime", 2152 types::DbusVariantType{ 2153 commonUtility::getCurrentTimeSinceEpoch()}); 2154 } 2155 else if (i_value == constants::vpdCollectionInProgress) 2156 { 2157 l_timeStampMap.emplace( 2158 "StartTime", types::DbusVariantType{ 2159 commonUtility::getCurrentTimeSinceEpoch()}); 2160 } 2161 else if (i_value == constants::vpdCollectionNotStarted) 2162 { 2163 l_timeStampMap.emplace("StartTime", 0); 2164 l_timeStampMap.emplace("CompletedTime", 0); 2165 } 2166 2167 types::ObjectMap l_objectInterfaceMap; 2168 2169 if (m_parsedJson["frus"].contains(i_vpdPath)) 2170 { 2171 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 2172 { 2173 sdbusplus::message::object_path l_fruObjectPath( 2174 l_Fru["inventoryPath"]); 2175 2176 types::PropertyMap l_propertyValueMap; 2177 l_propertyValueMap.emplace("Status", i_value); 2178 l_propertyValueMap.insert(l_timeStampMap.begin(), 2179 l_timeStampMap.end()); 2180 2181 types::InterfaceMap l_interfaces; 2182 vpdSpecificUtility::insertOrMerge( 2183 l_interfaces, constants::vpdCollectionInterface, 2184 move(l_propertyValueMap)); 2185 2186 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 2187 std::move(l_interfaces)); 2188 } 2189 } 2190 else 2191 { 2192 // consider it as an inventory path. 2193 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 2194 { 2195 throw std::runtime_error( 2196 "Invalid inventory path: " + i_vpdPath + 2197 ". Can't set collection Status property"); 2198 } 2199 2200 types::PropertyMap l_propertyValueMap; 2201 l_propertyValueMap.emplace("Status", i_value); 2202 l_propertyValueMap.insert(l_timeStampMap.begin(), 2203 l_timeStampMap.end()); 2204 2205 types::InterfaceMap l_interfaces; 2206 vpdSpecificUtility::insertOrMerge(l_interfaces, 2207 constants::vpdCollectionInterface, 2208 move(l_propertyValueMap)); 2209 2210 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 2211 } 2212 2213 // Notify PIM 2214 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 2215 { 2216 throw DbusException( 2217 std::string(__FUNCTION__) + 2218 "Call to PIM failed while setting collection Status property for path " + 2219 i_vpdPath); 2220 } 2221 } 2222 catch (const std::exception& l_ex) 2223 { 2224 EventLogger::createSyncPel( 2225 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 2226 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 2227 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 2228 } 2229 } 2230 } // namespace vpd 2231