1 #include "config.h"
2
3 #include "worker.hpp"
4
5 #include "backup_restore.hpp"
6 #include "configuration.hpp"
7 #include "constants.hpp"
8 #include "event_logger.hpp"
9 #include "exceptions.hpp"
10 #include "logger.hpp"
11 #include "parser.hpp"
12 #include "parser_factory.hpp"
13 #include "parser_interface.hpp"
14
15 #include <utility/common_utility.hpp>
16 #include <utility/dbus_utility.hpp>
17 #include <utility/json_utility.hpp>
18 #include <utility/vpd_specific_utility.hpp>
19
20 #include <filesystem>
21 #include <fstream>
22 #include <future>
23 #include <typeindex>
24 #include <unordered_set>
25
26 namespace vpd
27 {
28
Worker(std::string pathToConfigJson,uint8_t i_maxThreadCount)29 Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount) :
30 m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount)
31 {
32 // Implies the processing is based on some config JSON
33 if (!m_configJsonPath.empty())
34 {
35 // Check if symlink is already there to confirm fresh boot/factory
36 // reset.
37 if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK))
38 {
39 logging::logMessage("Sym Link already present");
40 m_configJsonPath = INVENTORY_JSON_SYM_LINK;
41 m_isSymlinkPresent = true;
42 }
43
44 try
45 {
46 uint16_t l_errCode = 0;
47 m_parsedJson =
48 jsonUtility::getParsedJson(m_configJsonPath, l_errCode);
49
50 if (l_errCode)
51 {
52 throw std::runtime_error(
53 "JSON parsing failed for file [ " + m_configJsonPath +
54 " ], error : " + commonUtility::getErrCodeMsg(l_errCode));
55 }
56
57 // check for mandatory fields at this point itself.
58 if (!m_parsedJson.contains("frus"))
59 {
60 throw std::runtime_error("Mandatory tag(s) missing from JSON");
61 }
62 }
63 catch (const std::exception& ex)
64 {
65 throw(JsonException(ex.what(), m_configJsonPath));
66 }
67 }
68 else
69 {
70 logging::logMessage("Processing in not based on any config JSON");
71 }
72 }
73
readFitConfigValue()74 static std::string readFitConfigValue()
75 {
76 std::vector<std::string> output =
77 commonUtility::executeCmd("/sbin/fw_printenv");
78 std::string fitConfigValue;
79
80 for (const auto& entry : output)
81 {
82 auto pos = entry.find("=");
83 auto key = entry.substr(0, pos);
84 if (key != "fitconfig")
85 {
86 continue;
87 }
88
89 if (pos + 1 < entry.size())
90 {
91 fitConfigValue = entry.substr(pos + 1);
92 }
93 }
94
95 return fitConfigValue;
96 }
97
isSystemVPDOnDBus() const98 bool Worker::isSystemVPDOnDBus() const
99 {
100 const std::string& mboardPath =
101 m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
102 "inventoryPath", "");
103
104 if (mboardPath.empty())
105 {
106 throw JsonException("System vpd file path missing in JSON",
107 INVENTORY_JSON_SYM_LINK);
108 }
109
110 std::array<const char*, 1> interfaces = {
111 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
112
113 const types::MapperGetObject& objectMap =
114 dbusUtility::getObjectMap(mboardPath, interfaces);
115
116 if (objectMap.empty())
117 {
118 return false;
119 }
120 return true;
121 }
122
getIMValue(const types::IPZVpdMap & parsedVpd) const123 std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
124 {
125 if (parsedVpd.empty())
126 {
127 throw std::runtime_error("Empty VPD map. Can't Extract IM value");
128 }
129
130 const auto& itrToVSBP = parsedVpd.find("VSBP");
131 if (itrToVSBP == parsedVpd.end())
132 {
133 throw DataException("VSBP record missing.");
134 }
135
136 const auto& itrToIM = (itrToVSBP->second).find("IM");
137 if (itrToIM == (itrToVSBP->second).end())
138 {
139 throw DataException("IM keyword missing.");
140 }
141
142 types::BinaryVector imVal;
143 std::copy(itrToIM->second.begin(), itrToIM->second.end(),
144 back_inserter(imVal));
145
146 std::ostringstream imData;
147 for (auto& aByte : imVal)
148 {
149 imData << std::setw(2) << std::setfill('0') << std::hex
150 << static_cast<int>(aByte);
151 }
152
153 return imData.str();
154 }
155
getHWVersion(const types::IPZVpdMap & parsedVpd) const156 std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
157 {
158 if (parsedVpd.empty())
159 {
160 throw std::runtime_error("Empty VPD map. Can't Extract HW value");
161 }
162
163 const auto& itrToVINI = parsedVpd.find("VINI");
164 if (itrToVINI == parsedVpd.end())
165 {
166 throw DataException("VINI record missing.");
167 }
168
169 const auto& itrToHW = (itrToVINI->second).find("HW");
170 if (itrToHW == (itrToVINI->second).end())
171 {
172 throw DataException("HW keyword missing.");
173 }
174
175 types::BinaryVector hwVal;
176 std::copy(itrToHW->second.begin(), itrToHW->second.end(),
177 back_inserter(hwVal));
178
179 // The planar pass only comes from the LSB of the HW keyword,
180 // where as the MSB is used for other purposes such as signifying clock
181 // termination.
182 hwVal[0] = 0x00;
183
184 std::ostringstream hwString;
185 for (auto& aByte : hwVal)
186 {
187 hwString << std::setw(2) << std::setfill('0') << std::hex
188 << static_cast<int>(aByte);
189 }
190
191 return hwString.str();
192 }
193
fillVPDMap(const std::string & vpdFilePath,types::VPDMapVariant & vpdMap)194 void Worker::fillVPDMap(const std::string& vpdFilePath,
195 types::VPDMapVariant& vpdMap)
196 {
197 if (vpdFilePath.empty())
198 {
199 throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
200 }
201
202 if (!std::filesystem::exists(vpdFilePath))
203 {
204 throw std::runtime_error("Can't Find physical file");
205 }
206
207 std::shared_ptr<Parser> vpdParser =
208 std::make_shared<Parser>(vpdFilePath, m_parsedJson);
209 vpdMap = vpdParser->parse();
210 }
211
getSystemJson(std::string & systemJson,const types::VPDMapVariant & parsedVpdMap)212 void Worker::getSystemJson(std::string& systemJson,
213 const types::VPDMapVariant& parsedVpdMap)
214 {
215 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
216 {
217 std::string hwKWdValue = getHWVersion(*pVal);
218 if (hwKWdValue.empty())
219 {
220 throw DataException("HW value fetched is empty.");
221 }
222
223 const std::string& imKwdValue = getIMValue(*pVal);
224 if (imKwdValue.empty())
225 {
226 throw DataException("IM value fetched is empty.");
227 }
228
229 auto itrToIM = config::systemType.find(imKwdValue);
230 if (itrToIM == config::systemType.end())
231 {
232 throw DataException("IM keyword does not map to any system type");
233 }
234
235 const types::HWVerList hwVersionList = itrToIM->second.second;
236 if (!hwVersionList.empty())
237 {
238 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
239 ::toupper);
240
241 auto itrToHW =
242 std::find_if(hwVersionList.begin(), hwVersionList.end(),
243 [&hwKWdValue](const auto& aPair) {
244 return aPair.first == hwKWdValue;
245 });
246
247 if (itrToHW != hwVersionList.end())
248 {
249 if (!(*itrToHW).second.empty())
250 {
251 systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
252 ".json";
253 }
254 else
255 {
256 systemJson += (*itrToIM).first + ".json";
257 }
258 return;
259 }
260 }
261 systemJson += itrToIM->second.first + ".json";
262 return;
263 }
264
265 throw DataException(
266 "Invalid VPD type returned from Parser. Can't get system JSON.");
267 }
268
setEnvAndReboot(const std::string & key,const std::string & value)269 static void setEnvAndReboot(const std::string& key, const std::string& value)
270 {
271 // set env and reboot and break.
272 commonUtility::executeCmd("/sbin/fw_setenv", key, value);
273 logging::logMessage("Rebooting BMC to pick up new device tree");
274
275 // make dbus call to reboot
276 auto bus = sdbusplus::bus::new_default_system();
277 auto method = bus.new_method_call(
278 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager", "Reboot");
280 bus.call_noreply(method);
281 }
282
setJsonSymbolicLink(const std::string & i_systemJson)283 void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
284 {
285 std::error_code l_ec;
286 l_ec.clear();
287
288 // Check if symlink file path exists and if the JSON at this location is a
289 // symlink.
290 if (m_isSymlinkPresent &&
291 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
292 { // Don't care about exception in "is_symlink". Will continue with creation
293 // of symlink.
294
295 const auto& l_symlinkFilePth =
296 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
297
298 if (l_ec)
299 {
300 logging::logMessage(
301 "Can't read existing symlink. Error =" + l_ec.message() +
302 "Trying removal of symlink and creation of new symlink.");
303 }
304
305 // If currently set JSON is the required one. No further processing
306 // required.
307 if (i_systemJson == l_symlinkFilePth)
308 {
309 // Correct symlink already set.
310 return;
311 }
312
313 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
314 {
315 // No point going further. If removal fails for existing symlink,
316 // create will anyways throw.
317 throw std::runtime_error(
318 "Removal of symlink failed with Error = " + l_ec.message() +
319 ". Can't proceed with create_symlink.");
320 }
321 }
322
323 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
324 {
325 if (l_ec)
326 {
327 throw std::runtime_error(
328 "File system call to exist failed with error = " +
329 l_ec.message());
330 }
331
332 // implies it is a fresh boot/factory reset.
333 // Create the directory for hosting the symlink
334 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
335 {
336 if (l_ec)
337 {
338 throw std::runtime_error(
339 "File system call to create directory failed with error = " +
340 l_ec.message());
341 }
342 }
343 }
344
345 // create a new symlink based on the system
346 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
347 l_ec);
348
349 if (l_ec)
350 {
351 throw std::runtime_error(
352 "create_symlink system call failed with error: " + l_ec.message());
353 }
354
355 // If the flow is at this point implies the symlink was not present there.
356 // Considering this as factory reset.
357 m_isFactoryResetDone = true;
358 }
359
setDeviceTreeAndJson()360 void Worker::setDeviceTreeAndJson()
361 {
362 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
363 constants::vpdCollectionInProgress);
364
365 // JSON is madatory for processing of this API.
366 if (m_parsedJson.empty())
367 {
368 throw JsonException("System config JSON is empty", m_configJsonPath);
369 }
370
371 types::VPDMapVariant parsedVpdMap;
372 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
373
374 // Implies it is default JSON.
375 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
376
377 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
378 // This is required to support movement from rainier to Blue Ridge on the
379 // fly.
380
381 getSystemJson(systemJson, parsedVpdMap);
382
383 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
384 {
385 throw DataException(
386 "No system JSON found corresponding to IM read from VPD.");
387 }
388
389 uint16_t l_errCode = 0;
390
391 // re-parse the JSON once appropriate JSON has been selected.
392 m_parsedJson = jsonUtility::getParsedJson(systemJson, l_errCode);
393
394 if (l_errCode)
395 {
396 throw(JsonException(
397 "JSON parsing failed for file [ " + systemJson +
398 " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
399 systemJson));
400 }
401
402 std::string devTreeFromJson;
403 if (m_parsedJson.contains("devTree"))
404 {
405 devTreeFromJson = m_parsedJson["devTree"];
406
407 if (devTreeFromJson.empty())
408 {
409 EventLogger::createSyncPel(
410 types::ErrorType::JsonFailure, types::SeverityType::Error,
411 __FILE__, __FUNCTION__, 0,
412 "Mandatory value for device tree missing from JSON[" +
413 systemJson + "]",
414 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
415 }
416 }
417
418 auto fitConfigVal = readFitConfigValue();
419
420 if (devTreeFromJson.empty() ||
421 fitConfigVal.find(devTreeFromJson) != std::string::npos)
422 { // Skipping setting device tree as either devtree info is missing from
423 // Json or it is rightly set.
424
425 setJsonSymbolicLink(systemJson);
426
427 if (isSystemVPDOnDBus())
428 {
429 uint16_t l_errCode = 0;
430 if (jsonUtility::isBackupAndRestoreRequired(m_parsedJson,
431 l_errCode))
432 {
433 performBackupAndRestore(parsedVpdMap);
434 }
435 else if (l_errCode)
436 {
437 logging::logMessage(
438 "Failed to check if backup and restore required. Reason : " +
439 commonUtility::getErrCodeMsg(l_errCode));
440 }
441 }
442
443 // proceed to publish system VPD.
444 publishSystemVPD(parsedVpdMap);
445 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
446 constants::vpdCollectionCompleted);
447 return;
448 }
449
450 setEnvAndReboot("fitconfig", devTreeFromJson);
451 exit(EXIT_SUCCESS);
452 }
453
populateIPZVPDpropertyMap(types::InterfaceMap & interfacePropMap,const types::IPZKwdValueMap & keyordValueMap,const std::string & interfaceName)454 void Worker::populateIPZVPDpropertyMap(
455 types::InterfaceMap& interfacePropMap,
456 const types::IPZKwdValueMap& keyordValueMap,
457 const std::string& interfaceName)
458 {
459 types::PropertyMap propertyValueMap;
460 for (const auto& kwdVal : keyordValueMap)
461 {
462 auto kwd = kwdVal.first;
463
464 if (kwd[0] == '#')
465 {
466 kwd = std::string("PD_") + kwd[1];
467 }
468 else if (isdigit(kwd[0]))
469 {
470 kwd = std::string("N_") + kwd;
471 }
472
473 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
474 propertyValueMap.emplace(move(kwd), move(value));
475 }
476
477 if (!propertyValueMap.empty())
478 {
479 interfacePropMap.emplace(interfaceName, propertyValueMap);
480 }
481 }
482
populateKwdVPDpropertyMap(const types::KeywordVpdMap & keyordVPDMap,types::InterfaceMap & interfaceMap)483 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
484 types::InterfaceMap& interfaceMap)
485 {
486 for (const auto& kwdValMap : keyordVPDMap)
487 {
488 types::PropertyMap propertyValueMap;
489 auto kwd = kwdValMap.first;
490
491 if (kwd[0] == '#')
492 {
493 kwd = std::string("PD_") + kwd[1];
494 }
495 else if (isdigit(kwd[0]))
496 {
497 kwd = std::string("N_") + kwd;
498 }
499
500 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
501 {
502 types::BinaryVector value((*keywordValue).begin(),
503 (*keywordValue).end());
504 propertyValueMap.emplace(move(kwd), move(value));
505 }
506 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
507 {
508 types::BinaryVector value((*keywordValue).begin(),
509 (*keywordValue).end());
510 propertyValueMap.emplace(move(kwd), move(value));
511 }
512 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
513 {
514 if (kwd == "MemorySizeInKB")
515 {
516 types::PropertyMap memProp;
517 memProp.emplace(move(kwd), ((*keywordValue)));
518 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
519 move(memProp));
520 continue;
521 }
522 else
523 {
524 logging::logMessage(
525 "Unknown Keyword =" + kwd + " found in keyword VPD map");
526 continue;
527 }
528 }
529 else
530 {
531 logging::logMessage(
532 "Unknown variant type found in keyword VPD map.");
533 continue;
534 }
535
536 if (!propertyValueMap.empty())
537 {
538 vpdSpecificUtility::insertOrMerge(
539 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
540 }
541 }
542 }
543
populateInterfaces(const nlohmann::json & interfaceJson,types::InterfaceMap & interfaceMap,const types::VPDMapVariant & parsedVpdMap)544 void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
545 types::InterfaceMap& interfaceMap,
546 const types::VPDMapVariant& parsedVpdMap)
547 {
548 for (const auto& interfacesPropPair : interfaceJson.items())
549 {
550 const std::string& interface = interfacesPropPair.key();
551 types::PropertyMap propertyMap;
552
553 for (const auto& propValuePair : interfacesPropPair.value().items())
554 {
555 const std::string property = propValuePair.key();
556
557 if (propValuePair.value().is_boolean())
558 {
559 propertyMap.emplace(property,
560 propValuePair.value().get<bool>());
561 }
562 else if (propValuePair.value().is_string())
563 {
564 if (property.compare("LocationCode") == 0 &&
565 interface.compare("com.ibm.ipzvpd.Location") == 0)
566 {
567 std::string value =
568 vpdSpecificUtility::getExpandedLocationCode(
569 propValuePair.value().get<std::string>(),
570 parsedVpdMap);
571 propertyMap.emplace(property, value);
572
573 auto l_locCodeProperty = propertyMap;
574 vpdSpecificUtility::insertOrMerge(
575 interfaceMap,
576 std::string(constants::xyzLocationCodeInf),
577 move(l_locCodeProperty));
578 }
579 else
580 {
581 propertyMap.emplace(
582 property, propValuePair.value().get<std::string>());
583 }
584 }
585 else if (propValuePair.value().is_array())
586 {
587 try
588 {
589 propertyMap.emplace(
590 property,
591 propValuePair.value().get<types::BinaryVector>());
592 }
593 catch (const nlohmann::detail::type_error& e)
594 {
595 std::cerr << "Type exception: " << e.what() << "\n";
596 }
597 }
598 else if (propValuePair.value().is_number())
599 {
600 // For now assume the value is a size_t. In the future it would
601 // be nice to come up with a way to get the type from the JSON.
602 propertyMap.emplace(property,
603 propValuePair.value().get<size_t>());
604 }
605 else if (propValuePair.value().is_object())
606 {
607 const std::string& record =
608 propValuePair.value().value("recordName", "");
609 const std::string& keyword =
610 propValuePair.value().value("keywordName", "");
611 const std::string& encoding =
612 propValuePair.value().value("encoding", "");
613
614 if (auto ipzVpdMap =
615 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
616 {
617 if (!record.empty() && !keyword.empty() &&
618 (*ipzVpdMap).count(record) &&
619 (*ipzVpdMap).at(record).count(keyword))
620 {
621 auto encoded = vpdSpecificUtility::encodeKeyword(
622 ((*ipzVpdMap).at(record).at(keyword)), encoding);
623 propertyMap.emplace(property, encoded);
624 }
625 }
626 else if (auto kwdVpdMap =
627 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
628 {
629 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
630 {
631 if (auto kwValue = std::get_if<types::BinaryVector>(
632 &(*kwdVpdMap).at(keyword)))
633 {
634 auto encodedValue =
635 vpdSpecificUtility::encodeKeyword(
636 std::string((*kwValue).begin(),
637 (*kwValue).end()),
638 encoding);
639
640 propertyMap.emplace(property, encodedValue);
641 }
642 else if (auto kwValue = std::get_if<std::string>(
643 &(*kwdVpdMap).at(keyword)))
644 {
645 auto encodedValue =
646 vpdSpecificUtility::encodeKeyword(
647 std::string((*kwValue).begin(),
648 (*kwValue).end()),
649 encoding);
650
651 propertyMap.emplace(property, encodedValue);
652 }
653 else if (auto uintValue = std::get_if<size_t>(
654 &(*kwdVpdMap).at(keyword)))
655 {
656 propertyMap.emplace(property, *uintValue);
657 }
658 else
659 {
660 logging::logMessage(
661 "Unknown keyword found, Keywrod = " + keyword);
662 }
663 }
664 }
665 }
666 }
667 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
668 move(propertyMap));
669 }
670 }
671
isCPUIOGoodOnly(const std::string & i_pgKeyword)672 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
673 {
674 const unsigned char l_io[] = {
675 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
676 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
677
678 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
679 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
680 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
681 // IO.
682 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
683 constants::SIZE_OF_8EQ_IN_PG) == 0)
684 {
685 return true;
686 }
687
688 // The CPU is not an IO
689 return false;
690 }
691
primeInventory(const std::string & i_vpdFilePath)692 bool Worker::primeInventory(const std::string& i_vpdFilePath)
693 {
694 if (i_vpdFilePath.empty())
695 {
696 logging::logMessage("Empty VPD file path given");
697 return false;
698 }
699
700 if (m_parsedJson.empty())
701 {
702 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
703 return false;
704 }
705 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
706 {
707 logging::logMessage("File " + i_vpdFilePath +
708 ", is not found in the system config JSON file.");
709 return false;
710 }
711
712 types::ObjectMap l_objectInterfaceMap;
713 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
714 {
715 types::InterfaceMap l_interfaces;
716 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
717
718 if (l_Fru.contains("ccin"))
719 {
720 continue;
721 }
722
723 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
724 {
725 continue;
726 }
727
728 // Reset data under PIM for this FRU only if the FRU is not synthesized
729 // and we handle it's Present property.
730 if (isPresentPropertyHandlingRequired(l_Fru))
731 {
732 // Clear data under PIM if already exists.
733 vpdSpecificUtility::resetDataUnderPIM(
734 std::string(l_Fru["inventoryPath"]), l_interfaces);
735 }
736
737 // Add extra interfaces mentioned in the Json config file
738 if (l_Fru.contains("extraInterfaces"))
739 {
740 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
741 std::monostate{});
742 }
743
744 types::PropertyMap l_propertyValueMap;
745
746 // Update Present property for this FRU only if we handle Present
747 // property for the FRU.
748 if (isPresentPropertyHandlingRequired(l_Fru))
749 {
750 l_propertyValueMap.emplace("Present", false);
751
752 // TODO: Present based on file will be taken care in future.
753 // By default present is set to false for FRU at the time of
754 // priming. Once collection goes through, it will be set to true in
755 // that flow.
756 /*if (std::filesystem::exists(i_vpdFilePath))
757 {
758 l_propertyValueMap["Present"] = true;
759 }*/
760 }
761
762 vpdSpecificUtility::insertOrMerge(l_interfaces,
763 "xyz.openbmc_project.Inventory.Item",
764 move(l_propertyValueMap));
765
766 if (l_Fru.value("inherit", true) &&
767 m_parsedJson.contains("commonInterfaces"))
768 {
769 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
770 std::monostate{});
771 }
772
773 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
774 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
775
776 // Emplace the default state of FRU VPD collection
777 types::PropertyMap l_fruCollectionProperty = {
778 {"Status", constants::vpdCollectionNotStarted}};
779
780 vpdSpecificUtility::insertOrMerge(l_interfaces,
781 constants::vpdCollectionInterface,
782 std::move(l_fruCollectionProperty));
783
784 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
785 std::move(l_interfaces));
786 }
787
788 // Notify PIM
789 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
790 {
791 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
792 return false;
793 }
794
795 return true;
796 }
797
processEmbeddedAndSynthesizedFrus(const nlohmann::json & singleFru,types::InterfaceMap & interfaces)798 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
799 types::InterfaceMap& interfaces)
800 {
801 // embedded property(true or false) says whether the subfru is embedded
802 // into the parent fru (or) not. VPD sets Present property only for
803 // embedded frus. If the subfru is not an embedded FRU, the subfru may
804 // or may not be physically present. Those non embedded frus will always
805 // have Present=false irrespective of its physical presence or absence.
806 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
807 // Present to true for such sub frus.
808 // Eg: ethernet port is embedded into bmc card. So set Present to true
809 // for such sub frus. Also donot populate present property for embedded
810 // subfru which is synthesized. Currently there is no subfru which are
811 // both embedded and synthesized. But still the case is handled here.
812
813 // Check if its required to handle presence for this FRU.
814 if (singleFru.value("handlePresence", true))
815 {
816 types::PropertyMap presProp;
817 presProp.emplace("Present", true);
818 vpdSpecificUtility::insertOrMerge(
819 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
820 }
821 }
822
processExtraInterfaces(const nlohmann::json & singleFru,types::InterfaceMap & interfaces,const types::VPDMapVariant & parsedVpdMap)823 void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
824 types::InterfaceMap& interfaces,
825 const types::VPDMapVariant& parsedVpdMap)
826 {
827 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
828 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
829 {
830 if (singleFru["extraInterfaces"].contains(
831 "xyz.openbmc_project.Inventory.Item.Cpu"))
832 {
833 auto itrToRec = (*ipzVpdMap).find("CP00");
834 if (itrToRec == (*ipzVpdMap).end())
835 {
836 return;
837 }
838
839 uint16_t l_errCode = 0;
840 const std::string pgKeywordValue{vpdSpecificUtility::getKwVal(
841 itrToRec->second, "PG", l_errCode)};
842
843 if (!pgKeywordValue.empty())
844 {
845 if (isCPUIOGoodOnly(pgKeywordValue))
846 {
847 interfaces["xyz.openbmc_project.Inventory.Item"]
848 ["PrettyName"] = "IO Module";
849 }
850 }
851 else
852 {
853 throw DataException(
854 std::string(__FUNCTION__) +
855 "Failed to get value for keyword PG, error : " +
856 commonUtility::getErrCodeMsg(l_errCode));
857 }
858 }
859 }
860 }
861
processCopyRecordFlag(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)862 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
863 const types::VPDMapVariant& parsedVpdMap,
864 types::InterfaceMap& interfaces)
865 {
866 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
867 {
868 for (const auto& record : singleFru["copyRecords"])
869 {
870 const std::string& recordName = record;
871 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
872 {
873 populateIPZVPDpropertyMap(interfaces,
874 (*ipzVpdMap).at(recordName),
875 constants::ipzVpdInf + recordName);
876 }
877 }
878 }
879 }
880
processInheritFlag(const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)881 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
882 types::InterfaceMap& interfaces)
883 {
884 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
885 {
886 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
887 {
888 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
889 constants::ipzVpdInf + recordName);
890 }
891 }
892 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
893 {
894 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
895 }
896
897 if (m_parsedJson.contains("commonInterfaces"))
898 {
899 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
900 parsedVpdMap);
901 }
902 }
903
processFruWithCCIN(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap)904 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
905 const types::VPDMapVariant& parsedVpdMap)
906 {
907 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
908 {
909 auto itrToRec = (*ipzVPDMap).find("VINI");
910 if (itrToRec == (*ipzVPDMap).end())
911 {
912 return false;
913 }
914
915 uint16_t l_errCode = 0;
916 std::string ccinFromVpd{
917 vpdSpecificUtility::getKwVal(itrToRec->second, "CC", l_errCode)};
918
919 if (ccinFromVpd.empty())
920 {
921 logging::logMessage("Failed to get CCIN kwd value, error : " +
922 commonUtility::getErrCodeMsg(l_errCode));
923 return false;
924 }
925
926 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
927 ::toupper);
928
929 std::vector<std::string> ccinList;
930 for (std::string ccin : singleFru["ccin"])
931 {
932 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
933 ccinList.push_back(ccin);
934 }
935
936 if (ccinList.empty())
937 {
938 return false;
939 }
940
941 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
942 ccinList.end())
943 {
944 return false;
945 }
946 }
947 return true;
948 }
949
processFunctionalProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)950 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
951 types::InterfaceMap& io_interfaces)
952 {
953 if (!dbusUtility::isChassisPowerOn())
954 {
955 std::array<const char*, 1> l_operationalStatusInf = {
956 constants::operationalStatusInf};
957
958 auto mapperObjectMap = dbusUtility::getObjectMap(
959 i_inventoryObjPath, l_operationalStatusInf);
960
961 // If the object has been found. Check if it is under PIM.
962 if (mapperObjectMap.size() != 0)
963 {
964 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
965 {
966 if (l_serviceName == constants::pimServiceName)
967 {
968 // The object is already under PIM. No need to process
969 // again. Retain the old value.
970 return;
971 }
972 }
973 }
974
975 // Implies value is not there in D-Bus. Populate it with default
976 // value "true".
977 types::PropertyMap l_functionalProp;
978 l_functionalProp.emplace("Functional", true);
979 vpdSpecificUtility::insertOrMerge(io_interfaces,
980 constants::operationalStatusInf,
981 move(l_functionalProp));
982 }
983
984 // if chassis is power on. Functional property should be there on D-Bus.
985 // Don't process.
986 return;
987 }
988
processEnabledProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)989 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
990 types::InterfaceMap& io_interfaces)
991 {
992 if (!dbusUtility::isChassisPowerOn())
993 {
994 std::array<const char*, 1> l_enableInf = {constants::enableInf};
995
996 auto mapperObjectMap =
997 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
998
999 // If the object has been found. Check if it is under PIM.
1000 if (mapperObjectMap.size() != 0)
1001 {
1002 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1003 {
1004 if (l_serviceName == constants::pimServiceName)
1005 {
1006 // The object is already under PIM. No need to process
1007 // again. Retain the old value.
1008 return;
1009 }
1010 }
1011 }
1012
1013 // Implies value is not there in D-Bus. Populate it with default
1014 // value "true".
1015 types::PropertyMap l_enabledProp;
1016 l_enabledProp.emplace("Enabled", true);
1017 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
1018 move(l_enabledProp));
1019 }
1020
1021 // if chassis is power on. Enabled property should be there on D-Bus.
1022 // Don't process.
1023 return;
1024 }
1025
populateDbus(const types::VPDMapVariant & parsedVpdMap,types::ObjectMap & objectInterfaceMap,const std::string & vpdFilePath)1026 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1027 types::ObjectMap& objectInterfaceMap,
1028 const std::string& vpdFilePath)
1029 {
1030 if (vpdFilePath.empty())
1031 {
1032 throw std::runtime_error(
1033 std::string(__FUNCTION__) +
1034 "Invalid parameter passed to populateDbus API.");
1035 }
1036
1037 // JSON config is mandatory for processing of "if". Add "else" for any
1038 // processing without config JSON.
1039 if (!m_parsedJson.empty())
1040 {
1041 types::InterfaceMap interfaces;
1042
1043 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1044 {
1045 const auto& inventoryPath = aFru["inventoryPath"];
1046 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1047 if (aFru.contains("ccin"))
1048 {
1049 if (!processFruWithCCIN(aFru, parsedVpdMap))
1050 {
1051 continue;
1052 }
1053 }
1054
1055 if (aFru.value("inherit", true))
1056 {
1057 processInheritFlag(parsedVpdMap, interfaces);
1058 }
1059
1060 // If specific record needs to be copied.
1061 if (aFru.contains("copyRecords"))
1062 {
1063 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1064 }
1065
1066 if (aFru.contains("extraInterfaces"))
1067 {
1068 // Process extra interfaces w.r.t a FRU.
1069 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1070 }
1071
1072 // Process FRUS which are embedded in the parent FRU and whose VPD
1073 // will be synthesized.
1074 if ((aFru.value("embedded", true)) &&
1075 (!aFru.value("synthesized", false)))
1076 {
1077 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1078 }
1079
1080 processFunctionalProperty(inventoryPath, interfaces);
1081 processEnabledProperty(inventoryPath, interfaces);
1082
1083 objectInterfaceMap.emplace(std::move(fruObjectPath),
1084 std::move(interfaces));
1085 }
1086 }
1087 }
1088
createAssetTagString(const types::VPDMapVariant & i_parsedVpdMap)1089 std::string Worker::createAssetTagString(
1090 const types::VPDMapVariant& i_parsedVpdMap)
1091 {
1092 std::string l_assetTag;
1093
1094 // system VPD will be in IPZ format.
1095 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1096 {
1097 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1098 if (l_itrToVsys != (*l_parsedVpdMap).end())
1099 {
1100 uint16_t l_errCode = 0;
1101 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1102 l_itrToVsys->second, constants::kwdTM, l_errCode)};
1103
1104 if (l_tmKwdValue.empty())
1105 {
1106 throw std::runtime_error(
1107 std::string("Failed to get value for keyword [") +
1108 constants::kwdTM +
1109 std::string("] while creating Asset tag. Error : " +
1110 commonUtility::getErrCodeMsg(l_errCode)));
1111 }
1112
1113 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1114 l_itrToVsys->second, constants::kwdSE, l_errCode)};
1115
1116 if (l_seKwdValue.empty())
1117 {
1118 throw std::runtime_error(
1119 std::string("Failed to get value for keyword [") +
1120 constants::kwdSE +
1121 std::string("] while creating Asset tag. Error : " +
1122 commonUtility::getErrCodeMsg(l_errCode)));
1123 }
1124
1125 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1126 std::string{"-"} + l_seKwdValue;
1127 }
1128 else
1129 {
1130 throw std::runtime_error(
1131 "VSYS record not found in parsed VPD map to create Asset tag.");
1132 }
1133 }
1134 else
1135 {
1136 throw std::runtime_error(
1137 "Invalid VPD type recieved to create Asset tag.");
1138 }
1139
1140 return l_assetTag;
1141 }
1142
publishSystemVPD(const types::VPDMapVariant & parsedVpdMap)1143 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1144 {
1145 types::ObjectMap objectInterfaceMap;
1146
1147 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1148 {
1149 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1150
1151 try
1152 {
1153 if (m_isFactoryResetDone)
1154 {
1155 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1156
1157 auto l_itrToSystemPath = objectInterfaceMap.find(
1158 sdbusplus::message::object_path(constants::systemInvPath));
1159 if (l_itrToSystemPath == objectInterfaceMap.end())
1160 {
1161 throw std::runtime_error(
1162 "Asset tag update failed. System Path not found in object map.");
1163 }
1164
1165 types::PropertyMap l_assetTagProperty;
1166 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1167
1168 (l_itrToSystemPath->second)
1169 .emplace(constants::assetTagInf,
1170 std::move(l_assetTagProperty));
1171 }
1172 }
1173 catch (const std::exception& l_ex)
1174 {
1175 EventLogger::createSyncPel(
1176 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1177 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1178 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1179 }
1180
1181 // Notify PIM
1182 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1183 {
1184 throw std::runtime_error("Call to PIM failed for system VPD");
1185 }
1186 }
1187 else
1188 {
1189 throw DataException("Invalid format of parsed VPD map.");
1190 }
1191 }
1192
processPreAction(const std::string & i_vpdFilePath,const std::string & i_flagToProcess,uint16_t & i_errCode)1193 bool Worker::processPreAction(const std::string& i_vpdFilePath,
1194 const std::string& i_flagToProcess,
1195 uint16_t& i_errCode)
1196 {
1197 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1198 {
1199 i_errCode = error_code::INVALID_INPUT_PARAMETER;
1200 return false;
1201 }
1202
1203 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1204 i_vpdFilePath, i_flagToProcess,
1205 i_errCode)) &&
1206 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1207 {
1208 // TODO: Need a way to delete inventory object from Dbus and persisted
1209 // data section in case any FRU is not present or there is any
1210 // problem in collecting it. Once it has been deleted, it can be
1211 // re-created in the flow of priming the inventory. This needs to be
1212 // done either here or in the exception section of "parseAndPublishVPD"
1213 // API. Any failure in the process of collecting FRU will land up in the
1214 // excpetion of "parseAndPublishVPD".
1215
1216 // If the FRU is not there, clear the VINI/CCIN data.
1217 // Enity manager probes for this keyword to look for this
1218 // FRU, now if the data is persistent on BMC and FRU is
1219 // removed this can lead to ambiguity. Hence clearing this
1220 // Keyword if FRU is absent.
1221 const auto& inventoryPath =
1222 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1223 "");
1224
1225 if (!inventoryPath.empty())
1226 {
1227 types::ObjectMap l_pimObjMap{
1228 {inventoryPath,
1229 {{constants::kwdVpdInf,
1230 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1231
1232 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1233 {
1234 logging::logMessage(
1235 "Call to PIM failed for file " + i_vpdFilePath);
1236 }
1237 }
1238 else
1239 {
1240 logging::logMessage(
1241 "Inventory path is empty in Json for file " + i_vpdFilePath);
1242 }
1243
1244 return false;
1245 }
1246 return true;
1247 }
1248
processPostAction(const std::string & i_vpdFruPath,const std::string & i_flagToProcess,const std::optional<types::VPDMapVariant> i_parsedVpd)1249 bool Worker::processPostAction(
1250 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1251 const std::optional<types::VPDMapVariant> i_parsedVpd)
1252 {
1253 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1254 {
1255 logging::logMessage(
1256 "Invalid input parameter. Abort processing post action");
1257 return false;
1258 }
1259
1260 // Check if post action tag is to be triggered in the flow of collection
1261 // based on some CCIN value?
1262 if (m_parsedJson["frus"][i_vpdFruPath]
1263 .at(0)["postAction"][i_flagToProcess]
1264 .contains("ccin"))
1265 {
1266 if (!i_parsedVpd.has_value())
1267 {
1268 logging::logMessage("Empty VPD Map");
1269 return false;
1270 }
1271
1272 // CCIN match is required to process post action for this FRU as it
1273 // contains the flag.
1274 if (!vpdSpecificUtility::findCcinInVpd(
1275 m_parsedJson["frus"][i_vpdFruPath].at(
1276 0)["postAction"]["collection"],
1277 i_parsedVpd.value()))
1278 {
1279 // If CCIN is not found, implies post action processing is not
1280 // required for this FRU. Let the flow continue.
1281 return true;
1282 }
1283 }
1284
1285 uint16_t l_errCode = 0;
1286 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1287 i_vpdFruPath, i_flagToProcess,
1288 l_errCode))
1289 {
1290 logging::logMessage(
1291 "Execution of post action failed for path: " + i_vpdFruPath +
1292 " . Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1293
1294 // If post action was required and failed only in that case return
1295 // false. In all other case post action is considered passed.
1296 return false;
1297 }
1298
1299 return true;
1300 }
1301
parseVpdFile(const std::string & i_vpdFilePath)1302 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1303 {
1304 try
1305 {
1306 uint16_t l_errCode = 0;
1307
1308 if (i_vpdFilePath.empty())
1309 {
1310 throw std::runtime_error(
1311 std::string(__FUNCTION__) +
1312 " Empty VPD file path passed. Abort processing");
1313 }
1314
1315 bool isPreActionRequired = false;
1316 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1317 "preAction", "collection", l_errCode))
1318 {
1319 l_errCode = 0;
1320 isPreActionRequired = true;
1321 if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
1322 {
1323 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
1324 {
1325 logging::logMessage(
1326 commonUtility::getErrCodeMsg(l_errCode) +
1327 i_vpdFilePath);
1328 // Presence pin has been read successfully and has been read
1329 // as false, so this is not a failure case, hence returning
1330 // empty variant so that pre action is not marked as failed.
1331 return types::VPDMapVariant{};
1332 }
1333 throw std::runtime_error(
1334 std::string(__FUNCTION__) +
1335 " Pre-Action failed with error: " +
1336 commonUtility::getErrCodeMsg(l_errCode));
1337 }
1338 }
1339 else if (l_errCode)
1340 {
1341 logging::logMessage(
1342 "Failed to check if pre action required for FRU [" +
1343 i_vpdFilePath +
1344 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1345 }
1346
1347 if (!std::filesystem::exists(i_vpdFilePath))
1348 {
1349 if (isPreActionRequired)
1350 {
1351 throw std::runtime_error(
1352 std::string(__FUNCTION__) + " Could not find file path " +
1353 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1354 }
1355 return types::VPDMapVariant{};
1356 }
1357
1358 std::shared_ptr<Parser> vpdParser =
1359 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1360
1361 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1362
1363 // Before returning, as collection is over, check if FRU qualifies for
1364 // any post action in the flow of collection.
1365 // Note: Don't change the order, post action needs to be processed only
1366 // after collection for FRU is successfully done.
1367 l_errCode = 0;
1368
1369 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1370 "postAction", "collection",
1371 l_errCode))
1372 {
1373 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1374 {
1375 // Post action was required but failed while executing.
1376 // Behaviour can be undefined.
1377 EventLogger::createSyncPel(
1378 types::ErrorType::InternalFailure,
1379 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1380 std::string("Required post action failed for path [" +
1381 i_vpdFilePath + "]"),
1382 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1383 }
1384 }
1385 else if (l_errCode)
1386 {
1387 logging::logMessage(
1388 "Error while checking if post action required for FRU [" +
1389 i_vpdFilePath +
1390 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1391 }
1392
1393 return l_parsedVpd;
1394 }
1395 catch (std::exception& l_ex)
1396 {
1397 uint16_t l_errCode = 0;
1398 std::string l_exMsg{
1399 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1400 i_vpdFilePath + " due to error: " + l_ex.what()};
1401
1402 // If post fail action is required, execute it.
1403 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1404 "postFailAction", "collection",
1405 l_errCode))
1406 {
1407 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1408 "collection", l_errCode))
1409 {
1410 l_exMsg += ". Post fail action also failed. Error : " +
1411 commonUtility::getErrCodeMsg(l_errCode) +
1412 " Aborting collection for this FRU.";
1413 }
1414 }
1415 else if (l_errCode)
1416 {
1417 l_exMsg +=
1418 ". Failed to check if post fail action required, error : " +
1419 commonUtility::getErrCodeMsg(l_errCode);
1420 }
1421
1422 if (typeid(l_ex) == typeid(DataException))
1423 {
1424 throw DataException(l_exMsg);
1425 }
1426 else if (typeid(l_ex) == typeid(EccException))
1427 {
1428 throw EccException(l_exMsg);
1429 }
1430 throw std::runtime_error(l_exMsg);
1431 }
1432 }
1433
parseAndPublishVPD(const std::string & i_vpdFilePath)1434 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1435 const std::string& i_vpdFilePath)
1436 {
1437 std::string l_inventoryPath{};
1438
1439 try
1440 {
1441 m_semaphore.acquire();
1442
1443 // Thread launched.
1444 m_mutex.lock();
1445 m_activeCollectionThreadCount++;
1446 m_mutex.unlock();
1447
1448 setCollectionStatusProperty(i_vpdFilePath,
1449 constants::vpdCollectionInProgress);
1450
1451 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
1452 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
1453 {
1454 types::ObjectMap objectInterfaceMap;
1455 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1456
1457 // Notify PIM
1458 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1459 {
1460 throw std::runtime_error(
1461 std::string(__FUNCTION__) +
1462 "Call to PIM failed while publishing VPD.");
1463 }
1464 }
1465 else
1466 {
1467 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1468 i_vpdFilePath + "]. Check PEL for reason.");
1469
1470 // As empty parsedVpdMap recieved for some reason, but still
1471 // considered VPD collection is completed. Hence FRU collection
1472 // Status will be set as completed.
1473 }
1474 }
1475 catch (const std::exception& ex)
1476 {
1477 setCollectionStatusProperty(i_vpdFilePath,
1478 constants::vpdCollectionFailed);
1479
1480 // handle all the exceptions internally. Return only true/false
1481 // based on status of execution.
1482 if (typeid(ex) == std::type_index(typeid(DataException)))
1483 {
1484 uint16_t l_errCode = 0;
1485 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1486 // logging error for these cases.
1487 if (vpdSpecificUtility::isPass1Planar())
1488 {
1489 std::string l_invPath =
1490 jsonUtility::getInventoryObjPathFromJson(
1491 m_parsedJson, i_vpdFilePath, l_errCode);
1492
1493 if (l_errCode != 0)
1494 {
1495 logging::logMessage(
1496 "Failed to get inventory object path from JSON for FRU [" +
1497 i_vpdFilePath +
1498 "], error: " + commonUtility::getErrCodeMsg(l_errCode));
1499 }
1500
1501 const std::string& l_invPathLeafValue =
1502 sdbusplus::message::object_path(l_invPath).filename();
1503
1504 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1505 std::string::npos))
1506 {
1507 // skip logging any PEL for PCIe cards on pass 1 planar.
1508 return std::make_tuple(false, i_vpdFilePath);
1509 }
1510 }
1511 }
1512
1513 EventLogger::createSyncPel(
1514 EventLogger::getErrorType(ex),
1515 (typeid(ex) == typeid(DataException)) ||
1516 (typeid(ex) == typeid(EccException))
1517 ? types::SeverityType::Warning
1518 : types::SeverityType::Informational,
1519 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1520 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1521
1522 // TODO: Figure out a way to clear data in case of any failure at
1523 // runtime.
1524
1525 // set present property to false for any error case. In future this will
1526 // be replaced by presence logic.
1527 // Update Present property for this FRU only if we handle Present
1528 // property for the FRU.
1529 if (isPresentPropertyHandlingRequired(
1530 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1531 {
1532 setPresentProperty(i_vpdFilePath, false);
1533 }
1534
1535 m_semaphore.release();
1536 return std::make_tuple(false, i_vpdFilePath);
1537 }
1538
1539 setCollectionStatusProperty(i_vpdFilePath,
1540 constants::vpdCollectionCompleted);
1541 m_semaphore.release();
1542 return std::make_tuple(true, i_vpdFilePath);
1543 }
1544
skipPathForCollection(const std::string & i_vpdFilePath)1545 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1546 {
1547 if (i_vpdFilePath.empty())
1548 {
1549 return true;
1550 }
1551
1552 // skip processing of system VPD again as it has been already collected.
1553 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1554 {
1555 return true;
1556 }
1557
1558 if (dbusUtility::isChassisPowerOn())
1559 {
1560 // If chassis is powered on, skip collecting FRUs which are
1561 // powerOffOnly.
1562
1563 uint16_t l_errCode = 0;
1564 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath,
1565 l_errCode))
1566 {
1567 return true;
1568 }
1569 else if (l_errCode)
1570 {
1571 logging::logMessage(
1572 "Failed to check if FRU is power off only for FRU [" +
1573 i_vpdFilePath +
1574 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1575 }
1576
1577 l_errCode = 0;
1578 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1579 m_parsedJson, i_vpdFilePath, l_errCode);
1580
1581 if (l_errCode)
1582 {
1583 logging::logMessage(
1584 "Failed to get inventory path from JSON for FRU [" +
1585 i_vpdFilePath +
1586 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1587
1588 return false;
1589 }
1590
1591 const std::string& l_invPathLeafValue =
1592 sdbusplus::message::object_path(l_invPath).filename();
1593
1594 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1595 {
1596 return true;
1597 }
1598 }
1599
1600 return false;
1601 }
1602
collectFrusFromJson()1603 void Worker::collectFrusFromJson()
1604 {
1605 // A parsed JSON file should be present to pick FRUs EEPROM paths
1606 if (m_parsedJson.empty())
1607 {
1608 throw JsonException(
1609 std::string(__FUNCTION__) +
1610 ": Config JSON is mandatory for processing of FRUs through this API.",
1611 m_configJsonPath);
1612 }
1613
1614 const nlohmann::json& listOfFrus =
1615 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1616
1617 for (const auto& itemFRUS : listOfFrus.items())
1618 {
1619 const std::string& vpdFilePath = itemFRUS.key();
1620
1621 if (skipPathForCollection(vpdFilePath))
1622 {
1623 continue;
1624 }
1625
1626 try
1627 {
1628 std::thread{[vpdFilePath, this]() {
1629 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1630
1631 m_mutex.lock();
1632 m_activeCollectionThreadCount--;
1633 m_mutex.unlock();
1634
1635 if (!m_activeCollectionThreadCount)
1636 {
1637 m_isAllFruCollected = true;
1638 }
1639 }}.detach();
1640 }
1641 catch (const std::exception& l_ex)
1642 {
1643 // add vpdFilePath(EEPROM path) to failed list
1644 m_failedEepromPaths.push_front(vpdFilePath);
1645 }
1646 }
1647 }
1648
1649 // ToDo: Move the API under IBM_SYSTEM
performBackupAndRestore(types::VPDMapVariant & io_srcVpdMap)1650 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1651 {
1652 try
1653 {
1654 uint16_t l_errCode = 0;
1655 std::string l_backupAndRestoreCfgFilePath =
1656 m_parsedJson.value("backupRestoreConfigPath", "");
1657
1658 nlohmann::json l_backupAndRestoreCfgJsonObj =
1659 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1660 l_errCode);
1661
1662 if (l_errCode)
1663 {
1664 throw JsonException(
1665 "JSON parsing failed for file [ " +
1666 l_backupAndRestoreCfgFilePath +
1667 " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
1668 l_backupAndRestoreCfgFilePath);
1669 }
1670
1671 // check if either of "source" or "destination" has inventory path.
1672 // this indicates that this sytem has System VPD on hardware
1673 // and other copy on D-Bus (BMC cache).
1674 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1675 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1676 l_backupAndRestoreCfgJsonObj["source"].contains(
1677 "inventoryPath")) ||
1678 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1679 l_backupAndRestoreCfgJsonObj["destination"].contains(
1680 "inventoryPath"))))
1681 {
1682 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1683 auto [l_srcVpdVariant,
1684 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1685
1686 // ToDo: Revisit is this check is required or not.
1687 if (auto l_srcVpdMap =
1688 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1689 l_srcVpdMap && !(*l_srcVpdMap).empty())
1690 {
1691 io_srcVpdMap = std::move(l_srcVpdVariant);
1692 }
1693 }
1694 }
1695 catch (const std::exception& l_ex)
1696 {
1697 EventLogger::createSyncPel(
1698 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1699 __FILE__, __FUNCTION__, 0,
1700 std::string(
1701 "Exception caught while backup and restore VPD keyword's.") +
1702 EventLogger::getErrorMsg(l_ex),
1703 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1704 }
1705 }
1706
deleteFruVpd(const std::string & i_dbusObjPath)1707 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1708 {
1709 if (i_dbusObjPath.empty())
1710 {
1711 throw std::runtime_error("Given DBus object path is empty.");
1712 }
1713
1714 uint16_t l_errCode = 0;
1715 const std::string& l_fruPath =
1716 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1717
1718 if (l_errCode)
1719 {
1720 logging::logMessage(
1721 "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1722 "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1723 " Aborting FRU VPD deletion.");
1724 return;
1725 }
1726
1727 try
1728 {
1729 auto l_presentPropValue = dbusUtility::readDbusProperty(
1730 constants::pimServiceName, i_dbusObjPath,
1731 constants::inventoryItemInf, "Present");
1732
1733 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1734 {
1735 uint16_t l_errCode = 0;
1736 // check if FRU's Present property is handled by vpd-manager
1737 const auto& l_isFruPresenceHandled =
1738 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1739 l_errCode);
1740
1741 if (l_errCode)
1742 {
1743 throw std::runtime_error(
1744 "Failed to check if FRU's presence is handled, reason: " +
1745 commonUtility::getErrCodeMsg(l_errCode));
1746 }
1747
1748 if (!(*l_value) && l_isFruPresenceHandled)
1749 {
1750 throw std::runtime_error("Given FRU is not present");
1751 }
1752 else if (*l_value && !l_isFruPresenceHandled)
1753 {
1754 throw std::runtime_error(
1755 "Given FRU is present and its presence is not handled by vpd-manager.");
1756 }
1757 else
1758 {
1759 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1760 "preAction", "deletion",
1761 l_errCode))
1762 {
1763 if (!processPreAction(l_fruPath, "deletion", l_errCode))
1764 {
1765 std::string l_msg = "Pre action failed";
1766 if (l_errCode)
1767 {
1768 l_msg += " Reason: " +
1769 commonUtility::getErrCodeMsg(l_errCode);
1770 }
1771 throw std::runtime_error(l_msg);
1772 }
1773 }
1774 else if (l_errCode)
1775 {
1776 logging::logMessage(
1777 "Failed to check if pre action required for FRU [" +
1778 l_fruPath + "], error : " +
1779 commonUtility::getErrCodeMsg(l_errCode));
1780 }
1781
1782 std::vector<std::string> l_interfaceList{
1783 constants::operationalStatusInf};
1784
1785 types::MapperGetSubTree l_subTreeMap =
1786 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1787 l_interfaceList);
1788
1789 types::ObjectMap l_objectMap;
1790
1791 // Updates VPD specific interfaces property value under PIM for
1792 // sub FRUs.
1793 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1794 l_subTreeMap)
1795 {
1796 types::InterfaceMap l_interfaceMap;
1797 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1798 l_interfaceMap);
1799 l_objectMap.emplace(l_objectPath,
1800 std::move(l_interfaceMap));
1801 }
1802
1803 types::InterfaceMap l_interfaceMap;
1804 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1805 l_interfaceMap);
1806
1807 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1808
1809 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1810 {
1811 throw std::runtime_error("Call to PIM failed.");
1812 }
1813
1814 l_errCode = 0;
1815
1816 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1817 "postAction", "deletion",
1818 l_errCode))
1819 {
1820 if (!processPostAction(l_fruPath, "deletion"))
1821 {
1822 throw std::runtime_error("Post action failed");
1823 }
1824 }
1825 else if (l_errCode)
1826 {
1827 logging::logMessage(
1828 "Failed to check if post action required during deletion for FRU [" +
1829 l_fruPath + "], error : " +
1830 commonUtility::getErrCodeMsg(l_errCode));
1831 }
1832 }
1833 }
1834 else
1835 {
1836 logging::logMessage(
1837 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1838 "] as unable to read present property");
1839 return;
1840 }
1841
1842 logging::logMessage(
1843 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1844 }
1845 catch (const std::exception& l_ex)
1846 {
1847 uint16_t l_errCode = 0;
1848 std::string l_errMsg =
1849 "Failed to delete VPD for FRU : " + i_dbusObjPath +
1850 " error: " + std::string(l_ex.what());
1851
1852 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1853 "postFailAction", "deletion",
1854 l_errCode))
1855 {
1856 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1857 "deletion", l_errCode))
1858 {
1859 l_errMsg += ". Post fail action also failed, error : " +
1860 commonUtility::getErrCodeMsg(l_errCode);
1861 }
1862 }
1863 else if (l_errCode)
1864 {
1865 l_errMsg +=
1866 ". Failed to check if post fail action required, error : " +
1867 commonUtility::getErrCodeMsg(l_errCode);
1868 }
1869
1870 logging::logMessage(l_errMsg);
1871 }
1872 }
1873
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1874 void Worker::setPresentProperty(const std::string& i_vpdPath,
1875 const bool& i_value)
1876 {
1877 try
1878 {
1879 if (i_vpdPath.empty())
1880 {
1881 throw std::runtime_error(
1882 "Path is empty. Can't set present property");
1883 }
1884
1885 types::ObjectMap l_objectInterfaceMap;
1886
1887 // If the given path is EEPROM path.
1888 if (m_parsedJson["frus"].contains(i_vpdPath))
1889 {
1890 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1891 {
1892 sdbusplus::message::object_path l_fruObjectPath(
1893 l_Fru["inventoryPath"]);
1894
1895 types::PropertyMap l_propertyValueMap;
1896 l_propertyValueMap.emplace("Present", i_value);
1897
1898 types::InterfaceMap l_interfaces;
1899 vpdSpecificUtility::insertOrMerge(l_interfaces,
1900 constants::inventoryItemInf,
1901 move(l_propertyValueMap));
1902
1903 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1904 std::move(l_interfaces));
1905 }
1906 }
1907 else
1908 {
1909 // consider it as an inventory path.
1910 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1911 {
1912 throw std::runtime_error(
1913 "Invalid inventory path: " + i_vpdPath);
1914 }
1915
1916 types::PropertyMap l_propertyValueMap;
1917 l_propertyValueMap.emplace("Present", i_value);
1918
1919 types::InterfaceMap l_interfaces;
1920 vpdSpecificUtility::insertOrMerge(l_interfaces,
1921 constants::inventoryItemInf,
1922 move(l_propertyValueMap));
1923
1924 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1925 }
1926
1927 // Notify PIM
1928 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1929 {
1930 throw DbusException(
1931 std::string(__FUNCTION__) +
1932 "Call to PIM failed while setting present property for path " +
1933 i_vpdPath);
1934 }
1935 }
1936 catch (const std::exception& l_ex)
1937 {
1938 EventLogger::createSyncPel(
1939 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1940 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1941 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1942 }
1943 }
1944
performVpdRecollection()1945 void Worker::performVpdRecollection()
1946 {
1947 try
1948 {
1949 // Check if system config JSON is present
1950 if (m_parsedJson.empty())
1951 {
1952 throw std::runtime_error(
1953 "System config json object is empty, can't process recollection.");
1954 }
1955
1956 uint16_t l_errCode = 0;
1957 const auto& l_frusReplaceableAtStandby =
1958 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson,
1959 l_errCode);
1960
1961 if (l_errCode)
1962 {
1963 logging::logMessage(
1964 "Failed to get list of FRUs replaceable at runtime, error : " +
1965 commonUtility::getErrCodeMsg(l_errCode));
1966 return;
1967 }
1968
1969 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1970 {
1971 // ToDo: Add some logic/trace to know the flow to
1972 // collectSingleFruVpd has been directed via
1973 // performVpdRecollection.
1974 collectSingleFruVpd(l_fruInventoryPath);
1975 }
1976 return;
1977 }
1978
1979 catch (const std::exception& l_ex)
1980 {
1981 // TODO Log PEL
1982 logging::logMessage(
1983 "VPD recollection failed with error: " + std::string(l_ex.what()));
1984 }
1985 }
1986
collectSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)1987 void Worker::collectSingleFruVpd(
1988 const sdbusplus::message::object_path& i_dbusObjPath)
1989 {
1990 std::string l_fruPath{};
1991 uint16_t l_errCode = 0;
1992
1993 try
1994 {
1995 // Check if system config JSON is present
1996 if (m_parsedJson.empty())
1997 {
1998 logging::logMessage(
1999 "System config JSON object not present. Single FRU VPD collection is not performed for " +
2000 std::string(i_dbusObjPath));
2001 return;
2002 }
2003
2004 // Get FRU path for the given D-bus object path from JSON
2005 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
2006 l_errCode);
2007
2008 if (l_fruPath.empty())
2009 {
2010 if (l_errCode)
2011 {
2012 logging::logMessage(
2013 "Failed to get FRU path for [" +
2014 std::string(i_dbusObjPath) +
2015 "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
2016 " Aborting single FRU VPD collection.");
2017 return;
2018 }
2019
2020 logging::logMessage(
2021 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
2022 std::string(i_dbusObjPath));
2023 return;
2024 }
2025
2026 // Check if host is up and running
2027 if (dbusUtility::isHostRunning())
2028 {
2029 uint16_t l_errCode = 0;
2030 bool isFruReplaceableAtRuntime =
2031 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2032 l_errCode);
2033
2034 if (l_errCode)
2035 {
2036 logging::logMessage(
2037 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2038 std::string(i_dbusObjPath) +
2039 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2040 return;
2041 }
2042
2043 if (!isFruReplaceableAtRuntime)
2044 {
2045 logging::logMessage(
2046 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
2047 std::string(i_dbusObjPath));
2048 return;
2049 }
2050 }
2051 else if (dbusUtility::isBMCReady())
2052 {
2053 uint16_t l_errCode = 0;
2054 bool isFruReplaceableAtStandby =
2055 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
2056 l_errCode);
2057
2058 if (l_errCode)
2059 {
2060 logging::logMessage(
2061 "Error while checking if FRU is replaceable at standby for FRU [" +
2062 std::string(i_dbusObjPath) +
2063 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2064 }
2065
2066 l_errCode = 0;
2067 bool isFruReplaceableAtRuntime =
2068 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2069 l_errCode);
2070
2071 if (l_errCode)
2072 {
2073 logging::logMessage(
2074 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2075 std::string(i_dbusObjPath) +
2076 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2077 return;
2078 }
2079
2080 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
2081 {
2082 logging::logMessage(
2083 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
2084 std::string(i_dbusObjPath));
2085 return;
2086 }
2087 }
2088
2089 // Set collection Status as InProgress. Since it's an intermediate state
2090 // D-bus set-property call is good enough to update the status.
2091 const std::string& l_collStatusProp = "Status";
2092
2093 setCollectionStatusProperty(l_fruPath,
2094 constants::vpdCollectionInProgress);
2095
2096 // Parse VPD
2097 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
2098
2099 // If l_parsedVpd is pointing to std::monostate
2100 if (l_parsedVpd.index() == 0)
2101 {
2102 throw std::runtime_error(
2103 "VPD parsing failed for " + std::string(i_dbusObjPath));
2104 }
2105
2106 // Get D-bus object map from worker class
2107 types::ObjectMap l_dbusObjectMap;
2108 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
2109
2110 if (l_dbusObjectMap.empty())
2111 {
2112 throw std::runtime_error(
2113 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
2114 std::string(i_dbusObjPath));
2115 }
2116
2117 // Call PIM's Notify method
2118 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2119 {
2120 throw std::runtime_error(
2121 "Notify PIM failed. Single FRU VPD collection failed for " +
2122 std::string(i_dbusObjPath));
2123 }
2124 setCollectionStatusProperty(l_fruPath,
2125 constants::vpdCollectionCompleted);
2126 }
2127 catch (const std::exception& l_error)
2128 {
2129 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
2130 // TODO: Log PEL
2131 logging::logMessage(std::string(l_error.what()));
2132 }
2133 }
2134
setCollectionStatusProperty(const std::string & i_vpdPath,const std::string & i_value) const2135 void Worker::setCollectionStatusProperty(
2136 const std::string& i_vpdPath, const std::string& i_value) const noexcept
2137 {
2138 try
2139 {
2140 if (i_vpdPath.empty())
2141 {
2142 throw std::runtime_error(
2143 "Given path is empty. Can't set collection Status property");
2144 }
2145
2146 types::PropertyMap l_timeStampMap;
2147 if (i_value == constants::vpdCollectionCompleted ||
2148 i_value == constants::vpdCollectionFailed)
2149 {
2150 l_timeStampMap.emplace(
2151 "CompletedTime",
2152 types::DbusVariantType{
2153 commonUtility::getCurrentTimeSinceEpoch()});
2154 }
2155 else if (i_value == constants::vpdCollectionInProgress)
2156 {
2157 l_timeStampMap.emplace(
2158 "StartTime", types::DbusVariantType{
2159 commonUtility::getCurrentTimeSinceEpoch()});
2160 }
2161 else if (i_value == constants::vpdCollectionNotStarted)
2162 {
2163 l_timeStampMap.emplace("StartTime", 0);
2164 l_timeStampMap.emplace("CompletedTime", 0);
2165 }
2166
2167 types::ObjectMap l_objectInterfaceMap;
2168
2169 if (m_parsedJson["frus"].contains(i_vpdPath))
2170 {
2171 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2172 {
2173 sdbusplus::message::object_path l_fruObjectPath(
2174 l_Fru["inventoryPath"]);
2175
2176 types::PropertyMap l_propertyValueMap;
2177 l_propertyValueMap.emplace("Status", i_value);
2178 l_propertyValueMap.insert(l_timeStampMap.begin(),
2179 l_timeStampMap.end());
2180
2181 types::InterfaceMap l_interfaces;
2182 vpdSpecificUtility::insertOrMerge(
2183 l_interfaces, constants::vpdCollectionInterface,
2184 move(l_propertyValueMap));
2185
2186 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2187 std::move(l_interfaces));
2188 }
2189 }
2190 else
2191 {
2192 // consider it as an inventory path.
2193 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2194 {
2195 throw std::runtime_error(
2196 "Invalid inventory path: " + i_vpdPath +
2197 ". Can't set collection Status property");
2198 }
2199
2200 types::PropertyMap l_propertyValueMap;
2201 l_propertyValueMap.emplace("Status", i_value);
2202 l_propertyValueMap.insert(l_timeStampMap.begin(),
2203 l_timeStampMap.end());
2204
2205 types::InterfaceMap l_interfaces;
2206 vpdSpecificUtility::insertOrMerge(l_interfaces,
2207 constants::vpdCollectionInterface,
2208 move(l_propertyValueMap));
2209
2210 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2211 }
2212
2213 // Notify PIM
2214 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2215 {
2216 throw DbusException(
2217 std::string(__FUNCTION__) +
2218 "Call to PIM failed while setting collection Status property for path " +
2219 i_vpdPath);
2220 }
2221 }
2222 catch (const std::exception& l_ex)
2223 {
2224 EventLogger::createSyncPel(
2225 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2226 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2227 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2228 }
2229 }
2230 } // namespace vpd
2231