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