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