blob: 179e7ec8521721084f3d7be42b2f943a0fec9bad [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#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/dbus_utility.hpp>
16#include <utility/json_utility.hpp>
17#include <utility/vpd_specific_utility.hpp>
18
19#include <filesystem>
20#include <fstream>
21#include <future>
22#include <typeindex>
23#include <unordered_set>
24
25namespace vpd
26{
27
Sunny Srivastava765cf7b2025-02-04 05:24:11 -060028Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount) :
29 m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050030{
31 // Implies the processing is based on some config JSON
32 if (!m_configJsonPath.empty())
33 {
34 // Check if symlink is already there to confirm fresh boot/factory
35 // reset.
36 if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK))
37 {
38 logging::logMessage("Sym Link already present");
39 m_configJsonPath = INVENTORY_JSON_SYM_LINK;
40 m_isSymlinkPresent = true;
41 }
42
43 try
44 {
45 m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath);
46
47 // check for mandatory fields at this point itself.
48 if (!m_parsedJson.contains("frus"))
49 {
50 throw std::runtime_error("Mandatory tag(s) missing from JSON");
51 }
52 }
53 catch (const std::exception& ex)
54 {
55 throw(JsonException(ex.what(), m_configJsonPath));
56 }
57 }
58 else
59 {
60 logging::logMessage("Processing in not based on any config JSON");
61 }
62}
63
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050064static std::string readFitConfigValue()
65{
66 std::vector<std::string> output =
67 commonUtility::executeCmd("/sbin/fw_printenv");
68 std::string fitConfigValue;
69
70 for (const auto& entry : output)
71 {
72 auto pos = entry.find("=");
73 auto key = entry.substr(0, pos);
74 if (key != "fitconfig")
75 {
76 continue;
77 }
78
79 if (pos + 1 < entry.size())
80 {
81 fitConfigValue = entry.substr(pos + 1);
82 }
83 }
84
85 return fitConfigValue;
86}
87
88bool Worker::isSystemVPDOnDBus() const
89{
90 const std::string& mboardPath =
91 m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
92 "inventoryPath", "");
93
94 if (mboardPath.empty())
95 {
96 throw JsonException("System vpd file path missing in JSON",
97 INVENTORY_JSON_SYM_LINK);
98 }
99
100 std::array<const char*, 1> interfaces = {
101 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
102
103 const types::MapperGetObject& objectMap =
104 dbusUtility::getObjectMap(mboardPath, interfaces);
105
106 if (objectMap.empty())
107 {
108 return false;
109 }
110 return true;
111}
112
113std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
114{
115 if (parsedVpd.empty())
116 {
117 throw std::runtime_error("Empty VPD map. Can't Extract IM value");
118 }
119
120 const auto& itrToVSBP = parsedVpd.find("VSBP");
121 if (itrToVSBP == parsedVpd.end())
122 {
123 throw DataException("VSBP record missing.");
124 }
125
126 const auto& itrToIM = (itrToVSBP->second).find("IM");
127 if (itrToIM == (itrToVSBP->second).end())
128 {
129 throw DataException("IM keyword missing.");
130 }
131
132 types::BinaryVector imVal;
133 std::copy(itrToIM->second.begin(), itrToIM->second.end(),
134 back_inserter(imVal));
135
136 std::ostringstream imData;
137 for (auto& aByte : imVal)
138 {
139 imData << std::setw(2) << std::setfill('0') << std::hex
140 << static_cast<int>(aByte);
141 }
142
143 return imData.str();
144}
145
146std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
147{
148 if (parsedVpd.empty())
149 {
150 throw std::runtime_error("Empty VPD map. Can't Extract HW value");
151 }
152
153 const auto& itrToVINI = parsedVpd.find("VINI");
154 if (itrToVINI == parsedVpd.end())
155 {
156 throw DataException("VINI record missing.");
157 }
158
159 const auto& itrToHW = (itrToVINI->second).find("HW");
160 if (itrToHW == (itrToVINI->second).end())
161 {
162 throw DataException("HW keyword missing.");
163 }
164
165 types::BinaryVector hwVal;
166 std::copy(itrToHW->second.begin(), itrToHW->second.end(),
167 back_inserter(hwVal));
168
169 // The planar pass only comes from the LSB of the HW keyword,
170 // where as the MSB is used for other purposes such as signifying clock
171 // termination.
172 hwVal[0] = 0x00;
173
174 std::ostringstream hwString;
175 for (auto& aByte : hwVal)
176 {
177 hwString << std::setw(2) << std::setfill('0') << std::hex
178 << static_cast<int>(aByte);
179 }
180
181 return hwString.str();
182}
183
184void Worker::fillVPDMap(const std::string& vpdFilePath,
185 types::VPDMapVariant& vpdMap)
186{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500187 if (vpdFilePath.empty())
188 {
189 throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
190 }
191
192 if (!std::filesystem::exists(vpdFilePath))
193 {
194 throw std::runtime_error("Can't Find physical file");
195 }
196
Sunny Srivastava043955d2025-01-21 18:04:49 +0530197 std::shared_ptr<Parser> vpdParser =
198 std::make_shared<Parser>(vpdFilePath, m_parsedJson);
199 vpdMap = vpdParser->parse();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500200}
201
202void Worker::getSystemJson(std::string& systemJson,
203 const types::VPDMapVariant& parsedVpdMap)
204{
205 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
206 {
207 std::string hwKWdValue = getHWVersion(*pVal);
208 if (hwKWdValue.empty())
209 {
210 throw DataException("HW value fetched is empty.");
211 }
212
213 const std::string& imKwdValue = getIMValue(*pVal);
214 if (imKwdValue.empty())
215 {
216 throw DataException("IM value fetched is empty.");
217 }
218
219 auto itrToIM = config::systemType.find(imKwdValue);
220 if (itrToIM == config::systemType.end())
221 {
222 throw DataException("IM keyword does not map to any system type");
223 }
224
225 const types::HWVerList hwVersionList = itrToIM->second.second;
226 if (!hwVersionList.empty())
227 {
228 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
229 ::toupper);
230
231 auto itrToHW =
232 std::find_if(hwVersionList.begin(), hwVersionList.end(),
233 [&hwKWdValue](const auto& aPair) {
234 return aPair.first == hwKWdValue;
235 });
236
237 if (itrToHW != hwVersionList.end())
238 {
239 if (!(*itrToHW).second.empty())
240 {
241 systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
242 ".json";
243 }
244 else
245 {
246 systemJson += (*itrToIM).first + ".json";
247 }
248 return;
249 }
250 }
251 systemJson += itrToIM->second.first + ".json";
252 return;
253 }
254
Sunny Srivastava043955d2025-01-21 18:04:49 +0530255 throw DataException(
256 "Invalid VPD type returned from Parser. Can't get system JSON.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500257}
258
259static void setEnvAndReboot(const std::string& key, const std::string& value)
260{
261 // set env and reboot and break.
262 commonUtility::executeCmd("/sbin/fw_setenv", key, value);
263 logging::logMessage("Rebooting BMC to pick up new device tree");
264
265 // make dbus call to reboot
266 auto bus = sdbusplus::bus::new_default_system();
267 auto method = bus.new_method_call(
268 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager", "Reboot");
270 bus.call_noreply(method);
271}
272
273void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
274{
275 std::error_code l_ec;
276 l_ec.clear();
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530277
278 // Check if symlink file path exists and if the JSON at this location is a
279 // symlink.
280 if (m_isSymlinkPresent &&
281 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
282 { // Don't care about exception in "is_symlink". Will continue with creation
283 // of symlink.
284
285 const auto& l_symlinkFilePth =
286 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
287
288 if (l_ec)
289 {
290 logging::logMessage(
291 "Can't read existing symlink. Error =" + l_ec.message() +
292 "Trying removal of symlink and creation of new symlink.");
293 }
294
295 // If currently set JSON is the required one. No further processing
296 // required.
297 if (i_systemJson == l_symlinkFilePth)
298 {
299 // Correct symlink already set.
300 return;
301 }
302
303 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
304 {
305 // No point going further. If removal fails for existing symlink,
306 // create will anyways throw.
307 throw std::runtime_error(
308 "Removal of symlink failed with Error = " + l_ec.message() +
309 ". Can't proceed with create_symlink.");
310 }
311 }
312
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500313 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
314 {
315 if (l_ec)
316 {
317 throw std::runtime_error(
318 "File system call to exist failed with error = " +
319 l_ec.message());
320 }
321
322 // implies it is a fresh boot/factory reset.
323 // Create the directory for hosting the symlink
324 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
325 {
326 if (l_ec)
327 {
328 throw std::runtime_error(
329 "File system call to create directory failed with error = " +
330 l_ec.message());
331 }
332 }
333 }
334
335 // create a new symlink based on the system
336 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
337 l_ec);
338
339 if (l_ec)
340 {
341 throw std::runtime_error(
342 "create_symlink system call failed with error: " + l_ec.message());
343 }
344
345 // If the flow is at this point implies the symlink was not present there.
346 // Considering this as factory reset.
347 m_isFactoryResetDone = true;
348}
349
350void Worker::setDeviceTreeAndJson()
351{
352 // JSON is madatory for processing of this API.
353 if (m_parsedJson.empty())
354 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530355 throw JsonException("System config JSON is empty", m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500356 }
357
358 types::VPDMapVariant parsedVpdMap;
359 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
360
361 // Implies it is default JSON.
362 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
363
364 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
365 // This is required to support movement from rainier to Blue Ridge on the
366 // fly.
367
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530368 getSystemJson(systemJson, parsedVpdMap);
369
370 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500371 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530372 throw DataException(
373 "No system JSON found corresponding to IM read from VPD.");
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530374 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500375
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530376 // re-parse the JSON once appropriate JSON has been selected.
377 m_parsedJson = jsonUtility::getParsedJson(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500378
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530379 if (m_parsedJson.empty())
380 {
381 throw(JsonException("Json parsing failed", systemJson));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500382 }
383
384 std::string devTreeFromJson;
385 if (m_parsedJson.contains("devTree"))
386 {
387 devTreeFromJson = m_parsedJson["devTree"];
388
389 if (devTreeFromJson.empty())
390 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530391 EventLogger::createSyncPel(
392 types::ErrorType::JsonFailure, types::SeverityType::Error,
393 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500394 "Mandatory value for device tree missing from JSON[" +
Sunny Srivastava043955d2025-01-21 18:04:49 +0530395 systemJson + "]",
396 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500397 }
398 }
399
400 auto fitConfigVal = readFitConfigValue();
401
402 if (devTreeFromJson.empty() ||
403 fitConfigVal.find(devTreeFromJson) != std::string::npos)
404 { // Skipping setting device tree as either devtree info is missing from
405 // Json or it is rightly set.
406
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530407 setJsonSymbolicLink(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500408
409 if (isSystemVPDOnDBus() &&
410 jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
411 {
412 performBackupAndRestore(parsedVpdMap);
413 }
414
415 // proceed to publish system VPD.
416 publishSystemVPD(parsedVpdMap);
417 return;
418 }
419
420 setEnvAndReboot("fitconfig", devTreeFromJson);
421 exit(EXIT_SUCCESS);
422}
423
424void Worker::populateIPZVPDpropertyMap(
425 types::InterfaceMap& interfacePropMap,
426 const types::IPZKwdValueMap& keyordValueMap,
427 const std::string& interfaceName)
428{
429 types::PropertyMap propertyValueMap;
430 for (const auto& kwdVal : keyordValueMap)
431 {
432 auto kwd = kwdVal.first;
433
434 if (kwd[0] == '#')
435 {
436 kwd = std::string("PD_") + kwd[1];
437 }
438 else if (isdigit(kwd[0]))
439 {
440 kwd = std::string("N_") + kwd;
441 }
442
443 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
444 propertyValueMap.emplace(move(kwd), move(value));
445 }
446
447 if (!propertyValueMap.empty())
448 {
449 interfacePropMap.emplace(interfaceName, propertyValueMap);
450 }
451}
452
453void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
454 types::InterfaceMap& interfaceMap)
455{
456 for (const auto& kwdValMap : keyordVPDMap)
457 {
458 types::PropertyMap propertyValueMap;
459 auto kwd = kwdValMap.first;
460
461 if (kwd[0] == '#')
462 {
463 kwd = std::string("PD_") + kwd[1];
464 }
465 else if (isdigit(kwd[0]))
466 {
467 kwd = std::string("N_") + kwd;
468 }
469
470 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
471 {
472 types::BinaryVector value((*keywordValue).begin(),
473 (*keywordValue).end());
474 propertyValueMap.emplace(move(kwd), move(value));
475 }
476 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
477 {
478 types::BinaryVector value((*keywordValue).begin(),
479 (*keywordValue).end());
480 propertyValueMap.emplace(move(kwd), move(value));
481 }
482 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
483 {
484 if (kwd == "MemorySizeInKB")
485 {
486 types::PropertyMap memProp;
487 memProp.emplace(move(kwd), ((*keywordValue)));
488 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
489 move(memProp));
490 continue;
491 }
492 else
493 {
494 logging::logMessage(
495 "Unknown Keyword =" + kwd + " found in keyword VPD map");
496 continue;
497 }
498 }
499 else
500 {
501 logging::logMessage(
502 "Unknown variant type found in keyword VPD map.");
503 continue;
504 }
505
506 if (!propertyValueMap.empty())
507 {
508 vpdSpecificUtility::insertOrMerge(
509 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
510 }
511 }
512}
513
514void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
515 types::InterfaceMap& interfaceMap,
516 const types::VPDMapVariant& parsedVpdMap)
517{
518 for (const auto& interfacesPropPair : interfaceJson.items())
519 {
520 const std::string& interface = interfacesPropPair.key();
521 types::PropertyMap propertyMap;
522
523 for (const auto& propValuePair : interfacesPropPair.value().items())
524 {
525 const std::string property = propValuePair.key();
526
527 if (propValuePair.value().is_boolean())
528 {
529 propertyMap.emplace(property,
530 propValuePair.value().get<bool>());
531 }
532 else if (propValuePair.value().is_string())
533 {
534 if (property.compare("LocationCode") == 0 &&
535 interface.compare("com.ibm.ipzvpd.Location") == 0)
536 {
537 std::string value =
538 vpdSpecificUtility::getExpandedLocationCode(
539 propValuePair.value().get<std::string>(),
540 parsedVpdMap);
541 propertyMap.emplace(property, value);
542
543 auto l_locCodeProperty = propertyMap;
544 vpdSpecificUtility::insertOrMerge(
545 interfaceMap,
546 std::string(constants::xyzLocationCodeInf),
547 move(l_locCodeProperty));
548 }
549 else
550 {
551 propertyMap.emplace(
552 property, propValuePair.value().get<std::string>());
553 }
554 }
555 else if (propValuePair.value().is_array())
556 {
557 try
558 {
559 propertyMap.emplace(
560 property,
561 propValuePair.value().get<types::BinaryVector>());
562 }
563 catch (const nlohmann::detail::type_error& e)
564 {
565 std::cerr << "Type exception: " << e.what() << "\n";
566 }
567 }
568 else if (propValuePair.value().is_number())
569 {
570 // For now assume the value is a size_t. In the future it would
571 // be nice to come up with a way to get the type from the JSON.
572 propertyMap.emplace(property,
573 propValuePair.value().get<size_t>());
574 }
575 else if (propValuePair.value().is_object())
576 {
577 const std::string& record =
578 propValuePair.value().value("recordName", "");
579 const std::string& keyword =
580 propValuePair.value().value("keywordName", "");
581 const std::string& encoding =
582 propValuePair.value().value("encoding", "");
583
584 if (auto ipzVpdMap =
585 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
586 {
587 if (!record.empty() && !keyword.empty() &&
588 (*ipzVpdMap).count(record) &&
589 (*ipzVpdMap).at(record).count(keyword))
590 {
591 auto encoded = vpdSpecificUtility::encodeKeyword(
592 ((*ipzVpdMap).at(record).at(keyword)), encoding);
593 propertyMap.emplace(property, encoded);
594 }
595 }
596 else if (auto kwdVpdMap =
597 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
598 {
599 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
600 {
601 if (auto kwValue = std::get_if<types::BinaryVector>(
602 &(*kwdVpdMap).at(keyword)))
603 {
604 auto encodedValue =
605 vpdSpecificUtility::encodeKeyword(
606 std::string((*kwValue).begin(),
607 (*kwValue).end()),
608 encoding);
609
610 propertyMap.emplace(property, encodedValue);
611 }
612 else if (auto kwValue = std::get_if<std::string>(
613 &(*kwdVpdMap).at(keyword)))
614 {
615 auto encodedValue =
616 vpdSpecificUtility::encodeKeyword(
617 std::string((*kwValue).begin(),
618 (*kwValue).end()),
619 encoding);
620
621 propertyMap.emplace(property, encodedValue);
622 }
623 else if (auto uintValue = std::get_if<size_t>(
624 &(*kwdVpdMap).at(keyword)))
625 {
626 propertyMap.emplace(property, *uintValue);
627 }
628 else
629 {
630 logging::logMessage(
631 "Unknown keyword found, Keywrod = " + keyword);
632 }
633 }
634 }
635 }
636 }
637 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
638 move(propertyMap));
639 }
640}
641
642bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
643{
644 const unsigned char l_io[] = {
645 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
646 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
647
648 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
649 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
650 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
651 // IO.
652 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
653 constants::SIZE_OF_8EQ_IN_PG) == 0)
654 {
655 return true;
656 }
657
658 // The CPU is not an IO
659 return false;
660}
661
662bool Worker::primeInventory(const std::string& i_vpdFilePath)
663{
664 if (i_vpdFilePath.empty())
665 {
666 logging::logMessage("Empty VPD file path given");
667 return false;
668 }
669
670 if (m_parsedJson.empty())
671 {
672 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
673 return false;
674 }
675 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
676 {
677 logging::logMessage("File " + i_vpdFilePath +
678 ", is not found in the system config JSON file.");
679 return false;
680 }
681
682 types::ObjectMap l_objectInterfaceMap;
683 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
684 {
685 types::InterfaceMap l_interfaces;
686 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
687
688 if (l_Fru.contains("ccin"))
689 {
690 continue;
691 }
692
693 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
694 {
695 continue;
696 }
697
Souvik Roy6a9553c2025-02-07 01:16:32 -0600698 // Reset data under PIM for this FRU only if the FRU is not synthesized
699 // and we handle it's Present property.
700 if (isPresentPropertyHandlingRequired(l_Fru))
701 {
702 // Clear data under PIM if already exists.
703 vpdSpecificUtility::resetDataUnderPIM(
704 std::string(l_Fru["inventoryPath"]), l_interfaces);
705 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500706
707 // Add extra interfaces mentioned in the Json config file
708 if (l_Fru.contains("extraInterfaces"))
709 {
710 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
711 std::monostate{});
712 }
713
714 types::PropertyMap l_propertyValueMap;
Sunny Srivastavad159bb42025-01-09 11:13:50 +0530715
Souvik Roy6a9553c2025-02-07 01:16:32 -0600716 // Update Present property for this FRU only if we handle Present
717 // property for the FRU.
718 if (isPresentPropertyHandlingRequired(l_Fru))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500719 {
Souvik Roy6a9553c2025-02-07 01:16:32 -0600720 l_propertyValueMap.emplace("Present", false);
721
722 // TODO: Present based on file will be taken care in future.
723 // By default present is set to false for FRU at the time of
724 // priming. Once collection goes through, it will be set to true in
725 // that flow.
726 /*if (std::filesystem::exists(i_vpdFilePath))
727 {
728 l_propertyValueMap["Present"] = true;
729 }*/
730 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500731
732 vpdSpecificUtility::insertOrMerge(l_interfaces,
733 "xyz.openbmc_project.Inventory.Item",
734 move(l_propertyValueMap));
735
736 if (l_Fru.value("inherit", true) &&
737 m_parsedJson.contains("commonInterfaces"))
738 {
739 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
740 std::monostate{});
741 }
742
743 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
744 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
745
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600746 // Emplace the default state of FRU VPD collection
747 types::PropertyMap l_fruCollectionProperty = {
Anupama B R5cd1b2d2025-08-05 04:57:40 -0500748 {"Status", constants::vpdCollectionNotStarted}};
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600749
750 vpdSpecificUtility::insertOrMerge(l_interfaces,
751 constants::vpdCollectionInterface,
752 std::move(l_fruCollectionProperty));
753
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500754 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
755 std::move(l_interfaces));
756 }
757
758 // Notify PIM
759 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
760 {
761 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
762 return false;
763 }
764
765 return true;
766}
767
768void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
769 types::InterfaceMap& interfaces)
770{
771 // embedded property(true or false) says whether the subfru is embedded
772 // into the parent fru (or) not. VPD sets Present property only for
773 // embedded frus. If the subfru is not an embedded FRU, the subfru may
774 // or may not be physically present. Those non embedded frus will always
775 // have Present=false irrespective of its physical presence or absence.
776 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
777 // Present to true for such sub frus.
778 // Eg: ethernet port is embedded into bmc card. So set Present to true
779 // for such sub frus. Also donot populate present property for embedded
780 // subfru which is synthesized. Currently there is no subfru which are
781 // both embedded and synthesized. But still the case is handled here.
782
783 // Check if its required to handle presence for this FRU.
784 if (singleFru.value("handlePresence", true))
785 {
786 types::PropertyMap presProp;
787 presProp.emplace("Present", true);
788 vpdSpecificUtility::insertOrMerge(
789 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
790 }
791}
792
793void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
794 types::InterfaceMap& interfaces,
795 const types::VPDMapVariant& parsedVpdMap)
796{
797 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
798 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
799 {
800 if (singleFru["extraInterfaces"].contains(
801 "xyz.openbmc_project.Inventory.Item.Cpu"))
802 {
803 auto itrToRec = (*ipzVpdMap).find("CP00");
804 if (itrToRec == (*ipzVpdMap).end())
805 {
806 return;
807 }
808
Souvik Roya55fcca2025-02-19 01:33:58 -0600809 const std::string pgKeywordValue{
810 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
811
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500812 if (!pgKeywordValue.empty())
813 {
814 if (isCPUIOGoodOnly(pgKeywordValue))
815 {
816 interfaces["xyz.openbmc_project.Inventory.Item"]
817 ["PrettyName"] = "IO Module";
818 }
819 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600820 else
821 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530822 throw DataException(std::string(__FUNCTION__) +
823 "Failed to get value for keyword PG");
Souvik Roya55fcca2025-02-19 01:33:58 -0600824 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500825 }
826 }
827}
828
829void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
830 const types::VPDMapVariant& parsedVpdMap,
831 types::InterfaceMap& interfaces)
832{
833 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
834 {
835 for (const auto& record : singleFru["copyRecords"])
836 {
837 const std::string& recordName = record;
838 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
839 {
840 populateIPZVPDpropertyMap(interfaces,
841 (*ipzVpdMap).at(recordName),
842 constants::ipzVpdInf + recordName);
843 }
844 }
845 }
846}
847
848void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
849 types::InterfaceMap& interfaces)
850{
851 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
852 {
853 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
854 {
855 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
856 constants::ipzVpdInf + recordName);
857 }
858 }
859 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
860 {
861 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
862 }
863
864 if (m_parsedJson.contains("commonInterfaces"))
865 {
866 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
867 parsedVpdMap);
868 }
869}
870
871bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
872 const types::VPDMapVariant& parsedVpdMap)
873{
874 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
875 {
876 auto itrToRec = (*ipzVPDMap).find("VINI");
877 if (itrToRec == (*ipzVPDMap).end())
878 {
879 return false;
880 }
881
Souvik Roya55fcca2025-02-19 01:33:58 -0600882 std::string ccinFromVpd{
883 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
884
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500885 if (ccinFromVpd.empty())
886 {
887 return false;
888 }
889
890 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
891 ::toupper);
892
893 std::vector<std::string> ccinList;
894 for (std::string ccin : singleFru["ccin"])
895 {
896 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
897 ccinList.push_back(ccin);
898 }
899
900 if (ccinList.empty())
901 {
902 return false;
903 }
904
905 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
906 ccinList.end())
907 {
908 return false;
909 }
910 }
911 return true;
912}
913
914void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
915 types::InterfaceMap& io_interfaces)
916{
917 if (!dbusUtility::isChassisPowerOn())
918 {
919 std::array<const char*, 1> l_operationalStatusInf = {
920 constants::operationalStatusInf};
921
922 auto mapperObjectMap = dbusUtility::getObjectMap(
923 i_inventoryObjPath, l_operationalStatusInf);
924
925 // If the object has been found. Check if it is under PIM.
926 if (mapperObjectMap.size() != 0)
927 {
928 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
929 {
930 if (l_serviceName == constants::pimServiceName)
931 {
932 // The object is already under PIM. No need to process
933 // again. Retain the old value.
934 return;
935 }
936 }
937 }
938
939 // Implies value is not there in D-Bus. Populate it with default
940 // value "true".
941 types::PropertyMap l_functionalProp;
942 l_functionalProp.emplace("Functional", true);
943 vpdSpecificUtility::insertOrMerge(io_interfaces,
944 constants::operationalStatusInf,
945 move(l_functionalProp));
946 }
947
948 // if chassis is power on. Functional property should be there on D-Bus.
949 // Don't process.
950 return;
951}
952
953void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
954 types::InterfaceMap& io_interfaces)
955{
956 if (!dbusUtility::isChassisPowerOn())
957 {
958 std::array<const char*, 1> l_enableInf = {constants::enableInf};
959
960 auto mapperObjectMap =
961 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
962
963 // If the object has been found. Check if it is under PIM.
964 if (mapperObjectMap.size() != 0)
965 {
966 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
967 {
968 if (l_serviceName == constants::pimServiceName)
969 {
970 // The object is already under PIM. No need to process
971 // again. Retain the old value.
972 return;
973 }
974 }
975 }
976
977 // Implies value is not there in D-Bus. Populate it with default
978 // value "true".
979 types::PropertyMap l_enabledProp;
980 l_enabledProp.emplace("Enabled", true);
981 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
982 move(l_enabledProp));
983 }
984
985 // if chassis is power on. Enabled property should be there on D-Bus.
986 // Don't process.
987 return;
988}
989
990void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
991 types::ObjectMap& objectInterfaceMap,
992 const std::string& vpdFilePath)
993{
994 if (vpdFilePath.empty())
995 {
996 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530997 std::string(__FUNCTION__) +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500998 "Invalid parameter passed to populateDbus API.");
999 }
1000
1001 // JSON config is mandatory for processing of "if". Add "else" for any
1002 // processing without config JSON.
1003 if (!m_parsedJson.empty())
1004 {
1005 types::InterfaceMap interfaces;
1006
1007 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1008 {
1009 const auto& inventoryPath = aFru["inventoryPath"];
1010 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1011 if (aFru.contains("ccin"))
1012 {
1013 if (!processFruWithCCIN(aFru, parsedVpdMap))
1014 {
1015 continue;
1016 }
1017 }
1018
1019 if (aFru.value("inherit", true))
1020 {
1021 processInheritFlag(parsedVpdMap, interfaces);
1022 }
1023
1024 // If specific record needs to be copied.
1025 if (aFru.contains("copyRecords"))
1026 {
1027 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1028 }
1029
1030 if (aFru.contains("extraInterfaces"))
1031 {
1032 // Process extra interfaces w.r.t a FRU.
1033 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1034 }
1035
1036 // Process FRUS which are embedded in the parent FRU and whose VPD
1037 // will be synthesized.
1038 if ((aFru.value("embedded", true)) &&
1039 (!aFru.value("synthesized", false)))
1040 {
1041 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1042 }
1043
1044 processFunctionalProperty(inventoryPath, interfaces);
1045 processEnabledProperty(inventoryPath, interfaces);
1046
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001047 // Update collection status as successful
1048 types::PropertyMap l_collectionProperty = {
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001049 {"Status", constants::vpdCollectionCompleted}};
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001050
1051 vpdSpecificUtility::insertOrMerge(interfaces,
1052 constants::vpdCollectionInterface,
1053 std::move(l_collectionProperty));
1054
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001055 objectInterfaceMap.emplace(std::move(fruObjectPath),
1056 std::move(interfaces));
1057 }
1058 }
1059}
1060
Patrick Williams43fedab2025-02-03 14:28:05 -05001061std::string Worker::createAssetTagString(
1062 const types::VPDMapVariant& i_parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001063{
1064 std::string l_assetTag;
1065
1066 // system VPD will be in IPZ format.
1067 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1068 {
1069 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1070 if (l_itrToVsys != (*l_parsedVpdMap).end())
1071 {
Souvik Roya55fcca2025-02-19 01:33:58 -06001072 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1073 l_itrToVsys->second, constants::kwdTM)};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001074
Souvik Roya55fcca2025-02-19 01:33:58 -06001075 if (l_tmKwdValue.empty())
1076 {
1077 throw std::runtime_error(
1078 std::string("Failed to get value for keyword [") +
1079 constants::kwdTM +
1080 std::string("] while creating Asset tag."));
1081 }
1082
1083 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1084 l_itrToVsys->second, constants::kwdSE)};
1085
1086 if (l_seKwdValue.empty())
1087 {
1088 throw std::runtime_error(
1089 std::string("Failed to get value for keyword [") +
1090 constants::kwdSE +
1091 std::string("] while creating Asset tag."));
1092 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001093
1094 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1095 std::string{"-"} + l_seKwdValue;
1096 }
1097 else
1098 {
1099 throw std::runtime_error(
1100 "VSYS record not found in parsed VPD map to create Asset tag.");
1101 }
1102 }
1103 else
1104 {
1105 throw std::runtime_error(
1106 "Invalid VPD type recieved to create Asset tag.");
1107 }
1108
1109 return l_assetTag;
1110}
1111
1112void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1113{
1114 types::ObjectMap objectInterfaceMap;
1115
1116 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1117 {
1118 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1119
1120 try
1121 {
1122 if (m_isFactoryResetDone)
1123 {
1124 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1125
1126 auto l_itrToSystemPath = objectInterfaceMap.find(
1127 sdbusplus::message::object_path(constants::systemInvPath));
1128 if (l_itrToSystemPath == objectInterfaceMap.end())
1129 {
1130 throw std::runtime_error(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301131 "Asset tag update failed. System Path not found in object map.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001132 }
1133
1134 types::PropertyMap l_assetTagProperty;
1135 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1136
1137 (l_itrToSystemPath->second)
1138 .emplace(constants::assetTagInf,
1139 std::move(l_assetTagProperty));
1140 }
1141 }
1142 catch (const std::exception& l_ex)
1143 {
1144 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301145 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1146 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001147 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1148 }
1149
1150 // Notify PIM
1151 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1152 {
1153 throw std::runtime_error("Call to PIM failed for system VPD");
1154 }
1155 }
1156 else
1157 {
1158 throw DataException("Invalid format of parsed VPD map.");
1159 }
1160}
1161
1162bool Worker::processPreAction(const std::string& i_vpdFilePath,
1163 const std::string& i_flagToProcess)
1164{
1165 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1166 {
1167 logging::logMessage(
1168 "Invalid input parameter. Abort processing pre action");
1169 return false;
1170 }
1171
1172 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1173 i_vpdFilePath, i_flagToProcess)) &&
1174 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1175 {
1176 // TODO: Need a way to delete inventory object from Dbus and persisted
1177 // data section in case any FRU is not present or there is any
1178 // problem in collecting it. Once it has been deleted, it can be
1179 // re-created in the flow of priming the inventory. This needs to be
1180 // done either here or in the exception section of "parseAndPublishVPD"
1181 // API. Any failure in the process of collecting FRU will land up in the
1182 // excpetion of "parseAndPublishVPD".
1183
1184 // If the FRU is not there, clear the VINI/CCIN data.
1185 // Enity manager probes for this keyword to look for this
1186 // FRU, now if the data is persistent on BMC and FRU is
1187 // removed this can lead to ambiguity. Hence clearing this
1188 // Keyword if FRU is absent.
1189 const auto& inventoryPath =
1190 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1191 "");
1192
1193 if (!inventoryPath.empty())
1194 {
1195 types::ObjectMap l_pimObjMap{
1196 {inventoryPath,
1197 {{constants::kwdVpdInf,
1198 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1199
1200 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1201 {
1202 logging::logMessage(
1203 "Call to PIM failed for file " + i_vpdFilePath);
1204 }
1205 }
1206 else
1207 {
1208 logging::logMessage(
1209 "Inventory path is empty in Json for file " + i_vpdFilePath);
1210 }
1211
1212 return false;
1213 }
1214 return true;
1215}
1216
1217bool Worker::processPostAction(
1218 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1219 const std::optional<types::VPDMapVariant> i_parsedVpd)
1220{
1221 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1222 {
1223 logging::logMessage(
1224 "Invalid input parameter. Abort processing post action");
1225 return false;
1226 }
1227
1228 // Check if post action tag is to be triggered in the flow of collection
1229 // based on some CCIN value?
1230 if (m_parsedJson["frus"][i_vpdFruPath]
1231 .at(0)["postAction"][i_flagToProcess]
1232 .contains("ccin"))
1233 {
1234 if (!i_parsedVpd.has_value())
1235 {
1236 logging::logMessage("Empty VPD Map");
1237 return false;
1238 }
1239
1240 // CCIN match is required to process post action for this FRU as it
1241 // contains the flag.
1242 if (!vpdSpecificUtility::findCcinInVpd(
1243 m_parsedJson["frus"][i_vpdFruPath].at(
1244 0)["postAction"]["collection"],
1245 i_parsedVpd.value()))
1246 {
1247 // If CCIN is not found, implies post action processing is not
1248 // required for this FRU. Let the flow continue.
1249 return true;
1250 }
1251 }
1252
1253 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1254 i_vpdFruPath, i_flagToProcess))
1255 {
1256 logging::logMessage(
1257 "Execution of post action failed for path: " + i_vpdFruPath);
1258
1259 // If post action was required and failed only in that case return
1260 // false. In all other case post action is considered passed.
1261 return false;
1262 }
1263
1264 return true;
1265}
1266
1267types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1268{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001269 try
1270 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301271 if (i_vpdFilePath.empty())
1272 {
1273 throw std::runtime_error(
1274 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301275 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301276 }
1277
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301278 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001279 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1280 "preAction", "collection"))
1281 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301282 isPreActionRequired = true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001283 if (!processPreAction(i_vpdFilePath, "collection"))
1284 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301285 throw std::runtime_error(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301286 std::string(__FUNCTION__) + " Pre-Action failed");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001287 }
1288 }
1289
1290 if (!std::filesystem::exists(i_vpdFilePath))
1291 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301292 if (isPreActionRequired)
1293 {
1294 throw std::runtime_error(
1295 std::string(__FUNCTION__) + " Could not find file path " +
1296 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1297 }
1298 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001299 }
1300
1301 std::shared_ptr<Parser> vpdParser =
1302 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1303
1304 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1305
1306 // Before returning, as collection is over, check if FRU qualifies for
1307 // any post action in the flow of collection.
1308 // Note: Don't change the order, post action needs to be processed only
1309 // after collection for FRU is successfully done.
1310 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1311 "postAction", "collection"))
1312 {
1313 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1314 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301315 // Post action was required but failed while executing.
1316 // Behaviour can be undefined.
1317 EventLogger::createSyncPel(
1318 types::ErrorType::InternalFailure,
1319 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1320 std::string("Required post action failed for path [" +
1321 i_vpdFilePath + "]"),
1322 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001323 }
1324 }
1325
1326 return l_parsedVpd;
1327 }
1328 catch (std::exception& l_ex)
1329 {
Souvik Roy37c6bef2025-07-17 00:55:59 -05001330 std::string l_exMsg{
1331 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1332 i_vpdFilePath + " due to error: " + l_ex.what()};
1333
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001334 // If post fail action is required, execute it.
1335 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -06001336 "postFailAction", "collection"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001337 {
1338 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1339 "collection"))
1340 {
Souvik Roy37c6bef2025-07-17 00:55:59 -05001341 l_exMsg +=
1342 ". Post Fail Action also failed, aborting collection for this FRU";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001343 }
1344 }
1345
Souvik Roy37c6bef2025-07-17 00:55:59 -05001346 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);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001355 }
1356}
1357
Patrick Williams43fedab2025-02-03 14:28:05 -05001358std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1359 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001360{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001361 std::string l_inventoryPath{};
1362
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001363 try
1364 {
1365 m_semaphore.acquire();
1366
1367 // Thread launched.
1368 m_mutex.lock();
1369 m_activeCollectionThreadCount++;
1370 m_mutex.unlock();
1371
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001372 // Set collection Status as InProgress. Since it's an intermediate state
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001373 // D-bus set-property call is good enough to update the status.
RekhaAparna011ef21002025-02-18 23:47:36 -06001374 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
1375 m_parsedJson, i_vpdFilePath);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001376
RekhaAparna011ef21002025-02-18 23:47:36 -06001377 if (!l_inventoryPath.empty())
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001378 {
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001379 // save time stamp under PIM.
1380 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1381 "StartTime");
1382
RekhaAparna01c532b182025-02-19 19:56:49 -06001383 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001384 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1385 l_inventoryPath, constants::vpdCollectionInterface,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001386 "Status",
RekhaAparna01c532b182025-02-19 19:56:49 -06001387 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001388 {
1389 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001390 "Unable to set collection Status as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001391 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001392 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001393 }
1394
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001395 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301396 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001397 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301398 types::ObjectMap objectInterfaceMap;
1399 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1400
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001401 // save end time stamp under PIM.
1402 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1403 "CompletedTime");
1404
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301405 // Notify PIM
1406 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1407 {
1408 throw std::runtime_error(
1409 std::string(__FUNCTION__) +
1410 "Call to PIM failed while publishing VPD.");
1411 }
1412 }
1413 else
1414 {
1415 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1416 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001417 }
1418 }
1419 catch (const std::exception& ex)
1420 {
Anupama B R24691d22025-05-21 08:14:15 -05001421 setCollectionStatusProperty(i_vpdFilePath,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001422 constants::vpdCollectionFailed);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001423
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001424 // save end time stamp under PIM.
1425 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1426 "CompletedTime");
1427
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001428 // handle all the exceptions internally. Return only true/false
1429 // based on status of execution.
1430 if (typeid(ex) == std::type_index(typeid(DataException)))
1431 {
Sunny Srivastava78c91072025-02-05 14:09:50 +05301432 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1433 // logging error for these cases.
1434 if (vpdSpecificUtility::isPass1Planar())
1435 {
RekhaAparna011ef21002025-02-18 23:47:36 -06001436 const std::string& l_invPathLeafValue =
1437 sdbusplus::message::object_path(
1438 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1439 i_vpdFilePath))
1440 .filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301441
RekhaAparna011ef21002025-02-18 23:47:36 -06001442 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1443 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301444 {
1445 // skip logging any PEL for PCIe cards on pass 1 planar.
1446 return std::make_tuple(false, i_vpdFilePath);
1447 }
1448 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301449 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301450
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301451 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001452 EventLogger::getErrorType(ex),
1453 (typeid(ex) == typeid(DataException)) ||
1454 (typeid(ex) == typeid(EccException))
1455 ? types::SeverityType::Warning
1456 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301457 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1458 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001459
1460 // TODO: Figure out a way to clear data in case of any failure at
1461 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301462
1463 // set present property to false for any error case. In future this will
1464 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001465 // Update Present property for this FRU only if we handle Present
1466 // property for the FRU.
1467 if (isPresentPropertyHandlingRequired(
1468 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1469 {
1470 setPresentProperty(i_vpdFilePath, false);
1471 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301472
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001473 m_semaphore.release();
1474 return std::make_tuple(false, i_vpdFilePath);
1475 }
1476 m_semaphore.release();
1477 return std::make_tuple(true, i_vpdFilePath);
1478}
1479
Sunny Srivastava61611752025-02-04 00:29:33 -06001480bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1481{
1482 if (i_vpdFilePath.empty())
1483 {
1484 return true;
1485 }
1486
1487 // skip processing of system VPD again as it has been already collected.
1488 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1489 {
1490 return true;
1491 }
1492
1493 if (dbusUtility::isChassisPowerOn())
1494 {
1495 // If chassis is powered on, skip collecting FRUs which are
1496 // powerOffOnly.
1497 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1498 {
1499 return true;
1500 }
1501
1502 const std::string& l_invPathLeafValue =
1503 sdbusplus::message::object_path(
1504 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1505 i_vpdFilePath))
1506 .filename();
1507
1508 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1509 {
1510 return true;
1511 }
1512 }
1513
1514 return false;
1515}
1516
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001517void Worker::collectFrusFromJson()
1518{
1519 // A parsed JSON file should be present to pick FRUs EEPROM paths
1520 if (m_parsedJson.empty())
1521 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301522 throw JsonException(
1523 std::string(__FUNCTION__) +
1524 ": Config JSON is mandatory for processing of FRUs through this API.",
1525 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001526 }
1527
1528 const nlohmann::json& listOfFrus =
1529 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1530
1531 for (const auto& itemFRUS : listOfFrus.items())
1532 {
1533 const std::string& vpdFilePath = itemFRUS.key();
1534
Sunny Srivastava61611752025-02-04 00:29:33 -06001535 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001536 {
1537 continue;
1538 }
1539
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001540 try
1541 {
1542 std::thread{[vpdFilePath, this]() {
1543 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001544
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001545 m_mutex.lock();
1546 m_activeCollectionThreadCount--;
1547 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001548
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001549 if (!m_activeCollectionThreadCount)
1550 {
1551 m_isAllFruCollected = true;
1552 }
1553 }}.detach();
1554 }
1555 catch (const std::exception& l_ex)
1556 {
1557 // add vpdFilePath(EEPROM path) to failed list
1558 m_failedEepromPaths.push_front(vpdFilePath);
1559 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001560 }
1561}
1562
1563// ToDo: Move the API under IBM_SYSTEM
1564void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1565{
1566 try
1567 {
1568 std::string l_backupAndRestoreCfgFilePath =
1569 m_parsedJson.value("backupRestoreConfigPath", "");
1570
1571 nlohmann::json l_backupAndRestoreCfgJsonObj =
1572 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1573
RekhaAparna011ef21002025-02-18 23:47:36 -06001574 if (l_backupAndRestoreCfgJsonObj.empty())
1575 {
1576 throw JsonException("JSON parsing failed",
1577 l_backupAndRestoreCfgFilePath);
1578 }
1579
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001580 // check if either of "source" or "destination" has inventory path.
1581 // this indicates that this sytem has System VPD on hardware
1582 // and other copy on D-Bus (BMC cache).
1583 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1584 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1585 l_backupAndRestoreCfgJsonObj["source"].contains(
1586 "inventoryPath")) ||
1587 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1588 l_backupAndRestoreCfgJsonObj["destination"].contains(
1589 "inventoryPath"))))
1590 {
1591 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1592 auto [l_srcVpdVariant,
1593 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1594
1595 // ToDo: Revisit is this check is required or not.
1596 if (auto l_srcVpdMap =
1597 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1598 l_srcVpdMap && !(*l_srcVpdMap).empty())
1599 {
1600 io_srcVpdMap = std::move(l_srcVpdVariant);
1601 }
1602 }
1603 }
1604 catch (const std::exception& l_ex)
1605 {
1606 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301607 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301608 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001609 std::string(
1610 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301611 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001612 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1613 }
1614}
1615
1616void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1617{
1618 if (i_dbusObjPath.empty())
1619 {
1620 throw std::runtime_error("Given DBus object path is empty.");
1621 }
1622
1623 const std::string& l_fruPath =
1624 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1625
1626 try
1627 {
1628 auto l_presentPropValue = dbusUtility::readDbusProperty(
1629 constants::pimServiceName, i_dbusObjPath,
1630 constants::inventoryItemInf, "Present");
1631
1632 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1633 {
Souvik Roye9120152025-07-02 08:24:38 -05001634 // check if FRU's Present property is handled by vpd-manager
1635 const auto& l_isFruPresenceHandled =
1636 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath);
1637
1638 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001639 {
1640 throw std::runtime_error("Given FRU is not present");
1641 }
Souvik Roye9120152025-07-02 08:24:38 -05001642 else if (*l_value && !l_isFruPresenceHandled)
1643 {
1644 throw std::runtime_error(
1645 "Given FRU is present and its presence is not handled by vpd-manager.");
1646 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001647 else
1648 {
1649 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1650 "preAction", "deletion"))
1651 {
1652 if (!processPreAction(l_fruPath, "deletion"))
1653 {
1654 throw std::runtime_error("Pre action failed");
1655 }
1656 }
1657
1658 std::vector<std::string> l_interfaceList{
1659 constants::operationalStatusInf};
1660
1661 types::MapperGetSubTree l_subTreeMap =
1662 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1663 l_interfaceList);
1664
1665 types::ObjectMap l_objectMap;
1666
1667 // Updates VPD specific interfaces property value under PIM for
1668 // sub FRUs.
1669 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1670 l_subTreeMap)
1671 {
1672 types::InterfaceMap l_interfaceMap;
1673 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1674 l_interfaceMap);
1675 l_objectMap.emplace(l_objectPath,
1676 std::move(l_interfaceMap));
1677 }
1678
1679 types::InterfaceMap l_interfaceMap;
1680 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1681 l_interfaceMap);
1682
1683 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1684
1685 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1686 {
1687 throw std::runtime_error("Call to PIM failed.");
1688 }
1689
1690 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1691 "postAction", "deletion"))
1692 {
1693 if (!processPostAction(l_fruPath, "deletion"))
1694 {
1695 throw std::runtime_error("Post action failed");
1696 }
1697 }
1698 }
1699 }
1700 else
1701 {
1702 logging::logMessage(
1703 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1704 "] as unable to read present property");
1705 return;
1706 }
1707
1708 logging::logMessage(
1709 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1710 }
1711 catch (const std::exception& l_ex)
1712 {
1713 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1714 "postFailAction", "deletion"))
1715 {
1716 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1717 "deletion"))
1718 {
1719 logging::logMessage(
1720 "Post fail action failed for: " + i_dbusObjPath);
1721 }
1722 }
1723
1724 logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1725 " error: " + std::string(l_ex.what()));
1726 }
1727}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301728
1729void Worker::setPresentProperty(const std::string& i_vpdPath,
1730 const bool& i_value)
1731{
1732 try
1733 {
1734 if (i_vpdPath.empty())
1735 {
1736 throw std::runtime_error(
1737 "Path is empty. Can't set present property");
1738 }
1739
1740 types::ObjectMap l_objectInterfaceMap;
1741
1742 // If the given path is EEPROM path.
1743 if (m_parsedJson["frus"].contains(i_vpdPath))
1744 {
1745 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1746 {
1747 sdbusplus::message::object_path l_fruObjectPath(
1748 l_Fru["inventoryPath"]);
1749
1750 types::PropertyMap l_propertyValueMap;
1751 l_propertyValueMap.emplace("Present", i_value);
1752
1753 types::InterfaceMap l_interfaces;
1754 vpdSpecificUtility::insertOrMerge(l_interfaces,
1755 constants::inventoryItemInf,
1756 move(l_propertyValueMap));
1757
1758 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1759 std::move(l_interfaces));
1760 }
1761 }
1762 else
1763 {
1764 // consider it as an inventory path.
1765 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1766 {
1767 throw std::runtime_error(
1768 "Invalid inventory path: " + i_vpdPath);
1769 }
1770
1771 types::PropertyMap l_propertyValueMap;
1772 l_propertyValueMap.emplace("Present", i_value);
1773
1774 types::InterfaceMap l_interfaces;
1775 vpdSpecificUtility::insertOrMerge(l_interfaces,
1776 constants::inventoryItemInf,
1777 move(l_propertyValueMap));
1778
1779 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1780 }
1781
1782 // Notify PIM
1783 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1784 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301785 throw DbusException(
1786 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301787 "Call to PIM failed while setting present property for path " +
1788 i_vpdPath);
1789 }
1790 }
1791 catch (const std::exception& l_ex)
1792 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301793 EventLogger::createSyncPel(
1794 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1795 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1796 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301797 }
1798}
1799
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301800void Worker::performVpdRecollection()
1801{
1802 try
1803 {
1804 // Check if system config JSON is present
1805 if (m_parsedJson.empty())
1806 {
1807 throw std::runtime_error(
1808 "System config json object is empty, can't process recollection.");
1809 }
1810
1811 const auto& l_frusReplaceableAtStandby =
1812 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1813
1814 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1815 {
1816 // ToDo: Add some logic/trace to know the flow to
1817 // collectSingleFruVpd has been directed via
1818 // performVpdRecollection.
1819 collectSingleFruVpd(l_fruInventoryPath);
1820 }
1821 return;
1822 }
1823
1824 catch (const std::exception& l_ex)
1825 {
1826 // TODO Log PEL
1827 logging::logMessage(
1828 "VPD recollection failed with error: " + std::string(l_ex.what()));
1829 }
1830}
1831
1832void Worker::collectSingleFruVpd(
1833 const sdbusplus::message::object_path& i_dbusObjPath)
1834{
Anupama B R48f297b2025-08-13 04:29:06 -05001835 std::string l_fruPath{};
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301836 try
1837 {
1838 // Check if system config JSON is present
1839 if (m_parsedJson.empty())
1840 {
1841 logging::logMessage(
1842 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1843 std::string(i_dbusObjPath));
1844 return;
1845 }
1846
1847 // Get FRU path for the given D-bus object path from JSON
Anupama B R48f297b2025-08-13 04:29:06 -05001848 l_fruPath =
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301849 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1850
1851 if (l_fruPath.empty())
1852 {
1853 logging::logMessage(
1854 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1855 std::string(i_dbusObjPath));
1856 return;
1857 }
1858
1859 // Check if host is up and running
1860 if (dbusUtility::isHostRunning())
1861 {
1862 if (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1863 l_fruPath))
1864 {
1865 logging::logMessage(
1866 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1867 std::string(i_dbusObjPath));
1868 return;
1869 }
1870 }
1871 else if (dbusUtility::isBMCReady())
1872 {
1873 if (!jsonUtility::isFruReplaceableAtStandby(m_parsedJson,
1874 l_fruPath) &&
1875 (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1876 l_fruPath)))
1877 {
1878 logging::logMessage(
1879 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1880 std::string(i_dbusObjPath));
1881 return;
1882 }
1883 }
1884
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001885 // Set collection Status as InProgress. Since it's an intermediate state
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301886 // D-bus set-property call is good enough to update the status.
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001887 const std::string& l_collStatusProp = "Status";
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301888
1889 if (!dbusUtility::writeDbusProperty(
1890 jsonUtility::getServiceName(m_parsedJson,
1891 std::string(i_dbusObjPath)),
1892 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
1893 l_collStatusProp,
1894 types::DbusVariantType{constants::vpdCollectionInProgress}))
1895 {
1896 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001897 "Unable to set collection Status as InProgress for " +
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301898 std::string(i_dbusObjPath) +
1899 ". Continue single FRU VPD collection.");
1900 }
1901
1902 // Parse VPD
1903 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1904
1905 // If l_parsedVpd is pointing to std::monostate
1906 if (l_parsedVpd.index() == 0)
1907 {
1908 throw std::runtime_error(
1909 "VPD parsing failed for " + std::string(i_dbusObjPath));
1910 }
1911
1912 // Get D-bus object map from worker class
1913 types::ObjectMap l_dbusObjectMap;
1914 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1915
1916 if (l_dbusObjectMap.empty())
1917 {
1918 throw std::runtime_error(
1919 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1920 std::string(i_dbusObjPath));
1921 }
1922
1923 // Call PIM's Notify method
1924 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
1925 {
1926 throw std::runtime_error(
1927 "Notify PIM failed. Single FRU VPD collection failed for " +
1928 std::string(i_dbusObjPath));
1929 }
1930 }
1931 catch (const std::exception& l_error)
1932 {
Anupama B R48f297b2025-08-13 04:29:06 -05001933 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301934 // TODO: Log PEL
1935 logging::logMessage(std::string(l_error.what()));
1936 }
1937}
Anupama B R24691d22025-05-21 08:14:15 -05001938
1939void Worker::setCollectionStatusProperty(
1940 const std::string& i_vpdPath, const std::string& i_value) const noexcept
1941{
1942 try
1943 {
1944 if (i_vpdPath.empty())
1945 {
1946 throw std::runtime_error(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001947 "Given path is empty. Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05001948 }
1949
1950 types::ObjectMap l_objectInterfaceMap;
1951
1952 if (m_parsedJson["frus"].contains(i_vpdPath))
1953 {
1954 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1955 {
1956 sdbusplus::message::object_path l_fruObjectPath(
1957 l_Fru["inventoryPath"]);
1958
1959 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001960 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05001961
1962 types::InterfaceMap l_interfaces;
1963 vpdSpecificUtility::insertOrMerge(
1964 l_interfaces, constants::vpdCollectionInterface,
1965 move(l_propertyValueMap));
1966
1967 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1968 std::move(l_interfaces));
1969 }
1970 }
1971 else
1972 {
1973 // consider it as an inventory path.
1974 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1975 {
1976 throw std::runtime_error(
1977 "Invalid inventory path: " + i_vpdPath +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001978 ". Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05001979 }
1980
1981 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001982 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05001983
1984 types::InterfaceMap l_interfaces;
1985 vpdSpecificUtility::insertOrMerge(l_interfaces,
1986 constants::vpdCollectionInterface,
1987 move(l_propertyValueMap));
1988
1989 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1990 }
1991
1992 // Notify PIM
1993 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1994 {
1995 throw DbusException(
1996 std::string(__FUNCTION__) +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001997 "Call to PIM failed while setting collection Status property for path " +
Anupama B R24691d22025-05-21 08:14:15 -05001998 i_vpdPath);
1999 }
2000 }
2001 catch (const std::exception& l_ex)
2002 {
2003 EventLogger::createSyncPel(
2004 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2005 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2006 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2007 }
2008}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002009} // namespace vpd