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