blob: 789a24038fc31d0d706ed224169660540038b9f7 [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 = {
748 {"CollectionStatus", constants::vpdCollectionNotStarted}};
749
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 = {
1049 {"CollectionStatus", constants::vpdCollectionSuccess}};
1050
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
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001372 // Set CollectionStatus as InProgress. Since it's an intermediate state
1373 // 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 {
RekhaAparna01c532b182025-02-19 19:56:49 -06001379 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001380 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1381 l_inventoryPath, constants::vpdCollectionInterface,
1382 "CollectionStatus",
RekhaAparna01c532b182025-02-19 19:56:49 -06001383 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001384 {
1385 logging::logMessage(
1386 "Unable to set CollectionStatus as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001387 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001388 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001389 }
1390
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001391 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301392 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001393 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301394 types::ObjectMap objectInterfaceMap;
1395 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1396
1397 // Notify PIM
1398 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1399 {
1400 throw std::runtime_error(
1401 std::string(__FUNCTION__) +
1402 "Call to PIM failed while publishing VPD.");
1403 }
1404 }
1405 else
1406 {
1407 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1408 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001409 }
1410 }
1411 catch (const std::exception& ex)
1412 {
Anupama B R24691d22025-05-21 08:14:15 -05001413 setCollectionStatusProperty(i_vpdFilePath,
1414 constants::vpdCollectionFailure);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001415
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001416 // handle all the exceptions internally. Return only true/false
1417 // based on status of execution.
1418 if (typeid(ex) == std::type_index(typeid(DataException)))
1419 {
Sunny Srivastava78c91072025-02-05 14:09:50 +05301420 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1421 // logging error for these cases.
1422 if (vpdSpecificUtility::isPass1Planar())
1423 {
RekhaAparna011ef21002025-02-18 23:47:36 -06001424 const std::string& l_invPathLeafValue =
1425 sdbusplus::message::object_path(
1426 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1427 i_vpdFilePath))
1428 .filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301429
RekhaAparna011ef21002025-02-18 23:47:36 -06001430 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1431 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301432 {
1433 // skip logging any PEL for PCIe cards on pass 1 planar.
1434 return std::make_tuple(false, i_vpdFilePath);
1435 }
1436 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301437 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301438
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301439 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001440 EventLogger::getErrorType(ex),
1441 (typeid(ex) == typeid(DataException)) ||
1442 (typeid(ex) == typeid(EccException))
1443 ? types::SeverityType::Warning
1444 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301445 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1446 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001447
1448 // TODO: Figure out a way to clear data in case of any failure at
1449 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301450
1451 // set present property to false for any error case. In future this will
1452 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001453 // Update Present property for this FRU only if we handle Present
1454 // property for the FRU.
1455 if (isPresentPropertyHandlingRequired(
1456 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1457 {
1458 setPresentProperty(i_vpdFilePath, false);
1459 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301460
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001461 m_semaphore.release();
1462 return std::make_tuple(false, i_vpdFilePath);
1463 }
1464 m_semaphore.release();
1465 return std::make_tuple(true, i_vpdFilePath);
1466}
1467
Sunny Srivastava61611752025-02-04 00:29:33 -06001468bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1469{
1470 if (i_vpdFilePath.empty())
1471 {
1472 return true;
1473 }
1474
1475 // skip processing of system VPD again as it has been already collected.
1476 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1477 {
1478 return true;
1479 }
1480
1481 if (dbusUtility::isChassisPowerOn())
1482 {
1483 // If chassis is powered on, skip collecting FRUs which are
1484 // powerOffOnly.
1485 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1486 {
1487 return true;
1488 }
1489
1490 const std::string& l_invPathLeafValue =
1491 sdbusplus::message::object_path(
1492 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1493 i_vpdFilePath))
1494 .filename();
1495
1496 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1497 {
1498 return true;
1499 }
1500 }
1501
1502 return false;
1503}
1504
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001505void Worker::collectFrusFromJson()
1506{
1507 // A parsed JSON file should be present to pick FRUs EEPROM paths
1508 if (m_parsedJson.empty())
1509 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301510 throw JsonException(
1511 std::string(__FUNCTION__) +
1512 ": Config JSON is mandatory for processing of FRUs through this API.",
1513 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001514 }
1515
1516 const nlohmann::json& listOfFrus =
1517 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1518
1519 for (const auto& itemFRUS : listOfFrus.items())
1520 {
1521 const std::string& vpdFilePath = itemFRUS.key();
1522
Sunny Srivastava61611752025-02-04 00:29:33 -06001523 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001524 {
1525 continue;
1526 }
1527
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001528 try
1529 {
1530 std::thread{[vpdFilePath, this]() {
1531 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001532
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001533 m_mutex.lock();
1534 m_activeCollectionThreadCount--;
1535 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001536
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001537 if (!m_activeCollectionThreadCount)
1538 {
1539 m_isAllFruCollected = true;
1540 }
1541 }}.detach();
1542 }
1543 catch (const std::exception& l_ex)
1544 {
1545 // add vpdFilePath(EEPROM path) to failed list
1546 m_failedEepromPaths.push_front(vpdFilePath);
1547 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001548 }
1549}
1550
1551// ToDo: Move the API under IBM_SYSTEM
1552void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1553{
1554 try
1555 {
1556 std::string l_backupAndRestoreCfgFilePath =
1557 m_parsedJson.value("backupRestoreConfigPath", "");
1558
1559 nlohmann::json l_backupAndRestoreCfgJsonObj =
1560 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1561
RekhaAparna011ef21002025-02-18 23:47:36 -06001562 if (l_backupAndRestoreCfgJsonObj.empty())
1563 {
1564 throw JsonException("JSON parsing failed",
1565 l_backupAndRestoreCfgFilePath);
1566 }
1567
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001568 // check if either of "source" or "destination" has inventory path.
1569 // this indicates that this sytem has System VPD on hardware
1570 // and other copy on D-Bus (BMC cache).
1571 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1572 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1573 l_backupAndRestoreCfgJsonObj["source"].contains(
1574 "inventoryPath")) ||
1575 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1576 l_backupAndRestoreCfgJsonObj["destination"].contains(
1577 "inventoryPath"))))
1578 {
1579 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1580 auto [l_srcVpdVariant,
1581 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1582
1583 // ToDo: Revisit is this check is required or not.
1584 if (auto l_srcVpdMap =
1585 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1586 l_srcVpdMap && !(*l_srcVpdMap).empty())
1587 {
1588 io_srcVpdMap = std::move(l_srcVpdVariant);
1589 }
1590 }
1591 }
1592 catch (const std::exception& l_ex)
1593 {
1594 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301595 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301596 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001597 std::string(
1598 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301599 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001600 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1601 }
1602}
1603
1604void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1605{
1606 if (i_dbusObjPath.empty())
1607 {
1608 throw std::runtime_error("Given DBus object path is empty.");
1609 }
1610
1611 const std::string& l_fruPath =
1612 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1613
1614 try
1615 {
1616 auto l_presentPropValue = dbusUtility::readDbusProperty(
1617 constants::pimServiceName, i_dbusObjPath,
1618 constants::inventoryItemInf, "Present");
1619
1620 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1621 {
Souvik Roye9120152025-07-02 08:24:38 -05001622 // check if FRU's Present property is handled by vpd-manager
1623 const auto& l_isFruPresenceHandled =
1624 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath);
1625
1626 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001627 {
1628 throw std::runtime_error("Given FRU is not present");
1629 }
Souvik Roye9120152025-07-02 08:24:38 -05001630 else if (*l_value && !l_isFruPresenceHandled)
1631 {
1632 throw std::runtime_error(
1633 "Given FRU is present and its presence is not handled by vpd-manager.");
1634 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001635 else
1636 {
1637 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1638 "preAction", "deletion"))
1639 {
1640 if (!processPreAction(l_fruPath, "deletion"))
1641 {
1642 throw std::runtime_error("Pre action failed");
1643 }
1644 }
1645
1646 std::vector<std::string> l_interfaceList{
1647 constants::operationalStatusInf};
1648
1649 types::MapperGetSubTree l_subTreeMap =
1650 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1651 l_interfaceList);
1652
1653 types::ObjectMap l_objectMap;
1654
1655 // Updates VPD specific interfaces property value under PIM for
1656 // sub FRUs.
1657 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1658 l_subTreeMap)
1659 {
1660 types::InterfaceMap l_interfaceMap;
1661 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1662 l_interfaceMap);
1663 l_objectMap.emplace(l_objectPath,
1664 std::move(l_interfaceMap));
1665 }
1666
1667 types::InterfaceMap l_interfaceMap;
1668 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1669 l_interfaceMap);
1670
1671 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1672
1673 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1674 {
1675 throw std::runtime_error("Call to PIM failed.");
1676 }
1677
1678 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1679 "postAction", "deletion"))
1680 {
1681 if (!processPostAction(l_fruPath, "deletion"))
1682 {
1683 throw std::runtime_error("Post action failed");
1684 }
1685 }
1686 }
1687 }
1688 else
1689 {
1690 logging::logMessage(
1691 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1692 "] as unable to read present property");
1693 return;
1694 }
1695
1696 logging::logMessage(
1697 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1698 }
1699 catch (const std::exception& l_ex)
1700 {
1701 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1702 "postFailAction", "deletion"))
1703 {
1704 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1705 "deletion"))
1706 {
1707 logging::logMessage(
1708 "Post fail action failed for: " + i_dbusObjPath);
1709 }
1710 }
1711
1712 logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1713 " error: " + std::string(l_ex.what()));
1714 }
1715}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301716
1717void Worker::setPresentProperty(const std::string& i_vpdPath,
1718 const bool& i_value)
1719{
1720 try
1721 {
1722 if (i_vpdPath.empty())
1723 {
1724 throw std::runtime_error(
1725 "Path is empty. Can't set present property");
1726 }
1727
1728 types::ObjectMap l_objectInterfaceMap;
1729
1730 // If the given path is EEPROM path.
1731 if (m_parsedJson["frus"].contains(i_vpdPath))
1732 {
1733 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1734 {
1735 sdbusplus::message::object_path l_fruObjectPath(
1736 l_Fru["inventoryPath"]);
1737
1738 types::PropertyMap l_propertyValueMap;
1739 l_propertyValueMap.emplace("Present", i_value);
1740
1741 types::InterfaceMap l_interfaces;
1742 vpdSpecificUtility::insertOrMerge(l_interfaces,
1743 constants::inventoryItemInf,
1744 move(l_propertyValueMap));
1745
1746 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1747 std::move(l_interfaces));
1748 }
1749 }
1750 else
1751 {
1752 // consider it as an inventory path.
1753 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1754 {
1755 throw std::runtime_error(
1756 "Invalid inventory path: " + i_vpdPath);
1757 }
1758
1759 types::PropertyMap l_propertyValueMap;
1760 l_propertyValueMap.emplace("Present", i_value);
1761
1762 types::InterfaceMap l_interfaces;
1763 vpdSpecificUtility::insertOrMerge(l_interfaces,
1764 constants::inventoryItemInf,
1765 move(l_propertyValueMap));
1766
1767 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1768 }
1769
1770 // Notify PIM
1771 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1772 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301773 throw DbusException(
1774 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301775 "Call to PIM failed while setting present property for path " +
1776 i_vpdPath);
1777 }
1778 }
1779 catch (const std::exception& l_ex)
1780 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301781 EventLogger::createSyncPel(
1782 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1783 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1784 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301785 }
1786}
1787
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301788void Worker::performVpdRecollection()
1789{
1790 try
1791 {
1792 // Check if system config JSON is present
1793 if (m_parsedJson.empty())
1794 {
1795 throw std::runtime_error(
1796 "System config json object is empty, can't process recollection.");
1797 }
1798
1799 const auto& l_frusReplaceableAtStandby =
1800 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1801
1802 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1803 {
1804 // ToDo: Add some logic/trace to know the flow to
1805 // collectSingleFruVpd has been directed via
1806 // performVpdRecollection.
1807 collectSingleFruVpd(l_fruInventoryPath);
1808 }
1809 return;
1810 }
1811
1812 catch (const std::exception& l_ex)
1813 {
1814 // TODO Log PEL
1815 logging::logMessage(
1816 "VPD recollection failed with error: " + std::string(l_ex.what()));
1817 }
1818}
1819
1820void Worker::collectSingleFruVpd(
1821 const sdbusplus::message::object_path& i_dbusObjPath)
1822{
1823 try
1824 {
1825 // Check if system config JSON is present
1826 if (m_parsedJson.empty())
1827 {
1828 logging::logMessage(
1829 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1830 std::string(i_dbusObjPath));
1831 return;
1832 }
1833
1834 // Get FRU path for the given D-bus object path from JSON
1835 const std::string& l_fruPath =
1836 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1837
1838 if (l_fruPath.empty())
1839 {
1840 logging::logMessage(
1841 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1842 std::string(i_dbusObjPath));
1843 return;
1844 }
1845
1846 // Check if host is up and running
1847 if (dbusUtility::isHostRunning())
1848 {
1849 if (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1850 l_fruPath))
1851 {
1852 logging::logMessage(
1853 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1854 std::string(i_dbusObjPath));
1855 return;
1856 }
1857 }
1858 else if (dbusUtility::isBMCReady())
1859 {
1860 if (!jsonUtility::isFruReplaceableAtStandby(m_parsedJson,
1861 l_fruPath) &&
1862 (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1863 l_fruPath)))
1864 {
1865 logging::logMessage(
1866 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1867 std::string(i_dbusObjPath));
1868 return;
1869 }
1870 }
1871
1872 // Set CollectionStatus as InProgress. Since it's an intermediate state
1873 // D-bus set-property call is good enough to update the status.
1874 const std::string& l_collStatusProp = "CollectionStatus";
1875
1876 if (!dbusUtility::writeDbusProperty(
1877 jsonUtility::getServiceName(m_parsedJson,
1878 std::string(i_dbusObjPath)),
1879 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
1880 l_collStatusProp,
1881 types::DbusVariantType{constants::vpdCollectionInProgress}))
1882 {
1883 logging::logMessage(
1884 "Unable to set CollectionStatus as InProgress for " +
1885 std::string(i_dbusObjPath) +
1886 ". Continue single FRU VPD collection.");
1887 }
1888
1889 // Parse VPD
1890 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1891
1892 // If l_parsedVpd is pointing to std::monostate
1893 if (l_parsedVpd.index() == 0)
1894 {
1895 throw std::runtime_error(
1896 "VPD parsing failed for " + std::string(i_dbusObjPath));
1897 }
1898
1899 // Get D-bus object map from worker class
1900 types::ObjectMap l_dbusObjectMap;
1901 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1902
1903 if (l_dbusObjectMap.empty())
1904 {
1905 throw std::runtime_error(
1906 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1907 std::string(i_dbusObjPath));
1908 }
1909
1910 // Call PIM's Notify method
1911 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
1912 {
1913 throw std::runtime_error(
1914 "Notify PIM failed. Single FRU VPD collection failed for " +
1915 std::string(i_dbusObjPath));
1916 }
1917 }
1918 catch (const std::exception& l_error)
1919 {
1920 // Notify FRU's VPD CollectionStatus as Failure
1921 if (!dbusUtility::notifyFRUCollectionStatus(
1922 std::string(i_dbusObjPath), constants::vpdCollectionFailure))
1923 {
1924 logging::logMessage(
1925 "Call to PIM Notify method failed to update Collection status as Failure for " +
1926 std::string(i_dbusObjPath));
1927 }
1928
1929 // TODO: Log PEL
1930 logging::logMessage(std::string(l_error.what()));
1931 }
1932}
Anupama B R24691d22025-05-21 08:14:15 -05001933
1934void Worker::setCollectionStatusProperty(
1935 const std::string& i_vpdPath, const std::string& i_value) const noexcept
1936{
1937 try
1938 {
1939 if (i_vpdPath.empty())
1940 {
1941 throw std::runtime_error(
1942 "Given path is empty. Can't set CollectionStatus property");
1943 }
1944
1945 types::ObjectMap l_objectInterfaceMap;
1946
1947 if (m_parsedJson["frus"].contains(i_vpdPath))
1948 {
1949 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1950 {
1951 sdbusplus::message::object_path l_fruObjectPath(
1952 l_Fru["inventoryPath"]);
1953
1954 types::PropertyMap l_propertyValueMap;
1955 l_propertyValueMap.emplace("CollectionStatus", i_value);
1956
1957 types::InterfaceMap l_interfaces;
1958 vpdSpecificUtility::insertOrMerge(
1959 l_interfaces, constants::vpdCollectionInterface,
1960 move(l_propertyValueMap));
1961
1962 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1963 std::move(l_interfaces));
1964 }
1965 }
1966 else
1967 {
1968 // consider it as an inventory path.
1969 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1970 {
1971 throw std::runtime_error(
1972 "Invalid inventory path: " + i_vpdPath +
1973 ". Can't set CollectionStatus property");
1974 }
1975
1976 types::PropertyMap l_propertyValueMap;
1977 l_propertyValueMap.emplace("CollectionStatus", i_value);
1978
1979 types::InterfaceMap l_interfaces;
1980 vpdSpecificUtility::insertOrMerge(l_interfaces,
1981 constants::vpdCollectionInterface,
1982 move(l_propertyValueMap));
1983
1984 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1985 }
1986
1987 // Notify PIM
1988 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1989 {
1990 throw DbusException(
1991 std::string(__FUNCTION__) +
1992 "Call to PIM failed while setting CollectionStatus property for path " +
1993 i_vpdPath);
1994 }
1995 }
1996 catch (const std::exception& l_ex)
1997 {
1998 EventLogger::createSyncPel(
1999 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2000 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2001 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2002 }
2003}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002004} // namespace vpd