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