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