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