blob: 48d0c3b7f1f2faf3ac633020a23eeeae610cf871 [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{
187 logging::logMessage(std::string("Parsing file = ") + vpdFilePath);
188
189 if (vpdFilePath.empty())
190 {
191 throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
192 }
193
194 if (!std::filesystem::exists(vpdFilePath))
195 {
196 throw std::runtime_error("Can't Find physical file");
197 }
198
Sunny Srivastava043955d2025-01-21 18:04:49 +0530199 std::shared_ptr<Parser> vpdParser =
200 std::make_shared<Parser>(vpdFilePath, m_parsedJson);
201 vpdMap = vpdParser->parse();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500202}
203
204void Worker::getSystemJson(std::string& systemJson,
205 const types::VPDMapVariant& parsedVpdMap)
206{
207 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
208 {
209 std::string hwKWdValue = getHWVersion(*pVal);
210 if (hwKWdValue.empty())
211 {
212 throw DataException("HW value fetched is empty.");
213 }
214
215 const std::string& imKwdValue = getIMValue(*pVal);
216 if (imKwdValue.empty())
217 {
218 throw DataException("IM value fetched is empty.");
219 }
220
221 auto itrToIM = config::systemType.find(imKwdValue);
222 if (itrToIM == config::systemType.end())
223 {
224 throw DataException("IM keyword does not map to any system type");
225 }
226
227 const types::HWVerList hwVersionList = itrToIM->second.second;
228 if (!hwVersionList.empty())
229 {
230 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
231 ::toupper);
232
233 auto itrToHW =
234 std::find_if(hwVersionList.begin(), hwVersionList.end(),
235 [&hwKWdValue](const auto& aPair) {
236 return aPair.first == hwKWdValue;
237 });
238
239 if (itrToHW != hwVersionList.end())
240 {
241 if (!(*itrToHW).second.empty())
242 {
243 systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
244 ".json";
245 }
246 else
247 {
248 systemJson += (*itrToIM).first + ".json";
249 }
250 return;
251 }
252 }
253 systemJson += itrToIM->second.first + ".json";
254 return;
255 }
256
Sunny Srivastava043955d2025-01-21 18:04:49 +0530257 throw DataException(
258 "Invalid VPD type returned from Parser. Can't get system JSON.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500259}
260
261static void setEnvAndReboot(const std::string& key, const std::string& value)
262{
263 // set env and reboot and break.
264 commonUtility::executeCmd("/sbin/fw_setenv", key, value);
265 logging::logMessage("Rebooting BMC to pick up new device tree");
266
267 // make dbus call to reboot
268 auto bus = sdbusplus::bus::new_default_system();
269 auto method = bus.new_method_call(
270 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
271 "org.freedesktop.systemd1.Manager", "Reboot");
272 bus.call_noreply(method);
273}
274
275void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
276{
277 std::error_code l_ec;
278 l_ec.clear();
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530279
280 // Check if symlink file path exists and if the JSON at this location is a
281 // symlink.
282 if (m_isSymlinkPresent &&
283 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
284 { // Don't care about exception in "is_symlink". Will continue with creation
285 // of symlink.
286
287 const auto& l_symlinkFilePth =
288 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
289
290 if (l_ec)
291 {
292 logging::logMessage(
293 "Can't read existing symlink. Error =" + l_ec.message() +
294 "Trying removal of symlink and creation of new symlink.");
295 }
296
297 // If currently set JSON is the required one. No further processing
298 // required.
299 if (i_systemJson == l_symlinkFilePth)
300 {
301 // Correct symlink already set.
302 return;
303 }
304
305 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
306 {
307 // No point going further. If removal fails for existing symlink,
308 // create will anyways throw.
309 throw std::runtime_error(
310 "Removal of symlink failed with Error = " + l_ec.message() +
311 ". Can't proceed with create_symlink.");
312 }
313 }
314
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500315 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
316 {
317 if (l_ec)
318 {
319 throw std::runtime_error(
320 "File system call to exist failed with error = " +
321 l_ec.message());
322 }
323
324 // implies it is a fresh boot/factory reset.
325 // Create the directory for hosting the symlink
326 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
327 {
328 if (l_ec)
329 {
330 throw std::runtime_error(
331 "File system call to create directory failed with error = " +
332 l_ec.message());
333 }
334 }
335 }
336
337 // create a new symlink based on the system
338 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
339 l_ec);
340
341 if (l_ec)
342 {
343 throw std::runtime_error(
344 "create_symlink system call failed with error: " + l_ec.message());
345 }
346
347 // If the flow is at this point implies the symlink was not present there.
348 // Considering this as factory reset.
349 m_isFactoryResetDone = true;
350}
351
352void Worker::setDeviceTreeAndJson()
353{
354 // JSON is madatory for processing of this API.
355 if (m_parsedJson.empty())
356 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530357 throw JsonException("System config JSON is empty", m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500358 }
359
360 types::VPDMapVariant parsedVpdMap;
361 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
362
363 // Implies it is default JSON.
364 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
365
366 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
367 // This is required to support movement from rainier to Blue Ridge on the
368 // fly.
369
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530370 getSystemJson(systemJson, parsedVpdMap);
371
372 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500373 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530374 throw DataException(
375 "No system JSON found corresponding to IM read from VPD.");
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530376 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500377
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530378 // re-parse the JSON once appropriate JSON has been selected.
379 m_parsedJson = jsonUtility::getParsedJson(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500380
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530381 if (m_parsedJson.empty())
382 {
383 throw(JsonException("Json parsing failed", systemJson));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500384 }
385
386 std::string devTreeFromJson;
387 if (m_parsedJson.contains("devTree"))
388 {
389 devTreeFromJson = m_parsedJson["devTree"];
390
391 if (devTreeFromJson.empty())
392 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530393 EventLogger::createSyncPel(
394 types::ErrorType::JsonFailure, types::SeverityType::Error,
395 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500396 "Mandatory value for device tree missing from JSON[" +
Sunny Srivastava043955d2025-01-21 18:04:49 +0530397 systemJson + "]",
398 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500399 }
400 }
401
402 auto fitConfigVal = readFitConfigValue();
403
404 if (devTreeFromJson.empty() ||
405 fitConfigVal.find(devTreeFromJson) != std::string::npos)
406 { // Skipping setting device tree as either devtree info is missing from
407 // Json or it is rightly set.
408
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530409 setJsonSymbolicLink(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500410
411 if (isSystemVPDOnDBus() &&
412 jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
413 {
414 performBackupAndRestore(parsedVpdMap);
415 }
416
417 // proceed to publish system VPD.
418 publishSystemVPD(parsedVpdMap);
419 return;
420 }
421
422 setEnvAndReboot("fitconfig", devTreeFromJson);
423 exit(EXIT_SUCCESS);
424}
425
426void Worker::populateIPZVPDpropertyMap(
427 types::InterfaceMap& interfacePropMap,
428 const types::IPZKwdValueMap& keyordValueMap,
429 const std::string& interfaceName)
430{
431 types::PropertyMap propertyValueMap;
432 for (const auto& kwdVal : keyordValueMap)
433 {
434 auto kwd = kwdVal.first;
435
436 if (kwd[0] == '#')
437 {
438 kwd = std::string("PD_") + kwd[1];
439 }
440 else if (isdigit(kwd[0]))
441 {
442 kwd = std::string("N_") + kwd;
443 }
444
445 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
446 propertyValueMap.emplace(move(kwd), move(value));
447 }
448
449 if (!propertyValueMap.empty())
450 {
451 interfacePropMap.emplace(interfaceName, propertyValueMap);
452 }
453}
454
455void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
456 types::InterfaceMap& interfaceMap)
457{
458 for (const auto& kwdValMap : keyordVPDMap)
459 {
460 types::PropertyMap propertyValueMap;
461 auto kwd = kwdValMap.first;
462
463 if (kwd[0] == '#')
464 {
465 kwd = std::string("PD_") + kwd[1];
466 }
467 else if (isdigit(kwd[0]))
468 {
469 kwd = std::string("N_") + kwd;
470 }
471
472 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
473 {
474 types::BinaryVector value((*keywordValue).begin(),
475 (*keywordValue).end());
476 propertyValueMap.emplace(move(kwd), move(value));
477 }
478 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
479 {
480 types::BinaryVector value((*keywordValue).begin(),
481 (*keywordValue).end());
482 propertyValueMap.emplace(move(kwd), move(value));
483 }
484 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
485 {
486 if (kwd == "MemorySizeInKB")
487 {
488 types::PropertyMap memProp;
489 memProp.emplace(move(kwd), ((*keywordValue)));
490 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
491 move(memProp));
492 continue;
493 }
494 else
495 {
496 logging::logMessage(
497 "Unknown Keyword =" + kwd + " found in keyword VPD map");
498 continue;
499 }
500 }
501 else
502 {
503 logging::logMessage(
504 "Unknown variant type found in keyword VPD map.");
505 continue;
506 }
507
508 if (!propertyValueMap.empty())
509 {
510 vpdSpecificUtility::insertOrMerge(
511 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
512 }
513 }
514}
515
516void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
517 types::InterfaceMap& interfaceMap,
518 const types::VPDMapVariant& parsedVpdMap)
519{
520 for (const auto& interfacesPropPair : interfaceJson.items())
521 {
522 const std::string& interface = interfacesPropPair.key();
523 types::PropertyMap propertyMap;
524
525 for (const auto& propValuePair : interfacesPropPair.value().items())
526 {
527 const std::string property = propValuePair.key();
528
529 if (propValuePair.value().is_boolean())
530 {
531 propertyMap.emplace(property,
532 propValuePair.value().get<bool>());
533 }
534 else if (propValuePair.value().is_string())
535 {
536 if (property.compare("LocationCode") == 0 &&
537 interface.compare("com.ibm.ipzvpd.Location") == 0)
538 {
539 std::string value =
540 vpdSpecificUtility::getExpandedLocationCode(
541 propValuePair.value().get<std::string>(),
542 parsedVpdMap);
543 propertyMap.emplace(property, value);
544
545 auto l_locCodeProperty = propertyMap;
546 vpdSpecificUtility::insertOrMerge(
547 interfaceMap,
548 std::string(constants::xyzLocationCodeInf),
549 move(l_locCodeProperty));
550 }
551 else
552 {
553 propertyMap.emplace(
554 property, propValuePair.value().get<std::string>());
555 }
556 }
557 else if (propValuePair.value().is_array())
558 {
559 try
560 {
561 propertyMap.emplace(
562 property,
563 propValuePair.value().get<types::BinaryVector>());
564 }
565 catch (const nlohmann::detail::type_error& e)
566 {
567 std::cerr << "Type exception: " << e.what() << "\n";
568 }
569 }
570 else if (propValuePair.value().is_number())
571 {
572 // For now assume the value is a size_t. In the future it would
573 // be nice to come up with a way to get the type from the JSON.
574 propertyMap.emplace(property,
575 propValuePair.value().get<size_t>());
576 }
577 else if (propValuePair.value().is_object())
578 {
579 const std::string& record =
580 propValuePair.value().value("recordName", "");
581 const std::string& keyword =
582 propValuePair.value().value("keywordName", "");
583 const std::string& encoding =
584 propValuePair.value().value("encoding", "");
585
586 if (auto ipzVpdMap =
587 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
588 {
589 if (!record.empty() && !keyword.empty() &&
590 (*ipzVpdMap).count(record) &&
591 (*ipzVpdMap).at(record).count(keyword))
592 {
593 auto encoded = vpdSpecificUtility::encodeKeyword(
594 ((*ipzVpdMap).at(record).at(keyword)), encoding);
595 propertyMap.emplace(property, encoded);
596 }
597 }
598 else if (auto kwdVpdMap =
599 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
600 {
601 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
602 {
603 if (auto kwValue = std::get_if<types::BinaryVector>(
604 &(*kwdVpdMap).at(keyword)))
605 {
606 auto encodedValue =
607 vpdSpecificUtility::encodeKeyword(
608 std::string((*kwValue).begin(),
609 (*kwValue).end()),
610 encoding);
611
612 propertyMap.emplace(property, encodedValue);
613 }
614 else if (auto kwValue = std::get_if<std::string>(
615 &(*kwdVpdMap).at(keyword)))
616 {
617 auto encodedValue =
618 vpdSpecificUtility::encodeKeyword(
619 std::string((*kwValue).begin(),
620 (*kwValue).end()),
621 encoding);
622
623 propertyMap.emplace(property, encodedValue);
624 }
625 else if (auto uintValue = std::get_if<size_t>(
626 &(*kwdVpdMap).at(keyword)))
627 {
628 propertyMap.emplace(property, *uintValue);
629 }
630 else
631 {
632 logging::logMessage(
633 "Unknown keyword found, Keywrod = " + keyword);
634 }
635 }
636 }
637 }
638 }
639 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
640 move(propertyMap));
641 }
642}
643
644bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
645{
646 const unsigned char l_io[] = {
647 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
648 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
649
650 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
651 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
652 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
653 // IO.
654 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
655 constants::SIZE_OF_8EQ_IN_PG) == 0)
656 {
657 return true;
658 }
659
660 // The CPU is not an IO
661 return false;
662}
663
664bool Worker::primeInventory(const std::string& i_vpdFilePath)
665{
666 if (i_vpdFilePath.empty())
667 {
668 logging::logMessage("Empty VPD file path given");
669 return false;
670 }
671
672 if (m_parsedJson.empty())
673 {
674 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
675 return false;
676 }
677 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
678 {
679 logging::logMessage("File " + i_vpdFilePath +
680 ", is not found in the system config JSON file.");
681 return false;
682 }
683
684 types::ObjectMap l_objectInterfaceMap;
685 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
686 {
687 types::InterfaceMap l_interfaces;
688 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
689
690 if (l_Fru.contains("ccin"))
691 {
692 continue;
693 }
694
695 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
696 {
697 continue;
698 }
699
Souvik Roy6a9553c2025-02-07 01:16:32 -0600700 // Reset data under PIM for this FRU only if the FRU is not synthesized
701 // and we handle it's Present property.
702 if (isPresentPropertyHandlingRequired(l_Fru))
703 {
704 // Clear data under PIM if already exists.
705 vpdSpecificUtility::resetDataUnderPIM(
706 std::string(l_Fru["inventoryPath"]), l_interfaces);
707 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500708
709 // Add extra interfaces mentioned in the Json config file
710 if (l_Fru.contains("extraInterfaces"))
711 {
712 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
713 std::monostate{});
714 }
715
716 types::PropertyMap l_propertyValueMap;
Sunny Srivastavad159bb42025-01-09 11:13:50 +0530717
Souvik Roy6a9553c2025-02-07 01:16:32 -0600718 // Update Present property for this FRU only if we handle Present
719 // property for the FRU.
720 if (isPresentPropertyHandlingRequired(l_Fru))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500721 {
Souvik Roy6a9553c2025-02-07 01:16:32 -0600722 l_propertyValueMap.emplace("Present", false);
723
724 // TODO: Present based on file will be taken care in future.
725 // By default present is set to false for FRU at the time of
726 // priming. Once collection goes through, it will be set to true in
727 // that flow.
728 /*if (std::filesystem::exists(i_vpdFilePath))
729 {
730 l_propertyValueMap["Present"] = true;
731 }*/
732 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500733
734 vpdSpecificUtility::insertOrMerge(l_interfaces,
735 "xyz.openbmc_project.Inventory.Item",
736 move(l_propertyValueMap));
737
738 if (l_Fru.value("inherit", true) &&
739 m_parsedJson.contains("commonInterfaces"))
740 {
741 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
742 std::monostate{});
743 }
744
745 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
746 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
747
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600748 // Emplace the default state of FRU VPD collection
749 types::PropertyMap l_fruCollectionProperty = {
750 {"CollectionStatus", constants::vpdCollectionNotStarted}};
751
752 vpdSpecificUtility::insertOrMerge(l_interfaces,
753 constants::vpdCollectionInterface,
754 std::move(l_fruCollectionProperty));
755
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500756 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
757 std::move(l_interfaces));
758 }
759
760 // Notify PIM
761 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
762 {
763 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
764 return false;
765 }
766
767 return true;
768}
769
770void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
771 types::InterfaceMap& interfaces)
772{
773 // embedded property(true or false) says whether the subfru is embedded
774 // into the parent fru (or) not. VPD sets Present property only for
775 // embedded frus. If the subfru is not an embedded FRU, the subfru may
776 // or may not be physically present. Those non embedded frus will always
777 // have Present=false irrespective of its physical presence or absence.
778 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
779 // Present to true for such sub frus.
780 // Eg: ethernet port is embedded into bmc card. So set Present to true
781 // for such sub frus. Also donot populate present property for embedded
782 // subfru which is synthesized. Currently there is no subfru which are
783 // both embedded and synthesized. But still the case is handled here.
784
785 // Check if its required to handle presence for this FRU.
786 if (singleFru.value("handlePresence", true))
787 {
788 types::PropertyMap presProp;
789 presProp.emplace("Present", true);
790 vpdSpecificUtility::insertOrMerge(
791 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
792 }
793}
794
795void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
796 types::InterfaceMap& interfaces,
797 const types::VPDMapVariant& parsedVpdMap)
798{
799 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
800 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
801 {
802 if (singleFru["extraInterfaces"].contains(
803 "xyz.openbmc_project.Inventory.Item.Cpu"))
804 {
805 auto itrToRec = (*ipzVpdMap).find("CP00");
806 if (itrToRec == (*ipzVpdMap).end())
807 {
808 return;
809 }
810
Souvik Roya55fcca2025-02-19 01:33:58 -0600811 const std::string pgKeywordValue{
812 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
813
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500814 if (!pgKeywordValue.empty())
815 {
816 if (isCPUIOGoodOnly(pgKeywordValue))
817 {
818 interfaces["xyz.openbmc_project.Inventory.Item"]
819 ["PrettyName"] = "IO Module";
820 }
821 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600822 else
823 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530824 throw DataException(std::string(__FUNCTION__) +
825 "Failed to get value for keyword PG");
Souvik Roya55fcca2025-02-19 01:33:58 -0600826 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500827 }
828 }
829}
830
831void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
832 const types::VPDMapVariant& parsedVpdMap,
833 types::InterfaceMap& interfaces)
834{
835 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
836 {
837 for (const auto& record : singleFru["copyRecords"])
838 {
839 const std::string& recordName = record;
840 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
841 {
842 populateIPZVPDpropertyMap(interfaces,
843 (*ipzVpdMap).at(recordName),
844 constants::ipzVpdInf + recordName);
845 }
846 }
847 }
848}
849
850void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
851 types::InterfaceMap& interfaces)
852{
853 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
854 {
855 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
856 {
857 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
858 constants::ipzVpdInf + recordName);
859 }
860 }
861 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
862 {
863 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
864 }
865
866 if (m_parsedJson.contains("commonInterfaces"))
867 {
868 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
869 parsedVpdMap);
870 }
871}
872
873bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
874 const types::VPDMapVariant& parsedVpdMap)
875{
876 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
877 {
878 auto itrToRec = (*ipzVPDMap).find("VINI");
879 if (itrToRec == (*ipzVPDMap).end())
880 {
881 return false;
882 }
883
Souvik Roya55fcca2025-02-19 01:33:58 -0600884 std::string ccinFromVpd{
885 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
886
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500887 if (ccinFromVpd.empty())
888 {
889 return false;
890 }
891
892 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
893 ::toupper);
894
895 std::vector<std::string> ccinList;
896 for (std::string ccin : singleFru["ccin"])
897 {
898 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
899 ccinList.push_back(ccin);
900 }
901
902 if (ccinList.empty())
903 {
904 return false;
905 }
906
907 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
908 ccinList.end())
909 {
910 return false;
911 }
912 }
913 return true;
914}
915
916void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
917 types::InterfaceMap& io_interfaces)
918{
919 if (!dbusUtility::isChassisPowerOn())
920 {
921 std::array<const char*, 1> l_operationalStatusInf = {
922 constants::operationalStatusInf};
923
924 auto mapperObjectMap = dbusUtility::getObjectMap(
925 i_inventoryObjPath, l_operationalStatusInf);
926
927 // If the object has been found. Check if it is under PIM.
928 if (mapperObjectMap.size() != 0)
929 {
930 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
931 {
932 if (l_serviceName == constants::pimServiceName)
933 {
934 // The object is already under PIM. No need to process
935 // again. Retain the old value.
936 return;
937 }
938 }
939 }
940
941 // Implies value is not there in D-Bus. Populate it with default
942 // value "true".
943 types::PropertyMap l_functionalProp;
944 l_functionalProp.emplace("Functional", true);
945 vpdSpecificUtility::insertOrMerge(io_interfaces,
946 constants::operationalStatusInf,
947 move(l_functionalProp));
948 }
949
950 // if chassis is power on. Functional property should be there on D-Bus.
951 // Don't process.
952 return;
953}
954
955void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
956 types::InterfaceMap& io_interfaces)
957{
958 if (!dbusUtility::isChassisPowerOn())
959 {
960 std::array<const char*, 1> l_enableInf = {constants::enableInf};
961
962 auto mapperObjectMap =
963 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
964
965 // If the object has been found. Check if it is under PIM.
966 if (mapperObjectMap.size() != 0)
967 {
968 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
969 {
970 if (l_serviceName == constants::pimServiceName)
971 {
972 // The object is already under PIM. No need to process
973 // again. Retain the old value.
974 return;
975 }
976 }
977 }
978
979 // Implies value is not there in D-Bus. Populate it with default
980 // value "true".
981 types::PropertyMap l_enabledProp;
982 l_enabledProp.emplace("Enabled", true);
983 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
984 move(l_enabledProp));
985 }
986
987 // if chassis is power on. Enabled property should be there on D-Bus.
988 // Don't process.
989 return;
990}
991
992void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
993 types::ObjectMap& objectInterfaceMap,
994 const std::string& vpdFilePath)
995{
996 if (vpdFilePath.empty())
997 {
998 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530999 std::string(__FUNCTION__) +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001000 "Invalid parameter passed to populateDbus API.");
1001 }
1002
1003 // JSON config is mandatory for processing of "if". Add "else" for any
1004 // processing without config JSON.
1005 if (!m_parsedJson.empty())
1006 {
1007 types::InterfaceMap interfaces;
1008
1009 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1010 {
1011 const auto& inventoryPath = aFru["inventoryPath"];
1012 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1013 if (aFru.contains("ccin"))
1014 {
1015 if (!processFruWithCCIN(aFru, parsedVpdMap))
1016 {
1017 continue;
1018 }
1019 }
1020
1021 if (aFru.value("inherit", true))
1022 {
1023 processInheritFlag(parsedVpdMap, interfaces);
1024 }
1025
1026 // If specific record needs to be copied.
1027 if (aFru.contains("copyRecords"))
1028 {
1029 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1030 }
1031
1032 if (aFru.contains("extraInterfaces"))
1033 {
1034 // Process extra interfaces w.r.t a FRU.
1035 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1036 }
1037
1038 // Process FRUS which are embedded in the parent FRU and whose VPD
1039 // will be synthesized.
1040 if ((aFru.value("embedded", true)) &&
1041 (!aFru.value("synthesized", false)))
1042 {
1043 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1044 }
1045
1046 processFunctionalProperty(inventoryPath, interfaces);
1047 processEnabledProperty(inventoryPath, interfaces);
1048
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001049 // Update collection status as successful
1050 types::PropertyMap l_collectionProperty = {
1051 {"CollectionStatus", constants::vpdCollectionSuccess}};
1052
1053 vpdSpecificUtility::insertOrMerge(interfaces,
1054 constants::vpdCollectionInterface,
1055 std::move(l_collectionProperty));
1056
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001057 objectInterfaceMap.emplace(std::move(fruObjectPath),
1058 std::move(interfaces));
1059 }
1060 }
1061}
1062
Patrick Williams43fedab2025-02-03 14:28:05 -05001063std::string Worker::createAssetTagString(
1064 const types::VPDMapVariant& i_parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001065{
1066 std::string l_assetTag;
1067
1068 // system VPD will be in IPZ format.
1069 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1070 {
1071 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1072 if (l_itrToVsys != (*l_parsedVpdMap).end())
1073 {
Souvik Roya55fcca2025-02-19 01:33:58 -06001074 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1075 l_itrToVsys->second, constants::kwdTM)};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001076
Souvik Roya55fcca2025-02-19 01:33:58 -06001077 if (l_tmKwdValue.empty())
1078 {
1079 throw std::runtime_error(
1080 std::string("Failed to get value for keyword [") +
1081 constants::kwdTM +
1082 std::string("] while creating Asset tag."));
1083 }
1084
1085 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1086 l_itrToVsys->second, constants::kwdSE)};
1087
1088 if (l_seKwdValue.empty())
1089 {
1090 throw std::runtime_error(
1091 std::string("Failed to get value for keyword [") +
1092 constants::kwdSE +
1093 std::string("] while creating Asset tag."));
1094 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001095
1096 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1097 std::string{"-"} + l_seKwdValue;
1098 }
1099 else
1100 {
1101 throw std::runtime_error(
1102 "VSYS record not found in parsed VPD map to create Asset tag.");
1103 }
1104 }
1105 else
1106 {
1107 throw std::runtime_error(
1108 "Invalid VPD type recieved to create Asset tag.");
1109 }
1110
1111 return l_assetTag;
1112}
1113
1114void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1115{
1116 types::ObjectMap objectInterfaceMap;
1117
1118 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1119 {
1120 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1121
1122 try
1123 {
1124 if (m_isFactoryResetDone)
1125 {
1126 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1127
1128 auto l_itrToSystemPath = objectInterfaceMap.find(
1129 sdbusplus::message::object_path(constants::systemInvPath));
1130 if (l_itrToSystemPath == objectInterfaceMap.end())
1131 {
1132 throw std::runtime_error(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301133 "Asset tag update failed. System Path not found in object map.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001134 }
1135
1136 types::PropertyMap l_assetTagProperty;
1137 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1138
1139 (l_itrToSystemPath->second)
1140 .emplace(constants::assetTagInf,
1141 std::move(l_assetTagProperty));
1142 }
1143 }
1144 catch (const std::exception& l_ex)
1145 {
1146 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301147 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1148 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001149 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1150 }
1151
1152 // Notify PIM
1153 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1154 {
1155 throw std::runtime_error("Call to PIM failed for system VPD");
1156 }
1157 }
1158 else
1159 {
1160 throw DataException("Invalid format of parsed VPD map.");
1161 }
1162}
1163
1164bool Worker::processPreAction(const std::string& i_vpdFilePath,
1165 const std::string& i_flagToProcess)
1166{
1167 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1168 {
1169 logging::logMessage(
1170 "Invalid input parameter. Abort processing pre action");
1171 return false;
1172 }
1173
1174 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1175 i_vpdFilePath, i_flagToProcess)) &&
1176 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1177 {
1178 // TODO: Need a way to delete inventory object from Dbus and persisted
1179 // data section in case any FRU is not present or there is any
1180 // problem in collecting it. Once it has been deleted, it can be
1181 // re-created in the flow of priming the inventory. This needs to be
1182 // done either here or in the exception section of "parseAndPublishVPD"
1183 // API. Any failure in the process of collecting FRU will land up in the
1184 // excpetion of "parseAndPublishVPD".
1185
1186 // If the FRU is not there, clear the VINI/CCIN data.
1187 // Enity manager probes for this keyword to look for this
1188 // FRU, now if the data is persistent on BMC and FRU is
1189 // removed this can lead to ambiguity. Hence clearing this
1190 // Keyword if FRU is absent.
1191 const auto& inventoryPath =
1192 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1193 "");
1194
1195 if (!inventoryPath.empty())
1196 {
1197 types::ObjectMap l_pimObjMap{
1198 {inventoryPath,
1199 {{constants::kwdVpdInf,
1200 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1201
1202 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1203 {
1204 logging::logMessage(
1205 "Call to PIM failed for file " + i_vpdFilePath);
1206 }
1207 }
1208 else
1209 {
1210 logging::logMessage(
1211 "Inventory path is empty in Json for file " + i_vpdFilePath);
1212 }
1213
1214 return false;
1215 }
1216 return true;
1217}
1218
1219bool Worker::processPostAction(
1220 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1221 const std::optional<types::VPDMapVariant> i_parsedVpd)
1222{
1223 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1224 {
1225 logging::logMessage(
1226 "Invalid input parameter. Abort processing post action");
1227 return false;
1228 }
1229
1230 // Check if post action tag is to be triggered in the flow of collection
1231 // based on some CCIN value?
1232 if (m_parsedJson["frus"][i_vpdFruPath]
1233 .at(0)["postAction"][i_flagToProcess]
1234 .contains("ccin"))
1235 {
1236 if (!i_parsedVpd.has_value())
1237 {
1238 logging::logMessage("Empty VPD Map");
1239 return false;
1240 }
1241
1242 // CCIN match is required to process post action for this FRU as it
1243 // contains the flag.
1244 if (!vpdSpecificUtility::findCcinInVpd(
1245 m_parsedJson["frus"][i_vpdFruPath].at(
1246 0)["postAction"]["collection"],
1247 i_parsedVpd.value()))
1248 {
1249 // If CCIN is not found, implies post action processing is not
1250 // required for this FRU. Let the flow continue.
1251 return true;
1252 }
1253 }
1254
1255 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1256 i_vpdFruPath, i_flagToProcess))
1257 {
1258 logging::logMessage(
1259 "Execution of post action failed for path: " + i_vpdFruPath);
1260
1261 // If post action was required and failed only in that case return
1262 // false. In all other case post action is considered passed.
1263 return false;
1264 }
1265
1266 return true;
1267}
1268
1269types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1270{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001271 try
1272 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301273 if (i_vpdFilePath.empty())
1274 {
1275 throw std::runtime_error(
1276 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301277 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301278 }
1279
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301280 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001281 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1282 "preAction", "collection"))
1283 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301284 isPreActionRequired = true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001285 if (!processPreAction(i_vpdFilePath, "collection"))
1286 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301287 throw std::runtime_error(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301288 std::string(__FUNCTION__) + " Pre-Action failed");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001289 }
1290 }
1291
1292 if (!std::filesystem::exists(i_vpdFilePath))
1293 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301294 if (isPreActionRequired)
1295 {
1296 throw std::runtime_error(
1297 std::string(__FUNCTION__) + " Could not find file path " +
1298 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1299 }
1300 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001301 }
1302
1303 std::shared_ptr<Parser> vpdParser =
1304 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1305
1306 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1307
1308 // Before returning, as collection is over, check if FRU qualifies for
1309 // any post action in the flow of collection.
1310 // Note: Don't change the order, post action needs to be processed only
1311 // after collection for FRU is successfully done.
1312 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1313 "postAction", "collection"))
1314 {
1315 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1316 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301317 // Post action was required but failed while executing.
1318 // Behaviour can be undefined.
1319 EventLogger::createSyncPel(
1320 types::ErrorType::InternalFailure,
1321 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1322 std::string("Required post action failed for path [" +
1323 i_vpdFilePath + "]"),
1324 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001325 }
1326 }
1327
1328 return l_parsedVpd;
1329 }
1330 catch (std::exception& l_ex)
1331 {
1332 // If post fail action is required, execute it.
1333 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -06001334 "postFailAction", "collection"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001335 {
1336 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1337 "collection"))
1338 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001339 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301340 std::string(__FUNCTION__) + "VPD parsing failed for " +
1341 i_vpdFilePath + " due to error: " + l_ex.what() +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001342 ". Post Fail Action also failed, aborting collection for this FRU");
1343 }
1344 }
1345
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301346 throw std::runtime_error(
1347 std::string(__FUNCTION__) + "VPD parsing failed for " +
1348 i_vpdFilePath + " due to error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001349 }
1350}
1351
Patrick Williams43fedab2025-02-03 14:28:05 -05001352std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1353 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001354{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001355 std::string l_inventoryPath{};
1356
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001357 try
1358 {
1359 m_semaphore.acquire();
1360
1361 // Thread launched.
1362 m_mutex.lock();
1363 m_activeCollectionThreadCount++;
1364 m_mutex.unlock();
1365
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001366 // Set CollectionStatus as InProgress. Since it's an intermediate state
1367 // D-bus set-property call is good enough to update the status.
RekhaAparna011ef21002025-02-18 23:47:36 -06001368 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
1369 m_parsedJson, i_vpdFilePath);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001370
RekhaAparna011ef21002025-02-18 23:47:36 -06001371 if (!l_inventoryPath.empty())
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001372 {
RekhaAparna01c532b182025-02-19 19:56:49 -06001373 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001374 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1375 l_inventoryPath, constants::vpdCollectionInterface,
1376 "CollectionStatus",
RekhaAparna01c532b182025-02-19 19:56:49 -06001377 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001378 {
1379 logging::logMessage(
1380 "Unable to set CollectionStatus as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001381 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001382 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001383 }
1384
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001385 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301386 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001387 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301388 types::ObjectMap objectInterfaceMap;
1389 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1390
1391 // Notify PIM
1392 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1393 {
1394 throw std::runtime_error(
1395 std::string(__FUNCTION__) +
1396 "Call to PIM failed while publishing VPD.");
1397 }
1398 }
1399 else
1400 {
1401 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1402 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001403 }
1404 }
1405 catch (const std::exception& ex)
1406 {
Anupama B R24691d22025-05-21 08:14:15 -05001407 setCollectionStatusProperty(i_vpdFilePath,
1408 constants::vpdCollectionFailure);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001409
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001410 // handle all the exceptions internally. Return only true/false
1411 // based on status of execution.
1412 if (typeid(ex) == std::type_index(typeid(DataException)))
1413 {
Sunny Srivastava78c91072025-02-05 14:09:50 +05301414 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1415 // logging error for these cases.
1416 if (vpdSpecificUtility::isPass1Planar())
1417 {
RekhaAparna011ef21002025-02-18 23:47:36 -06001418 const std::string& l_invPathLeafValue =
1419 sdbusplus::message::object_path(
1420 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1421 i_vpdFilePath))
1422 .filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301423
RekhaAparna011ef21002025-02-18 23:47:36 -06001424 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1425 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301426 {
1427 // skip logging any PEL for PCIe cards on pass 1 planar.
1428 return std::make_tuple(false, i_vpdFilePath);
1429 }
1430 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301431 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301432
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301433 EventLogger::createSyncPel(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301434 EventLogger::getErrorType(ex), types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301435 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1436 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001437
1438 // TODO: Figure out a way to clear data in case of any failure at
1439 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301440
1441 // set present property to false for any error case. In future this will
1442 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001443 // Update Present property for this FRU only if we handle Present
1444 // property for the FRU.
1445 if (isPresentPropertyHandlingRequired(
1446 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1447 {
1448 setPresentProperty(i_vpdFilePath, false);
1449 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301450
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001451 m_semaphore.release();
1452 return std::make_tuple(false, i_vpdFilePath);
1453 }
1454 m_semaphore.release();
1455 return std::make_tuple(true, i_vpdFilePath);
1456}
1457
Sunny Srivastava61611752025-02-04 00:29:33 -06001458bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1459{
1460 if (i_vpdFilePath.empty())
1461 {
1462 return true;
1463 }
1464
1465 // skip processing of system VPD again as it has been already collected.
1466 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1467 {
1468 return true;
1469 }
1470
1471 if (dbusUtility::isChassisPowerOn())
1472 {
1473 // If chassis is powered on, skip collecting FRUs which are
1474 // powerOffOnly.
1475 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1476 {
1477 return true;
1478 }
1479
1480 const std::string& l_invPathLeafValue =
1481 sdbusplus::message::object_path(
1482 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1483 i_vpdFilePath))
1484 .filename();
1485
1486 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1487 {
1488 return true;
1489 }
1490 }
1491
1492 return false;
1493}
1494
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001495void Worker::collectFrusFromJson()
1496{
1497 // A parsed JSON file should be present to pick FRUs EEPROM paths
1498 if (m_parsedJson.empty())
1499 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301500 throw JsonException(
1501 std::string(__FUNCTION__) +
1502 ": Config JSON is mandatory for processing of FRUs through this API.",
1503 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001504 }
1505
1506 const nlohmann::json& listOfFrus =
1507 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1508
1509 for (const auto& itemFRUS : listOfFrus.items())
1510 {
1511 const std::string& vpdFilePath = itemFRUS.key();
1512
Sunny Srivastava61611752025-02-04 00:29:33 -06001513 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001514 {
1515 continue;
1516 }
1517
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001518 try
1519 {
1520 std::thread{[vpdFilePath, this]() {
1521 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001522
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001523 m_mutex.lock();
1524 m_activeCollectionThreadCount--;
1525 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001526
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001527 if (!m_activeCollectionThreadCount)
1528 {
1529 m_isAllFruCollected = true;
1530 }
1531 }}.detach();
1532 }
1533 catch (const std::exception& l_ex)
1534 {
1535 // add vpdFilePath(EEPROM path) to failed list
1536 m_failedEepromPaths.push_front(vpdFilePath);
1537 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001538 }
1539}
1540
1541// ToDo: Move the API under IBM_SYSTEM
1542void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1543{
1544 try
1545 {
1546 std::string l_backupAndRestoreCfgFilePath =
1547 m_parsedJson.value("backupRestoreConfigPath", "");
1548
1549 nlohmann::json l_backupAndRestoreCfgJsonObj =
1550 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1551
RekhaAparna011ef21002025-02-18 23:47:36 -06001552 if (l_backupAndRestoreCfgJsonObj.empty())
1553 {
1554 throw JsonException("JSON parsing failed",
1555 l_backupAndRestoreCfgFilePath);
1556 }
1557
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001558 // check if either of "source" or "destination" has inventory path.
1559 // this indicates that this sytem has System VPD on hardware
1560 // and other copy on D-Bus (BMC cache).
1561 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1562 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1563 l_backupAndRestoreCfgJsonObj["source"].contains(
1564 "inventoryPath")) ||
1565 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1566 l_backupAndRestoreCfgJsonObj["destination"].contains(
1567 "inventoryPath"))))
1568 {
1569 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1570 auto [l_srcVpdVariant,
1571 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1572
1573 // ToDo: Revisit is this check is required or not.
1574 if (auto l_srcVpdMap =
1575 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1576 l_srcVpdMap && !(*l_srcVpdMap).empty())
1577 {
1578 io_srcVpdMap = std::move(l_srcVpdVariant);
1579 }
1580 }
1581 }
1582 catch (const std::exception& l_ex)
1583 {
1584 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301585 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301586 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001587 std::string(
1588 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301589 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001590 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1591 }
1592}
1593
1594void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1595{
1596 if (i_dbusObjPath.empty())
1597 {
1598 throw std::runtime_error("Given DBus object path is empty.");
1599 }
1600
1601 const std::string& l_fruPath =
1602 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1603
1604 try
1605 {
1606 auto l_presentPropValue = dbusUtility::readDbusProperty(
1607 constants::pimServiceName, i_dbusObjPath,
1608 constants::inventoryItemInf, "Present");
1609
1610 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1611 {
1612 if (!(*l_value))
1613 {
1614 throw std::runtime_error("Given FRU is not present");
1615 }
1616 else
1617 {
1618 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1619 "preAction", "deletion"))
1620 {
1621 if (!processPreAction(l_fruPath, "deletion"))
1622 {
1623 throw std::runtime_error("Pre action failed");
1624 }
1625 }
1626
1627 std::vector<std::string> l_interfaceList{
1628 constants::operationalStatusInf};
1629
1630 types::MapperGetSubTree l_subTreeMap =
1631 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1632 l_interfaceList);
1633
1634 types::ObjectMap l_objectMap;
1635
1636 // Updates VPD specific interfaces property value under PIM for
1637 // sub FRUs.
1638 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1639 l_subTreeMap)
1640 {
1641 types::InterfaceMap l_interfaceMap;
1642 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1643 l_interfaceMap);
1644 l_objectMap.emplace(l_objectPath,
1645 std::move(l_interfaceMap));
1646 }
1647
1648 types::InterfaceMap l_interfaceMap;
1649 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1650 l_interfaceMap);
1651
1652 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1653
1654 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1655 {
1656 throw std::runtime_error("Call to PIM failed.");
1657 }
1658
1659 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1660 "postAction", "deletion"))
1661 {
1662 if (!processPostAction(l_fruPath, "deletion"))
1663 {
1664 throw std::runtime_error("Post action failed");
1665 }
1666 }
1667 }
1668 }
1669 else
1670 {
1671 logging::logMessage(
1672 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1673 "] as unable to read present property");
1674 return;
1675 }
1676
1677 logging::logMessage(
1678 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1679 }
1680 catch (const std::exception& l_ex)
1681 {
1682 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1683 "postFailAction", "deletion"))
1684 {
1685 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1686 "deletion"))
1687 {
1688 logging::logMessage(
1689 "Post fail action failed for: " + i_dbusObjPath);
1690 }
1691 }
1692
1693 logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1694 " error: " + std::string(l_ex.what()));
1695 }
1696}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301697
1698void Worker::setPresentProperty(const std::string& i_vpdPath,
1699 const bool& i_value)
1700{
1701 try
1702 {
1703 if (i_vpdPath.empty())
1704 {
1705 throw std::runtime_error(
1706 "Path is empty. Can't set present property");
1707 }
1708
1709 types::ObjectMap l_objectInterfaceMap;
1710
1711 // If the given path is EEPROM path.
1712 if (m_parsedJson["frus"].contains(i_vpdPath))
1713 {
1714 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1715 {
1716 sdbusplus::message::object_path l_fruObjectPath(
1717 l_Fru["inventoryPath"]);
1718
1719 types::PropertyMap l_propertyValueMap;
1720 l_propertyValueMap.emplace("Present", i_value);
1721
1722 types::InterfaceMap l_interfaces;
1723 vpdSpecificUtility::insertOrMerge(l_interfaces,
1724 constants::inventoryItemInf,
1725 move(l_propertyValueMap));
1726
1727 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1728 std::move(l_interfaces));
1729 }
1730 }
1731 else
1732 {
1733 // consider it as an inventory path.
1734 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1735 {
1736 throw std::runtime_error(
1737 "Invalid inventory path: " + i_vpdPath);
1738 }
1739
1740 types::PropertyMap l_propertyValueMap;
1741 l_propertyValueMap.emplace("Present", i_value);
1742
1743 types::InterfaceMap l_interfaces;
1744 vpdSpecificUtility::insertOrMerge(l_interfaces,
1745 constants::inventoryItemInf,
1746 move(l_propertyValueMap));
1747
1748 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1749 }
1750
1751 // Notify PIM
1752 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1753 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301754 throw DbusException(
1755 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301756 "Call to PIM failed while setting present property for path " +
1757 i_vpdPath);
1758 }
1759 }
1760 catch (const std::exception& l_ex)
1761 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301762 EventLogger::createSyncPel(
1763 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1764 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1765 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301766 }
1767}
1768
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301769void Worker::performVpdRecollection()
1770{
1771 try
1772 {
1773 // Check if system config JSON is present
1774 if (m_parsedJson.empty())
1775 {
1776 throw std::runtime_error(
1777 "System config json object is empty, can't process recollection.");
1778 }
1779
1780 const auto& l_frusReplaceableAtStandby =
1781 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1782
1783 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1784 {
1785 // ToDo: Add some logic/trace to know the flow to
1786 // collectSingleFruVpd has been directed via
1787 // performVpdRecollection.
1788 collectSingleFruVpd(l_fruInventoryPath);
1789 }
1790 return;
1791 }
1792
1793 catch (const std::exception& l_ex)
1794 {
1795 // TODO Log PEL
1796 logging::logMessage(
1797 "VPD recollection failed with error: " + std::string(l_ex.what()));
1798 }
1799}
1800
1801void Worker::collectSingleFruVpd(
1802 const sdbusplus::message::object_path& i_dbusObjPath)
1803{
1804 try
1805 {
1806 // Check if system config JSON is present
1807 if (m_parsedJson.empty())
1808 {
1809 logging::logMessage(
1810 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1811 std::string(i_dbusObjPath));
1812 return;
1813 }
1814
1815 // Get FRU path for the given D-bus object path from JSON
1816 const std::string& l_fruPath =
1817 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1818
1819 if (l_fruPath.empty())
1820 {
1821 logging::logMessage(
1822 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1823 std::string(i_dbusObjPath));
1824 return;
1825 }
1826
1827 // Check if host is up and running
1828 if (dbusUtility::isHostRunning())
1829 {
1830 if (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1831 l_fruPath))
1832 {
1833 logging::logMessage(
1834 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1835 std::string(i_dbusObjPath));
1836 return;
1837 }
1838 }
1839 else if (dbusUtility::isBMCReady())
1840 {
1841 if (!jsonUtility::isFruReplaceableAtStandby(m_parsedJson,
1842 l_fruPath) &&
1843 (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1844 l_fruPath)))
1845 {
1846 logging::logMessage(
1847 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1848 std::string(i_dbusObjPath));
1849 return;
1850 }
1851 }
1852
1853 // Set CollectionStatus as InProgress. Since it's an intermediate state
1854 // D-bus set-property call is good enough to update the status.
1855 const std::string& l_collStatusProp = "CollectionStatus";
1856
1857 if (!dbusUtility::writeDbusProperty(
1858 jsonUtility::getServiceName(m_parsedJson,
1859 std::string(i_dbusObjPath)),
1860 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
1861 l_collStatusProp,
1862 types::DbusVariantType{constants::vpdCollectionInProgress}))
1863 {
1864 logging::logMessage(
1865 "Unable to set CollectionStatus as InProgress for " +
1866 std::string(i_dbusObjPath) +
1867 ". Continue single FRU VPD collection.");
1868 }
1869
1870 // Parse VPD
1871 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1872
1873 // If l_parsedVpd is pointing to std::monostate
1874 if (l_parsedVpd.index() == 0)
1875 {
1876 throw std::runtime_error(
1877 "VPD parsing failed for " + std::string(i_dbusObjPath));
1878 }
1879
1880 // Get D-bus object map from worker class
1881 types::ObjectMap l_dbusObjectMap;
1882 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1883
1884 if (l_dbusObjectMap.empty())
1885 {
1886 throw std::runtime_error(
1887 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1888 std::string(i_dbusObjPath));
1889 }
1890
1891 // Call PIM's Notify method
1892 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
1893 {
1894 throw std::runtime_error(
1895 "Notify PIM failed. Single FRU VPD collection failed for " +
1896 std::string(i_dbusObjPath));
1897 }
1898 }
1899 catch (const std::exception& l_error)
1900 {
1901 // Notify FRU's VPD CollectionStatus as Failure
1902 if (!dbusUtility::notifyFRUCollectionStatus(
1903 std::string(i_dbusObjPath), constants::vpdCollectionFailure))
1904 {
1905 logging::logMessage(
1906 "Call to PIM Notify method failed to update Collection status as Failure for " +
1907 std::string(i_dbusObjPath));
1908 }
1909
1910 // TODO: Log PEL
1911 logging::logMessage(std::string(l_error.what()));
1912 }
1913}
Anupama B R24691d22025-05-21 08:14:15 -05001914
1915void Worker::setCollectionStatusProperty(
1916 const std::string& i_vpdPath, const std::string& i_value) const noexcept
1917{
1918 try
1919 {
1920 if (i_vpdPath.empty())
1921 {
1922 throw std::runtime_error(
1923 "Given path is empty. Can't set CollectionStatus property");
1924 }
1925
1926 types::ObjectMap l_objectInterfaceMap;
1927
1928 if (m_parsedJson["frus"].contains(i_vpdPath))
1929 {
1930 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1931 {
1932 sdbusplus::message::object_path l_fruObjectPath(
1933 l_Fru["inventoryPath"]);
1934
1935 types::PropertyMap l_propertyValueMap;
1936 l_propertyValueMap.emplace("CollectionStatus", i_value);
1937
1938 types::InterfaceMap l_interfaces;
1939 vpdSpecificUtility::insertOrMerge(
1940 l_interfaces, constants::vpdCollectionInterface,
1941 move(l_propertyValueMap));
1942
1943 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1944 std::move(l_interfaces));
1945 }
1946 }
1947 else
1948 {
1949 // consider it as an inventory path.
1950 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1951 {
1952 throw std::runtime_error(
1953 "Invalid inventory path: " + i_vpdPath +
1954 ". Can't set CollectionStatus property");
1955 }
1956
1957 types::PropertyMap l_propertyValueMap;
1958 l_propertyValueMap.emplace("CollectionStatus", i_value);
1959
1960 types::InterfaceMap l_interfaces;
1961 vpdSpecificUtility::insertOrMerge(l_interfaces,
1962 constants::vpdCollectionInterface,
1963 move(l_propertyValueMap));
1964
1965 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1966 }
1967
1968 // Notify PIM
1969 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1970 {
1971 throw DbusException(
1972 std::string(__FUNCTION__) +
1973 "Call to PIM failed while setting CollectionStatus property for path " +
1974 i_vpdPath);
1975 }
1976 }
1977 catch (const std::exception& l_ex)
1978 {
1979 EventLogger::createSyncPel(
1980 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1981 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1982 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1983 }
1984}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001985} // namespace vpd