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