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