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