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 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 l_errCode = 0; 593 } 594 595 propertyMap.emplace(property, value); 596 597 auto l_locCodeProperty = propertyMap; 598 vpdSpecificUtility::insertOrMerge( 599 interfaceMap, 600 std::string(constants::xyzLocationCodeInf), 601 move(l_locCodeProperty), l_errCode); 602 603 if (l_errCode) 604 { 605 logging::logMessage( 606 "Failed to insert value into map, error : " + 607 commonUtility::getErrCodeMsg(l_errCode)); 608 609 l_errCode = 0; 610 } 611 } 612 else 613 { 614 propertyMap.emplace( 615 property, propValuePair.value().get<std::string>()); 616 } 617 } 618 else if (propValuePair.value().is_array()) 619 { 620 try 621 { 622 propertyMap.emplace( 623 property, 624 propValuePair.value().get<types::BinaryVector>()); 625 } 626 catch (const nlohmann::detail::type_error& e) 627 { 628 std::cerr << "Type exception: " << e.what() << "\n"; 629 } 630 } 631 else if (propValuePair.value().is_number()) 632 { 633 // For now assume the value is a size_t. In the future it would 634 // be nice to come up with a way to get the type from the JSON. 635 propertyMap.emplace(property, 636 propValuePair.value().get<size_t>()); 637 } 638 else if (propValuePair.value().is_object()) 639 { 640 const std::string& record = 641 propValuePair.value().value("recordName", ""); 642 const std::string& keyword = 643 propValuePair.value().value("keywordName", ""); 644 const std::string& encoding = 645 propValuePair.value().value("encoding", ""); 646 647 if (auto ipzVpdMap = 648 std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 649 { 650 if (!record.empty() && !keyword.empty() && 651 (*ipzVpdMap).count(record) && 652 (*ipzVpdMap).at(record).count(keyword)) 653 { 654 auto encoded = vpdSpecificUtility::encodeKeyword( 655 ((*ipzVpdMap).at(record).at(keyword)), encoding, 656 l_errCode); 657 658 if (l_errCode) 659 { 660 logging::logMessage( 661 std::string( 662 "Failed to get encoded keyword value for : ") + 663 keyword + std::string(", error : ") + 664 commonUtility::getErrCodeMsg(l_errCode)); 665 666 l_errCode = 0; 667 } 668 669 propertyMap.emplace(property, encoded); 670 } 671 } 672 else if (auto kwdVpdMap = 673 std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 674 { 675 if (!keyword.empty() && (*kwdVpdMap).count(keyword)) 676 { 677 if (auto kwValue = std::get_if<types::BinaryVector>( 678 &(*kwdVpdMap).at(keyword))) 679 { 680 auto encodedValue = 681 vpdSpecificUtility::encodeKeyword( 682 std::string((*kwValue).begin(), 683 (*kwValue).end()), 684 encoding, l_errCode); 685 686 if (l_errCode) 687 { 688 logging::logMessage( 689 std::string( 690 "Failed to get encoded keyword value for : ") + 691 keyword + std::string(", error : ") + 692 commonUtility::getErrCodeMsg(l_errCode)); 693 694 l_errCode = 0; 695 } 696 697 propertyMap.emplace(property, encodedValue); 698 } 699 else if (auto kwValue = std::get_if<std::string>( 700 &(*kwdVpdMap).at(keyword))) 701 { 702 auto encodedValue = 703 vpdSpecificUtility::encodeKeyword( 704 std::string((*kwValue).begin(), 705 (*kwValue).end()), 706 encoding, l_errCode); 707 708 if (l_errCode) 709 { 710 logging::logMessage( 711 "Failed to get encoded keyword value for : " + 712 keyword + ", error : " + 713 commonUtility::getErrCodeMsg(l_errCode)); 714 715 l_errCode = 0; 716 } 717 718 propertyMap.emplace(property, encodedValue); 719 } 720 else if (auto uintValue = std::get_if<size_t>( 721 &(*kwdVpdMap).at(keyword))) 722 { 723 propertyMap.emplace(property, *uintValue); 724 } 725 else 726 { 727 logging::logMessage( 728 "Unknown keyword found, Keywrod = " + keyword); 729 } 730 } 731 } 732 } 733 } 734 vpdSpecificUtility::insertOrMerge(interfaceMap, interface, 735 move(propertyMap), l_errCode); 736 737 if (l_errCode) 738 { 739 logging::logMessage("Failed to insert value into map, error : " + 740 commonUtility::getErrCodeMsg(l_errCode)); 741 742 l_errCode = 0; 743 } 744 } 745 } 746 747 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword) 748 { 749 const unsigned char l_io[] = { 750 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 751 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF}; 752 753 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0). 754 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs' 755 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as 756 // IO. 757 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG, 758 constants::SIZE_OF_8EQ_IN_PG) == 0) 759 { 760 return true; 761 } 762 763 // The CPU is not an IO 764 return false; 765 } 766 767 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru, 768 types::InterfaceMap& interfaces) 769 { 770 // embedded property(true or false) says whether the subfru is embedded 771 // into the parent fru (or) not. VPD sets Present property only for 772 // embedded frus. If the subfru is not an embedded FRU, the subfru may 773 // or may not be physically present. Those non embedded frus will always 774 // have Present=false irrespective of its physical presence or absence. 775 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set 776 // Present to true for such sub frus. 777 // Eg: ethernet port is embedded into bmc card. So set Present to true 778 // for such sub frus. Also donot populate present property for embedded 779 // subfru which is synthesized. Currently there is no subfru which are 780 // both embedded and synthesized. But still the case is handled here. 781 782 // Check if its required to handle presence for this FRU. 783 if (singleFru.value("handlePresence", true)) 784 { 785 uint16_t l_errCode = 0; 786 types::PropertyMap presProp; 787 presProp.emplace("Present", true); 788 vpdSpecificUtility::insertOrMerge(interfaces, 789 "xyz.openbmc_project.Inventory.Item", 790 move(presProp), l_errCode); 791 792 if (l_errCode) 793 { 794 logging::logMessage("Failed to insert value into map, error : " + 795 commonUtility::getErrCodeMsg(l_errCode)); 796 } 797 } 798 } 799 800 void Worker::processExtraInterfaces(const nlohmann::json& singleFru, 801 types::InterfaceMap& interfaces, 802 const types::VPDMapVariant& parsedVpdMap) 803 { 804 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap); 805 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 806 { 807 if (singleFru["extraInterfaces"].contains( 808 "xyz.openbmc_project.Inventory.Item.Cpu")) 809 { 810 auto itrToRec = (*ipzVpdMap).find("CP00"); 811 if (itrToRec == (*ipzVpdMap).end()) 812 { 813 return; 814 } 815 816 uint16_t l_errCode = 0; 817 const std::string pgKeywordValue{vpdSpecificUtility::getKwVal( 818 itrToRec->second, "PG", l_errCode)}; 819 820 if (!pgKeywordValue.empty()) 821 { 822 if (isCPUIOGoodOnly(pgKeywordValue)) 823 { 824 interfaces["xyz.openbmc_project.Inventory.Item"] 825 ["PrettyName"] = "IO Module"; 826 } 827 } 828 else 829 { 830 throw DataException( 831 std::string(__FUNCTION__) + 832 "Failed to get value for keyword PG, error : " + 833 commonUtility::getErrCodeMsg(l_errCode)); 834 } 835 } 836 } 837 } 838 839 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru, 840 const types::VPDMapVariant& parsedVpdMap, 841 types::InterfaceMap& interfaces) 842 { 843 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 844 { 845 for (const auto& record : singleFru["copyRecords"]) 846 { 847 const std::string& recordName = record; 848 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end()) 849 { 850 populateIPZVPDpropertyMap(interfaces, 851 (*ipzVpdMap).at(recordName), 852 constants::ipzVpdInf + recordName); 853 } 854 } 855 } 856 } 857 858 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap, 859 types::InterfaceMap& interfaces) 860 { 861 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 862 { 863 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap) 864 { 865 populateIPZVPDpropertyMap(interfaces, kwdValueMap, 866 constants::ipzVpdInf + recordName); 867 } 868 } 869 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 870 { 871 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces); 872 } 873 874 if (m_parsedJson.contains("commonInterfaces")) 875 { 876 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces, 877 parsedVpdMap); 878 } 879 } 880 881 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru, 882 const types::VPDMapVariant& parsedVpdMap) 883 { 884 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 885 { 886 auto itrToRec = (*ipzVPDMap).find("VINI"); 887 if (itrToRec == (*ipzVPDMap).end()) 888 { 889 return false; 890 } 891 892 uint16_t l_errCode = 0; 893 std::string ccinFromVpd{ 894 vpdSpecificUtility::getKwVal(itrToRec->second, "CC", l_errCode)}; 895 896 if (ccinFromVpd.empty()) 897 { 898 logging::logMessage("Failed to get CCIN kwd value, error : " + 899 commonUtility::getErrCodeMsg(l_errCode)); 900 return false; 901 } 902 903 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(), 904 ::toupper); 905 906 std::vector<std::string> ccinList; 907 for (std::string ccin : singleFru["ccin"]) 908 { 909 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper); 910 ccinList.push_back(ccin); 911 } 912 913 if (ccinList.empty()) 914 { 915 return false; 916 } 917 918 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) == 919 ccinList.end()) 920 { 921 return false; 922 } 923 } 924 return true; 925 } 926 927 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath, 928 types::InterfaceMap& io_interfaces) 929 { 930 if (!dbusUtility::isChassisPowerOn()) 931 { 932 std::vector<std::string> l_operationalStatusInf = { 933 constants::operationalStatusInf}; 934 935 auto mapperObjectMap = dbusUtility::getObjectMap( 936 i_inventoryObjPath, l_operationalStatusInf); 937 938 // If the object has been found. Check if it is under PIM. 939 if (mapperObjectMap.size() != 0) 940 { 941 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 942 { 943 if (l_serviceName == constants::pimServiceName) 944 { 945 // The object is already under PIM. No need to process 946 // again. Retain the old value. 947 return; 948 } 949 } 950 } 951 952 // Implies value is not there in D-Bus. Populate it with default 953 // value "true". 954 uint16_t l_errCode = 0; 955 types::PropertyMap l_functionalProp; 956 l_functionalProp.emplace("Functional", true); 957 vpdSpecificUtility::insertOrMerge(io_interfaces, 958 constants::operationalStatusInf, 959 move(l_functionalProp), l_errCode); 960 961 if (l_errCode) 962 { 963 logging::logMessage( 964 "Failed to insert interface into map, error : " + 965 commonUtility::getErrCodeMsg(l_errCode)); 966 } 967 } 968 969 // if chassis is power on. Functional property should be there on D-Bus. 970 // Don't process. 971 return; 972 } 973 974 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath, 975 types::InterfaceMap& io_interfaces) 976 { 977 if (!dbusUtility::isChassisPowerOn()) 978 { 979 std::vector<std::string> l_enableInf = {constants::enableInf}; 980 981 auto mapperObjectMap = 982 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 983 984 // If the object has been found. Check if it is under PIM. 985 if (mapperObjectMap.size() != 0) 986 { 987 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 988 { 989 if (l_serviceName == constants::pimServiceName) 990 { 991 // The object is already under PIM. No need to process 992 // again. Retain the old value. 993 return; 994 } 995 } 996 } 997 998 // Implies value is not there in D-Bus. Populate it with default 999 // value "true". 1000 uint16_t l_errCode = 0; 1001 types::PropertyMap l_enabledProp; 1002 l_enabledProp.emplace("Enabled", true); 1003 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf, 1004 move(l_enabledProp), l_errCode); 1005 1006 if (l_errCode) 1007 { 1008 logging::logMessage( 1009 "Failed to insert interface into map, error : " + 1010 commonUtility::getErrCodeMsg(l_errCode)); 1011 } 1012 } 1013 1014 // if chassis is power on. Enabled property should be there on D-Bus. 1015 // Don't process. 1016 return; 1017 } 1018 1019 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap, 1020 types::ObjectMap& objectInterfaceMap, 1021 const std::string& vpdFilePath) 1022 { 1023 if (vpdFilePath.empty()) 1024 { 1025 throw std::runtime_error( 1026 std::string(__FUNCTION__) + 1027 "Invalid parameter passed to populateDbus API."); 1028 } 1029 1030 // JSON config is mandatory for processing of "if". Add "else" for any 1031 // processing without config JSON. 1032 if (!m_parsedJson.empty()) 1033 { 1034 types::InterfaceMap interfaces; 1035 1036 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath]) 1037 { 1038 const auto& inventoryPath = aFru["inventoryPath"]; 1039 sdbusplus::message::object_path fruObjectPath(inventoryPath); 1040 if (aFru.contains("ccin")) 1041 { 1042 if (!processFruWithCCIN(aFru, parsedVpdMap)) 1043 { 1044 continue; 1045 } 1046 } 1047 1048 if (aFru.value("inherit", true)) 1049 { 1050 processInheritFlag(parsedVpdMap, interfaces); 1051 } 1052 1053 // If specific record needs to be copied. 1054 if (aFru.contains("copyRecords")) 1055 { 1056 processCopyRecordFlag(aFru, parsedVpdMap, interfaces); 1057 } 1058 1059 if (aFru.contains("extraInterfaces")) 1060 { 1061 // Process extra interfaces w.r.t a FRU. 1062 processExtraInterfaces(aFru, interfaces, parsedVpdMap); 1063 } 1064 1065 // Process FRUS which are embedded in the parent FRU and whose VPD 1066 // will be synthesized. 1067 if ((aFru.value("embedded", true)) && 1068 (!aFru.value("synthesized", false))) 1069 { 1070 processEmbeddedAndSynthesizedFrus(aFru, interfaces); 1071 } 1072 1073 processFunctionalProperty(inventoryPath, interfaces); 1074 processEnabledProperty(inventoryPath, interfaces); 1075 1076 objectInterfaceMap.emplace(std::move(fruObjectPath), 1077 std::move(interfaces)); 1078 } 1079 } 1080 } 1081 1082 std::string Worker::createAssetTagString( 1083 const types::VPDMapVariant& i_parsedVpdMap) 1084 { 1085 std::string l_assetTag; 1086 1087 // system VPD will be in IPZ format. 1088 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap)) 1089 { 1090 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS); 1091 if (l_itrToVsys != (*l_parsedVpdMap).end()) 1092 { 1093 uint16_t l_errCode = 0; 1094 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal( 1095 l_itrToVsys->second, constants::kwdTM, l_errCode)}; 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. Error : " + 1103 commonUtility::getErrCodeMsg(l_errCode))); 1104 } 1105 1106 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal( 1107 l_itrToVsys->second, constants::kwdSE, l_errCode)}; 1108 1109 if (l_seKwdValue.empty()) 1110 { 1111 throw std::runtime_error( 1112 std::string("Failed to get value for keyword [") + 1113 constants::kwdSE + 1114 std::string("] while creating Asset tag. Error : " + 1115 commonUtility::getErrCodeMsg(l_errCode))); 1116 } 1117 1118 l_assetTag = std::string{"Server-"} + l_tmKwdValue + 1119 std::string{"-"} + l_seKwdValue; 1120 } 1121 else 1122 { 1123 throw std::runtime_error( 1124 "VSYS record not found in parsed VPD map to create Asset tag."); 1125 } 1126 } 1127 else 1128 { 1129 throw std::runtime_error( 1130 "Invalid VPD type recieved to create Asset tag."); 1131 } 1132 1133 return l_assetTag; 1134 } 1135 1136 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap) 1137 { 1138 types::ObjectMap objectInterfaceMap; 1139 1140 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 1141 { 1142 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH); 1143 1144 try 1145 { 1146 if (m_isFactoryResetDone) 1147 { 1148 const auto& l_assetTag = createAssetTagString(parsedVpdMap); 1149 1150 auto l_itrToSystemPath = objectInterfaceMap.find( 1151 sdbusplus::message::object_path(constants::systemInvPath)); 1152 if (l_itrToSystemPath == objectInterfaceMap.end()) 1153 { 1154 throw std::runtime_error( 1155 "Asset tag update failed. System Path not found in object map."); 1156 } 1157 1158 types::PropertyMap l_assetTagProperty; 1159 l_assetTagProperty.emplace("AssetTag", l_assetTag); 1160 1161 (l_itrToSystemPath->second) 1162 .emplace(constants::assetTagInf, 1163 std::move(l_assetTagProperty)); 1164 } 1165 } 1166 catch (const std::exception& l_ex) 1167 { 1168 EventLogger::createSyncPel( 1169 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1170 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1171 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1172 } 1173 1174 // Notify PIM 1175 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1176 { 1177 throw std::runtime_error("Call to PIM failed for system VPD"); 1178 } 1179 } 1180 else 1181 { 1182 throw DataException("Invalid format of parsed VPD map."); 1183 } 1184 } 1185 1186 bool Worker::processPreAction(const std::string& i_vpdFilePath, 1187 const std::string& i_flagToProcess, 1188 uint16_t& i_errCode) 1189 { 1190 if (i_vpdFilePath.empty() || i_flagToProcess.empty()) 1191 { 1192 i_errCode = error_code::INVALID_INPUT_PARAMETER; 1193 return false; 1194 } 1195 1196 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction", 1197 i_vpdFilePath, i_flagToProcess, 1198 i_errCode)) && 1199 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS)) 1200 { 1201 // TODO: Need a way to delete inventory object from Dbus and persisted 1202 // data section in case any FRU is not present or there is any 1203 // problem in collecting it. Once it has been deleted, it can be 1204 // re-created in the flow of priming the inventory. This needs to be 1205 // done either here or in the exception section of "parseAndPublishVPD" 1206 // API. Any failure in the process of collecting FRU will land up in the 1207 // excpetion of "parseAndPublishVPD". 1208 1209 // If the FRU is not there, clear the VINI/CCIN data. 1210 // Enity manager probes for this keyword to look for this 1211 // FRU, now if the data is persistent on BMC and FRU is 1212 // removed this can lead to ambiguity. Hence clearing this 1213 // Keyword if FRU is absent. 1214 const auto& inventoryPath = 1215 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath", 1216 ""); 1217 1218 if (!inventoryPath.empty()) 1219 { 1220 types::ObjectMap l_pimObjMap{ 1221 {inventoryPath, 1222 {{constants::kwdVpdInf, 1223 {{constants::kwdCCIN, types::BinaryVector{}}}}}}}; 1224 1225 if (!dbusUtility::callPIM(std::move(l_pimObjMap))) 1226 { 1227 logging::logMessage( 1228 "Call to PIM failed for file " + i_vpdFilePath); 1229 } 1230 } 1231 else 1232 { 1233 logging::logMessage( 1234 "Inventory path is empty in Json for file " + i_vpdFilePath); 1235 } 1236 1237 return false; 1238 } 1239 return true; 1240 } 1241 1242 bool Worker::processPostAction( 1243 const std::string& i_vpdFruPath, const std::string& i_flagToProcess, 1244 const std::optional<types::VPDMapVariant> i_parsedVpd) 1245 { 1246 if (i_vpdFruPath.empty() || i_flagToProcess.empty()) 1247 { 1248 logging::logMessage( 1249 "Invalid input parameter. Abort processing post action"); 1250 return false; 1251 } 1252 1253 // Check if post action tag is to be triggered in the flow of collection 1254 // based on some CCIN value? 1255 if (m_parsedJson["frus"][i_vpdFruPath] 1256 .at(0)["postAction"][i_flagToProcess] 1257 .contains("ccin")) 1258 { 1259 if (!i_parsedVpd.has_value()) 1260 { 1261 logging::logMessage("Empty VPD Map"); 1262 return false; 1263 } 1264 1265 // CCIN match is required to process post action for this FRU as it 1266 // contains the flag. 1267 if (!vpdSpecificUtility::findCcinInVpd( 1268 m_parsedJson["frus"][i_vpdFruPath].at( 1269 0)["postAction"]["collection"], 1270 i_parsedVpd.value())) 1271 { 1272 // If CCIN is not found, implies post action processing is not 1273 // required for this FRU. Let the flow continue. 1274 return true; 1275 } 1276 } 1277 1278 uint16_t l_errCode = 0; 1279 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction", 1280 i_vpdFruPath, i_flagToProcess, 1281 l_errCode)) 1282 { 1283 logging::logMessage( 1284 "Execution of post action failed for path: " + i_vpdFruPath + 1285 " . Reason: " + commonUtility::getErrCodeMsg(l_errCode)); 1286 1287 // If post action was required and failed only in that case return 1288 // false. In all other case post action is considered passed. 1289 return false; 1290 } 1291 1292 return true; 1293 } 1294 1295 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath) 1296 { 1297 try 1298 { 1299 uint16_t l_errCode = 0; 1300 1301 if (i_vpdFilePath.empty()) 1302 { 1303 throw std::runtime_error( 1304 std::string(__FUNCTION__) + 1305 " Empty VPD file path passed. Abort processing"); 1306 } 1307 1308 bool isPreActionRequired = false; 1309 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1310 "preAction", "collection", l_errCode)) 1311 { 1312 l_errCode = 0; 1313 isPreActionRequired = true; 1314 if (!processPreAction(i_vpdFilePath, "collection", l_errCode)) 1315 { 1316 if (l_errCode == error_code::DEVICE_NOT_PRESENT) 1317 { 1318 logging::logMessage( 1319 commonUtility::getErrCodeMsg(l_errCode) + 1320 i_vpdFilePath); 1321 // Presence pin has been read successfully and has been read 1322 // as false, so this is not a failure case, hence returning 1323 // empty variant so that pre action is not marked as failed. 1324 return types::VPDMapVariant{}; 1325 } 1326 throw std::runtime_error( 1327 std::string(__FUNCTION__) + 1328 " Pre-Action failed with error: " + 1329 commonUtility::getErrCodeMsg(l_errCode)); 1330 } 1331 } 1332 else if (l_errCode) 1333 { 1334 logging::logMessage( 1335 "Failed to check if pre action required for FRU [" + 1336 i_vpdFilePath + 1337 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1338 } 1339 1340 if (!std::filesystem::exists(i_vpdFilePath)) 1341 { 1342 if (isPreActionRequired) 1343 { 1344 throw std::runtime_error( 1345 std::string(__FUNCTION__) + " Could not find file path " + 1346 i_vpdFilePath + "Skipping parser trigger for the EEPROM"); 1347 } 1348 return types::VPDMapVariant{}; 1349 } 1350 1351 std::shared_ptr<Parser> vpdParser = 1352 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson); 1353 1354 types::VPDMapVariant l_parsedVpd = vpdParser->parse(); 1355 1356 // Before returning, as collection is over, check if FRU qualifies for 1357 // any post action in the flow of collection. 1358 // Note: Don't change the order, post action needs to be processed only 1359 // after collection for FRU is successfully done. 1360 l_errCode = 0; 1361 1362 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1363 "postAction", "collection", 1364 l_errCode)) 1365 { 1366 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd)) 1367 { 1368 // Post action was required but failed while executing. 1369 // Behaviour can be undefined. 1370 EventLogger::createSyncPel( 1371 types::ErrorType::InternalFailure, 1372 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0, 1373 std::string("Required post action failed for path [" + 1374 i_vpdFilePath + "]"), 1375 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1376 } 1377 } 1378 else if (l_errCode) 1379 { 1380 logging::logMessage( 1381 "Error while checking if post action required for FRU [" + 1382 i_vpdFilePath + 1383 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1384 } 1385 1386 return l_parsedVpd; 1387 } 1388 catch (std::exception& l_ex) 1389 { 1390 uint16_t l_errCode = 0; 1391 std::string l_exMsg{ 1392 std::string(__FUNCTION__) + " : VPD parsing failed for " + 1393 i_vpdFilePath + " due to error: " + l_ex.what()}; 1394 1395 // If post fail action is required, execute it. 1396 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1397 "postFailAction", "collection", 1398 l_errCode)) 1399 { 1400 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath, 1401 "collection", l_errCode)) 1402 { 1403 l_exMsg += ". Post fail action also failed. Error : " + 1404 commonUtility::getErrCodeMsg(l_errCode) + 1405 " Aborting collection for this FRU."; 1406 } 1407 } 1408 else if (l_errCode) 1409 { 1410 l_exMsg += 1411 ". Failed to check if post fail action required, error : " + 1412 commonUtility::getErrCodeMsg(l_errCode); 1413 } 1414 1415 if (typeid(l_ex) == typeid(DataException)) 1416 { 1417 throw DataException(l_exMsg); 1418 } 1419 else if (typeid(l_ex) == typeid(EccException)) 1420 { 1421 throw EccException(l_exMsg); 1422 } 1423 throw std::runtime_error(l_exMsg); 1424 } 1425 } 1426 1427 std::tuple<bool, std::string> Worker::parseAndPublishVPD( 1428 const std::string& i_vpdFilePath) 1429 { 1430 std::string l_inventoryPath{}; 1431 1432 try 1433 { 1434 m_semaphore.acquire(); 1435 1436 // Thread launched. 1437 m_mutex.lock(); 1438 m_activeCollectionThreadCount++; 1439 m_mutex.unlock(); 1440 1441 setCollectionStatusProperty(i_vpdFilePath, 1442 constants::vpdCollectionInProgress); 1443 1444 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath); 1445 if (!std::holds_alternative<std::monostate>(parsedVpdMap)) 1446 { 1447 types::ObjectMap objectInterfaceMap; 1448 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath); 1449 1450 // Notify PIM 1451 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1452 { 1453 throw std::runtime_error( 1454 std::string(__FUNCTION__) + 1455 "Call to PIM failed while publishing VPD."); 1456 } 1457 } 1458 else 1459 { 1460 logging::logMessage("Empty parsedVpdMap recieved for path [" + 1461 i_vpdFilePath + "]. Check PEL for reason."); 1462 1463 // As empty parsedVpdMap recieved for some reason, but still 1464 // considered VPD collection is completed. Hence FRU collection 1465 // Status will be set as completed. 1466 } 1467 } 1468 catch (const std::exception& ex) 1469 { 1470 setCollectionStatusProperty(i_vpdFilePath, 1471 constants::vpdCollectionFailed); 1472 1473 // handle all the exceptions internally. Return only true/false 1474 // based on status of execution. 1475 if (typeid(ex) == std::type_index(typeid(DataException))) 1476 { 1477 uint16_t l_errCode = 0; 1478 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip 1479 // logging error for these cases. 1480 if (vpdSpecificUtility::isPass1Planar()) 1481 { 1482 std::string l_invPath = 1483 jsonUtility::getInventoryObjPathFromJson( 1484 m_parsedJson, i_vpdFilePath, l_errCode); 1485 1486 if (l_errCode != 0) 1487 { 1488 logging::logMessage( 1489 "Failed to get inventory object path from JSON for FRU [" + 1490 i_vpdFilePath + 1491 "], error: " + commonUtility::getErrCodeMsg(l_errCode)); 1492 } 1493 1494 const std::string& l_invPathLeafValue = 1495 sdbusplus::message::object_path(l_invPath).filename(); 1496 1497 if ((l_invPathLeafValue.find("pcie_card", 0) != 1498 std::string::npos)) 1499 { 1500 // skip logging any PEL for PCIe cards on pass 1 planar. 1501 return std::make_tuple(false, i_vpdFilePath); 1502 } 1503 } 1504 } 1505 1506 EventLogger::createSyncPel( 1507 EventLogger::getErrorType(ex), 1508 (typeid(ex) == typeid(DataException)) || 1509 (typeid(ex) == typeid(EccException)) 1510 ? types::SeverityType::Warning 1511 : types::SeverityType::Informational, 1512 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex), 1513 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1514 1515 // TODO: Figure out a way to clear data in case of any failure at 1516 // runtime. 1517 1518 // set present property to false for any error case. In future this will 1519 // be replaced by presence logic. 1520 // Update Present property for this FRU only if we handle Present 1521 // property for the FRU. 1522 if (isPresentPropertyHandlingRequired( 1523 m_parsedJson["frus"][i_vpdFilePath].at(0))) 1524 { 1525 setPresentProperty(i_vpdFilePath, false); 1526 } 1527 1528 m_semaphore.release(); 1529 return std::make_tuple(false, i_vpdFilePath); 1530 } 1531 1532 setCollectionStatusProperty(i_vpdFilePath, 1533 constants::vpdCollectionCompleted); 1534 m_semaphore.release(); 1535 return std::make_tuple(true, i_vpdFilePath); 1536 } 1537 1538 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath) 1539 { 1540 if (i_vpdFilePath.empty()) 1541 { 1542 return true; 1543 } 1544 1545 // skip processing of system VPD again as it has been already collected. 1546 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH) 1547 { 1548 return true; 1549 } 1550 1551 if (dbusUtility::isChassisPowerOn()) 1552 { 1553 // If chassis is powered on, skip collecting FRUs which are 1554 // powerOffOnly. 1555 1556 uint16_t l_errCode = 0; 1557 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath, 1558 l_errCode)) 1559 { 1560 return true; 1561 } 1562 else if (l_errCode) 1563 { 1564 logging::logMessage( 1565 "Failed to check if FRU is power off only for FRU [" + 1566 i_vpdFilePath + 1567 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 1568 } 1569 1570 l_errCode = 0; 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 l_errCode = 0; 1790 types::InterfaceMap l_interfaceMap; 1791 vpdSpecificUtility::resetDataUnderPIM( 1792 l_objectPath, l_interfaceMap, l_errCode); 1793 1794 if (l_errCode) 1795 { 1796 throw std::runtime_error( 1797 "Failed to reset data under PIM for sub FRU [" + 1798 l_objectPath + "], error : " + 1799 commonUtility::getErrCodeMsg(l_errCode)); 1800 } 1801 1802 l_objectMap.emplace(l_objectPath, 1803 std::move(l_interfaceMap)); 1804 } 1805 1806 l_errCode = 0; 1807 types::InterfaceMap l_interfaceMap; 1808 vpdSpecificUtility::resetDataUnderPIM( 1809 i_dbusObjPath, l_interfaceMap, l_errCode); 1810 1811 if (l_errCode) 1812 { 1813 throw std::runtime_error( 1814 "Failed to reset data under PIM, error : " + 1815 commonUtility::getErrCodeMsg(l_errCode)); 1816 } 1817 1818 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap)); 1819 1820 if (!dbusUtility::callPIM(std::move(l_objectMap))) 1821 { 1822 throw std::runtime_error("Call to PIM failed."); 1823 } 1824 1825 l_errCode = 0; 1826 1827 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1828 "postAction", "deletion", 1829 l_errCode)) 1830 { 1831 if (!processPostAction(l_fruPath, "deletion")) 1832 { 1833 throw std::runtime_error("Post action failed"); 1834 } 1835 } 1836 else if (l_errCode) 1837 { 1838 logging::logMessage( 1839 "Failed to check if post action required during deletion for FRU [" + 1840 l_fruPath + "], error : " + 1841 commonUtility::getErrCodeMsg(l_errCode)); 1842 } 1843 } 1844 } 1845 else 1846 { 1847 logging::logMessage( 1848 "Can't process delete VPD for FRU [" + i_dbusObjPath + 1849 "] as unable to read present property"); 1850 return; 1851 } 1852 1853 logging::logMessage( 1854 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath); 1855 } 1856 catch (const std::exception& l_ex) 1857 { 1858 uint16_t l_errCode = 0; 1859 std::string l_errMsg = 1860 "Failed to delete VPD for FRU : " + i_dbusObjPath + 1861 " error: " + std::string(l_ex.what()); 1862 1863 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1864 "postFailAction", "deletion", 1865 l_errCode)) 1866 { 1867 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath, 1868 "deletion", l_errCode)) 1869 { 1870 l_errMsg += ". Post fail action also failed, error : " + 1871 commonUtility::getErrCodeMsg(l_errCode); 1872 } 1873 } 1874 else if (l_errCode) 1875 { 1876 l_errMsg += 1877 ". Failed to check if post fail action required, error : " + 1878 commonUtility::getErrCodeMsg(l_errCode); 1879 } 1880 1881 logging::logMessage(l_errMsg); 1882 } 1883 } 1884 1885 void Worker::setPresentProperty(const std::string& i_vpdPath, 1886 const bool& i_value) 1887 { 1888 try 1889 { 1890 if (i_vpdPath.empty()) 1891 { 1892 throw std::runtime_error( 1893 "Path is empty. Can't set present property"); 1894 } 1895 1896 types::ObjectMap l_objectInterfaceMap; 1897 1898 // If the given path is EEPROM path. 1899 if (m_parsedJson["frus"].contains(i_vpdPath)) 1900 { 1901 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 1902 { 1903 sdbusplus::message::object_path l_fruObjectPath( 1904 l_Fru["inventoryPath"]); 1905 1906 types::PropertyMap l_propertyValueMap; 1907 l_propertyValueMap.emplace("Present", i_value); 1908 1909 uint16_t l_errCode = 0; 1910 types::InterfaceMap l_interfaces; 1911 vpdSpecificUtility::insertOrMerge( 1912 l_interfaces, constants::inventoryItemInf, 1913 move(l_propertyValueMap), l_errCode); 1914 1915 if (l_errCode) 1916 { 1917 logging::logMessage( 1918 "Failed to insert value into map, error : " + 1919 commonUtility::getErrCodeMsg(l_errCode)); 1920 } 1921 1922 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 1923 std::move(l_interfaces)); 1924 } 1925 } 1926 else 1927 { 1928 // consider it as an inventory path. 1929 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 1930 { 1931 throw std::runtime_error( 1932 "Invalid inventory path: " + i_vpdPath); 1933 } 1934 1935 types::PropertyMap l_propertyValueMap; 1936 l_propertyValueMap.emplace("Present", i_value); 1937 1938 uint16_t l_errCode = 0; 1939 types::InterfaceMap l_interfaces; 1940 vpdSpecificUtility::insertOrMerge( 1941 l_interfaces, constants::inventoryItemInf, 1942 move(l_propertyValueMap), l_errCode); 1943 1944 if (l_errCode) 1945 { 1946 logging::logMessage( 1947 "Failed to insert value into map, error : " + 1948 commonUtility::getErrCodeMsg(l_errCode)); 1949 } 1950 1951 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 1952 } 1953 1954 // Notify PIM 1955 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1956 { 1957 throw DbusException( 1958 std::string(__FUNCTION__) + 1959 "Call to PIM failed while setting present property for path " + 1960 i_vpdPath); 1961 } 1962 } 1963 catch (const std::exception& l_ex) 1964 { 1965 EventLogger::createSyncPel( 1966 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1967 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1968 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1969 } 1970 } 1971 1972 void Worker::performVpdRecollection() 1973 { 1974 try 1975 { 1976 // Check if system config JSON is present 1977 if (m_parsedJson.empty()) 1978 { 1979 throw std::runtime_error( 1980 "System config json object is empty, can't process recollection."); 1981 } 1982 1983 uint16_t l_errCode = 0; 1984 const auto& l_frusReplaceableAtStandby = 1985 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson, 1986 l_errCode); 1987 1988 if (l_errCode) 1989 { 1990 logging::logMessage( 1991 "Failed to get list of FRUs replaceable at runtime, error : " + 1992 commonUtility::getErrCodeMsg(l_errCode)); 1993 return; 1994 } 1995 1996 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby) 1997 { 1998 // ToDo: Add some logic/trace to know the flow to 1999 // collectSingleFruVpd has been directed via 2000 // performVpdRecollection. 2001 collectSingleFruVpd(l_fruInventoryPath); 2002 } 2003 return; 2004 } 2005 2006 catch (const std::exception& l_ex) 2007 { 2008 // TODO Log PEL 2009 logging::logMessage( 2010 "VPD recollection failed with error: " + std::string(l_ex.what())); 2011 } 2012 } 2013 2014 void Worker::collectSingleFruVpd( 2015 const sdbusplus::message::object_path& i_dbusObjPath) 2016 { 2017 std::string l_fruPath{}; 2018 uint16_t l_errCode = 0; 2019 2020 try 2021 { 2022 // Check if system config JSON is present 2023 if (m_parsedJson.empty()) 2024 { 2025 logging::logMessage( 2026 "System config JSON object not present. Single FRU VPD collection is not performed for " + 2027 std::string(i_dbusObjPath)); 2028 return; 2029 } 2030 2031 // Get FRU path for the given D-bus object path from JSON 2032 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, 2033 l_errCode); 2034 2035 if (l_fruPath.empty()) 2036 { 2037 if (l_errCode) 2038 { 2039 logging::logMessage( 2040 "Failed to get FRU path for [" + 2041 std::string(i_dbusObjPath) + 2042 "], error : " + commonUtility::getErrCodeMsg(l_errCode) + 2043 " Aborting single FRU VPD collection."); 2044 return; 2045 } 2046 2047 logging::logMessage( 2048 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " + 2049 std::string(i_dbusObjPath)); 2050 return; 2051 } 2052 2053 // Check if host is up and running 2054 if (dbusUtility::isHostRunning()) 2055 { 2056 uint16_t l_errCode = 0; 2057 bool isFruReplaceableAtRuntime = 2058 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath, 2059 l_errCode); 2060 2061 if (l_errCode) 2062 { 2063 logging::logMessage( 2064 "Failed to check if FRU is replaceable at runtime for FRU : [" + 2065 std::string(i_dbusObjPath) + 2066 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2067 return; 2068 } 2069 2070 if (!isFruReplaceableAtRuntime) 2071 { 2072 logging::logMessage( 2073 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " + 2074 std::string(i_dbusObjPath)); 2075 return; 2076 } 2077 } 2078 else if (dbusUtility::isBMCReady()) 2079 { 2080 uint16_t l_errCode = 0; 2081 bool isFruReplaceableAtStandby = 2082 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath, 2083 l_errCode); 2084 2085 if (l_errCode) 2086 { 2087 logging::logMessage( 2088 "Error while checking if FRU is replaceable at standby for FRU [" + 2089 std::string(i_dbusObjPath) + 2090 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2091 } 2092 2093 l_errCode = 0; 2094 bool isFruReplaceableAtRuntime = 2095 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath, 2096 l_errCode); 2097 2098 if (l_errCode) 2099 { 2100 logging::logMessage( 2101 "Failed to check if FRU is replaceable at runtime for FRU : [" + 2102 std::string(i_dbusObjPath) + 2103 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 2104 return; 2105 } 2106 2107 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime)) 2108 { 2109 logging::logMessage( 2110 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " + 2111 std::string(i_dbusObjPath)); 2112 return; 2113 } 2114 } 2115 2116 // Set collection Status as InProgress. Since it's an intermediate state 2117 // D-bus set-property call is good enough to update the status. 2118 const std::string& l_collStatusProp = "Status"; 2119 2120 setCollectionStatusProperty(l_fruPath, 2121 constants::vpdCollectionInProgress); 2122 2123 // Parse VPD 2124 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath); 2125 2126 // If l_parsedVpd is pointing to std::monostate 2127 if (l_parsedVpd.index() == 0) 2128 { 2129 throw std::runtime_error( 2130 "VPD parsing failed for " + std::string(i_dbusObjPath)); 2131 } 2132 2133 // Get D-bus object map from worker class 2134 types::ObjectMap l_dbusObjectMap; 2135 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath); 2136 2137 if (l_dbusObjectMap.empty()) 2138 { 2139 throw std::runtime_error( 2140 "Failed to create D-bus object map. Single FRU VPD collection failed for " + 2141 std::string(i_dbusObjPath)); 2142 } 2143 2144 // Call PIM's Notify method 2145 if (!dbusUtility::callPIM(move(l_dbusObjectMap))) 2146 { 2147 throw std::runtime_error( 2148 "Notify PIM failed. Single FRU VPD collection failed for " + 2149 std::string(i_dbusObjPath)); 2150 } 2151 setCollectionStatusProperty(l_fruPath, 2152 constants::vpdCollectionCompleted); 2153 } 2154 catch (const std::exception& l_error) 2155 { 2156 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed); 2157 // TODO: Log PEL 2158 logging::logMessage(std::string(l_error.what())); 2159 } 2160 } 2161 2162 void Worker::setCollectionStatusProperty( 2163 const std::string& i_vpdPath, const std::string& i_value) const noexcept 2164 { 2165 try 2166 { 2167 if (i_vpdPath.empty()) 2168 { 2169 throw std::runtime_error( 2170 "Given path is empty. Can't set collection Status property"); 2171 } 2172 2173 types::PropertyMap l_timeStampMap; 2174 if (i_value == constants::vpdCollectionCompleted || 2175 i_value == constants::vpdCollectionFailed) 2176 { 2177 l_timeStampMap.emplace( 2178 "CompletedTime", 2179 types::DbusVariantType{ 2180 commonUtility::getCurrentTimeSinceEpoch()}); 2181 } 2182 else if (i_value == constants::vpdCollectionInProgress) 2183 { 2184 l_timeStampMap.emplace( 2185 "StartTime", types::DbusVariantType{ 2186 commonUtility::getCurrentTimeSinceEpoch()}); 2187 } 2188 else if (i_value == constants::vpdCollectionNotStarted) 2189 { 2190 l_timeStampMap.emplace("StartTime", 0); 2191 l_timeStampMap.emplace("CompletedTime", 0); 2192 } 2193 2194 types::ObjectMap l_objectInterfaceMap; 2195 2196 if (m_parsedJson["frus"].contains(i_vpdPath)) 2197 { 2198 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 2199 { 2200 sdbusplus::message::object_path l_fruObjectPath( 2201 l_Fru["inventoryPath"]); 2202 2203 types::PropertyMap l_propertyValueMap; 2204 l_propertyValueMap.emplace("Status", i_value); 2205 l_propertyValueMap.insert(l_timeStampMap.begin(), 2206 l_timeStampMap.end()); 2207 2208 uint16_t l_errCode = 0; 2209 types::InterfaceMap l_interfaces; 2210 vpdSpecificUtility::insertOrMerge( 2211 l_interfaces, constants::vpdCollectionInterface, 2212 move(l_propertyValueMap), l_errCode); 2213 2214 if (l_errCode) 2215 { 2216 logging::logMessage( 2217 "Failed to insert value into map, error : " + 2218 commonUtility::getErrCodeMsg(l_errCode)); 2219 } 2220 2221 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 2222 std::move(l_interfaces)); 2223 } 2224 } 2225 else 2226 { 2227 // consider it as an inventory path. 2228 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 2229 { 2230 throw std::runtime_error( 2231 "Invalid inventory path: " + i_vpdPath + 2232 ". Can't set collection Status property"); 2233 } 2234 2235 types::PropertyMap l_propertyValueMap; 2236 l_propertyValueMap.emplace("Status", i_value); 2237 l_propertyValueMap.insert(l_timeStampMap.begin(), 2238 l_timeStampMap.end()); 2239 2240 uint16_t l_errCode = 0; 2241 types::InterfaceMap l_interfaces; 2242 vpdSpecificUtility::insertOrMerge( 2243 l_interfaces, constants::vpdCollectionInterface, 2244 move(l_propertyValueMap), l_errCode); 2245 2246 if (l_errCode) 2247 { 2248 logging::logMessage( 2249 "Failed to insert value into map, error : " + 2250 commonUtility::getErrCodeMsg(l_errCode)); 2251 } 2252 2253 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 2254 } 2255 2256 // Notify PIM 2257 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 2258 { 2259 throw DbusException( 2260 std::string(__FUNCTION__) + 2261 "Call to PIM failed while setting collection Status property for path " + 2262 i_vpdPath); 2263 } 2264 } 2265 catch (const std::exception& l_ex) 2266 { 2267 EventLogger::createSyncPel( 2268 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 2269 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 2270 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 2271 } 2272 } 2273 } // namespace vpd 2274