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