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