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