xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision ea0fb8f7346f3565b63c0c5a508258dc48594e31)
1 #include "config.h"
2 
3 #include "worker.hpp"
4 
5 #include "backup_restore.hpp"
6 #include "constants.hpp"
7 #include "exceptions.hpp"
8 #include "parser.hpp"
9 #include "parser_factory.hpp"
10 #include "parser_interface.hpp"
11 
12 #include <utility/common_utility.hpp>
13 #include <utility/dbus_utility.hpp>
14 #include <utility/event_logger_utility.hpp>
15 #include <utility/json_utility.hpp>
16 #include <utility/vpd_specific_utility.hpp>
17 
18 #include <filesystem>
19 #include <fstream>
20 #include <future>
21 #include <typeindex>
22 #include <unordered_set>
23 
24 namespace vpd
25 {
26 
Worker(std::string pathToConfigJson,uint8_t i_maxThreadCount,types::VpdCollectionMode i_vpdCollectionMode)27 Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount,
28                types::VpdCollectionMode i_vpdCollectionMode) :
29     m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount),
30     m_vpdCollectionMode(i_vpdCollectionMode),
31     m_logger(Logger::getLoggerInstance())
32 {
33     // Implies the processing is based on some config JSON
34     if (!m_configJsonPath.empty())
35     {
36         uint16_t l_errCode = 0;
37         m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath, l_errCode);
38 
39         if (l_errCode)
40         {
41             throw JsonException("JSON parsing failed. error : " +
42                                     commonUtility::getErrCodeMsg(l_errCode),
43                                 m_configJsonPath);
44         }
45 
46         // check for mandatory fields at this point itself.
47         if (!m_parsedJson.contains("frus"))
48         {
49             throw JsonException("Mandatory tag(s) missing from JSON",
50                                 m_configJsonPath);
51         }
52     }
53     else
54     {
55         logging::logMessage("Processing in not based on any config JSON");
56     }
57 }
58 
populateIPZVPDpropertyMap(types::InterfaceMap & interfacePropMap,const types::IPZKwdValueMap & keyordValueMap,const std::string & interfaceName)59 void Worker::populateIPZVPDpropertyMap(
60     types::InterfaceMap& interfacePropMap,
61     const types::IPZKwdValueMap& keyordValueMap,
62     const std::string& interfaceName)
63 {
64     types::PropertyMap propertyValueMap;
65     for (const auto& kwdVal : keyordValueMap)
66     {
67         auto kwd = kwdVal.first;
68 
69         if (kwd[0] == '#')
70         {
71             kwd = std::string("PD_") + kwd[1];
72         }
73         else if (isdigit(kwd[0]))
74         {
75             kwd = std::string("N_") + kwd;
76         }
77 
78         types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
79         propertyValueMap.emplace(move(kwd), move(value));
80     }
81 
82     if (!propertyValueMap.empty())
83     {
84         interfacePropMap.emplace(interfaceName, propertyValueMap);
85     }
86 }
87 
populateKwdVPDpropertyMap(const types::KeywordVpdMap & keyordVPDMap,types::InterfaceMap & interfaceMap)88 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
89                                        types::InterfaceMap& interfaceMap)
90 {
91     for (const auto& kwdValMap : keyordVPDMap)
92     {
93         types::PropertyMap propertyValueMap;
94         auto kwd = kwdValMap.first;
95 
96         if (kwd[0] == '#')
97         {
98             kwd = std::string("PD_") + kwd[1];
99         }
100         else if (isdigit(kwd[0]))
101         {
102             kwd = std::string("N_") + kwd;
103         }
104 
105         if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
106         {
107             types::BinaryVector value((*keywordValue).begin(),
108                                       (*keywordValue).end());
109             propertyValueMap.emplace(move(kwd), move(value));
110         }
111         else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
112         {
113             types::BinaryVector value((*keywordValue).begin(),
114                                       (*keywordValue).end());
115             propertyValueMap.emplace(move(kwd), move(value));
116         }
117         else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
118         {
119             if (kwd == "MemorySizeInKB")
120             {
121                 types::PropertyMap memProp;
122                 memProp.emplace(move(kwd), ((*keywordValue)));
123                 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
124                                      move(memProp));
125                 continue;
126             }
127             else
128             {
129                 logging::logMessage(
130                     "Unknown Keyword =" + kwd + " found in keyword VPD map");
131                 continue;
132             }
133         }
134         else
135         {
136             logging::logMessage(
137                 "Unknown variant type found in keyword VPD map.");
138             continue;
139         }
140 
141         if (!propertyValueMap.empty())
142         {
143             uint16_t l_errCode = 0;
144             vpdSpecificUtility::insertOrMerge(
145                 interfaceMap, constants::kwdVpdInf, move(propertyValueMap),
146                 l_errCode);
147 
148             if (l_errCode)
149             {
150                 logging::logMessage(
151                     "Failed to insert value into map, error : " +
152                     commonUtility::getErrCodeMsg(l_errCode));
153             }
154         }
155     }
156 }
157 
populateInterfaces(const nlohmann::json & interfaceJson,types::InterfaceMap & interfaceMap,const types::VPDMapVariant & parsedVpdMap)158 void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
159                                 types::InterfaceMap& interfaceMap,
160                                 const types::VPDMapVariant& parsedVpdMap)
161 {
162     for (const auto& interfacesPropPair : interfaceJson.items())
163     {
164         const std::string& interface = interfacesPropPair.key();
165         types::PropertyMap propertyMap;
166         uint16_t l_errCode = 0;
167 
168         for (const auto& propValuePair : interfacesPropPair.value().items())
169         {
170             const std::string property = propValuePair.key();
171 
172             if (propValuePair.value().is_boolean())
173             {
174                 propertyMap.emplace(property,
175                                     propValuePair.value().get<bool>());
176             }
177             else if (propValuePair.value().is_string())
178             {
179                 if (property.compare("LocationCode") == 0 &&
180                     interface.compare("com.ibm.ipzvpd.Location") == 0)
181                 {
182                     std::string value =
183                         vpdSpecificUtility::getExpandedLocationCode(
184                             propValuePair.value().get<std::string>(),
185                             parsedVpdMap, l_errCode);
186 
187                     if (l_errCode)
188                     {
189                         logging::logMessage(
190                             "Failed to get expanded location code for location code - " +
191                             propValuePair.value().get<std::string>() +
192                             " ,error : " +
193                             commonUtility::getErrCodeMsg(l_errCode));
194                     }
195 
196                     propertyMap.emplace(property, value);
197 
198                     auto l_locCodeProperty = propertyMap;
199                     vpdSpecificUtility::insertOrMerge(
200                         interfaceMap,
201                         std::string(constants::xyzLocationCodeInf),
202                         move(l_locCodeProperty), l_errCode);
203 
204                     if (l_errCode)
205                     {
206                         logging::logMessage(
207                             "Failed to insert value into map, error : " +
208                             commonUtility::getErrCodeMsg(l_errCode));
209                     }
210                 }
211                 else
212                 {
213                     propertyMap.emplace(
214                         property, propValuePair.value().get<std::string>());
215                 }
216             }
217             else if (propValuePair.value().is_array())
218             {
219                 try
220                 {
221                     propertyMap.emplace(
222                         property,
223                         propValuePair.value().get<types::BinaryVector>());
224                 }
225                 catch (const nlohmann::detail::type_error& e)
226                 {
227                     std::cerr << "Type exception: " << e.what() << "\n";
228                 }
229             }
230             else if (propValuePair.value().is_number())
231             {
232                 // For now assume the value is a size_t.  In the future it would
233                 // be nice to come up with a way to get the type from the JSON.
234                 propertyMap.emplace(property,
235                                     propValuePair.value().get<size_t>());
236             }
237             else if (propValuePair.value().is_object())
238             {
239                 const std::string& record =
240                     propValuePair.value().value("recordName", "");
241                 const std::string& keyword =
242                     propValuePair.value().value("keywordName", "");
243                 const std::string& encoding =
244                     propValuePair.value().value("encoding", "");
245 
246                 if (auto ipzVpdMap =
247                         std::get_if<types::IPZVpdMap>(&parsedVpdMap))
248                 {
249                     if (!record.empty() && !keyword.empty() &&
250                         (*ipzVpdMap).count(record) &&
251                         (*ipzVpdMap).at(record).count(keyword))
252                     {
253                         auto encoded = vpdSpecificUtility::encodeKeyword(
254                             ((*ipzVpdMap).at(record).at(keyword)), encoding,
255                             l_errCode);
256 
257                         if (l_errCode)
258                         {
259                             logging::logMessage(
260                                 std::string(
261                                     "Failed to get encoded keyword value for : ") +
262                                 keyword + std::string(", error : ") +
263                                 commonUtility::getErrCodeMsg(l_errCode));
264                         }
265 
266                         propertyMap.emplace(property, encoded);
267                     }
268                 }
269                 else if (auto kwdVpdMap =
270                              std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
271                 {
272                     if (!keyword.empty() && (*kwdVpdMap).count(keyword))
273                     {
274                         if (auto kwValue = std::get_if<types::BinaryVector>(
275                                 &(*kwdVpdMap).at(keyword)))
276                         {
277                             auto encodedValue =
278                                 vpdSpecificUtility::encodeKeyword(
279                                     std::string((*kwValue).begin(),
280                                                 (*kwValue).end()),
281                                     encoding, l_errCode);
282 
283                             if (l_errCode)
284                             {
285                                 logging::logMessage(
286                                     std::string(
287                                         "Failed to get encoded keyword value for : ") +
288                                     keyword + std::string(", error : ") +
289                                     commonUtility::getErrCodeMsg(l_errCode));
290                             }
291 
292                             propertyMap.emplace(property, encodedValue);
293                         }
294                         else if (auto kwValue = std::get_if<std::string>(
295                                      &(*kwdVpdMap).at(keyword)))
296                         {
297                             auto encodedValue =
298                                 vpdSpecificUtility::encodeKeyword(
299                                     std::string((*kwValue).begin(),
300                                                 (*kwValue).end()),
301                                     encoding, l_errCode);
302 
303                             if (l_errCode)
304                             {
305                                 logging::logMessage(
306                                     "Failed to get encoded keyword value for : " +
307                                     keyword + ", error : " +
308                                     commonUtility::getErrCodeMsg(l_errCode));
309                             }
310 
311                             propertyMap.emplace(property, encodedValue);
312                         }
313                         else if (auto uintValue = std::get_if<size_t>(
314                                      &(*kwdVpdMap).at(keyword)))
315                         {
316                             propertyMap.emplace(property, *uintValue);
317                         }
318                         else
319                         {
320                             logging::logMessage(
321                                 "Unknown keyword found, Keywrod = " + keyword);
322                         }
323                     }
324                 }
325             }
326         }
327         vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
328                                           move(propertyMap), l_errCode);
329 
330         if (l_errCode)
331         {
332             logging::logMessage("Failed to insert value into map, error : " +
333                                 commonUtility::getErrCodeMsg(l_errCode));
334         }
335     }
336 }
337 
isCPUIOGoodOnly(const std::string & i_pgKeyword)338 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
339 {
340     const unsigned char l_io[] = {
341         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
342         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
343 
344     // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
345     // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
346     // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
347     // IO.
348     if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
349                constants::SIZE_OF_8EQ_IN_PG) == 0)
350     {
351         return true;
352     }
353 
354     // The CPU is not an IO
355     return false;
356 }
357 
processEmbeddedAndSynthesizedFrus(const nlohmann::json & singleFru,types::InterfaceMap & interfaces)358 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
359                                                types::InterfaceMap& interfaces)
360 {
361     // embedded property(true or false) says whether the subfru is embedded
362     // into the parent fru (or) not. VPD sets Present property only for
363     // embedded frus. If the subfru is not an embedded FRU, the subfru may
364     // or may not be physically present. Those non embedded frus will always
365     // have Present=false irrespective of its physical presence or absence.
366     // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
367     // Present to true for such sub frus.
368     // Eg: ethernet port is embedded into bmc card. So set Present to true
369     // for such sub frus. Also donot populate present property for embedded
370     // subfru which is synthesized. Currently there is no subfru which are
371     // both embedded and synthesized. But still the case is handled here.
372 
373     // Check if its required to handle presence for this FRU.
374     if (singleFru.value("handlePresence", true))
375     {
376         uint16_t l_errCode = 0;
377         types::PropertyMap presProp;
378         presProp.emplace("Present", true);
379         vpdSpecificUtility::insertOrMerge(interfaces,
380                                           "xyz.openbmc_project.Inventory.Item",
381                                           move(presProp), l_errCode);
382 
383         if (l_errCode)
384         {
385             logging::logMessage("Failed to insert value into map, error : " +
386                                 commonUtility::getErrCodeMsg(l_errCode));
387         }
388     }
389 }
390 
processExtraInterfaces(const nlohmann::json & singleFru,types::InterfaceMap & interfaces,const types::VPDMapVariant & parsedVpdMap)391 void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
392                                     types::InterfaceMap& interfaces,
393                                     const types::VPDMapVariant& parsedVpdMap)
394 {
395     populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
396     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
397     {
398         if (singleFru["extraInterfaces"].contains(
399                 "xyz.openbmc_project.Inventory.Item.Cpu"))
400         {
401             auto itrToRec = (*ipzVpdMap).find("CP00");
402             if (itrToRec == (*ipzVpdMap).end())
403             {
404                 return;
405             }
406 
407             uint16_t l_errCode = 0;
408             const std::string pgKeywordValue{vpdSpecificUtility::getKwVal(
409                 itrToRec->second, "PG", l_errCode)};
410 
411             if (!pgKeywordValue.empty())
412             {
413                 if (isCPUIOGoodOnly(pgKeywordValue))
414                 {
415                     interfaces["xyz.openbmc_project.Inventory.Item"]
416                               ["PrettyName"] = "IO Module";
417                 }
418             }
419             else
420             {
421                 throw DataException(
422                     std::string(__FUNCTION__) +
423                     "Failed to get value for keyword PG, error : " +
424                     commonUtility::getErrCodeMsg(l_errCode));
425             }
426         }
427     }
428 }
429 
processCopyRecordFlag(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)430 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
431                                    const types::VPDMapVariant& parsedVpdMap,
432                                    types::InterfaceMap& interfaces)
433 {
434     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
435     {
436         for (const auto& record : singleFru["copyRecords"])
437         {
438             const std::string& recordName = record;
439             if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
440             {
441                 populateIPZVPDpropertyMap(interfaces,
442                                           (*ipzVpdMap).at(recordName),
443                                           constants::ipzVpdInf + recordName);
444             }
445         }
446     }
447 }
448 
processInheritFlag(const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)449 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
450                                 types::InterfaceMap& interfaces)
451 {
452     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
453     {
454         for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
455         {
456             populateIPZVPDpropertyMap(interfaces, kwdValueMap,
457                                       constants::ipzVpdInf + recordName);
458         }
459     }
460     else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
461     {
462         populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
463     }
464 
465     if (m_parsedJson.contains("commonInterfaces"))
466     {
467         populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
468                            parsedVpdMap);
469     }
470 }
471 
processFruWithCCIN(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap)472 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
473                                 const types::VPDMapVariant& parsedVpdMap)
474 {
475     if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
476     {
477         auto itrToRec = (*ipzVPDMap).find("VINI");
478         if (itrToRec == (*ipzVPDMap).end())
479         {
480             return false;
481         }
482 
483         uint16_t l_errCode = 0;
484         std::string ccinFromVpd{
485             vpdSpecificUtility::getKwVal(itrToRec->second, "CC", l_errCode)};
486 
487         if (ccinFromVpd.empty())
488         {
489             logging::logMessage("Failed to get CCIN kwd value, error : " +
490                                 commonUtility::getErrCodeMsg(l_errCode));
491             return false;
492         }
493 
494         transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
495                   ::toupper);
496 
497         std::vector<std::string> ccinList;
498         for (std::string ccin : singleFru["ccin"])
499         {
500             transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
501             ccinList.push_back(ccin);
502         }
503 
504         if (ccinList.empty())
505         {
506             return false;
507         }
508 
509         if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
510             ccinList.end())
511         {
512             return false;
513         }
514     }
515     return true;
516 }
517 
processFunctionalProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)518 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
519                                        types::InterfaceMap& io_interfaces)
520 {
521     if (!dbusUtility::isChassisPowerOn())
522     {
523         std::vector<std::string> l_operationalStatusInf = {
524             constants::operationalStatusInf};
525 
526         auto mapperObjectMap = dbusUtility::getObjectMap(
527             i_inventoryObjPath, l_operationalStatusInf);
528 
529         // If the object has been found. Check if it is under PIM.
530         if (mapperObjectMap.size() != 0)
531         {
532             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
533             {
534                 if (l_serviceName == constants::pimServiceName)
535                 {
536                     // The object is already under PIM. No need to process
537                     // again. Retain the old value.
538                     return;
539                 }
540             }
541         }
542 
543         // Implies value is not there in D-Bus. Populate it with default
544         // value "true".
545         uint16_t l_errCode = 0;
546         types::PropertyMap l_functionalProp;
547         l_functionalProp.emplace("Functional", true);
548         vpdSpecificUtility::insertOrMerge(io_interfaces,
549                                           constants::operationalStatusInf,
550                                           move(l_functionalProp), l_errCode);
551 
552         if (l_errCode)
553         {
554             logging::logMessage(
555                 "Failed to insert interface into map, error : " +
556                 commonUtility::getErrCodeMsg(l_errCode));
557         }
558     }
559 
560     // if chassis is power on. Functional property should be there on D-Bus.
561     // Don't process.
562     return;
563 }
564 
processEnabledProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)565 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
566                                     types::InterfaceMap& io_interfaces)
567 {
568     if (!dbusUtility::isChassisPowerOn())
569     {
570         std::vector<std::string> l_enableInf = {constants::enableInf};
571 
572         auto mapperObjectMap =
573             dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
574 
575         // If the object has been found. Check if it is under PIM.
576         if (mapperObjectMap.size() != 0)
577         {
578             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
579             {
580                 if (l_serviceName == constants::pimServiceName)
581                 {
582                     // The object is already under PIM. No need to process
583                     // again. Retain the old value.
584                     return;
585                 }
586             }
587         }
588 
589         // Implies value is not there in D-Bus. Populate it with default
590         // value "true".
591         uint16_t l_errCode = 0;
592         types::PropertyMap l_enabledProp;
593         l_enabledProp.emplace("Enabled", true);
594         vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
595                                           move(l_enabledProp), l_errCode);
596 
597         if (l_errCode)
598         {
599             logging::logMessage(
600                 "Failed to insert interface into map, error : " +
601                 commonUtility::getErrCodeMsg(l_errCode));
602         }
603     }
604 
605     // if chassis is power on. Enabled property should be there on D-Bus.
606     // Don't process.
607     return;
608 }
609 
populateDbus(const types::VPDMapVariant & parsedVpdMap,types::ObjectMap & objectInterfaceMap,const std::string & vpdFilePath)610 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
611                           types::ObjectMap& objectInterfaceMap,
612                           const std::string& vpdFilePath)
613 {
614     if (vpdFilePath.empty())
615     {
616         throw std::runtime_error(
617             std::string(__FUNCTION__) +
618             "Invalid parameter passed to populateDbus API.");
619     }
620 
621     // JSON config is mandatory for processing of "if". Add "else" for any
622     // processing without config JSON.
623     if (!m_parsedJson.empty())
624     {
625         types::InterfaceMap interfaces;
626 
627         for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
628         {
629             const auto& inventoryPath = aFru["inventoryPath"];
630             sdbusplus::message::object_path fruObjectPath(inventoryPath);
631             if (aFru.contains("ccin"))
632             {
633                 if (!processFruWithCCIN(aFru, parsedVpdMap))
634                 {
635                     continue;
636                 }
637             }
638 
639             if (aFru.value("inherit", true))
640             {
641                 processInheritFlag(parsedVpdMap, interfaces);
642             }
643 
644             // If specific record needs to be copied.
645             if (aFru.contains("copyRecords"))
646             {
647                 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
648             }
649 
650             if (aFru.contains("extraInterfaces"))
651             {
652                 // Process extra interfaces w.r.t a FRU.
653                 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
654             }
655 
656             // Process FRUS which are embedded in the parent FRU and whose VPD
657             // will be synthesized.
658             if ((aFru.value("embedded", true)) &&
659                 (!aFru.value("synthesized", false)))
660             {
661                 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
662             }
663 
664             processFunctionalProperty(inventoryPath, interfaces);
665             processEnabledProperty(inventoryPath, interfaces);
666 
667             objectInterfaceMap.emplace(std::move(fruObjectPath),
668                                        std::move(interfaces));
669         }
670     }
671 }
672 
processPreAction(const std::string & i_vpdFilePath,const std::string & i_flagToProcess,uint16_t & i_errCode)673 bool Worker::processPreAction(const std::string& i_vpdFilePath,
674                               const std::string& i_flagToProcess,
675                               uint16_t& i_errCode)
676 {
677     i_errCode = 0;
678     if (i_vpdFilePath.empty() || i_flagToProcess.empty())
679     {
680         i_errCode = error_code::INVALID_INPUT_PARAMETER;
681         return false;
682     }
683 
684     if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
685                                          i_vpdFilePath, i_flagToProcess,
686                                          i_errCode)) &&
687         (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
688     {
689         // TODO: Need a way to delete inventory object from Dbus and persisted
690         // data section in case any FRU is not present or there is any
691         // problem in collecting it. Once it has been deleted, it can be
692         // re-created in the flow of priming the inventory. This needs to be
693         // done either here or in the exception section of "parseAndPublishVPD"
694         // API. Any failure in the process of collecting FRU will land up in the
695         // excpetion of "parseAndPublishVPD".
696 
697         // If the FRU is not there, clear the VINI/CCIN data.
698         // Enity manager probes for this keyword to look for this
699         // FRU, now if the data is persistent on BMC and FRU is
700         // removed this can lead to ambiguity. Hence clearing this
701         // Keyword if FRU is absent.
702         const auto& inventoryPath =
703             m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
704                                                             "");
705 
706         if (!inventoryPath.empty())
707         {
708             types::ObjectMap l_pimObjMap{
709                 {inventoryPath,
710                  {{constants::kwdVpdInf,
711                    {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
712 
713             // Call dbus method to update on dbus
714             if (!dbusUtility::publishVpdOnDBus(std::move(l_pimObjMap)))
715             {
716                 logging::logMessage(
717                     "Call to PIM failed for file " + i_vpdFilePath);
718             }
719         }
720         else
721         {
722             logging::logMessage(
723                 "Inventory path is empty in Json for file " + i_vpdFilePath);
724         }
725 
726         return false;
727     }
728     return true;
729 }
730 
processPostAction(const std::string & i_vpdFruPath,const std::string & i_flagToProcess,const std::optional<types::VPDMapVariant> i_parsedVpd)731 bool Worker::processPostAction(
732     const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
733     const std::optional<types::VPDMapVariant> i_parsedVpd)
734 {
735     if (i_vpdFruPath.empty() || i_flagToProcess.empty())
736     {
737         logging::logMessage(
738             "Invalid input parameter. Abort processing post action");
739         return false;
740     }
741 
742     // Check if post action tag is to be triggered in the flow of collection
743     // based on some CCIN value?
744     uint16_t l_errCode = 0;
745 
746     if (m_parsedJson["frus"][i_vpdFruPath]
747             .at(0)["postAction"][i_flagToProcess]
748             .contains("ccin"))
749     {
750         if (!i_parsedVpd.has_value())
751         {
752             logging::logMessage("Empty VPD Map");
753             return false;
754         }
755 
756         // CCIN match is required to process post action for this FRU as it
757         // contains the flag.
758         if (!vpdSpecificUtility::findCcinInVpd(
759                 m_parsedJson["frus"][i_vpdFruPath].at(
760                     0)["postAction"]["collection"],
761                 i_parsedVpd.value(), l_errCode))
762         {
763             if (l_errCode)
764             {
765                 // ToDo - Check if PEL is required in case of RECORD_NOT_FOUND
766                 // and KEYWORD_NOT_FOUND error codes.
767                 logging::logMessage("Failed to find CCIN in VPD, error : " +
768                                     commonUtility::getErrCodeMsg(l_errCode));
769             }
770 
771             // If CCIN is not found, implies post action processing is not
772             // required for this FRU. Let the flow continue.
773             return true;
774         }
775     }
776 
777     if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
778                                         i_vpdFruPath, i_flagToProcess,
779                                         l_errCode))
780     {
781         logging::logMessage(
782             "Execution of post action failed for path: " + i_vpdFruPath +
783             " . Reason: " + commonUtility::getErrCodeMsg(l_errCode));
784 
785         // If post action was required and failed only in that case return
786         // false. In all other case post action is considered passed.
787         return false;
788     }
789 
790     return true;
791 }
792 
parseVpdFile(const std::string & i_vpdFilePath)793 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
794 {
795     try
796     {
797         uint16_t l_errCode = 0;
798 
799         if (i_vpdFilePath.empty())
800         {
801             throw std::runtime_error(
802                 std::string(__FUNCTION__) +
803                 " Empty VPD file path passed. Abort processing");
804         }
805 
806         bool isPreActionRequired = false;
807         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
808                                           "preAction", "collection", l_errCode))
809         {
810             isPreActionRequired = true;
811             if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
812             {
813                 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
814                 {
815                     logging::logMessage(
816                         commonUtility::getErrCodeMsg(l_errCode) +
817                         i_vpdFilePath);
818 
819                     // since pre action is reporting device not present, execute
820                     // post fail action
821                     checkAndExecutePostFailAction(i_vpdFilePath, "collection");
822 
823                     // Presence pin has been read successfully and has been read
824                     // as false, so this is not a failure case, hence returning
825                     // empty variant so that pre action is not marked as failed.
826                     return types::VPDMapVariant{};
827                 }
828                 throw std::runtime_error(
829                     std::string(__FUNCTION__) +
830                     " Pre-Action failed with error: " +
831                     commonUtility::getErrCodeMsg(l_errCode));
832             }
833         }
834         else if (l_errCode)
835         {
836             logging::logMessage(
837                 "Failed to check if pre action required for FRU [" +
838                 i_vpdFilePath +
839                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
840         }
841 
842         if (!std::filesystem::exists(i_vpdFilePath))
843         {
844             if (isPreActionRequired)
845             {
846                 throw std::runtime_error(
847                     std::string(__FUNCTION__) + " Could not find file path " +
848                     i_vpdFilePath + "Skipping parser trigger for the EEPROM");
849             }
850             return types::VPDMapVariant{};
851         }
852 
853         std::shared_ptr<Parser> vpdParser =
854             std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
855 
856         types::VPDMapVariant l_parsedVpd = vpdParser->parse();
857 
858         // Before returning, as collection is over, check if FRU qualifies for
859         // any post action in the flow of collection.
860         // Note: Don't change the order, post action needs to be processed only
861         // after collection for FRU is successfully done.
862         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
863                                           "postAction", "collection",
864                                           l_errCode))
865         {
866             if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
867             {
868                 // Post action was required but failed while executing.
869                 // Behaviour can be undefined.
870                 EventLogger::createSyncPel(
871                     types::ErrorType::InternalFailure,
872                     types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
873                     std::string("Required post action failed for path [" +
874                                 i_vpdFilePath + "]"),
875                     std::nullopt, std::nullopt, std::nullopt, std::nullopt);
876             }
877         }
878         else if (l_errCode)
879         {
880             logging::logMessage(
881                 "Error while checking if post action required for FRU [" +
882                 i_vpdFilePath +
883                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
884         }
885 
886         return l_parsedVpd;
887     }
888     catch (std::exception& l_ex)
889     {
890         std::string l_exMsg{
891             std::string(__FUNCTION__) + " : VPD parsing failed for " +
892             i_vpdFilePath + " due to error: " + l_ex.what()};
893 
894         // If post fail action is required, execute it.
895         checkAndExecutePostFailAction(i_vpdFilePath, "collection");
896 
897         if (typeid(l_ex) == typeid(DataException))
898         {
899             throw DataException(l_exMsg);
900         }
901         else if (typeid(l_ex) == typeid(EccException))
902         {
903             throw EccException(l_exMsg);
904         }
905         throw std::runtime_error(l_exMsg);
906     }
907 }
908 
parseAndPublishVPD(const std::string & i_vpdFilePath)909 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
910     const std::string& i_vpdFilePath)
911 {
912     std::string l_inventoryPath{};
913     uint16_t l_errCode = 0;
914     try
915     {
916         m_semaphore.acquire();
917 
918         // Thread launched.
919         m_mutex.lock();
920         m_activeCollectionThreadCount++;
921         m_mutex.unlock();
922 
923         vpdSpecificUtility::setCollectionStatusProperty(
924             i_vpdFilePath, types::VpdCollectionStatus::InProgress, m_parsedJson,
925             l_errCode);
926         if (l_errCode)
927         {
928             m_logger->logMessage(
929                 "Failed to set collection status for path " + i_vpdFilePath +
930                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
931         }
932 
933         const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
934         if (!std::holds_alternative<std::monostate>(parsedVpdMap))
935         {
936             types::ObjectMap objectInterfaceMap;
937             populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
938 
939             // Call dbus method to update on dbus
940             if (!dbusUtility::publishVpdOnDBus(move(objectInterfaceMap)))
941             {
942                 throw std::runtime_error(
943                     std::string(__FUNCTION__) +
944                     "Call to PIM failed while publishing VPD.");
945             }
946         }
947         else
948         {
949             // Stale data from the previous boot can be present on the system.
950             // so clearing of data incase of empty map received.
951             // As empty parsedVpdMap recieved for some reason, but still
952             // considered VPD collection is completed. Hence FRU collection
953             // Status will be set as completed.
954 
955             vpdSpecificUtility::resetObjTreeVpd(i_vpdFilePath, m_parsedJson,
956                                                 l_errCode);
957 
958             if (l_errCode)
959             {
960                 m_logger->logMessage(
961                     "Failed to reset data under PIM for path [" +
962                     i_vpdFilePath +
963                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
964             }
965 
966             m_logger->logMessage("Empty parsedVpdMap recieved for path [" +
967                                      i_vpdFilePath + "]. Check PEL for reason.",
968                                  PlaceHolder::COLLECTION);
969         }
970 
971         vpdSpecificUtility::setCollectionStatusProperty(
972             i_vpdFilePath, types::VpdCollectionStatus::Completed, m_parsedJson,
973             l_errCode);
974 
975         if (l_errCode)
976         {
977             m_logger->logMessage(
978                 "Failed to set collection status as completed for path " +
979                 i_vpdFilePath +
980                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
981         }
982 
983         m_semaphore.release();
984         return std::make_tuple(true, i_vpdFilePath);
985     }
986     catch (const std::exception& ex)
987     {
988         uint16_t l_errCode = 0;
989 
990         // stale data can be present on the system from previous boot. so
991         // clearing of data in case of failure.
992         vpdSpecificUtility::resetObjTreeVpd(i_vpdFilePath, m_parsedJson,
993                                             l_errCode);
994 
995         if (l_errCode)
996         {
997             m_logger->logMessage(
998                 "Failed to reset under PIM for path [" + i_vpdFilePath +
999                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1000         }
1001 
1002         vpdSpecificUtility::setCollectionStatusProperty(
1003             i_vpdFilePath, types::VpdCollectionStatus::Failed, m_parsedJson,
1004             l_errCode);
1005         if (l_errCode)
1006         {
1007             m_logger->logMessage(
1008                 "Failed to set collection status as failed for path " +
1009                 i_vpdFilePath +
1010                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1011         }
1012 
1013         // handle all the exceptions internally. Return only true/false
1014         // based on status of execution.
1015         if (typeid(ex) == std::type_index(typeid(DataException)))
1016         {
1017             // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1018             // logging error for these cases.
1019             if (vpdSpecificUtility::isPass1Planar(l_errCode))
1020             {
1021                 std::string l_invPath =
1022                     jsonUtility::getInventoryObjPathFromJson(
1023                         m_parsedJson, i_vpdFilePath, l_errCode);
1024 
1025                 if (l_errCode != 0)
1026                 {
1027                     m_logger->logMessage(
1028                         "Failed to get inventory object path from JSON for FRU [" +
1029                             i_vpdFilePath + "], error: " +
1030                             commonUtility::getErrCodeMsg(l_errCode),
1031                         PlaceHolder::COLLECTION);
1032                 }
1033 
1034                 const std::string& l_invPathLeafValue =
1035                     sdbusplus::message::object_path(l_invPath).filename();
1036 
1037                 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1038                      std::string::npos))
1039                 {
1040                     // skip logging any PEL for PCIe cards on pass 1 planar.
1041                     return std::make_tuple(false, i_vpdFilePath);
1042                 }
1043             }
1044             else if (l_errCode)
1045             {
1046                 m_logger->logMessage(
1047                     "Failed to check if system is Pass 1 Planar, error : " +
1048                         commonUtility::getErrCodeMsg(l_errCode),
1049                     PlaceHolder::COLLECTION);
1050             }
1051         }
1052 
1053         EventLogger::createSyncPel(
1054             EventLogger::getErrorType(ex),
1055             (typeid(ex) == typeid(DataException)) ||
1056                     (typeid(ex) == typeid(EccException))
1057                 ? types::SeverityType::Warning
1058                 : types::SeverityType::Informational,
1059             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1060             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1061 
1062         // TODO: Figure out a way to clear data in case of any failure at
1063         // runtime.
1064 
1065         // set present property to false for any error case. In future this will
1066         // be replaced by presence logic.
1067         // Update Present property for this FRU only if we handle Present
1068         // property for the FRU.
1069         if (isPresentPropertyHandlingRequired(
1070                 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1071         {
1072             setPresentProperty(i_vpdFilePath, false);
1073         }
1074 
1075         m_semaphore.release();
1076         return std::make_tuple(false, i_vpdFilePath);
1077     }
1078 }
1079 
skipPathForCollection(const std::string & i_vpdFilePath)1080 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1081 {
1082     if (i_vpdFilePath.empty())
1083     {
1084         return true;
1085     }
1086 
1087     // skip processing of system VPD again as it has been already collected.
1088     if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1089     {
1090         return true;
1091     }
1092 
1093     if (dbusUtility::isChassisPowerOn())
1094     {
1095         // If chassis is powered on, skip collecting FRUs which are
1096         // powerOffOnly.
1097 
1098         uint16_t l_errCode = 0;
1099         if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath,
1100                                            l_errCode))
1101         {
1102             return true;
1103         }
1104         else if (l_errCode)
1105         {
1106             m_logger->logMessage(
1107                 "Failed to check if FRU is power off only for FRU [" +
1108                     i_vpdFilePath +
1109                     "], error : " + commonUtility::getErrCodeMsg(l_errCode),
1110                 PlaceHolder::COLLECTION);
1111         }
1112 
1113         std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1114             m_parsedJson, i_vpdFilePath, l_errCode);
1115 
1116         if (l_errCode)
1117         {
1118             m_logger->logMessage(
1119                 "Failed to get inventory path from JSON for FRU [" +
1120                     i_vpdFilePath +
1121                     "], error : " + commonUtility::getErrCodeMsg(l_errCode),
1122                 PlaceHolder::COLLECTION);
1123 
1124             return false;
1125         }
1126 
1127         const std::string& l_invPathLeafValue =
1128             sdbusplus::message::object_path(l_invPath).filename();
1129 
1130         if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1131         {
1132             return true;
1133         }
1134     }
1135 
1136     return false;
1137 }
1138 
collectFrusFromJson()1139 void Worker::collectFrusFromJson()
1140 {
1141     // A parsed JSON file should be present to pick FRUs EEPROM paths
1142     if (m_parsedJson.empty())
1143     {
1144         throw JsonException(
1145             std::string(__FUNCTION__) +
1146                 ": Config JSON is mandatory for processing of FRUs through this API.",
1147             m_configJsonPath);
1148     }
1149 
1150     const nlohmann::json& listOfFrus =
1151         m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1152 
1153     for (const auto& itemFRUS : listOfFrus.items())
1154     {
1155         const std::string& vpdFilePath = itemFRUS.key();
1156 
1157         if (skipPathForCollection(vpdFilePath))
1158         {
1159             continue;
1160         }
1161 
1162         try
1163         {
1164             std::thread{[vpdFilePath, this]() {
1165                 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1166 
1167                 m_mutex.lock();
1168                 m_activeCollectionThreadCount--;
1169                 m_mutex.unlock();
1170 
1171                 if (!m_activeCollectionThreadCount)
1172                 {
1173                     m_isAllFruCollected = true;
1174                 }
1175             }}.detach();
1176         }
1177         catch (const std::exception& l_ex)
1178         {
1179             // add vpdFilePath(EEPROM path) to failed list
1180             m_failedEepromPaths.push_front(vpdFilePath);
1181         }
1182     }
1183 }
1184 
deleteFruVpd(const std::string & i_dbusObjPath)1185 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1186 {
1187     if (i_dbusObjPath.empty())
1188     {
1189         throw std::runtime_error("Given DBus object path is empty.");
1190     }
1191 
1192     uint16_t l_errCode = 0;
1193     const std::string& l_fruPath =
1194         jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1195 
1196     if (l_errCode)
1197     {
1198         logging::logMessage(
1199             "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1200             "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1201             " Aborting FRU VPD deletion.");
1202         return;
1203     }
1204 
1205     try
1206     {
1207         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, "preAction",
1208                                           "deletion", l_errCode))
1209         {
1210             if (!processPreAction(l_fruPath, "deletion", l_errCode))
1211             {
1212                 std::string l_msg = "Pre action failed";
1213                 if (l_errCode)
1214                 {
1215                     l_msg += " Reason: " +
1216                              commonUtility::getErrCodeMsg(l_errCode);
1217                 }
1218                 throw std::runtime_error(l_msg);
1219             }
1220         }
1221         else if (l_errCode)
1222         {
1223             logging::logMessage(
1224                 "Failed to check if pre action required for FRU [" + l_fruPath +
1225                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1226         }
1227 
1228         vpdSpecificUtility::resetObjTreeVpd(l_fruPath, m_parsedJson, l_errCode);
1229 
1230         if (l_errCode)
1231         {
1232             throw std::runtime_error(
1233                 "Failed to clear data under PIM for FRU [" + l_fruPath +
1234                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1235         }
1236 
1237         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, "postAction",
1238                                           "deletion", l_errCode))
1239         {
1240             if (!processPostAction(l_fruPath, "deletion"))
1241             {
1242                 throw std::runtime_error("Post action failed");
1243             }
1244         }
1245         else if (l_errCode)
1246         {
1247             logging::logMessage(
1248                 "Failed to check if post action required during deletion for FRU [" +
1249                 l_fruPath +
1250                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1251         }
1252 
1253         logging::logMessage(
1254             "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1255     }
1256     catch (const std::exception& l_ex)
1257     {
1258         uint16_t l_errCode = 0;
1259         std::string l_errMsg =
1260             "Failed to delete VPD for FRU : " + i_dbusObjPath +
1261             " error: " + std::string(l_ex.what());
1262 
1263         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1264                                           "postFailAction", "deletion",
1265                                           l_errCode))
1266         {
1267             if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1268                                                     "deletion", l_errCode))
1269             {
1270                 l_errMsg += ". Post fail action also failed, error : " +
1271                             commonUtility::getErrCodeMsg(l_errCode);
1272             }
1273         }
1274         else if (l_errCode)
1275         {
1276             l_errMsg +=
1277                 ". Failed to check if post fail action required, error : " +
1278                 commonUtility::getErrCodeMsg(l_errCode);
1279         }
1280 
1281         logging::logMessage(l_errMsg);
1282     }
1283 }
1284 
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1285 void Worker::setPresentProperty(const std::string& i_vpdPath,
1286                                 const bool& i_value)
1287 {
1288     try
1289     {
1290         if (i_vpdPath.empty())
1291         {
1292             throw std::runtime_error(
1293                 "Path is empty. Can't set present property");
1294         }
1295 
1296         types::ObjectMap l_objectInterfaceMap;
1297 
1298         // If the given path is EEPROM path.
1299         if (m_parsedJson["frus"].contains(i_vpdPath))
1300         {
1301             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1302             {
1303                 sdbusplus::message::object_path l_fruObjectPath(
1304                     l_Fru["inventoryPath"]);
1305 
1306                 types::PropertyMap l_propertyValueMap;
1307                 l_propertyValueMap.emplace("Present", i_value);
1308 
1309                 uint16_t l_errCode = 0;
1310                 types::InterfaceMap l_interfaces;
1311                 vpdSpecificUtility::insertOrMerge(
1312                     l_interfaces, constants::inventoryItemInf,
1313                     move(l_propertyValueMap), l_errCode);
1314 
1315                 if (l_errCode)
1316                 {
1317                     logging::logMessage(
1318                         "Failed to insert value into map, error : " +
1319                         commonUtility::getErrCodeMsg(l_errCode));
1320                 }
1321 
1322                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1323                                              std::move(l_interfaces));
1324             }
1325         }
1326         else
1327         {
1328             // consider it as an inventory path.
1329             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1330             {
1331                 throw std::runtime_error(
1332                     "Invalid inventory path: " + i_vpdPath);
1333             }
1334 
1335             types::PropertyMap l_propertyValueMap;
1336             l_propertyValueMap.emplace("Present", i_value);
1337 
1338             uint16_t l_errCode = 0;
1339             types::InterfaceMap l_interfaces;
1340             vpdSpecificUtility::insertOrMerge(
1341                 l_interfaces, constants::inventoryItemInf,
1342                 move(l_propertyValueMap), l_errCode);
1343 
1344             if (l_errCode)
1345             {
1346                 logging::logMessage(
1347                     "Failed to insert value into map, error : " +
1348                     commonUtility::getErrCodeMsg(l_errCode));
1349             }
1350 
1351             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1352         }
1353 
1354         // Call dbus method to update on dbus
1355         if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1356         {
1357             throw DbusException(
1358                 std::string(__FUNCTION__) +
1359                 "Call to PIM failed while setting present property for path " +
1360                 i_vpdPath);
1361         }
1362     }
1363     catch (const std::exception& l_ex)
1364     {
1365         EventLogger::createSyncPel(
1366             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1367             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1368             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1369     }
1370 }
1371 
performVpdRecollection()1372 void Worker::performVpdRecollection()
1373 {
1374     try
1375     {
1376         // Check if system config JSON is present
1377         if (m_parsedJson.empty())
1378         {
1379             throw std::runtime_error(
1380                 "System config json object is empty, can't process recollection.");
1381         }
1382 
1383         uint16_t l_errCode = 0;
1384         const auto& l_frusReplaceableAtStandby =
1385             jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson,
1386                                                            l_errCode);
1387 
1388         if (l_errCode)
1389         {
1390             logging::logMessage(
1391                 "Failed to get list of FRUs replaceable at runtime, error : " +
1392                 commonUtility::getErrCodeMsg(l_errCode));
1393             return;
1394         }
1395 
1396         for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1397         {
1398             // ToDo: Add some logic/trace to know the flow to
1399             // collectSingleFruVpd has been directed via
1400             // performVpdRecollection.
1401             collectSingleFruVpd(l_fruInventoryPath);
1402         }
1403         return;
1404     }
1405 
1406     catch (const std::exception& l_ex)
1407     {
1408         // TODO Log PEL
1409         logging::logMessage(
1410             "VPD recollection failed with error: " + std::string(l_ex.what()));
1411     }
1412 }
1413 
collectSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)1414 void Worker::collectSingleFruVpd(
1415     const sdbusplus::message::object_path& i_dbusObjPath)
1416 {
1417     std::string l_fruPath{};
1418     uint16_t l_errCode = 0;
1419 
1420     try
1421     {
1422         // Check if system config JSON is present
1423         if (m_parsedJson.empty())
1424         {
1425             logging::logMessage(
1426                 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1427                 std::string(i_dbusObjPath));
1428             return;
1429         }
1430 
1431         // Get FRU path for the given D-bus object path from JSON
1432         l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
1433                                                     l_errCode);
1434 
1435         if (l_fruPath.empty())
1436         {
1437             if (l_errCode)
1438             {
1439                 logging::logMessage(
1440                     "Failed to get FRU path for [" +
1441                     std::string(i_dbusObjPath) +
1442                     "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1443                     " Aborting single FRU VPD collection.");
1444                 return;
1445             }
1446 
1447             logging::logMessage(
1448                 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1449                 std::string(i_dbusObjPath));
1450             return;
1451         }
1452 
1453         // Check if host is up and running
1454         if (dbusUtility::isHostRunning())
1455         {
1456             uint16_t l_errCode = 0;
1457             bool isFruReplaceableAtRuntime =
1458                 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1459                                                        l_errCode);
1460 
1461             if (l_errCode)
1462             {
1463                 logging::logMessage(
1464                     "Failed to check if FRU is replaceable at runtime for FRU : [" +
1465                     std::string(i_dbusObjPath) +
1466                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1467                 return;
1468             }
1469 
1470             if (!isFruReplaceableAtRuntime)
1471             {
1472                 logging::logMessage(
1473                     "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1474                     std::string(i_dbusObjPath));
1475                 return;
1476             }
1477         }
1478         else if (dbusUtility::isBMCReady())
1479         {
1480             uint16_t l_errCode = 0;
1481             bool isFruReplaceableAtStandby =
1482                 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
1483                                                        l_errCode);
1484 
1485             if (l_errCode)
1486             {
1487                 logging::logMessage(
1488                     "Error while checking if FRU is replaceable at standby for FRU [" +
1489                     std::string(i_dbusObjPath) +
1490                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1491             }
1492 
1493             bool isFruReplaceableAtRuntime =
1494                 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1495                                                        l_errCode);
1496 
1497             if (l_errCode)
1498             {
1499                 logging::logMessage(
1500                     "Failed to check if FRU is replaceable at runtime for FRU : [" +
1501                     std::string(i_dbusObjPath) +
1502                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1503                 return;
1504             }
1505 
1506             if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
1507             {
1508                 logging::logMessage(
1509                     "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1510                     std::string(i_dbusObjPath));
1511                 return;
1512             }
1513         }
1514 
1515         vpdSpecificUtility::setCollectionStatusProperty(
1516             l_fruPath, types::VpdCollectionStatus::InProgress, m_parsedJson,
1517             l_errCode);
1518         if (l_errCode)
1519         {
1520             m_logger->logMessage(
1521                 "Failed to set collection status for path " + l_fruPath +
1522                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1523         }
1524 
1525         // Parse VPD
1526         types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1527 
1528         // If l_parsedVpd is pointing to std::monostate
1529         if (l_parsedVpd.index() == 0)
1530         {
1531             // As empty parsedVpdMap recieved for some reason, but still
1532             // considered VPD collection is completed. Hence FRU collection
1533             // Status will be set as completed.
1534             m_logger->logMessage("Empty parsed VPD map received for " +
1535                                  std::string(i_dbusObjPath));
1536 
1537             // Stale data from the previous boot can be present on the system.
1538             // so clearing of data.
1539             vpdSpecificUtility::resetObjTreeVpd(std::string(i_dbusObjPath),
1540                                                 m_parsedJson, l_errCode);
1541 
1542             if (l_errCode)
1543             {
1544                 m_logger->logMessage(
1545                     "Failed to reset data under PIM for path [" +
1546                     std::string(i_dbusObjPath) +
1547                     "] error : " + commonUtility::getErrCodeMsg(l_errCode));
1548             }
1549         }
1550         else
1551         {
1552             types::ObjectMap l_dbusObjectMap;
1553             // Get D-bus object map from worker class
1554             populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1555 
1556             if (l_dbusObjectMap.empty())
1557             {
1558                 throw std::runtime_error(
1559                     "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1560                     std::string(i_dbusObjPath));
1561             }
1562 
1563             // Call method to update the dbus
1564             if (!dbusUtility::publishVpdOnDBus(move(l_dbusObjectMap)))
1565             {
1566                 throw std::runtime_error(
1567                     "publishVpdOnDBus failed. Single FRU VPD collection failed for " +
1568                     std::string(i_dbusObjPath));
1569             }
1570         }
1571 
1572         vpdSpecificUtility::setCollectionStatusProperty(
1573             l_fruPath, types::VpdCollectionStatus::Completed, m_parsedJson,
1574             l_errCode);
1575         if (l_errCode)
1576         {
1577             m_logger->logMessage(
1578                 "Failed to set collection status as completed for path " +
1579                 l_fruPath +
1580                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1581         }
1582     }
1583     catch (const std::exception& l_error)
1584     {
1585         std::string l_errMsg;
1586         vpdSpecificUtility::resetObjTreeVpd(std::string(i_dbusObjPath),
1587                                             m_parsedJson, l_errCode);
1588 
1589         if (l_errCode)
1590         {
1591             l_errMsg += "Failed to reset data under PIM for path [" +
1592                         std::string(i_dbusObjPath) + "], error : " +
1593                         commonUtility::getErrCodeMsg(l_errCode) + ". ";
1594         }
1595 
1596         vpdSpecificUtility::setCollectionStatusProperty(
1597             l_fruPath, types::VpdCollectionStatus::Failed, m_parsedJson,
1598             l_errCode);
1599         if (l_errCode)
1600         {
1601             l_errMsg += "Failed to set collection status as failed for path " +
1602                         l_fruPath +
1603                         "Reason: " + commonUtility::getErrCodeMsg(l_errCode);
1604         }
1605         // TODO: Log PEL
1606         m_logger->logMessage(l_errMsg + std::string(l_error.what()));
1607     }
1608 }
1609 
checkAndExecutePostFailAction(const std::string & i_vpdFilePath,const std::string & i_flowFlag) const1610 void Worker::checkAndExecutePostFailAction(
1611     const std::string& i_vpdFilePath,
1612     const std::string& i_flowFlag) const noexcept
1613 {
1614     try
1615     {
1616         uint16_t l_errCode{0};
1617         if (!jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1618                                            "postFailAction", i_flowFlag,
1619                                            l_errCode))
1620         {
1621             if (l_errCode)
1622             {
1623                 m_logger->logMessage(
1624                     "Failed to check if postFailAction is required. Error: " +
1625                         commonUtility::getErrCodeMsg(l_errCode),
1626                     i_flowFlag == "collection" ? PlaceHolder::COLLECTION
1627                                                : PlaceHolder::DEFAULT);
1628             }
1629             return;
1630         }
1631 
1632         if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1633                                                 i_flowFlag, l_errCode))
1634         {
1635             m_logger->logMessage("Failed to execute postFailAction. Error: " +
1636                                      commonUtility::getErrCodeMsg(l_errCode),
1637                                  i_flowFlag == "collection"
1638                                      ? PlaceHolder::COLLECTION
1639                                      : PlaceHolder::DEFAULT);
1640         }
1641     }
1642     catch (const std::exception& l_ex)
1643     {
1644         m_logger->logMessage(
1645             "Failed to check and execute postFailAction. Error: " +
1646                 std::string(l_ex.what()),
1647             i_flowFlag == "collection" ? PlaceHolder::COLLECTION
1648                                        : PlaceHolder::DEFAULT);
1649     }
1650 }
1651 
1652 } // namespace vpd
1653