xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision d3662226ea3d25bae316acd4387c3ee26fcee3b9)
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