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