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 const std::string pgKeywordValue{ 840 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")}; 841 842 if (!pgKeywordValue.empty()) 843 { 844 if (isCPUIOGoodOnly(pgKeywordValue)) 845 { 846 interfaces["xyz.openbmc_project.Inventory.Item"] 847 ["PrettyName"] = "IO Module"; 848 } 849 } 850 else 851 { 852 throw DataException(std::string(__FUNCTION__) + 853 "Failed to get value for keyword PG"); 854 } 855 } 856 } 857 } 858 859 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru, 860 const types::VPDMapVariant& parsedVpdMap, 861 types::InterfaceMap& interfaces) 862 { 863 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 864 { 865 for (const auto& record : singleFru["copyRecords"]) 866 { 867 const std::string& recordName = record; 868 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end()) 869 { 870 populateIPZVPDpropertyMap(interfaces, 871 (*ipzVpdMap).at(recordName), 872 constants::ipzVpdInf + recordName); 873 } 874 } 875 } 876 } 877 878 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap, 879 types::InterfaceMap& interfaces) 880 { 881 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 882 { 883 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap) 884 { 885 populateIPZVPDpropertyMap(interfaces, kwdValueMap, 886 constants::ipzVpdInf + recordName); 887 } 888 } 889 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 890 { 891 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces); 892 } 893 894 if (m_parsedJson.contains("commonInterfaces")) 895 { 896 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces, 897 parsedVpdMap); 898 } 899 } 900 901 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru, 902 const types::VPDMapVariant& parsedVpdMap) 903 { 904 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 905 { 906 auto itrToRec = (*ipzVPDMap).find("VINI"); 907 if (itrToRec == (*ipzVPDMap).end()) 908 { 909 return false; 910 } 911 912 std::string ccinFromVpd{ 913 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")}; 914 915 if (ccinFromVpd.empty()) 916 { 917 return false; 918 } 919 920 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(), 921 ::toupper); 922 923 std::vector<std::string> ccinList; 924 for (std::string ccin : singleFru["ccin"]) 925 { 926 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper); 927 ccinList.push_back(ccin); 928 } 929 930 if (ccinList.empty()) 931 { 932 return false; 933 } 934 935 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) == 936 ccinList.end()) 937 { 938 return false; 939 } 940 } 941 return true; 942 } 943 944 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath, 945 types::InterfaceMap& io_interfaces) 946 { 947 if (!dbusUtility::isChassisPowerOn()) 948 { 949 std::array<const char*, 1> l_operationalStatusInf = { 950 constants::operationalStatusInf}; 951 952 auto mapperObjectMap = dbusUtility::getObjectMap( 953 i_inventoryObjPath, l_operationalStatusInf); 954 955 // If the object has been found. Check if it is under PIM. 956 if (mapperObjectMap.size() != 0) 957 { 958 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 959 { 960 if (l_serviceName == constants::pimServiceName) 961 { 962 // The object is already under PIM. No need to process 963 // again. Retain the old value. 964 return; 965 } 966 } 967 } 968 969 // Implies value is not there in D-Bus. Populate it with default 970 // value "true". 971 types::PropertyMap l_functionalProp; 972 l_functionalProp.emplace("Functional", true); 973 vpdSpecificUtility::insertOrMerge(io_interfaces, 974 constants::operationalStatusInf, 975 move(l_functionalProp)); 976 } 977 978 // if chassis is power on. Functional property should be there on D-Bus. 979 // Don't process. 980 return; 981 } 982 983 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath, 984 types::InterfaceMap& io_interfaces) 985 { 986 if (!dbusUtility::isChassisPowerOn()) 987 { 988 std::array<const char*, 1> l_enableInf = {constants::enableInf}; 989 990 auto mapperObjectMap = 991 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 992 993 // If the object has been found. Check if it is under PIM. 994 if (mapperObjectMap.size() != 0) 995 { 996 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 997 { 998 if (l_serviceName == constants::pimServiceName) 999 { 1000 // The object is already under PIM. No need to process 1001 // again. Retain the old value. 1002 return; 1003 } 1004 } 1005 } 1006 1007 // Implies value is not there in D-Bus. Populate it with default 1008 // value "true". 1009 types::PropertyMap l_enabledProp; 1010 l_enabledProp.emplace("Enabled", true); 1011 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf, 1012 move(l_enabledProp)); 1013 } 1014 1015 // if chassis is power on. Enabled property should be there on D-Bus. 1016 // Don't process. 1017 return; 1018 } 1019 1020 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap, 1021 types::ObjectMap& objectInterfaceMap, 1022 const std::string& vpdFilePath) 1023 { 1024 if (vpdFilePath.empty()) 1025 { 1026 throw std::runtime_error( 1027 std::string(__FUNCTION__) + 1028 "Invalid parameter passed to populateDbus API."); 1029 } 1030 1031 // JSON config is mandatory for processing of "if". Add "else" for any 1032 // processing without config JSON. 1033 if (!m_parsedJson.empty()) 1034 { 1035 types::InterfaceMap interfaces; 1036 1037 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath]) 1038 { 1039 const auto& inventoryPath = aFru["inventoryPath"]; 1040 sdbusplus::message::object_path fruObjectPath(inventoryPath); 1041 if (aFru.contains("ccin")) 1042 { 1043 if (!processFruWithCCIN(aFru, parsedVpdMap)) 1044 { 1045 continue; 1046 } 1047 } 1048 1049 if (aFru.value("inherit", true)) 1050 { 1051 processInheritFlag(parsedVpdMap, interfaces); 1052 } 1053 1054 // If specific record needs to be copied. 1055 if (aFru.contains("copyRecords")) 1056 { 1057 processCopyRecordFlag(aFru, parsedVpdMap, interfaces); 1058 } 1059 1060 if (aFru.contains("extraInterfaces")) 1061 { 1062 // Process extra interfaces w.r.t a FRU. 1063 processExtraInterfaces(aFru, interfaces, parsedVpdMap); 1064 } 1065 1066 // Process FRUS which are embedded in the parent FRU and whose VPD 1067 // will be synthesized. 1068 if ((aFru.value("embedded", true)) && 1069 (!aFru.value("synthesized", false))) 1070 { 1071 processEmbeddedAndSynthesizedFrus(aFru, interfaces); 1072 } 1073 1074 processFunctionalProperty(inventoryPath, interfaces); 1075 processEnabledProperty(inventoryPath, interfaces); 1076 1077 objectInterfaceMap.emplace(std::move(fruObjectPath), 1078 std::move(interfaces)); 1079 } 1080 } 1081 } 1082 1083 std::string Worker::createAssetTagString( 1084 const types::VPDMapVariant& i_parsedVpdMap) 1085 { 1086 std::string l_assetTag; 1087 1088 // system VPD will be in IPZ format. 1089 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap)) 1090 { 1091 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS); 1092 if (l_itrToVsys != (*l_parsedVpdMap).end()) 1093 { 1094 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal( 1095 l_itrToVsys->second, constants::kwdTM)}; 1096 1097 if (l_tmKwdValue.empty()) 1098 { 1099 throw std::runtime_error( 1100 std::string("Failed to get value for keyword [") + 1101 constants::kwdTM + 1102 std::string("] while creating Asset tag.")); 1103 } 1104 1105 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal( 1106 l_itrToVsys->second, constants::kwdSE)}; 1107 1108 if (l_seKwdValue.empty()) 1109 { 1110 throw std::runtime_error( 1111 std::string("Failed to get value for keyword [") + 1112 constants::kwdSE + 1113 std::string("] while creating Asset tag.")); 1114 } 1115 1116 l_assetTag = std::string{"Server-"} + l_tmKwdValue + 1117 std::string{"-"} + l_seKwdValue; 1118 } 1119 else 1120 { 1121 throw std::runtime_error( 1122 "VSYS record not found in parsed VPD map to create Asset tag."); 1123 } 1124 } 1125 else 1126 { 1127 throw std::runtime_error( 1128 "Invalid VPD type recieved to create Asset tag."); 1129 } 1130 1131 return l_assetTag; 1132 } 1133 1134 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap) 1135 { 1136 types::ObjectMap objectInterfaceMap; 1137 1138 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 1139 { 1140 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH); 1141 1142 try 1143 { 1144 if (m_isFactoryResetDone) 1145 { 1146 const auto& l_assetTag = createAssetTagString(parsedVpdMap); 1147 1148 auto l_itrToSystemPath = objectInterfaceMap.find( 1149 sdbusplus::message::object_path(constants::systemInvPath)); 1150 if (l_itrToSystemPath == objectInterfaceMap.end()) 1151 { 1152 throw std::runtime_error( 1153 "Asset tag update failed. System Path not found in object map."); 1154 } 1155 1156 types::PropertyMap l_assetTagProperty; 1157 l_assetTagProperty.emplace("AssetTag", l_assetTag); 1158 1159 (l_itrToSystemPath->second) 1160 .emplace(constants::assetTagInf, 1161 std::move(l_assetTagProperty)); 1162 } 1163 } 1164 catch (const std::exception& l_ex) 1165 { 1166 EventLogger::createSyncPel( 1167 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1168 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1169 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1170 } 1171 1172 // Notify PIM 1173 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1174 { 1175 throw std::runtime_error("Call to PIM failed for system VPD"); 1176 } 1177 } 1178 else 1179 { 1180 throw DataException("Invalid format of parsed VPD map."); 1181 } 1182 } 1183 1184 bool Worker::processPreAction(const std::string& i_vpdFilePath, 1185 const std::string& i_flagToProcess, 1186 uint16_t& i_errCode) 1187 { 1188 if (i_vpdFilePath.empty() || i_flagToProcess.empty()) 1189 { 1190 i_errCode = error_code::INVALID_INPUT_PARAMETER; 1191 return false; 1192 } 1193 1194 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction", 1195 i_vpdFilePath, i_flagToProcess, 1196 i_errCode)) && 1197 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS)) 1198 { 1199 // TODO: Need a way to delete inventory object from Dbus and persisted 1200 // data section in case any FRU is not present or there is any 1201 // problem in collecting it. Once it has been deleted, it can be 1202 // re-created in the flow of priming the inventory. This needs to be 1203 // done either here or in the exception section of "parseAndPublishVPD" 1204 // API. Any failure in the process of collecting FRU will land up in the 1205 // excpetion of "parseAndPublishVPD". 1206 1207 // If the FRU is not there, clear the VINI/CCIN data. 1208 // Enity manager probes for this keyword to look for this 1209 // FRU, now if the data is persistent on BMC and FRU is 1210 // removed this can lead to ambiguity. Hence clearing this 1211 // Keyword if FRU is absent. 1212 const auto& inventoryPath = 1213 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath", 1214 ""); 1215 1216 if (!inventoryPath.empty()) 1217 { 1218 types::ObjectMap l_pimObjMap{ 1219 {inventoryPath, 1220 {{constants::kwdVpdInf, 1221 {{constants::kwdCCIN, types::BinaryVector{}}}}}}}; 1222 1223 if (!dbusUtility::callPIM(std::move(l_pimObjMap))) 1224 { 1225 logging::logMessage( 1226 "Call to PIM failed for file " + i_vpdFilePath); 1227 } 1228 } 1229 else 1230 { 1231 logging::logMessage( 1232 "Inventory path is empty in Json for file " + i_vpdFilePath); 1233 } 1234 1235 return false; 1236 } 1237 return true; 1238 } 1239 1240 bool Worker::processPostAction( 1241 const std::string& i_vpdFruPath, const std::string& i_flagToProcess, 1242 const std::optional<types::VPDMapVariant> i_parsedVpd) 1243 { 1244 if (i_vpdFruPath.empty() || i_flagToProcess.empty()) 1245 { 1246 logging::logMessage( 1247 "Invalid input parameter. Abort processing post action"); 1248 return false; 1249 } 1250 1251 // Check if post action tag is to be triggered in the flow of collection 1252 // based on some CCIN value? 1253 if (m_parsedJson["frus"][i_vpdFruPath] 1254 .at(0)["postAction"][i_flagToProcess] 1255 .contains("ccin")) 1256 { 1257 if (!i_parsedVpd.has_value()) 1258 { 1259 logging::logMessage("Empty VPD Map"); 1260 return false; 1261 } 1262 1263 // CCIN match is required to process post action for this FRU as it 1264 // contains the flag. 1265 if (!vpdSpecificUtility::findCcinInVpd( 1266 m_parsedJson["frus"][i_vpdFruPath].at( 1267 0)["postAction"]["collection"], 1268 i_parsedVpd.value())) 1269 { 1270 // If CCIN is not found, implies post action processing is not 1271 // required for this FRU. Let the flow continue. 1272 return true; 1273 } 1274 } 1275 1276 uint16_t l_errCode = 0; 1277 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction", 1278 i_vpdFruPath, i_flagToProcess, 1279 l_errCode)) 1280 { 1281 logging::logMessage( 1282 "Execution of post action failed for path: " + i_vpdFruPath + 1283 " . Reason: " + commonUtility::getErrCodeMsg(l_errCode)); 1284 1285 // If post action was required and failed only in that case return 1286 // false. In all other case post action is considered passed. 1287 return false; 1288 } 1289 1290 return true; 1291 } 1292 1293 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath) 1294 { 1295 try 1296 { 1297 uint16_t l_errCode = 0; 1298 1299 if (i_vpdFilePath.empty()) 1300 { 1301 throw std::runtime_error( 1302 std::string(__FUNCTION__) + 1303 " Empty VPD file path passed. Abort processing"); 1304 } 1305 1306 bool isPreActionRequired = false; 1307 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1308 "preAction", "collection", l_errCode)) 1309 { 1310 l_errCode = 0; 1311 isPreActionRequired = true; 1312 if (!processPreAction(i_vpdFilePath, "collection", l_errCode)) 1313 { 1314 if (l_errCode == error_code::DEVICE_NOT_PRESENT) 1315 { 1316 logging::logMessage( 1317 commonUtility::getErrCodeMsg(l_errCode) + 1318 i_vpdFilePath); 1319 // Presence pin has been read successfully and has been read 1320 // as false, so this is not a failure case, hence returning 1321 // empty variant so that pre action is not marked as failed. 1322 return types::VPDMapVariant{}; 1323 } 1324 throw std::runtime_error( 1325 std::string(__FUNCTION__) + 1326 " Pre-Action failed with error: " + 1327 commonUtility::getErrCodeMsg(l_errCode)); 1328 } 1329 } 1330 else if (l_errCode) 1331 { 1332 logging::logMessage( 1333 "Failed to check if pre action required for FRU [" + 1334 i_vpdFilePath + 1335 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1336 } 1337 1338 if (!std::filesystem::exists(i_vpdFilePath)) 1339 { 1340 if (isPreActionRequired) 1341 { 1342 throw std::runtime_error( 1343 std::string(__FUNCTION__) + " Could not find file path " + 1344 i_vpdFilePath + "Skipping parser trigger for the EEPROM"); 1345 } 1346 return types::VPDMapVariant{}; 1347 } 1348 1349 std::shared_ptr<Parser> vpdParser = 1350 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson); 1351 1352 types::VPDMapVariant l_parsedVpd = vpdParser->parse(); 1353 1354 // Before returning, as collection is over, check if FRU qualifies for 1355 // any post action in the flow of collection. 1356 // Note: Don't change the order, post action needs to be processed only 1357 // after collection for FRU is successfully done. 1358 l_errCode = 0; 1359 1360 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1361 "postAction", "collection", 1362 l_errCode)) 1363 { 1364 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd)) 1365 { 1366 // Post action was required but failed while executing. 1367 // Behaviour can be undefined. 1368 EventLogger::createSyncPel( 1369 types::ErrorType::InternalFailure, 1370 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0, 1371 std::string("Required post action failed for path [" + 1372 i_vpdFilePath + "]"), 1373 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1374 } 1375 } 1376 else if (l_errCode) 1377 { 1378 logging::logMessage( 1379 "Error while checking if post action required for FRU [" + 1380 i_vpdFilePath + 1381 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1382 } 1383 1384 return l_parsedVpd; 1385 } 1386 catch (std::exception& l_ex) 1387 { 1388 uint16_t l_errCode = 0; 1389 std::string l_exMsg{ 1390 std::string(__FUNCTION__) + " : VPD parsing failed for " + 1391 i_vpdFilePath + " due to error: " + l_ex.what()}; 1392 1393 // If post fail action is required, execute it. 1394 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1395 "postFailAction", "collection", 1396 l_errCode)) 1397 { 1398 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath, 1399 "collection", l_errCode)) 1400 { 1401 l_exMsg += ". Post fail action also failed. Error : " + 1402 commonUtility::getErrCodeMsg(l_errCode) + 1403 " Aborting collection for this FRU."; 1404 } 1405 } 1406 else if (l_errCode) 1407 { 1408 l_exMsg += 1409 ". Failed to check if post fail action required, error : " + 1410 commonUtility::getErrCodeMsg(l_errCode); 1411 } 1412 1413 if (typeid(l_ex) == typeid(DataException)) 1414 { 1415 throw DataException(l_exMsg); 1416 } 1417 else if (typeid(l_ex) == typeid(EccException)) 1418 { 1419 throw EccException(l_exMsg); 1420 } 1421 throw std::runtime_error(l_exMsg); 1422 } 1423 } 1424 1425 std::tuple<bool, std::string> Worker::parseAndPublishVPD( 1426 const std::string& i_vpdFilePath) 1427 { 1428 std::string l_inventoryPath{}; 1429 1430 try 1431 { 1432 m_semaphore.acquire(); 1433 1434 // Thread launched. 1435 m_mutex.lock(); 1436 m_activeCollectionThreadCount++; 1437 m_mutex.unlock(); 1438 1439 setCollectionStatusProperty(i_vpdFilePath, 1440 constants::vpdCollectionInProgress); 1441 1442 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath); 1443 if (!std::holds_alternative<std::monostate>(parsedVpdMap)) 1444 { 1445 types::ObjectMap objectInterfaceMap; 1446 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath); 1447 1448 // Notify PIM 1449 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1450 { 1451 throw std::runtime_error( 1452 std::string(__FUNCTION__) + 1453 "Call to PIM failed while publishing VPD."); 1454 } 1455 } 1456 else 1457 { 1458 logging::logMessage("Empty parsedVpdMap recieved for path [" + 1459 i_vpdFilePath + "]. Check PEL for reason."); 1460 1461 // As empty parsedVpdMap recieved for some reason, but still 1462 // considered VPD collection is completed. Hence FRU collection 1463 // Status will be set as completed. 1464 } 1465 } 1466 catch (const std::exception& ex) 1467 { 1468 setCollectionStatusProperty(i_vpdFilePath, 1469 constants::vpdCollectionFailed); 1470 1471 // handle all the exceptions internally. Return only true/false 1472 // based on status of execution. 1473 if (typeid(ex) == std::type_index(typeid(DataException))) 1474 { 1475 uint16_t l_errCode = 0; 1476 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip 1477 // logging error for these cases. 1478 if (vpdSpecificUtility::isPass1Planar()) 1479 { 1480 std::string l_invPath = 1481 jsonUtility::getInventoryObjPathFromJson( 1482 m_parsedJson, i_vpdFilePath, l_errCode); 1483 1484 if (l_errCode != 0) 1485 { 1486 logging::logMessage( 1487 "Failed to get inventory object path from JSON for FRU [" + 1488 i_vpdFilePath + 1489 "], error: " + commonUtility::getErrCodeMsg(l_errCode)); 1490 } 1491 1492 const std::string& l_invPathLeafValue = 1493 sdbusplus::message::object_path(l_invPath).filename(); 1494 1495 if ((l_invPathLeafValue.find("pcie_card", 0) != 1496 std::string::npos)) 1497 { 1498 // skip logging any PEL for PCIe cards on pass 1 planar. 1499 return std::make_tuple(false, i_vpdFilePath); 1500 } 1501 } 1502 } 1503 1504 EventLogger::createSyncPel( 1505 EventLogger::getErrorType(ex), 1506 (typeid(ex) == typeid(DataException)) || 1507 (typeid(ex) == typeid(EccException)) 1508 ? types::SeverityType::Warning 1509 : types::SeverityType::Informational, 1510 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex), 1511 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1512 1513 // TODO: Figure out a way to clear data in case of any failure at 1514 // runtime. 1515 1516 // set present property to false for any error case. In future this will 1517 // be replaced by presence logic. 1518 // Update Present property for this FRU only if we handle Present 1519 // property for the FRU. 1520 if (isPresentPropertyHandlingRequired( 1521 m_parsedJson["frus"][i_vpdFilePath].at(0))) 1522 { 1523 setPresentProperty(i_vpdFilePath, false); 1524 } 1525 1526 m_semaphore.release(); 1527 return std::make_tuple(false, i_vpdFilePath); 1528 } 1529 1530 setCollectionStatusProperty(i_vpdFilePath, 1531 constants::vpdCollectionCompleted); 1532 m_semaphore.release(); 1533 return std::make_tuple(true, i_vpdFilePath); 1534 } 1535 1536 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath) 1537 { 1538 if (i_vpdFilePath.empty()) 1539 { 1540 return true; 1541 } 1542 1543 // skip processing of system VPD again as it has been already collected. 1544 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH) 1545 { 1546 return true; 1547 } 1548 1549 if (dbusUtility::isChassisPowerOn()) 1550 { 1551 // If chassis is powered on, skip collecting FRUs which are 1552 // powerOffOnly. 1553 1554 uint16_t l_errCode = 0; 1555 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath, 1556 l_errCode)) 1557 { 1558 return true; 1559 } 1560 else if (l_errCode) 1561 { 1562 logging::logMessage( 1563 "Failed to check if FRU is power off only for FRU [" + 1564 i_vpdFilePath + 1565 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1566 } 1567 1568 l_errCode = 0; 1569 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson( 1570 m_parsedJson, i_vpdFilePath, l_errCode); 1571 1572 if (l_errCode) 1573 { 1574 logging::logMessage( 1575 "Failed to get inventory path from JSON for FRU [" + 1576 i_vpdFilePath + 1577 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1578 1579 return false; 1580 } 1581 1582 const std::string& l_invPathLeafValue = 1583 sdbusplus::message::object_path(l_invPath).filename(); 1584 1585 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos)) 1586 { 1587 return true; 1588 } 1589 } 1590 1591 return false; 1592 } 1593 1594 void Worker::collectFrusFromJson() 1595 { 1596 // A parsed JSON file should be present to pick FRUs EEPROM paths 1597 if (m_parsedJson.empty()) 1598 { 1599 throw JsonException( 1600 std::string(__FUNCTION__) + 1601 ": Config JSON is mandatory for processing of FRUs through this API.", 1602 m_configJsonPath); 1603 } 1604 1605 const nlohmann::json& listOfFrus = 1606 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>(); 1607 1608 for (const auto& itemFRUS : listOfFrus.items()) 1609 { 1610 const std::string& vpdFilePath = itemFRUS.key(); 1611 1612 if (skipPathForCollection(vpdFilePath)) 1613 { 1614 continue; 1615 } 1616 1617 try 1618 { 1619 std::thread{[vpdFilePath, this]() { 1620 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath); 1621 1622 m_mutex.lock(); 1623 m_activeCollectionThreadCount--; 1624 m_mutex.unlock(); 1625 1626 if (!m_activeCollectionThreadCount) 1627 { 1628 m_isAllFruCollected = true; 1629 } 1630 }}.detach(); 1631 } 1632 catch (const std::exception& l_ex) 1633 { 1634 // add vpdFilePath(EEPROM path) to failed list 1635 m_failedEepromPaths.push_front(vpdFilePath); 1636 } 1637 } 1638 } 1639 1640 // ToDo: Move the API under IBM_SYSTEM 1641 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap) 1642 { 1643 try 1644 { 1645 uint16_t l_errCode = 0; 1646 std::string l_backupAndRestoreCfgFilePath = 1647 m_parsedJson.value("backupRestoreConfigPath", ""); 1648 1649 nlohmann::json l_backupAndRestoreCfgJsonObj = 1650 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath, 1651 l_errCode); 1652 1653 if (l_errCode) 1654 { 1655 throw JsonException( 1656 "JSON parsing failed for file [ " + 1657 l_backupAndRestoreCfgFilePath + 1658 " ], error : " + commonUtility::getErrCodeMsg(l_errCode), 1659 l_backupAndRestoreCfgFilePath); 1660 } 1661 1662 // check if either of "source" or "destination" has inventory path. 1663 // this indicates that this sytem has System VPD on hardware 1664 // and other copy on D-Bus (BMC cache). 1665 if (!l_backupAndRestoreCfgJsonObj.empty() && 1666 ((l_backupAndRestoreCfgJsonObj.contains("source") && 1667 l_backupAndRestoreCfgJsonObj["source"].contains( 1668 "inventoryPath")) || 1669 (l_backupAndRestoreCfgJsonObj.contains("destination") && 1670 l_backupAndRestoreCfgJsonObj["destination"].contains( 1671 "inventoryPath")))) 1672 { 1673 BackupAndRestore l_backupAndRestoreObj(m_parsedJson); 1674 auto [l_srcVpdVariant, 1675 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore(); 1676 1677 // ToDo: Revisit is this check is required or not. 1678 if (auto l_srcVpdMap = 1679 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant); 1680 l_srcVpdMap && !(*l_srcVpdMap).empty()) 1681 { 1682 io_srcVpdMap = std::move(l_srcVpdVariant); 1683 } 1684 } 1685 } 1686 catch (const std::exception& l_ex) 1687 { 1688 EventLogger::createSyncPel( 1689 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1690 __FILE__, __FUNCTION__, 0, 1691 std::string( 1692 "Exception caught while backup and restore VPD keyword's.") + 1693 EventLogger::getErrorMsg(l_ex), 1694 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1695 } 1696 } 1697 1698 void Worker::deleteFruVpd(const std::string& i_dbusObjPath) 1699 { 1700 if (i_dbusObjPath.empty()) 1701 { 1702 throw std::runtime_error("Given DBus object path is empty."); 1703 } 1704 1705 uint16_t l_errCode = 0; 1706 const std::string& l_fruPath = 1707 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode); 1708 1709 if (l_errCode) 1710 { 1711 logging::logMessage( 1712 "Failed to get FRU path for inventory path [" + i_dbusObjPath + 1713 "], error : " + commonUtility::getErrCodeMsg(l_errCode) + 1714 " Aborting FRU VPD deletion."); 1715 return; 1716 } 1717 1718 try 1719 { 1720 auto l_presentPropValue = dbusUtility::readDbusProperty( 1721 constants::pimServiceName, i_dbusObjPath, 1722 constants::inventoryItemInf, "Present"); 1723 1724 if (auto l_value = std::get_if<bool>(&l_presentPropValue)) 1725 { 1726 uint16_t l_errCode = 0; 1727 // check if FRU's Present property is handled by vpd-manager 1728 const auto& l_isFruPresenceHandled = 1729 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath, 1730 l_errCode); 1731 1732 if (l_errCode) 1733 { 1734 throw std::runtime_error( 1735 "Failed to check if FRU's presence is handled, reason: " + 1736 commonUtility::getErrCodeMsg(l_errCode)); 1737 } 1738 1739 if (!(*l_value) && l_isFruPresenceHandled) 1740 { 1741 throw std::runtime_error("Given FRU is not present"); 1742 } 1743 else if (*l_value && !l_isFruPresenceHandled) 1744 { 1745 throw std::runtime_error( 1746 "Given FRU is present and its presence is not handled by vpd-manager."); 1747 } 1748 else 1749 { 1750 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1751 "preAction", "deletion", 1752 l_errCode)) 1753 { 1754 if (!processPreAction(l_fruPath, "deletion", l_errCode)) 1755 { 1756 std::string l_msg = "Pre action failed"; 1757 if (l_errCode) 1758 { 1759 l_msg += " Reason: " + 1760 commonUtility::getErrCodeMsg(l_errCode); 1761 } 1762 throw std::runtime_error(l_msg); 1763 } 1764 } 1765 else if (l_errCode) 1766 { 1767 logging::logMessage( 1768 "Failed to check if pre action required for FRU [" + 1769 l_fruPath + "], error : " + 1770 commonUtility::getErrCodeMsg(l_errCode)); 1771 } 1772 1773 std::vector<std::string> l_interfaceList{ 1774 constants::operationalStatusInf}; 1775 1776 types::MapperGetSubTree l_subTreeMap = 1777 dbusUtility::getObjectSubTree(i_dbusObjPath, 0, 1778 l_interfaceList); 1779 1780 types::ObjectMap l_objectMap; 1781 1782 // Updates VPD specific interfaces property value under PIM for 1783 // sub FRUs. 1784 for (const auto& [l_objectPath, l_serviceInterfaceMap] : 1785 l_subTreeMap) 1786 { 1787 types::InterfaceMap l_interfaceMap; 1788 vpdSpecificUtility::resetDataUnderPIM(l_objectPath, 1789 l_interfaceMap); 1790 l_objectMap.emplace(l_objectPath, 1791 std::move(l_interfaceMap)); 1792 } 1793 1794 types::InterfaceMap l_interfaceMap; 1795 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath, 1796 l_interfaceMap); 1797 1798 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap)); 1799 1800 if (!dbusUtility::callPIM(std::move(l_objectMap))) 1801 { 1802 throw std::runtime_error("Call to PIM failed."); 1803 } 1804 1805 l_errCode = 0; 1806 1807 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1808 "postAction", "deletion", 1809 l_errCode)) 1810 { 1811 if (!processPostAction(l_fruPath, "deletion")) 1812 { 1813 throw std::runtime_error("Post action failed"); 1814 } 1815 } 1816 else if (l_errCode) 1817 { 1818 logging::logMessage( 1819 "Failed to check if post action required during deletion for FRU [" + 1820 l_fruPath + "], error : " + 1821 commonUtility::getErrCodeMsg(l_errCode)); 1822 } 1823 } 1824 } 1825 else 1826 { 1827 logging::logMessage( 1828 "Can't process delete VPD for FRU [" + i_dbusObjPath + 1829 "] as unable to read present property"); 1830 return; 1831 } 1832 1833 logging::logMessage( 1834 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath); 1835 } 1836 catch (const std::exception& l_ex) 1837 { 1838 uint16_t l_errCode = 0; 1839 std::string l_errMsg = 1840 "Failed to delete VPD for FRU : " + i_dbusObjPath + 1841 " error: " + std::string(l_ex.what()); 1842 1843 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1844 "postFailAction", "deletion", 1845 l_errCode)) 1846 { 1847 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath, 1848 "deletion", l_errCode)) 1849 { 1850 l_errMsg += ". Post fail action also failed, error : " + 1851 commonUtility::getErrCodeMsg(l_errCode); 1852 } 1853 } 1854 else if (l_errCode) 1855 { 1856 l_errMsg += 1857 ". Failed to check if post fail action required, error : " + 1858 commonUtility::getErrCodeMsg(l_errCode); 1859 } 1860 1861 logging::logMessage(l_errMsg); 1862 } 1863 } 1864 1865 void Worker::setPresentProperty(const std::string& i_vpdPath, 1866 const bool& i_value) 1867 { 1868 try 1869 { 1870 if (i_vpdPath.empty()) 1871 { 1872 throw std::runtime_error( 1873 "Path is empty. Can't set present property"); 1874 } 1875 1876 types::ObjectMap l_objectInterfaceMap; 1877 1878 // If the given path is EEPROM path. 1879 if (m_parsedJson["frus"].contains(i_vpdPath)) 1880 { 1881 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 1882 { 1883 sdbusplus::message::object_path l_fruObjectPath( 1884 l_Fru["inventoryPath"]); 1885 1886 types::PropertyMap l_propertyValueMap; 1887 l_propertyValueMap.emplace("Present", i_value); 1888 1889 types::InterfaceMap l_interfaces; 1890 vpdSpecificUtility::insertOrMerge(l_interfaces, 1891 constants::inventoryItemInf, 1892 move(l_propertyValueMap)); 1893 1894 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 1895 std::move(l_interfaces)); 1896 } 1897 } 1898 else 1899 { 1900 // consider it as an inventory path. 1901 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 1902 { 1903 throw std::runtime_error( 1904 "Invalid inventory path: " + i_vpdPath); 1905 } 1906 1907 types::PropertyMap l_propertyValueMap; 1908 l_propertyValueMap.emplace("Present", i_value); 1909 1910 types::InterfaceMap l_interfaces; 1911 vpdSpecificUtility::insertOrMerge(l_interfaces, 1912 constants::inventoryItemInf, 1913 move(l_propertyValueMap)); 1914 1915 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 1916 } 1917 1918 // Notify PIM 1919 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1920 { 1921 throw DbusException( 1922 std::string(__FUNCTION__) + 1923 "Call to PIM failed while setting present property for path " + 1924 i_vpdPath); 1925 } 1926 } 1927 catch (const std::exception& l_ex) 1928 { 1929 EventLogger::createSyncPel( 1930 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1931 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1932 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1933 } 1934 } 1935 1936 void Worker::performVpdRecollection() 1937 { 1938 try 1939 { 1940 // Check if system config JSON is present 1941 if (m_parsedJson.empty()) 1942 { 1943 throw std::runtime_error( 1944 "System config json object is empty, can't process recollection."); 1945 } 1946 1947 uint16_t l_errCode = 0; 1948 const auto& l_frusReplaceableAtStandby = 1949 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson, 1950 l_errCode); 1951 1952 if (l_errCode) 1953 { 1954 logging::logMessage( 1955 "Failed to get list of FRUs replaceable at runtime, error : " + 1956 commonUtility::getErrCodeMsg(l_errCode)); 1957 return; 1958 } 1959 1960 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby) 1961 { 1962 // ToDo: Add some logic/trace to know the flow to 1963 // collectSingleFruVpd has been directed via 1964 // performVpdRecollection. 1965 collectSingleFruVpd(l_fruInventoryPath); 1966 } 1967 return; 1968 } 1969 1970 catch (const std::exception& l_ex) 1971 { 1972 // TODO Log PEL 1973 logging::logMessage( 1974 "VPD recollection failed with error: " + std::string(l_ex.what())); 1975 } 1976 } 1977 1978 void Worker::collectSingleFruVpd( 1979 const sdbusplus::message::object_path& i_dbusObjPath) 1980 { 1981 std::string l_fruPath{}; 1982 uint16_t l_errCode = 0; 1983 1984 try 1985 { 1986 // Check if system config JSON is present 1987 if (m_parsedJson.empty()) 1988 { 1989 logging::logMessage( 1990 "System config JSON object not present. Single FRU VPD collection is not performed for " + 1991 std::string(i_dbusObjPath)); 1992 return; 1993 } 1994 1995 // Get FRU path for the given D-bus object path from JSON 1996 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, 1997 l_errCode); 1998 1999 if (l_fruPath.empty()) 2000 { 2001 if (l_errCode) 2002 { 2003 logging::logMessage( 2004 "Failed to get FRU path for [" + 2005 std::string(i_dbusObjPath) + 2006 "], error : " + commonUtility::getErrCodeMsg(l_errCode) + 2007 " Aborting single FRU VPD collection."); 2008 return; 2009 } 2010 2011 logging::logMessage( 2012 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " + 2013 std::string(i_dbusObjPath)); 2014 return; 2015 } 2016 2017 // Check if host is up and running 2018 if (dbusUtility::isHostRunning()) 2019 { 2020 uint16_t l_errCode = 0; 2021 bool isFruReplaceableAtRuntime = 2022 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath, 2023 l_errCode); 2024 2025 if (l_errCode) 2026 { 2027 logging::logMessage( 2028 "Failed to check if FRU is replaceable at runtime for FRU : [" + 2029 std::string(i_dbusObjPath) + 2030 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2031 return; 2032 } 2033 2034 if (!isFruReplaceableAtRuntime) 2035 { 2036 logging::logMessage( 2037 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " + 2038 std::string(i_dbusObjPath)); 2039 return; 2040 } 2041 } 2042 else if (dbusUtility::isBMCReady()) 2043 { 2044 uint16_t l_errCode = 0; 2045 bool isFruReplaceableAtStandby = 2046 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath, 2047 l_errCode); 2048 2049 if (l_errCode) 2050 { 2051 logging::logMessage( 2052 "Error while checking if FRU is replaceable at standby for FRU [" + 2053 std::string(i_dbusObjPath) + 2054 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2055 } 2056 2057 l_errCode = 0; 2058 bool isFruReplaceableAtRuntime = 2059 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath, 2060 l_errCode); 2061 2062 if (l_errCode) 2063 { 2064 logging::logMessage( 2065 "Failed to check if FRU is replaceable at runtime for FRU : [" + 2066 std::string(i_dbusObjPath) + 2067 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2068 return; 2069 } 2070 2071 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime)) 2072 { 2073 logging::logMessage( 2074 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " + 2075 std::string(i_dbusObjPath)); 2076 return; 2077 } 2078 } 2079 2080 // Set collection Status as InProgress. Since it's an intermediate state 2081 // D-bus set-property call is good enough to update the status. 2082 const std::string& l_collStatusProp = "Status"; 2083 2084 setCollectionStatusProperty(l_fruPath, 2085 constants::vpdCollectionInProgress); 2086 2087 // Parse VPD 2088 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath); 2089 2090 // If l_parsedVpd is pointing to std::monostate 2091 if (l_parsedVpd.index() == 0) 2092 { 2093 throw std::runtime_error( 2094 "VPD parsing failed for " + std::string(i_dbusObjPath)); 2095 } 2096 2097 // Get D-bus object map from worker class 2098 types::ObjectMap l_dbusObjectMap; 2099 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath); 2100 2101 if (l_dbusObjectMap.empty()) 2102 { 2103 throw std::runtime_error( 2104 "Failed to create D-bus object map. Single FRU VPD collection failed for " + 2105 std::string(i_dbusObjPath)); 2106 } 2107 2108 // Call PIM's Notify method 2109 if (!dbusUtility::callPIM(move(l_dbusObjectMap))) 2110 { 2111 throw std::runtime_error( 2112 "Notify PIM failed. Single FRU VPD collection failed for " + 2113 std::string(i_dbusObjPath)); 2114 } 2115 setCollectionStatusProperty(l_fruPath, 2116 constants::vpdCollectionCompleted); 2117 } 2118 catch (const std::exception& l_error) 2119 { 2120 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed); 2121 // TODO: Log PEL 2122 logging::logMessage(std::string(l_error.what())); 2123 } 2124 } 2125 2126 void Worker::setCollectionStatusProperty( 2127 const std::string& i_vpdPath, const std::string& i_value) const noexcept 2128 { 2129 try 2130 { 2131 if (i_vpdPath.empty()) 2132 { 2133 throw std::runtime_error( 2134 "Given path is empty. Can't set collection Status property"); 2135 } 2136 2137 types::PropertyMap l_timeStampMap; 2138 if (i_value == constants::vpdCollectionCompleted || 2139 i_value == constants::vpdCollectionFailed) 2140 { 2141 l_timeStampMap.emplace( 2142 "CompletedTime", 2143 types::DbusVariantType{ 2144 commonUtility::getCurrentTimeSinceEpoch()}); 2145 } 2146 else if (i_value == constants::vpdCollectionInProgress) 2147 { 2148 l_timeStampMap.emplace( 2149 "StartTime", types::DbusVariantType{ 2150 commonUtility::getCurrentTimeSinceEpoch()}); 2151 } 2152 else if (i_value == constants::vpdCollectionNotStarted) 2153 { 2154 l_timeStampMap.emplace("StartTime", 0); 2155 l_timeStampMap.emplace("CompletedTime", 0); 2156 } 2157 2158 types::ObjectMap l_objectInterfaceMap; 2159 2160 if (m_parsedJson["frus"].contains(i_vpdPath)) 2161 { 2162 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 2163 { 2164 sdbusplus::message::object_path l_fruObjectPath( 2165 l_Fru["inventoryPath"]); 2166 2167 types::PropertyMap l_propertyValueMap; 2168 l_propertyValueMap.emplace("Status", i_value); 2169 l_propertyValueMap.insert(l_timeStampMap.begin(), 2170 l_timeStampMap.end()); 2171 2172 types::InterfaceMap l_interfaces; 2173 vpdSpecificUtility::insertOrMerge( 2174 l_interfaces, constants::vpdCollectionInterface, 2175 move(l_propertyValueMap)); 2176 2177 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 2178 std::move(l_interfaces)); 2179 } 2180 } 2181 else 2182 { 2183 // consider it as an inventory path. 2184 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 2185 { 2186 throw std::runtime_error( 2187 "Invalid inventory path: " + i_vpdPath + 2188 ". Can't set collection Status property"); 2189 } 2190 2191 types::PropertyMap l_propertyValueMap; 2192 l_propertyValueMap.emplace("Status", i_value); 2193 l_propertyValueMap.insert(l_timeStampMap.begin(), 2194 l_timeStampMap.end()); 2195 2196 types::InterfaceMap l_interfaces; 2197 vpdSpecificUtility::insertOrMerge(l_interfaces, 2198 constants::vpdCollectionInterface, 2199 move(l_propertyValueMap)); 2200 2201 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 2202 } 2203 2204 // Notify PIM 2205 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 2206 { 2207 throw DbusException( 2208 std::string(__FUNCTION__) + 2209 "Call to PIM failed while setting collection Status property for path " + 2210 i_vpdPath); 2211 } 2212 } 2213 catch (const std::exception& l_ex) 2214 { 2215 EventLogger::createSyncPel( 2216 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 2217 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 2218 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 2219 } 2220 } 2221 } // namespace vpd 2222