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