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