xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision 8bc3c2346b90a2cfd95b26ba192cc1cba3eb66a1)
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 
871                 m_logger->logMessage(
872                     std::string("Required post action failed for path [" +
873                                 i_vpdFilePath + "]."),
874                     PlaceHolder::PEL,
875                     types::PelInfoTuple{types::ErrorType::InternalFailure,
876                                         types::SeverityType::Warning, 0,
877                                         std::nullopt, std::nullopt,
878                                         std::nullopt, std::nullopt});
879             }
880         }
881         else if (l_errCode)
882         {
883             logging::logMessage(
884                 "Error while checking if post action required for FRU [" +
885                 i_vpdFilePath +
886                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
887         }
888 
889         return l_parsedVpd;
890     }
891     catch (std::exception& l_ex)
892     {
893         std::string l_exMsg{
894             std::string(__FUNCTION__) + " : VPD parsing failed for " +
895             i_vpdFilePath + " due to error: " + l_ex.what()};
896 
897         // If post fail action is required, execute it.
898         checkAndExecutePostFailAction(i_vpdFilePath, "collection");
899 
900         if (typeid(l_ex) == typeid(DataException))
901         {
902             throw DataException(l_exMsg);
903         }
904         else if (typeid(l_ex) == typeid(EccException))
905         {
906             throw EccException(l_exMsg);
907         }
908         throw std::runtime_error(l_exMsg);
909     }
910 }
911 
parseAndPublishVPD(const std::string & i_vpdFilePath)912 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
913     const std::string& i_vpdFilePath)
914 {
915     std::string l_inventoryPath{};
916     uint16_t l_errCode = 0;
917     try
918     {
919         m_semaphore.acquire();
920 
921         // Thread launched.
922         m_mutex.lock();
923         m_activeCollectionThreadCount++;
924         m_mutex.unlock();
925 
926         vpdSpecificUtility::setCollectionStatusProperty(
927             i_vpdFilePath, types::VpdCollectionStatus::InProgress, m_parsedJson,
928             l_errCode);
929         if (l_errCode)
930         {
931             m_logger->logMessage(
932                 "Failed to set collection status for path " + i_vpdFilePath +
933                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
934         }
935 
936         const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
937         if (!std::holds_alternative<std::monostate>(parsedVpdMap))
938         {
939             types::ObjectMap objectInterfaceMap;
940             populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
941 
942             // Call dbus method to update on dbus
943             if (!dbusUtility::publishVpdOnDBus(move(objectInterfaceMap)))
944             {
945                 throw std::runtime_error(
946                     std::string(__FUNCTION__) +
947                     "Call to PIM failed while publishing VPD.");
948             }
949         }
950         else
951         {
952             // Stale data from the previous boot can be present on the system.
953             // so clearing of data incase of empty map received.
954             // As empty parsedVpdMap recieved for some reason, but still
955             // considered VPD collection is completed. Hence FRU collection
956             // Status will be set as completed.
957 
958             vpdSpecificUtility::resetObjTreeVpd(i_vpdFilePath, m_parsedJson,
959                                                 l_errCode);
960 
961             if (l_errCode)
962             {
963                 m_logger->logMessage(
964                     "Failed to reset data under PIM for path [" +
965                     i_vpdFilePath +
966                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
967             }
968 
969             m_logger->logMessage("Empty parsedVpdMap recieved for path [" +
970                                      i_vpdFilePath + "]. Check PEL for reason.",
971                                  PlaceHolder::COLLECTION);
972         }
973 
974         vpdSpecificUtility::setCollectionStatusProperty(
975             i_vpdFilePath, types::VpdCollectionStatus::Completed, m_parsedJson,
976             l_errCode);
977 
978         if (l_errCode)
979         {
980             m_logger->logMessage(
981                 "Failed to set collection status as completed for path " +
982                 i_vpdFilePath +
983                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
984         }
985 
986         m_semaphore.release();
987         return std::make_tuple(true, i_vpdFilePath);
988     }
989     catch (const std::exception& l_ex)
990     {
991         uint16_t l_errCode = 0;
992 
993         // stale data can be present on the system from previous boot. so
994         // clearing of data in case of failure.
995         vpdSpecificUtility::resetObjTreeVpd(i_vpdFilePath, m_parsedJson,
996                                             l_errCode);
997 
998         if (l_errCode)
999         {
1000             m_logger->logMessage(
1001                 "Failed to reset under PIM for path [" + i_vpdFilePath +
1002                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1003         }
1004 
1005         vpdSpecificUtility::setCollectionStatusProperty(
1006             i_vpdFilePath, types::VpdCollectionStatus::Failed, m_parsedJson,
1007             l_errCode);
1008         if (l_errCode)
1009         {
1010             m_logger->logMessage(
1011                 "Failed to set collection status as failed for path " +
1012                 i_vpdFilePath +
1013                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1014         }
1015 
1016         // handle all the exceptions internally. Return only true/false
1017         // based on status of execution.
1018         if (typeid(l_ex) == std::type_index(typeid(DataException)))
1019         {
1020             // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1021             // logging error for these cases.
1022             if (vpdSpecificUtility::isPass1Planar(l_errCode))
1023             {
1024                 std::string l_invPath =
1025                     jsonUtility::getInventoryObjPathFromJson(
1026                         m_parsedJson, i_vpdFilePath, l_errCode);
1027 
1028                 if (l_errCode != 0)
1029                 {
1030                     m_logger->logMessage(
1031                         "Failed to get inventory object path from JSON for FRU [" +
1032                             i_vpdFilePath + "], error: " +
1033                             commonUtility::getErrCodeMsg(l_errCode),
1034                         PlaceHolder::COLLECTION);
1035                 }
1036 
1037                 const std::string& l_invPathLeafValue =
1038                     sdbusplus::message::object_path(l_invPath).filename();
1039 
1040                 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1041                      std::string::npos))
1042                 {
1043                     // skip logging any PEL for PCIe cards on pass 1 planar.
1044                     return std::make_tuple(false, i_vpdFilePath);
1045                 }
1046             }
1047             else if (l_errCode)
1048             {
1049                 m_logger->logMessage(
1050                     "Failed to check if system is Pass 1 Planar, error : " +
1051                         commonUtility::getErrCodeMsg(l_errCode),
1052                     PlaceHolder::COLLECTION);
1053             }
1054         }
1055 
1056         m_logger->logMessage(
1057             std::string("ParseAndPublish VPD failed for [reason] ") +
1058                 EventLogger::getErrorMsg(l_ex),
1059             PlaceHolder::PEL,
1060             types::PelInfoTuple{
1061                 EventLogger::getErrorType(l_ex),
1062                 (typeid(l_ex) == typeid(DataException)) ||
1063                         (typeid(l_ex) == typeid(EccException))
1064                     ? types::SeverityType::Warning
1065                     : types::SeverityType::Informational,
1066                 0, std::nullopt, std::nullopt, std::nullopt, std::nullopt});
1067 
1068         // TODO: Figure out a way to clear data in case of any failure at
1069         // runtime.
1070 
1071         // set present property to false for any error case. In future this will
1072         // be replaced by presence logic.
1073         // Update Present property for this FRU only if we handle Present
1074         // property for the FRU.
1075         if (isPresentPropertyHandlingRequired(
1076                 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1077         {
1078             setPresentProperty(i_vpdFilePath, false);
1079         }
1080 
1081         m_semaphore.release();
1082         return std::make_tuple(false, i_vpdFilePath);
1083     }
1084 }
1085 
skipPathForCollection(const std::string & i_vpdFilePath)1086 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1087 {
1088     if (i_vpdFilePath.empty())
1089     {
1090         return true;
1091     }
1092 
1093     // skip processing of system VPD again as it has been already collected.
1094     if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1095     {
1096         return true;
1097     }
1098 
1099     if (dbusUtility::isChassisPowerOn())
1100     {
1101         // If chassis is powered on, skip collecting FRUs which are
1102         // powerOffOnly.
1103 
1104         uint16_t l_errCode = 0;
1105         if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath,
1106                                            l_errCode))
1107         {
1108             return true;
1109         }
1110         else if (l_errCode)
1111         {
1112             m_logger->logMessage(
1113                 "Failed to check if FRU is power off only for FRU [" +
1114                     i_vpdFilePath +
1115                     "], error : " + commonUtility::getErrCodeMsg(l_errCode),
1116                 PlaceHolder::COLLECTION);
1117         }
1118 
1119         std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1120             m_parsedJson, i_vpdFilePath, l_errCode);
1121 
1122         if (l_errCode)
1123         {
1124             m_logger->logMessage(
1125                 "Failed to get inventory path from JSON for FRU [" +
1126                     i_vpdFilePath +
1127                     "], error : " + commonUtility::getErrCodeMsg(l_errCode),
1128                 PlaceHolder::COLLECTION);
1129 
1130             return false;
1131         }
1132 
1133         const std::string& l_invPathLeafValue =
1134             sdbusplus::message::object_path(l_invPath).filename();
1135 
1136         if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1137         {
1138             return true;
1139         }
1140     }
1141 
1142     return false;
1143 }
1144 
collectFrusFromJson()1145 void Worker::collectFrusFromJson()
1146 {
1147     // A parsed JSON file should be present to pick FRUs EEPROM paths
1148     if (m_parsedJson.empty())
1149     {
1150         throw JsonException(
1151             std::string(__FUNCTION__) +
1152                 ": Config JSON is mandatory for processing of FRUs through this API.",
1153             m_configJsonPath);
1154     }
1155 
1156     const nlohmann::json& listOfFrus =
1157         m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1158 
1159     for (const auto& itemFRUS : listOfFrus.items())
1160     {
1161         const std::string& vpdFilePath = itemFRUS.key();
1162 
1163         if (skipPathForCollection(vpdFilePath))
1164         {
1165             continue;
1166         }
1167 
1168         try
1169         {
1170             std::thread{[vpdFilePath, this]() {
1171                 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1172 
1173                 m_mutex.lock();
1174                 m_activeCollectionThreadCount--;
1175                 m_mutex.unlock();
1176 
1177                 if (!m_activeCollectionThreadCount)
1178                 {
1179                     m_isAllFruCollected = true;
1180                 }
1181             }}.detach();
1182         }
1183         catch (const std::exception& l_ex)
1184         {
1185             // add vpdFilePath(EEPROM path) to failed list
1186             m_failedEepromPaths.push_front(vpdFilePath);
1187         }
1188     }
1189 }
1190 
deleteFruVpd(const std::string & i_dbusObjPath)1191 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1192 {
1193     if (i_dbusObjPath.empty())
1194     {
1195         throw std::runtime_error("Given DBus object path is empty.");
1196     }
1197 
1198     uint16_t l_errCode = 0;
1199     const std::string& l_fruPath =
1200         jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1201 
1202     if (l_errCode)
1203     {
1204         logging::logMessage(
1205             "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1206             "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1207             " Aborting FRU VPD deletion.");
1208         return;
1209     }
1210 
1211     try
1212     {
1213         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, "preAction",
1214                                           "deletion", l_errCode))
1215         {
1216             if (!processPreAction(l_fruPath, "deletion", l_errCode))
1217             {
1218                 std::string l_msg = "Pre action failed";
1219                 if (l_errCode)
1220                 {
1221                     l_msg += " Reason: " +
1222                              commonUtility::getErrCodeMsg(l_errCode);
1223                 }
1224                 throw std::runtime_error(l_msg);
1225             }
1226         }
1227         else if (l_errCode)
1228         {
1229             logging::logMessage(
1230                 "Failed to check if pre action required for FRU [" + l_fruPath +
1231                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1232         }
1233 
1234         vpdSpecificUtility::resetObjTreeVpd(l_fruPath, m_parsedJson, l_errCode);
1235 
1236         if (l_errCode)
1237         {
1238             throw std::runtime_error(
1239                 "Failed to clear data under PIM for FRU [" + l_fruPath +
1240                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1241         }
1242 
1243         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, "postAction",
1244                                           "deletion", l_errCode))
1245         {
1246             if (!processPostAction(l_fruPath, "deletion"))
1247             {
1248                 throw std::runtime_error("Post action failed");
1249             }
1250         }
1251         else if (l_errCode)
1252         {
1253             logging::logMessage(
1254                 "Failed to check if post action required during deletion for FRU [" +
1255                 l_fruPath +
1256                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1257         }
1258 
1259         logging::logMessage(
1260             "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1261     }
1262     catch (const std::exception& l_ex)
1263     {
1264         uint16_t l_errCode = 0;
1265         std::string l_errMsg =
1266             "Failed to delete VPD for FRU : " + i_dbusObjPath +
1267             " error: " + std::string(l_ex.what());
1268 
1269         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1270                                           "postFailAction", "deletion",
1271                                           l_errCode))
1272         {
1273             if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1274                                                     "deletion", l_errCode))
1275             {
1276                 l_errMsg += ". Post fail action also failed, error : " +
1277                             commonUtility::getErrCodeMsg(l_errCode);
1278             }
1279         }
1280         else if (l_errCode)
1281         {
1282             l_errMsg +=
1283                 ". Failed to check if post fail action required, error : " +
1284                 commonUtility::getErrCodeMsg(l_errCode);
1285         }
1286 
1287         logging::logMessage(l_errMsg);
1288     }
1289 }
1290 
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1291 void Worker::setPresentProperty(const std::string& i_vpdPath,
1292                                 const bool& i_value)
1293 {
1294     try
1295     {
1296         if (i_vpdPath.empty())
1297         {
1298             throw std::runtime_error(
1299                 "Path is empty. Can't set present property");
1300         }
1301 
1302         types::ObjectMap l_objectInterfaceMap;
1303 
1304         // If the given path is EEPROM path.
1305         if (m_parsedJson["frus"].contains(i_vpdPath))
1306         {
1307             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1308             {
1309                 sdbusplus::message::object_path l_fruObjectPath(
1310                     l_Fru["inventoryPath"]);
1311 
1312                 types::PropertyMap l_propertyValueMap;
1313                 l_propertyValueMap.emplace("Present", i_value);
1314 
1315                 uint16_t l_errCode = 0;
1316                 types::InterfaceMap l_interfaces;
1317                 vpdSpecificUtility::insertOrMerge(
1318                     l_interfaces, constants::inventoryItemInf,
1319                     move(l_propertyValueMap), l_errCode);
1320 
1321                 if (l_errCode)
1322                 {
1323                     logging::logMessage(
1324                         "Failed to insert value into map, error : " +
1325                         commonUtility::getErrCodeMsg(l_errCode));
1326                 }
1327 
1328                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1329                                              std::move(l_interfaces));
1330             }
1331         }
1332         else
1333         {
1334             // consider it as an inventory path.
1335             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1336             {
1337                 throw std::runtime_error(
1338                     "Invalid inventory path: " + i_vpdPath);
1339             }
1340 
1341             types::PropertyMap l_propertyValueMap;
1342             l_propertyValueMap.emplace("Present", i_value);
1343 
1344             uint16_t l_errCode = 0;
1345             types::InterfaceMap l_interfaces;
1346             vpdSpecificUtility::insertOrMerge(
1347                 l_interfaces, constants::inventoryItemInf,
1348                 move(l_propertyValueMap), l_errCode);
1349 
1350             if (l_errCode)
1351             {
1352                 logging::logMessage(
1353                     "Failed to insert value into map, error : " +
1354                     commonUtility::getErrCodeMsg(l_errCode));
1355             }
1356 
1357             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1358         }
1359 
1360         // Call dbus method to update on dbus
1361         if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1362         {
1363             throw DbusException(
1364                 std::string(__FUNCTION__) +
1365                 "Call to PIM failed while setting present property for path " +
1366                 i_vpdPath);
1367         }
1368     }
1369     catch (const std::exception& l_ex)
1370     {
1371         m_logger->logMessage(
1372             std::string("Exception while setting the present property.") +
1373                 EventLogger::getErrorMsg(l_ex),
1374             PlaceHolder::PEL,
1375             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
1376                                 types::SeverityType::Warning, 0, std::nullopt,
1377                                 std::nullopt, std::nullopt, std::nullopt});
1378     }
1379 }
1380 
performVpdRecollection()1381 void Worker::performVpdRecollection()
1382 {
1383     try
1384     {
1385         // Check if system config JSON is present
1386         if (m_parsedJson.empty())
1387         {
1388             throw std::runtime_error(
1389                 "System config json object is empty, can't process recollection.");
1390         }
1391 
1392         uint16_t l_errCode = 0;
1393         const auto& l_frusReplaceableAtStandby =
1394             jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson,
1395                                                            l_errCode);
1396 
1397         if (l_errCode)
1398         {
1399             logging::logMessage(
1400                 "Failed to get list of FRUs replaceable at runtime, error : " +
1401                 commonUtility::getErrCodeMsg(l_errCode));
1402             return;
1403         }
1404 
1405         for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1406         {
1407             // ToDo: Add some logic/trace to know the flow to
1408             // collectSingleFruVpd has been directed via
1409             // performVpdRecollection.
1410             collectSingleFruVpd(l_fruInventoryPath);
1411         }
1412         return;
1413     }
1414 
1415     catch (const std::exception& l_ex)
1416     {
1417         // TODO Log PEL
1418         logging::logMessage(
1419             "VPD recollection failed with error: " + std::string(l_ex.what()));
1420     }
1421 }
1422 
collectSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)1423 void Worker::collectSingleFruVpd(
1424     const sdbusplus::message::object_path& i_dbusObjPath)
1425 {
1426     std::string l_fruPath{};
1427     uint16_t l_errCode = 0;
1428 
1429     try
1430     {
1431         // Check if system config JSON is present
1432         if (m_parsedJson.empty())
1433         {
1434             logging::logMessage(
1435                 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1436                 std::string(i_dbusObjPath));
1437             return;
1438         }
1439 
1440         // Get FRU path for the given D-bus object path from JSON
1441         l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
1442                                                     l_errCode);
1443 
1444         if (l_fruPath.empty())
1445         {
1446             if (l_errCode)
1447             {
1448                 logging::logMessage(
1449                     "Failed to get FRU path for [" +
1450                     std::string(i_dbusObjPath) +
1451                     "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1452                     " Aborting single FRU VPD collection.");
1453                 return;
1454             }
1455 
1456             logging::logMessage(
1457                 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1458                 std::string(i_dbusObjPath));
1459             return;
1460         }
1461 
1462         // Check if host is up and running
1463         if (dbusUtility::isHostRunning())
1464         {
1465             uint16_t l_errCode = 0;
1466             bool isFruReplaceableAtRuntime =
1467                 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1468                                                        l_errCode);
1469 
1470             if (l_errCode)
1471             {
1472                 logging::logMessage(
1473                     "Failed to check if FRU is replaceable at runtime for FRU : [" +
1474                     std::string(i_dbusObjPath) +
1475                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1476                 return;
1477             }
1478 
1479             if (!isFruReplaceableAtRuntime)
1480             {
1481                 logging::logMessage(
1482                     "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1483                     std::string(i_dbusObjPath));
1484                 return;
1485             }
1486         }
1487         else if (dbusUtility::isBMCReady())
1488         {
1489             uint16_t l_errCode = 0;
1490             bool isFruReplaceableAtStandby =
1491                 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
1492                                                        l_errCode);
1493 
1494             if (l_errCode)
1495             {
1496                 logging::logMessage(
1497                     "Error while checking if FRU is replaceable at standby for FRU [" +
1498                     std::string(i_dbusObjPath) +
1499                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1500             }
1501 
1502             bool isFruReplaceableAtRuntime =
1503                 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1504                                                        l_errCode);
1505 
1506             if (l_errCode)
1507             {
1508                 logging::logMessage(
1509                     "Failed to check if FRU is replaceable at runtime for FRU : [" +
1510                     std::string(i_dbusObjPath) +
1511                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1512                 return;
1513             }
1514 
1515             if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
1516             {
1517                 logging::logMessage(
1518                     "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1519                     std::string(i_dbusObjPath));
1520                 return;
1521             }
1522         }
1523 
1524         vpdSpecificUtility::setCollectionStatusProperty(
1525             l_fruPath, types::VpdCollectionStatus::InProgress, m_parsedJson,
1526             l_errCode);
1527         if (l_errCode)
1528         {
1529             m_logger->logMessage(
1530                 "Failed to set collection status for path " + l_fruPath +
1531                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1532         }
1533 
1534         // Parse VPD
1535         types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1536 
1537         // If l_parsedVpd is pointing to std::monostate
1538         if (l_parsedVpd.index() == 0)
1539         {
1540             // As empty parsedVpdMap recieved for some reason, but still
1541             // considered VPD collection is completed. Hence FRU collection
1542             // Status will be set as completed.
1543             m_logger->logMessage("Empty parsed VPD map received for " +
1544                                  std::string(i_dbusObjPath));
1545 
1546             // Stale data from the previous boot can be present on the system.
1547             // so clearing of data.
1548             vpdSpecificUtility::resetObjTreeVpd(std::string(i_dbusObjPath),
1549                                                 m_parsedJson, l_errCode);
1550 
1551             if (l_errCode)
1552             {
1553                 m_logger->logMessage(
1554                     "Failed to reset data under PIM for path [" +
1555                     std::string(i_dbusObjPath) +
1556                     "] error : " + commonUtility::getErrCodeMsg(l_errCode));
1557             }
1558         }
1559         else
1560         {
1561             types::ObjectMap l_dbusObjectMap;
1562             // Get D-bus object map from worker class
1563             populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1564 
1565             if (l_dbusObjectMap.empty())
1566             {
1567                 throw std::runtime_error(
1568                     "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1569                     std::string(i_dbusObjPath));
1570             }
1571 
1572             // Call method to update the dbus
1573             if (!dbusUtility::publishVpdOnDBus(move(l_dbusObjectMap)))
1574             {
1575                 throw std::runtime_error(
1576                     "publishVpdOnDBus failed. Single FRU VPD collection failed for " +
1577                     std::string(i_dbusObjPath));
1578             }
1579         }
1580 
1581         vpdSpecificUtility::setCollectionStatusProperty(
1582             l_fruPath, types::VpdCollectionStatus::Completed, m_parsedJson,
1583             l_errCode);
1584         if (l_errCode)
1585         {
1586             m_logger->logMessage(
1587                 "Failed to set collection status as completed for path " +
1588                 l_fruPath +
1589                 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1590         }
1591     }
1592     catch (const std::exception& l_error)
1593     {
1594         std::string l_errMsg;
1595         vpdSpecificUtility::resetObjTreeVpd(std::string(i_dbusObjPath),
1596                                             m_parsedJson, l_errCode);
1597 
1598         if (l_errCode)
1599         {
1600             l_errMsg += "Failed to reset data under PIM for path [" +
1601                         std::string(i_dbusObjPath) + "], error : " +
1602                         commonUtility::getErrCodeMsg(l_errCode) + ". ";
1603         }
1604 
1605         vpdSpecificUtility::setCollectionStatusProperty(
1606             l_fruPath, types::VpdCollectionStatus::Failed, m_parsedJson,
1607             l_errCode);
1608         if (l_errCode)
1609         {
1610             l_errMsg += "Failed to set collection status as failed for path " +
1611                         l_fruPath +
1612                         "Reason: " + commonUtility::getErrCodeMsg(l_errCode);
1613         }
1614         // TODO: Log PEL
1615         m_logger->logMessage(l_errMsg + std::string(l_error.what()));
1616     }
1617 }
1618 
checkAndExecutePostFailAction(const std::string & i_vpdFilePath,const std::string & i_flowFlag) const1619 void Worker::checkAndExecutePostFailAction(
1620     const std::string& i_vpdFilePath,
1621     const std::string& i_flowFlag) const noexcept
1622 {
1623     try
1624     {
1625         uint16_t l_errCode{0};
1626         if (!jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1627                                            "postFailAction", i_flowFlag,
1628                                            l_errCode))
1629         {
1630             if (l_errCode)
1631             {
1632                 m_logger->logMessage(
1633                     "Failed to check if postFailAction is required. Error: " +
1634                         commonUtility::getErrCodeMsg(l_errCode),
1635                     i_flowFlag == "collection" ? PlaceHolder::COLLECTION
1636                                                : PlaceHolder::DEFAULT);
1637             }
1638             return;
1639         }
1640 
1641         if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1642                                                 i_flowFlag, l_errCode))
1643         {
1644             m_logger->logMessage("Failed to execute postFailAction. Error: " +
1645                                      commonUtility::getErrCodeMsg(l_errCode),
1646                                  i_flowFlag == "collection"
1647                                      ? PlaceHolder::COLLECTION
1648                                      : PlaceHolder::DEFAULT);
1649         }
1650     }
1651     catch (const std::exception& l_ex)
1652     {
1653         m_logger->logMessage(
1654             "Failed to check and execute postFailAction. Error: " +
1655                 std::string(l_ex.what()),
1656             i_flowFlag == "collection" ? PlaceHolder::COLLECTION
1657                                        : PlaceHolder::DEFAULT);
1658     }
1659 }
1660 
1661 } // namespace vpd
1662