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::vector<std::string> 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 uint16_t l_errCode = 0;
615
616 if (auto ipzVpdMap =
617 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
618 {
619 if (!record.empty() && !keyword.empty() &&
620 (*ipzVpdMap).count(record) &&
621 (*ipzVpdMap).at(record).count(keyword))
622 {
623 auto encoded = vpdSpecificUtility::encodeKeyword(
624 ((*ipzVpdMap).at(record).at(keyword)), encoding,
625 l_errCode);
626
627 if (l_errCode)
628 {
629 logging::logMessage(
630 std::string(
631 "Failed to get encoded keyword value for : ") +
632 keyword + std::string(", error : ") +
633 commonUtility::getErrCodeMsg(l_errCode));
634 }
635
636 propertyMap.emplace(property, encoded);
637 }
638 }
639 else if (auto kwdVpdMap =
640 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
641 {
642 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
643 {
644 if (auto kwValue = std::get_if<types::BinaryVector>(
645 &(*kwdVpdMap).at(keyword)))
646 {
647 auto encodedValue =
648 vpdSpecificUtility::encodeKeyword(
649 std::string((*kwValue).begin(),
650 (*kwValue).end()),
651 encoding, l_errCode);
652
653 if (l_errCode)
654 {
655 logging::logMessage(
656 std::string(
657 "Failed to get encoded keyword value for : ") +
658 keyword + std::string(", error : ") +
659 commonUtility::getErrCodeMsg(l_errCode));
660 }
661
662 propertyMap.emplace(property, encodedValue);
663 }
664 else if (auto kwValue = std::get_if<std::string>(
665 &(*kwdVpdMap).at(keyword)))
666 {
667 auto encodedValue =
668 vpdSpecificUtility::encodeKeyword(
669 std::string((*kwValue).begin(),
670 (*kwValue).end()),
671 encoding, l_errCode);
672
673 if (l_errCode)
674 {
675 logging::logMessage(
676 "Failed to get encoded keyword value for : " +
677 keyword + ", error : " +
678 commonUtility::getErrCodeMsg(l_errCode));
679 }
680
681 propertyMap.emplace(property, encodedValue);
682 }
683 else if (auto uintValue = std::get_if<size_t>(
684 &(*kwdVpdMap).at(keyword)))
685 {
686 propertyMap.emplace(property, *uintValue);
687 }
688 else
689 {
690 logging::logMessage(
691 "Unknown keyword found, Keywrod = " + keyword);
692 }
693 }
694 }
695 }
696 }
697 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
698 move(propertyMap));
699 }
700 }
701
isCPUIOGoodOnly(const std::string & i_pgKeyword)702 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
703 {
704 const unsigned char l_io[] = {
705 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
706 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
707
708 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
709 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
710 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
711 // IO.
712 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
713 constants::SIZE_OF_8EQ_IN_PG) == 0)
714 {
715 return true;
716 }
717
718 // The CPU is not an IO
719 return false;
720 }
721
processEmbeddedAndSynthesizedFrus(const nlohmann::json & singleFru,types::InterfaceMap & interfaces)722 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
723 types::InterfaceMap& interfaces)
724 {
725 // embedded property(true or false) says whether the subfru is embedded
726 // into the parent fru (or) not. VPD sets Present property only for
727 // embedded frus. If the subfru is not an embedded FRU, the subfru may
728 // or may not be physically present. Those non embedded frus will always
729 // have Present=false irrespective of its physical presence or absence.
730 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
731 // Present to true for such sub frus.
732 // Eg: ethernet port is embedded into bmc card. So set Present to true
733 // for such sub frus. Also donot populate present property for embedded
734 // subfru which is synthesized. Currently there is no subfru which are
735 // both embedded and synthesized. But still the case is handled here.
736
737 // Check if its required to handle presence for this FRU.
738 if (singleFru.value("handlePresence", true))
739 {
740 types::PropertyMap presProp;
741 presProp.emplace("Present", true);
742 vpdSpecificUtility::insertOrMerge(
743 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
744 }
745 }
746
processExtraInterfaces(const nlohmann::json & singleFru,types::InterfaceMap & interfaces,const types::VPDMapVariant & parsedVpdMap)747 void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
748 types::InterfaceMap& interfaces,
749 const types::VPDMapVariant& parsedVpdMap)
750 {
751 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
752 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
753 {
754 if (singleFru["extraInterfaces"].contains(
755 "xyz.openbmc_project.Inventory.Item.Cpu"))
756 {
757 auto itrToRec = (*ipzVpdMap).find("CP00");
758 if (itrToRec == (*ipzVpdMap).end())
759 {
760 return;
761 }
762
763 uint16_t l_errCode = 0;
764 const std::string pgKeywordValue{vpdSpecificUtility::getKwVal(
765 itrToRec->second, "PG", l_errCode)};
766
767 if (!pgKeywordValue.empty())
768 {
769 if (isCPUIOGoodOnly(pgKeywordValue))
770 {
771 interfaces["xyz.openbmc_project.Inventory.Item"]
772 ["PrettyName"] = "IO Module";
773 }
774 }
775 else
776 {
777 throw DataException(
778 std::string(__FUNCTION__) +
779 "Failed to get value for keyword PG, error : " +
780 commonUtility::getErrCodeMsg(l_errCode));
781 }
782 }
783 }
784 }
785
processCopyRecordFlag(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)786 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
787 const types::VPDMapVariant& parsedVpdMap,
788 types::InterfaceMap& interfaces)
789 {
790 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
791 {
792 for (const auto& record : singleFru["copyRecords"])
793 {
794 const std::string& recordName = record;
795 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
796 {
797 populateIPZVPDpropertyMap(interfaces,
798 (*ipzVpdMap).at(recordName),
799 constants::ipzVpdInf + recordName);
800 }
801 }
802 }
803 }
804
processInheritFlag(const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)805 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
806 types::InterfaceMap& interfaces)
807 {
808 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
809 {
810 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
811 {
812 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
813 constants::ipzVpdInf + recordName);
814 }
815 }
816 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
817 {
818 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
819 }
820
821 if (m_parsedJson.contains("commonInterfaces"))
822 {
823 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
824 parsedVpdMap);
825 }
826 }
827
processFruWithCCIN(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap)828 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
829 const types::VPDMapVariant& parsedVpdMap)
830 {
831 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
832 {
833 auto itrToRec = (*ipzVPDMap).find("VINI");
834 if (itrToRec == (*ipzVPDMap).end())
835 {
836 return false;
837 }
838
839 uint16_t l_errCode = 0;
840 std::string ccinFromVpd{
841 vpdSpecificUtility::getKwVal(itrToRec->second, "CC", l_errCode)};
842
843 if (ccinFromVpd.empty())
844 {
845 logging::logMessage("Failed to get CCIN kwd value, error : " +
846 commonUtility::getErrCodeMsg(l_errCode));
847 return false;
848 }
849
850 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
851 ::toupper);
852
853 std::vector<std::string> ccinList;
854 for (std::string ccin : singleFru["ccin"])
855 {
856 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
857 ccinList.push_back(ccin);
858 }
859
860 if (ccinList.empty())
861 {
862 return false;
863 }
864
865 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
866 ccinList.end())
867 {
868 return false;
869 }
870 }
871 return true;
872 }
873
processFunctionalProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)874 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
875 types::InterfaceMap& io_interfaces)
876 {
877 if (!dbusUtility::isChassisPowerOn())
878 {
879 std::vector<std::string> l_operationalStatusInf = {
880 constants::operationalStatusInf};
881
882 auto mapperObjectMap = dbusUtility::getObjectMap(
883 i_inventoryObjPath, l_operationalStatusInf);
884
885 // If the object has been found. Check if it is under PIM.
886 if (mapperObjectMap.size() != 0)
887 {
888 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
889 {
890 if (l_serviceName == constants::pimServiceName)
891 {
892 // The object is already under PIM. No need to process
893 // again. Retain the old value.
894 return;
895 }
896 }
897 }
898
899 // Implies value is not there in D-Bus. Populate it with default
900 // value "true".
901 types::PropertyMap l_functionalProp;
902 l_functionalProp.emplace("Functional", true);
903 vpdSpecificUtility::insertOrMerge(io_interfaces,
904 constants::operationalStatusInf,
905 move(l_functionalProp));
906 }
907
908 // if chassis is power on. Functional property should be there on D-Bus.
909 // Don't process.
910 return;
911 }
912
processEnabledProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)913 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
914 types::InterfaceMap& io_interfaces)
915 {
916 if (!dbusUtility::isChassisPowerOn())
917 {
918 std::vector<std::string> l_enableInf = {constants::enableInf};
919
920 auto mapperObjectMap =
921 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
922
923 // If the object has been found. Check if it is under PIM.
924 if (mapperObjectMap.size() != 0)
925 {
926 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
927 {
928 if (l_serviceName == constants::pimServiceName)
929 {
930 // The object is already under PIM. No need to process
931 // again. Retain the old value.
932 return;
933 }
934 }
935 }
936
937 // Implies value is not there in D-Bus. Populate it with default
938 // value "true".
939 types::PropertyMap l_enabledProp;
940 l_enabledProp.emplace("Enabled", true);
941 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
942 move(l_enabledProp));
943 }
944
945 // if chassis is power on. Enabled property should be there on D-Bus.
946 // Don't process.
947 return;
948 }
949
populateDbus(const types::VPDMapVariant & parsedVpdMap,types::ObjectMap & objectInterfaceMap,const std::string & vpdFilePath)950 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
951 types::ObjectMap& objectInterfaceMap,
952 const std::string& vpdFilePath)
953 {
954 if (vpdFilePath.empty())
955 {
956 throw std::runtime_error(
957 std::string(__FUNCTION__) +
958 "Invalid parameter passed to populateDbus API.");
959 }
960
961 // JSON config is mandatory for processing of "if". Add "else" for any
962 // processing without config JSON.
963 if (!m_parsedJson.empty())
964 {
965 types::InterfaceMap interfaces;
966
967 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
968 {
969 const auto& inventoryPath = aFru["inventoryPath"];
970 sdbusplus::message::object_path fruObjectPath(inventoryPath);
971 if (aFru.contains("ccin"))
972 {
973 if (!processFruWithCCIN(aFru, parsedVpdMap))
974 {
975 continue;
976 }
977 }
978
979 if (aFru.value("inherit", true))
980 {
981 processInheritFlag(parsedVpdMap, interfaces);
982 }
983
984 // If specific record needs to be copied.
985 if (aFru.contains("copyRecords"))
986 {
987 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
988 }
989
990 if (aFru.contains("extraInterfaces"))
991 {
992 // Process extra interfaces w.r.t a FRU.
993 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
994 }
995
996 // Process FRUS which are embedded in the parent FRU and whose VPD
997 // will be synthesized.
998 if ((aFru.value("embedded", true)) &&
999 (!aFru.value("synthesized", false)))
1000 {
1001 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1002 }
1003
1004 processFunctionalProperty(inventoryPath, interfaces);
1005 processEnabledProperty(inventoryPath, interfaces);
1006
1007 objectInterfaceMap.emplace(std::move(fruObjectPath),
1008 std::move(interfaces));
1009 }
1010 }
1011 }
1012
createAssetTagString(const types::VPDMapVariant & i_parsedVpdMap)1013 std::string Worker::createAssetTagString(
1014 const types::VPDMapVariant& i_parsedVpdMap)
1015 {
1016 std::string l_assetTag;
1017
1018 // system VPD will be in IPZ format.
1019 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1020 {
1021 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1022 if (l_itrToVsys != (*l_parsedVpdMap).end())
1023 {
1024 uint16_t l_errCode = 0;
1025 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1026 l_itrToVsys->second, constants::kwdTM, l_errCode)};
1027
1028 if (l_tmKwdValue.empty())
1029 {
1030 throw std::runtime_error(
1031 std::string("Failed to get value for keyword [") +
1032 constants::kwdTM +
1033 std::string("] while creating Asset tag. Error : " +
1034 commonUtility::getErrCodeMsg(l_errCode)));
1035 }
1036
1037 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1038 l_itrToVsys->second, constants::kwdSE, l_errCode)};
1039
1040 if (l_seKwdValue.empty())
1041 {
1042 throw std::runtime_error(
1043 std::string("Failed to get value for keyword [") +
1044 constants::kwdSE +
1045 std::string("] while creating Asset tag. Error : " +
1046 commonUtility::getErrCodeMsg(l_errCode)));
1047 }
1048
1049 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1050 std::string{"-"} + l_seKwdValue;
1051 }
1052 else
1053 {
1054 throw std::runtime_error(
1055 "VSYS record not found in parsed VPD map to create Asset tag.");
1056 }
1057 }
1058 else
1059 {
1060 throw std::runtime_error(
1061 "Invalid VPD type recieved to create Asset tag.");
1062 }
1063
1064 return l_assetTag;
1065 }
1066
publishSystemVPD(const types::VPDMapVariant & parsedVpdMap)1067 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1068 {
1069 types::ObjectMap objectInterfaceMap;
1070
1071 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1072 {
1073 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1074
1075 try
1076 {
1077 if (m_isFactoryResetDone)
1078 {
1079 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1080
1081 auto l_itrToSystemPath = objectInterfaceMap.find(
1082 sdbusplus::message::object_path(constants::systemInvPath));
1083 if (l_itrToSystemPath == objectInterfaceMap.end())
1084 {
1085 throw std::runtime_error(
1086 "Asset tag update failed. System Path not found in object map.");
1087 }
1088
1089 types::PropertyMap l_assetTagProperty;
1090 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1091
1092 (l_itrToSystemPath->second)
1093 .emplace(constants::assetTagInf,
1094 std::move(l_assetTagProperty));
1095 }
1096 }
1097 catch (const std::exception& l_ex)
1098 {
1099 EventLogger::createSyncPel(
1100 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1101 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1102 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1103 }
1104
1105 // Notify PIM
1106 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1107 {
1108 throw std::runtime_error("Call to PIM failed for system VPD");
1109 }
1110 }
1111 else
1112 {
1113 throw DataException("Invalid format of parsed VPD map.");
1114 }
1115 }
1116
processPreAction(const std::string & i_vpdFilePath,const std::string & i_flagToProcess,uint16_t & i_errCode)1117 bool Worker::processPreAction(const std::string& i_vpdFilePath,
1118 const std::string& i_flagToProcess,
1119 uint16_t& i_errCode)
1120 {
1121 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1122 {
1123 i_errCode = error_code::INVALID_INPUT_PARAMETER;
1124 return false;
1125 }
1126
1127 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1128 i_vpdFilePath, i_flagToProcess,
1129 i_errCode)) &&
1130 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1131 {
1132 // TODO: Need a way to delete inventory object from Dbus and persisted
1133 // data section in case any FRU is not present or there is any
1134 // problem in collecting it. Once it has been deleted, it can be
1135 // re-created in the flow of priming the inventory. This needs to be
1136 // done either here or in the exception section of "parseAndPublishVPD"
1137 // API. Any failure in the process of collecting FRU will land up in the
1138 // excpetion of "parseAndPublishVPD".
1139
1140 // If the FRU is not there, clear the VINI/CCIN data.
1141 // Enity manager probes for this keyword to look for this
1142 // FRU, now if the data is persistent on BMC and FRU is
1143 // removed this can lead to ambiguity. Hence clearing this
1144 // Keyword if FRU is absent.
1145 const auto& inventoryPath =
1146 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1147 "");
1148
1149 if (!inventoryPath.empty())
1150 {
1151 types::ObjectMap l_pimObjMap{
1152 {inventoryPath,
1153 {{constants::kwdVpdInf,
1154 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1155
1156 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1157 {
1158 logging::logMessage(
1159 "Call to PIM failed for file " + i_vpdFilePath);
1160 }
1161 }
1162 else
1163 {
1164 logging::logMessage(
1165 "Inventory path is empty in Json for file " + i_vpdFilePath);
1166 }
1167
1168 return false;
1169 }
1170 return true;
1171 }
1172
processPostAction(const std::string & i_vpdFruPath,const std::string & i_flagToProcess,const std::optional<types::VPDMapVariant> i_parsedVpd)1173 bool Worker::processPostAction(
1174 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1175 const std::optional<types::VPDMapVariant> i_parsedVpd)
1176 {
1177 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1178 {
1179 logging::logMessage(
1180 "Invalid input parameter. Abort processing post action");
1181 return false;
1182 }
1183
1184 // Check if post action tag is to be triggered in the flow of collection
1185 // based on some CCIN value?
1186 if (m_parsedJson["frus"][i_vpdFruPath]
1187 .at(0)["postAction"][i_flagToProcess]
1188 .contains("ccin"))
1189 {
1190 if (!i_parsedVpd.has_value())
1191 {
1192 logging::logMessage("Empty VPD Map");
1193 return false;
1194 }
1195
1196 // CCIN match is required to process post action for this FRU as it
1197 // contains the flag.
1198 if (!vpdSpecificUtility::findCcinInVpd(
1199 m_parsedJson["frus"][i_vpdFruPath].at(
1200 0)["postAction"]["collection"],
1201 i_parsedVpd.value()))
1202 {
1203 // If CCIN is not found, implies post action processing is not
1204 // required for this FRU. Let the flow continue.
1205 return true;
1206 }
1207 }
1208
1209 uint16_t l_errCode = 0;
1210 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1211 i_vpdFruPath, i_flagToProcess,
1212 l_errCode))
1213 {
1214 logging::logMessage(
1215 "Execution of post action failed for path: " + i_vpdFruPath +
1216 " . Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1217
1218 // If post action was required and failed only in that case return
1219 // false. In all other case post action is considered passed.
1220 return false;
1221 }
1222
1223 return true;
1224 }
1225
parseVpdFile(const std::string & i_vpdFilePath)1226 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1227 {
1228 try
1229 {
1230 uint16_t l_errCode = 0;
1231
1232 if (i_vpdFilePath.empty())
1233 {
1234 throw std::runtime_error(
1235 std::string(__FUNCTION__) +
1236 " Empty VPD file path passed. Abort processing");
1237 }
1238
1239 bool isPreActionRequired = false;
1240 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1241 "preAction", "collection", l_errCode))
1242 {
1243 l_errCode = 0;
1244 isPreActionRequired = true;
1245 if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
1246 {
1247 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
1248 {
1249 logging::logMessage(
1250 commonUtility::getErrCodeMsg(l_errCode) +
1251 i_vpdFilePath);
1252 // Presence pin has been read successfully and has been read
1253 // as false, so this is not a failure case, hence returning
1254 // empty variant so that pre action is not marked as failed.
1255 return types::VPDMapVariant{};
1256 }
1257 throw std::runtime_error(
1258 std::string(__FUNCTION__) +
1259 " Pre-Action failed with error: " +
1260 commonUtility::getErrCodeMsg(l_errCode));
1261 }
1262 }
1263 else if (l_errCode)
1264 {
1265 logging::logMessage(
1266 "Failed to check if pre action required for FRU [" +
1267 i_vpdFilePath +
1268 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1269 }
1270
1271 if (!std::filesystem::exists(i_vpdFilePath))
1272 {
1273 if (isPreActionRequired)
1274 {
1275 throw std::runtime_error(
1276 std::string(__FUNCTION__) + " Could not find file path " +
1277 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1278 }
1279 return types::VPDMapVariant{};
1280 }
1281
1282 std::shared_ptr<Parser> vpdParser =
1283 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1284
1285 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1286
1287 // Before returning, as collection is over, check if FRU qualifies for
1288 // any post action in the flow of collection.
1289 // Note: Don't change the order, post action needs to be processed only
1290 // after collection for FRU is successfully done.
1291 l_errCode = 0;
1292
1293 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1294 "postAction", "collection",
1295 l_errCode))
1296 {
1297 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1298 {
1299 // Post action was required but failed while executing.
1300 // Behaviour can be undefined.
1301 EventLogger::createSyncPel(
1302 types::ErrorType::InternalFailure,
1303 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1304 std::string("Required post action failed for path [" +
1305 i_vpdFilePath + "]"),
1306 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1307 }
1308 }
1309 else if (l_errCode)
1310 {
1311 logging::logMessage(
1312 "Error while checking if post action required for FRU [" +
1313 i_vpdFilePath +
1314 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1315 }
1316
1317 return l_parsedVpd;
1318 }
1319 catch (std::exception& l_ex)
1320 {
1321 uint16_t l_errCode = 0;
1322 std::string l_exMsg{
1323 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1324 i_vpdFilePath + " due to error: " + l_ex.what()};
1325
1326 // If post fail action is required, execute it.
1327 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1328 "postFailAction", "collection",
1329 l_errCode))
1330 {
1331 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1332 "collection", l_errCode))
1333 {
1334 l_exMsg += ". Post fail action also failed. Error : " +
1335 commonUtility::getErrCodeMsg(l_errCode) +
1336 " Aborting collection for this FRU.";
1337 }
1338 }
1339 else if (l_errCode)
1340 {
1341 l_exMsg +=
1342 ". Failed to check if post fail action required, error : " +
1343 commonUtility::getErrCodeMsg(l_errCode);
1344 }
1345
1346 if (typeid(l_ex) == typeid(DataException))
1347 {
1348 throw DataException(l_exMsg);
1349 }
1350 else if (typeid(l_ex) == typeid(EccException))
1351 {
1352 throw EccException(l_exMsg);
1353 }
1354 throw std::runtime_error(l_exMsg);
1355 }
1356 }
1357
parseAndPublishVPD(const std::string & i_vpdFilePath)1358 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1359 const std::string& i_vpdFilePath)
1360 {
1361 std::string l_inventoryPath{};
1362
1363 try
1364 {
1365 m_semaphore.acquire();
1366
1367 // Thread launched.
1368 m_mutex.lock();
1369 m_activeCollectionThreadCount++;
1370 m_mutex.unlock();
1371
1372 setCollectionStatusProperty(i_vpdFilePath,
1373 constants::vpdCollectionInProgress);
1374
1375 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
1376 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
1377 {
1378 types::ObjectMap objectInterfaceMap;
1379 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1380
1381 // Notify PIM
1382 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1383 {
1384 throw std::runtime_error(
1385 std::string(__FUNCTION__) +
1386 "Call to PIM failed while publishing VPD.");
1387 }
1388 }
1389 else
1390 {
1391 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1392 i_vpdFilePath + "]. Check PEL for reason.");
1393
1394 // As empty parsedVpdMap recieved for some reason, but still
1395 // considered VPD collection is completed. Hence FRU collection
1396 // Status will be set as completed.
1397 }
1398 }
1399 catch (const std::exception& ex)
1400 {
1401 setCollectionStatusProperty(i_vpdFilePath,
1402 constants::vpdCollectionFailed);
1403
1404 // handle all the exceptions internally. Return only true/false
1405 // based on status of execution.
1406 if (typeid(ex) == std::type_index(typeid(DataException)))
1407 {
1408 uint16_t l_errCode = 0;
1409 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1410 // logging error for these cases.
1411 if (vpdSpecificUtility::isPass1Planar())
1412 {
1413 std::string l_invPath =
1414 jsonUtility::getInventoryObjPathFromJson(
1415 m_parsedJson, i_vpdFilePath, l_errCode);
1416
1417 if (l_errCode != 0)
1418 {
1419 logging::logMessage(
1420 "Failed to get inventory object path from JSON for FRU [" +
1421 i_vpdFilePath +
1422 "], error: " + commonUtility::getErrCodeMsg(l_errCode));
1423 }
1424
1425 const std::string& l_invPathLeafValue =
1426 sdbusplus::message::object_path(l_invPath).filename();
1427
1428 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1429 std::string::npos))
1430 {
1431 // skip logging any PEL for PCIe cards on pass 1 planar.
1432 return std::make_tuple(false, i_vpdFilePath);
1433 }
1434 }
1435 }
1436
1437 EventLogger::createSyncPel(
1438 EventLogger::getErrorType(ex),
1439 (typeid(ex) == typeid(DataException)) ||
1440 (typeid(ex) == typeid(EccException))
1441 ? types::SeverityType::Warning
1442 : types::SeverityType::Informational,
1443 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1444 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1445
1446 // TODO: Figure out a way to clear data in case of any failure at
1447 // runtime.
1448
1449 // set present property to false for any error case. In future this will
1450 // be replaced by presence logic.
1451 // Update Present property for this FRU only if we handle Present
1452 // property for the FRU.
1453 if (isPresentPropertyHandlingRequired(
1454 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1455 {
1456 setPresentProperty(i_vpdFilePath, false);
1457 }
1458
1459 m_semaphore.release();
1460 return std::make_tuple(false, i_vpdFilePath);
1461 }
1462
1463 setCollectionStatusProperty(i_vpdFilePath,
1464 constants::vpdCollectionCompleted);
1465 m_semaphore.release();
1466 return std::make_tuple(true, i_vpdFilePath);
1467 }
1468
skipPathForCollection(const std::string & i_vpdFilePath)1469 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1470 {
1471 if (i_vpdFilePath.empty())
1472 {
1473 return true;
1474 }
1475
1476 // skip processing of system VPD again as it has been already collected.
1477 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1478 {
1479 return true;
1480 }
1481
1482 if (dbusUtility::isChassisPowerOn())
1483 {
1484 // If chassis is powered on, skip collecting FRUs which are
1485 // powerOffOnly.
1486
1487 uint16_t l_errCode = 0;
1488 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath,
1489 l_errCode))
1490 {
1491 return true;
1492 }
1493 else if (l_errCode)
1494 {
1495 logging::logMessage(
1496 "Failed to check if FRU is power off only for FRU [" +
1497 i_vpdFilePath +
1498 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1499 }
1500
1501 l_errCode = 0;
1502 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1503 m_parsedJson, i_vpdFilePath, l_errCode);
1504
1505 if (l_errCode)
1506 {
1507 logging::logMessage(
1508 "Failed to get inventory path from JSON for FRU [" +
1509 i_vpdFilePath +
1510 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1511
1512 return false;
1513 }
1514
1515 const std::string& l_invPathLeafValue =
1516 sdbusplus::message::object_path(l_invPath).filename();
1517
1518 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1519 {
1520 return true;
1521 }
1522 }
1523
1524 return false;
1525 }
1526
collectFrusFromJson()1527 void Worker::collectFrusFromJson()
1528 {
1529 // A parsed JSON file should be present to pick FRUs EEPROM paths
1530 if (m_parsedJson.empty())
1531 {
1532 throw JsonException(
1533 std::string(__FUNCTION__) +
1534 ": Config JSON is mandatory for processing of FRUs through this API.",
1535 m_configJsonPath);
1536 }
1537
1538 const nlohmann::json& listOfFrus =
1539 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1540
1541 for (const auto& itemFRUS : listOfFrus.items())
1542 {
1543 const std::string& vpdFilePath = itemFRUS.key();
1544
1545 if (skipPathForCollection(vpdFilePath))
1546 {
1547 continue;
1548 }
1549
1550 try
1551 {
1552 std::thread{[vpdFilePath, this]() {
1553 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1554
1555 m_mutex.lock();
1556 m_activeCollectionThreadCount--;
1557 m_mutex.unlock();
1558
1559 if (!m_activeCollectionThreadCount)
1560 {
1561 m_isAllFruCollected = true;
1562 }
1563 }}.detach();
1564 }
1565 catch (const std::exception& l_ex)
1566 {
1567 // add vpdFilePath(EEPROM path) to failed list
1568 m_failedEepromPaths.push_front(vpdFilePath);
1569 }
1570 }
1571 }
1572
1573 // ToDo: Move the API under IBM_SYSTEM
performBackupAndRestore(types::VPDMapVariant & io_srcVpdMap)1574 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1575 {
1576 try
1577 {
1578 uint16_t l_errCode = 0;
1579 std::string l_backupAndRestoreCfgFilePath =
1580 m_parsedJson.value("backupRestoreConfigPath", "");
1581
1582 nlohmann::json l_backupAndRestoreCfgJsonObj =
1583 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1584 l_errCode);
1585
1586 if (l_errCode)
1587 {
1588 throw JsonException(
1589 "JSON parsing failed for file [ " +
1590 l_backupAndRestoreCfgFilePath +
1591 " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
1592 l_backupAndRestoreCfgFilePath);
1593 }
1594
1595 // check if either of "source" or "destination" has inventory path.
1596 // this indicates that this sytem has System VPD on hardware
1597 // and other copy on D-Bus (BMC cache).
1598 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1599 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1600 l_backupAndRestoreCfgJsonObj["source"].contains(
1601 "inventoryPath")) ||
1602 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1603 l_backupAndRestoreCfgJsonObj["destination"].contains(
1604 "inventoryPath"))))
1605 {
1606 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1607 auto [l_srcVpdVariant,
1608 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1609
1610 // ToDo: Revisit is this check is required or not.
1611 if (auto l_srcVpdMap =
1612 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1613 l_srcVpdMap && !(*l_srcVpdMap).empty())
1614 {
1615 io_srcVpdMap = std::move(l_srcVpdVariant);
1616 }
1617 }
1618 }
1619 catch (const std::exception& l_ex)
1620 {
1621 EventLogger::createSyncPel(
1622 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1623 __FILE__, __FUNCTION__, 0,
1624 std::string(
1625 "Exception caught while backup and restore VPD keyword's.") +
1626 EventLogger::getErrorMsg(l_ex),
1627 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1628 }
1629 }
1630
deleteFruVpd(const std::string & i_dbusObjPath)1631 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1632 {
1633 if (i_dbusObjPath.empty())
1634 {
1635 throw std::runtime_error("Given DBus object path is empty.");
1636 }
1637
1638 uint16_t l_errCode = 0;
1639 const std::string& l_fruPath =
1640 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1641
1642 if (l_errCode)
1643 {
1644 logging::logMessage(
1645 "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1646 "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1647 " Aborting FRU VPD deletion.");
1648 return;
1649 }
1650
1651 try
1652 {
1653 auto l_presentPropValue = dbusUtility::readDbusProperty(
1654 constants::pimServiceName, i_dbusObjPath,
1655 constants::inventoryItemInf, "Present");
1656
1657 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1658 {
1659 uint16_t l_errCode = 0;
1660 // check if FRU's Present property is handled by vpd-manager
1661 const auto& l_isFruPresenceHandled =
1662 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1663 l_errCode);
1664
1665 if (l_errCode)
1666 {
1667 throw std::runtime_error(
1668 "Failed to check if FRU's presence is handled, reason: " +
1669 commonUtility::getErrCodeMsg(l_errCode));
1670 }
1671
1672 if (!(*l_value) && l_isFruPresenceHandled)
1673 {
1674 throw std::runtime_error("Given FRU is not present");
1675 }
1676 else if (*l_value && !l_isFruPresenceHandled)
1677 {
1678 throw std::runtime_error(
1679 "Given FRU is present and its presence is not handled by vpd-manager.");
1680 }
1681 else
1682 {
1683 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1684 "preAction", "deletion",
1685 l_errCode))
1686 {
1687 if (!processPreAction(l_fruPath, "deletion", l_errCode))
1688 {
1689 std::string l_msg = "Pre action failed";
1690 if (l_errCode)
1691 {
1692 l_msg += " Reason: " +
1693 commonUtility::getErrCodeMsg(l_errCode);
1694 }
1695 throw std::runtime_error(l_msg);
1696 }
1697 }
1698 else if (l_errCode)
1699 {
1700 logging::logMessage(
1701 "Failed to check if pre action required for FRU [" +
1702 l_fruPath + "], error : " +
1703 commonUtility::getErrCodeMsg(l_errCode));
1704 }
1705
1706 std::vector<std::string> l_interfaceList{
1707 constants::operationalStatusInf};
1708
1709 types::MapperGetSubTree l_subTreeMap =
1710 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1711 l_interfaceList);
1712
1713 types::ObjectMap l_objectMap;
1714
1715 // Updates VPD specific interfaces property value under PIM for
1716 // sub FRUs.
1717 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1718 l_subTreeMap)
1719 {
1720 types::InterfaceMap l_interfaceMap;
1721 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1722 l_interfaceMap);
1723 l_objectMap.emplace(l_objectPath,
1724 std::move(l_interfaceMap));
1725 }
1726
1727 types::InterfaceMap l_interfaceMap;
1728 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1729 l_interfaceMap);
1730
1731 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1732
1733 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1734 {
1735 throw std::runtime_error("Call to PIM failed.");
1736 }
1737
1738 l_errCode = 0;
1739
1740 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1741 "postAction", "deletion",
1742 l_errCode))
1743 {
1744 if (!processPostAction(l_fruPath, "deletion"))
1745 {
1746 throw std::runtime_error("Post action failed");
1747 }
1748 }
1749 else if (l_errCode)
1750 {
1751 logging::logMessage(
1752 "Failed to check if post action required during deletion for FRU [" +
1753 l_fruPath + "], error : " +
1754 commonUtility::getErrCodeMsg(l_errCode));
1755 }
1756 }
1757 }
1758 else
1759 {
1760 logging::logMessage(
1761 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1762 "] as unable to read present property");
1763 return;
1764 }
1765
1766 logging::logMessage(
1767 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1768 }
1769 catch (const std::exception& l_ex)
1770 {
1771 uint16_t l_errCode = 0;
1772 std::string l_errMsg =
1773 "Failed to delete VPD for FRU : " + i_dbusObjPath +
1774 " error: " + std::string(l_ex.what());
1775
1776 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1777 "postFailAction", "deletion",
1778 l_errCode))
1779 {
1780 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1781 "deletion", l_errCode))
1782 {
1783 l_errMsg += ". Post fail action also failed, error : " +
1784 commonUtility::getErrCodeMsg(l_errCode);
1785 }
1786 }
1787 else if (l_errCode)
1788 {
1789 l_errMsg +=
1790 ". Failed to check if post fail action required, error : " +
1791 commonUtility::getErrCodeMsg(l_errCode);
1792 }
1793
1794 logging::logMessage(l_errMsg);
1795 }
1796 }
1797
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1798 void Worker::setPresentProperty(const std::string& i_vpdPath,
1799 const bool& i_value)
1800 {
1801 try
1802 {
1803 if (i_vpdPath.empty())
1804 {
1805 throw std::runtime_error(
1806 "Path is empty. Can't set present property");
1807 }
1808
1809 types::ObjectMap l_objectInterfaceMap;
1810
1811 // If the given path is EEPROM path.
1812 if (m_parsedJson["frus"].contains(i_vpdPath))
1813 {
1814 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1815 {
1816 sdbusplus::message::object_path l_fruObjectPath(
1817 l_Fru["inventoryPath"]);
1818
1819 types::PropertyMap l_propertyValueMap;
1820 l_propertyValueMap.emplace("Present", i_value);
1821
1822 types::InterfaceMap l_interfaces;
1823 vpdSpecificUtility::insertOrMerge(l_interfaces,
1824 constants::inventoryItemInf,
1825 move(l_propertyValueMap));
1826
1827 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1828 std::move(l_interfaces));
1829 }
1830 }
1831 else
1832 {
1833 // consider it as an inventory path.
1834 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1835 {
1836 throw std::runtime_error(
1837 "Invalid inventory path: " + i_vpdPath);
1838 }
1839
1840 types::PropertyMap l_propertyValueMap;
1841 l_propertyValueMap.emplace("Present", i_value);
1842
1843 types::InterfaceMap l_interfaces;
1844 vpdSpecificUtility::insertOrMerge(l_interfaces,
1845 constants::inventoryItemInf,
1846 move(l_propertyValueMap));
1847
1848 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1849 }
1850
1851 // Notify PIM
1852 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1853 {
1854 throw DbusException(
1855 std::string(__FUNCTION__) +
1856 "Call to PIM failed while setting present property for path " +
1857 i_vpdPath);
1858 }
1859 }
1860 catch (const std::exception& l_ex)
1861 {
1862 EventLogger::createSyncPel(
1863 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1864 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1865 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1866 }
1867 }
1868
performVpdRecollection()1869 void Worker::performVpdRecollection()
1870 {
1871 try
1872 {
1873 // Check if system config JSON is present
1874 if (m_parsedJson.empty())
1875 {
1876 throw std::runtime_error(
1877 "System config json object is empty, can't process recollection.");
1878 }
1879
1880 uint16_t l_errCode = 0;
1881 const auto& l_frusReplaceableAtStandby =
1882 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson,
1883 l_errCode);
1884
1885 if (l_errCode)
1886 {
1887 logging::logMessage(
1888 "Failed to get list of FRUs replaceable at runtime, error : " +
1889 commonUtility::getErrCodeMsg(l_errCode));
1890 return;
1891 }
1892
1893 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1894 {
1895 // ToDo: Add some logic/trace to know the flow to
1896 // collectSingleFruVpd has been directed via
1897 // performVpdRecollection.
1898 collectSingleFruVpd(l_fruInventoryPath);
1899 }
1900 return;
1901 }
1902
1903 catch (const std::exception& l_ex)
1904 {
1905 // TODO Log PEL
1906 logging::logMessage(
1907 "VPD recollection failed with error: " + std::string(l_ex.what()));
1908 }
1909 }
1910
collectSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)1911 void Worker::collectSingleFruVpd(
1912 const sdbusplus::message::object_path& i_dbusObjPath)
1913 {
1914 std::string l_fruPath{};
1915 uint16_t l_errCode = 0;
1916
1917 try
1918 {
1919 // Check if system config JSON is present
1920 if (m_parsedJson.empty())
1921 {
1922 logging::logMessage(
1923 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1924 std::string(i_dbusObjPath));
1925 return;
1926 }
1927
1928 // Get FRU path for the given D-bus object path from JSON
1929 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
1930 l_errCode);
1931
1932 if (l_fruPath.empty())
1933 {
1934 if (l_errCode)
1935 {
1936 logging::logMessage(
1937 "Failed to get FRU path for [" +
1938 std::string(i_dbusObjPath) +
1939 "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1940 " Aborting single FRU VPD collection.");
1941 return;
1942 }
1943
1944 logging::logMessage(
1945 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1946 std::string(i_dbusObjPath));
1947 return;
1948 }
1949
1950 // Check if host is up and running
1951 if (dbusUtility::isHostRunning())
1952 {
1953 uint16_t l_errCode = 0;
1954 bool isFruReplaceableAtRuntime =
1955 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1956 l_errCode);
1957
1958 if (l_errCode)
1959 {
1960 logging::logMessage(
1961 "Failed to check if FRU is replaceable at runtime for FRU : [" +
1962 std::string(i_dbusObjPath) +
1963 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1964 return;
1965 }
1966
1967 if (!isFruReplaceableAtRuntime)
1968 {
1969 logging::logMessage(
1970 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1971 std::string(i_dbusObjPath));
1972 return;
1973 }
1974 }
1975 else if (dbusUtility::isBMCReady())
1976 {
1977 uint16_t l_errCode = 0;
1978 bool isFruReplaceableAtStandby =
1979 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
1980 l_errCode);
1981
1982 if (l_errCode)
1983 {
1984 logging::logMessage(
1985 "Error while checking if FRU is replaceable at standby for FRU [" +
1986 std::string(i_dbusObjPath) +
1987 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1988 }
1989
1990 l_errCode = 0;
1991 bool isFruReplaceableAtRuntime =
1992 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1993 l_errCode);
1994
1995 if (l_errCode)
1996 {
1997 logging::logMessage(
1998 "Failed to check if FRU is replaceable at runtime for FRU : [" +
1999 std::string(i_dbusObjPath) +
2000 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2001 return;
2002 }
2003
2004 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
2005 {
2006 logging::logMessage(
2007 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
2008 std::string(i_dbusObjPath));
2009 return;
2010 }
2011 }
2012
2013 // Set collection Status as InProgress. Since it's an intermediate state
2014 // D-bus set-property call is good enough to update the status.
2015 const std::string& l_collStatusProp = "Status";
2016
2017 setCollectionStatusProperty(l_fruPath,
2018 constants::vpdCollectionInProgress);
2019
2020 // Parse VPD
2021 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
2022
2023 // If l_parsedVpd is pointing to std::monostate
2024 if (l_parsedVpd.index() == 0)
2025 {
2026 throw std::runtime_error(
2027 "VPD parsing failed for " + std::string(i_dbusObjPath));
2028 }
2029
2030 // Get D-bus object map from worker class
2031 types::ObjectMap l_dbusObjectMap;
2032 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
2033
2034 if (l_dbusObjectMap.empty())
2035 {
2036 throw std::runtime_error(
2037 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
2038 std::string(i_dbusObjPath));
2039 }
2040
2041 // Call PIM's Notify method
2042 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2043 {
2044 throw std::runtime_error(
2045 "Notify PIM failed. Single FRU VPD collection failed for " +
2046 std::string(i_dbusObjPath));
2047 }
2048 setCollectionStatusProperty(l_fruPath,
2049 constants::vpdCollectionCompleted);
2050 }
2051 catch (const std::exception& l_error)
2052 {
2053 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
2054 // TODO: Log PEL
2055 logging::logMessage(std::string(l_error.what()));
2056 }
2057 }
2058
setCollectionStatusProperty(const std::string & i_vpdPath,const std::string & i_value) const2059 void Worker::setCollectionStatusProperty(
2060 const std::string& i_vpdPath, const std::string& i_value) const noexcept
2061 {
2062 try
2063 {
2064 if (i_vpdPath.empty())
2065 {
2066 throw std::runtime_error(
2067 "Given path is empty. Can't set collection Status property");
2068 }
2069
2070 types::PropertyMap l_timeStampMap;
2071 if (i_value == constants::vpdCollectionCompleted ||
2072 i_value == constants::vpdCollectionFailed)
2073 {
2074 l_timeStampMap.emplace(
2075 "CompletedTime",
2076 types::DbusVariantType{
2077 commonUtility::getCurrentTimeSinceEpoch()});
2078 }
2079 else if (i_value == constants::vpdCollectionInProgress)
2080 {
2081 l_timeStampMap.emplace(
2082 "StartTime", types::DbusVariantType{
2083 commonUtility::getCurrentTimeSinceEpoch()});
2084 }
2085 else if (i_value == constants::vpdCollectionNotStarted)
2086 {
2087 l_timeStampMap.emplace("StartTime", 0);
2088 l_timeStampMap.emplace("CompletedTime", 0);
2089 }
2090
2091 types::ObjectMap l_objectInterfaceMap;
2092
2093 if (m_parsedJson["frus"].contains(i_vpdPath))
2094 {
2095 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2096 {
2097 sdbusplus::message::object_path l_fruObjectPath(
2098 l_Fru["inventoryPath"]);
2099
2100 types::PropertyMap l_propertyValueMap;
2101 l_propertyValueMap.emplace("Status", i_value);
2102 l_propertyValueMap.insert(l_timeStampMap.begin(),
2103 l_timeStampMap.end());
2104
2105 types::InterfaceMap l_interfaces;
2106 vpdSpecificUtility::insertOrMerge(
2107 l_interfaces, constants::vpdCollectionInterface,
2108 move(l_propertyValueMap));
2109
2110 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2111 std::move(l_interfaces));
2112 }
2113 }
2114 else
2115 {
2116 // consider it as an inventory path.
2117 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2118 {
2119 throw std::runtime_error(
2120 "Invalid inventory path: " + i_vpdPath +
2121 ". Can't set collection Status property");
2122 }
2123
2124 types::PropertyMap l_propertyValueMap;
2125 l_propertyValueMap.emplace("Status", i_value);
2126 l_propertyValueMap.insert(l_timeStampMap.begin(),
2127 l_timeStampMap.end());
2128
2129 types::InterfaceMap l_interfaces;
2130 vpdSpecificUtility::insertOrMerge(l_interfaces,
2131 constants::vpdCollectionInterface,
2132 move(l_propertyValueMap));
2133
2134 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2135 }
2136
2137 // Notify PIM
2138 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2139 {
2140 throw DbusException(
2141 std::string(__FUNCTION__) +
2142 "Call to PIM failed while setting collection Status property for path " +
2143 i_vpdPath);
2144 }
2145 }
2146 catch (const std::exception& l_ex)
2147 {
2148 EventLogger::createSyncPel(
2149 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2150 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2151 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2152 }
2153 }
2154 } // namespace vpd
2155