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