xref: /openbmc/openpower-vpd-parser/vpd-manager/src/manager.cpp (revision a39aafa337352262596e07577fdf88cc294284c9)
1 #include "config.h"
2 
3 #include "manager.hpp"
4 
5 #include "constants.hpp"
6 #include "exceptions.hpp"
7 #include "logger.hpp"
8 #include "parser.hpp"
9 #include "parser_factory.hpp"
10 #include "parser_interface.hpp"
11 #include "single_fab.hpp"
12 #include "types.hpp"
13 #include "utility/dbus_utility.hpp"
14 #include "utility/json_utility.hpp"
15 #include "utility/vpd_specific_utility.hpp"
16 
17 #include <boost/asio/steady_timer.hpp>
18 #include <sdbusplus/bus/match.hpp>
19 #include <sdbusplus/message.hpp>
20 
21 namespace vpd
22 {
23 Manager::Manager(
24     const std::shared_ptr<boost::asio::io_context>& ioCon,
25     const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
26     const std::shared_ptr<sdbusplus::asio::dbus_interface>& progressiFace,
27     const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
28     m_ioContext(ioCon), m_interface(iFace), m_progressInterface(progressiFace),
29     m_asioConnection(asioConnection)
30 {
31 #ifdef IBM_SYSTEM_SINGLE_FAB
32     if (!dbusUtility::isChassisPowerOn())
33     {
34         SingleFab l_singleFab;
35         const int& l_rc = l_singleFab.singleFabImOverride();
36 
37         if (l_rc == constants::FAILURE)
38         {
39             throw std::runtime_error(
40                 std::string(__FUNCTION__) +
41                 " : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced.");
42         }
43     }
44 #endif
45 
46     try
47     {
48         // For backward compatibility. Should be depricated.
49         iFace->register_method(
50             "WriteKeyword",
51             [this](const sdbusplus::message::object_path i_path,
52                    const std::string i_recordName, const std::string i_keyword,
53                    const types::BinaryVector i_value) -> int {
54                 return this->updateKeyword(
55                     i_path, std::make_tuple(i_recordName, i_keyword, i_value));
56             });
57 
58         // Register methods under com.ibm.VPD.Manager interface
59         iFace->register_method(
60             "UpdateKeyword",
61             [this](const types::Path i_vpdPath,
62                    const types::WriteVpdParams i_paramsToWriteData) -> int {
63                 return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
64             });
65 
66         iFace->register_method(
67             "WriteKeywordOnHardware",
68             [this](const types::Path i_fruPath,
69                    const types::WriteVpdParams i_paramsToWriteData) -> int {
70                 return this->updateKeywordOnHardware(i_fruPath,
71                                                      i_paramsToWriteData);
72             });
73 
74         iFace->register_method(
75             "ReadKeyword",
76             [this](const types::Path i_fruPath,
77                    const types::ReadVpdParams i_paramsToReadData)
78                 -> types::DbusVariantType {
79                 return this->readKeyword(i_fruPath, i_paramsToReadData);
80             });
81 
82         iFace->register_method(
83             "CollectFRUVPD",
84             [this](const sdbusplus::message::object_path& i_dbusObjPath) {
85                 this->collectSingleFruVpd(i_dbusObjPath);
86             });
87 
88         iFace->register_method(
89             "deleteFRUVPD",
90             [this](const sdbusplus::message::object_path& i_dbusObjPath) {
91                 this->deleteSingleFruVpd(i_dbusObjPath);
92             });
93 
94         iFace->register_method(
95             "GetExpandedLocationCode",
96             [this](const std::string& i_unexpandedLocationCode,
97                    uint16_t& i_nodeNumber) -> std::string {
98                 return this->getExpandedLocationCode(i_unexpandedLocationCode,
99                                                      i_nodeNumber);
100             });
101 
102         iFace->register_method("GetFRUsByExpandedLocationCode",
103                                [this](const std::string& i_expandedLocationCode)
104                                    -> types::ListOfPaths {
105                                    return this->getFrusByExpandedLocationCode(
106                                        i_expandedLocationCode);
107                                });
108 
109         iFace->register_method(
110             "GetFRUsByUnexpandedLocationCode",
111             [this](const std::string& i_unexpandedLocationCode,
112                    uint16_t& i_nodeNumber) -> types::ListOfPaths {
113                 return this->getFrusByUnexpandedLocationCode(
114                     i_unexpandedLocationCode, i_nodeNumber);
115             });
116 
117         iFace->register_method(
118             "GetHardwarePath",
119             [this](const sdbusplus::message::object_path& i_dbusObjPath)
120                 -> std::string { return this->getHwPath(i_dbusObjPath); });
121 
122         iFace->register_method("PerformVPDRecollection", [this]() {
123             this->performVpdRecollection();
124         });
125 
126         iFace->register_method("CollectAllFRUVPD", [this]() -> bool {
127             return this->collectAllFruVpd();
128         });
129 
130         // Indicates FRU VPD collection for the system has not started.
131         progressiFace->register_property_rw<std::string>(
132             "Status", sdbusplus::vtable::property_::emits_change,
133             [this](const std::string& l_currStatus, const auto&) {
134                 if (m_vpdCollectionStatus != l_currStatus)
135                 {
136                     m_vpdCollectionStatus = l_currStatus;
137                     m_interface->signal_property("Status");
138                 }
139                 return true;
140             },
141             [this](const auto&) { return m_vpdCollectionStatus; });
142 
143         // If required, instantiate OEM specific handler here.
144 #ifdef IBM_SYSTEM
145         m_ibmHandler = std::make_shared<IbmHandler>(
146             m_worker, m_backupAndRestoreObj, m_interface, m_progressInterface,
147             m_ioContext, m_asioConnection);
148 #else
149         m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
150         m_progressInterface->set_property(
151             "Status", std::string(constants::vpdCollectionCompleted));
152 #endif
153     }
154     catch (const std::exception& e)
155     {
156         logging::logMessage(
157             "Manager class instantiation failed. " + std::string(e.what()));
158 
159         vpd::EventLogger::createSyncPel(
160             vpd::EventLogger::getErrorType(e), vpd::types::SeverityType::Error,
161             __FILE__, __FUNCTION__, 0, vpd::EventLogger::getErrorMsg(e),
162             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
163     }
164 }
165 
166 int Manager::updateKeyword(const types::Path i_vpdPath,
167                            const types::WriteVpdParams i_paramsToWriteData)
168 {
169     if (i_vpdPath.empty())
170     {
171         logging::logMessage("Given VPD path is empty.");
172         return -1;
173     }
174 
175     uint16_t l_errCode = 0;
176     types::Path l_fruPath;
177     nlohmann::json l_sysCfgJsonObj{};
178 
179     if (m_worker.get() != nullptr)
180     {
181         l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
182 
183         // Get the EEPROM path
184         if (!l_sysCfgJsonObj.empty())
185         {
186             l_fruPath = jsonUtility::getFruPathFromJson(l_sysCfgJsonObj,
187                                                         i_vpdPath, l_errCode);
188         }
189     }
190 
191     if (l_fruPath.empty())
192     {
193         if (l_errCode)
194         {
195             logging::logMessage(
196                 "Failed to get FRU path from JSON for [" + i_vpdPath +
197                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
198         }
199 
200         l_fruPath = i_vpdPath;
201     }
202 
203     try
204     {
205         std::shared_ptr<Parser> l_parserObj =
206             std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
207 
208         types::DbusVariantType l_updatedValue;
209         auto l_rc =
210             l_parserObj->updateVpdKeyword(i_paramsToWriteData, l_updatedValue);
211 
212         if (l_rc != constants::FAILURE && m_backupAndRestoreObj)
213         {
214             if (m_backupAndRestoreObj->updateKeywordOnPrimaryOrBackupPath(
215                     l_fruPath, i_paramsToWriteData) < constants::VALUE_0)
216             {
217                 logging::logMessage(
218                     "Write success, but backup and restore failed for file[" +
219                     l_fruPath + "]");
220             }
221         }
222 
223         types::WriteVpdParams l_writeParams;
224         types::BinaryVector l_valueToUpdate;
225 
226         if (const types::IpzData* l_ipzData =
227                 std::get_if<types::IpzData>(&i_paramsToWriteData))
228         {
229             if (const types::BinaryVector* l_val =
230                     std::get_if<types::BinaryVector>(&l_updatedValue))
231             {
232                 l_valueToUpdate = *l_val;
233             }
234             else
235             {
236                 l_valueToUpdate = std::get<2>(*l_ipzData);
237             }
238             l_writeParams =
239                 std::make_tuple(std::get<0>(*l_ipzData),
240                                 std::get<1>(*l_ipzData), l_valueToUpdate);
241         }
242         else if (const types::KwData* l_kwData =
243                      std::get_if<types::KwData>(&i_paramsToWriteData))
244         {
245             if (const types::BinaryVector* l_val =
246                     std::get_if<types::BinaryVector>(&l_updatedValue))
247             {
248                 l_valueToUpdate = *l_val;
249             }
250             else
251             {
252                 l_valueToUpdate = std::get<1>(*l_kwData);
253             }
254 
255             l_writeParams =
256                 std::make_tuple(std::get<0>(*l_kwData), l_valueToUpdate);
257         }
258 
259         // update keyword in inherited FRUs
260         if (l_rc != constants::FAILURE)
261         {
262             vpdSpecificUtility::updateKwdOnInheritedFrus(
263                 l_fruPath, l_writeParams, l_sysCfgJsonObj, l_errCode);
264 
265             if (l_errCode)
266             {
267                 logging::logMessage(
268                     "Failed to update keyword on inherited FRUs for FRU [" +
269                     l_fruPath +
270                     "] , error : " + commonUtility::getErrCodeMsg(l_errCode));
271             }
272         }
273 
274         // log VPD write success or failure
275         auto l_logger = Logger::getLoggerInstance();
276 
277         // update common interface(s) properties
278         if (l_rc != constants::FAILURE)
279         {
280             vpdSpecificUtility::updateCiPropertyOfInheritedFrus(
281                 l_fruPath, l_writeParams, l_sysCfgJsonObj, l_errCode);
282 
283             if (l_errCode)
284             {
285                 l_logger->logMessage(
286                     "Failed to update Ci property of inherited FRUs, error : " +
287                     commonUtility::getErrCodeMsg(l_errCode));
288             }
289         }
290 
291         l_logger->logMessage(
292             "VPD write " +
293                 std::string(
294                     (l_rc != constants::FAILURE) ? "successful" : "failed") +
295                 " on path[" + i_vpdPath + "] : " +
296                 vpdSpecificUtility::convertWriteVpdParamsToString(l_writeParams,
297                                                                   l_errCode),
298             PlaceHolder::VPD_WRITE);
299 
300         return l_rc;
301     }
302     catch (const std::exception& l_exception)
303     {
304         // TODO:: error log needed
305         logging::logMessage("Update keyword failed for file[" + i_vpdPath +
306                             "], reason: " + std::string(l_exception.what()));
307         return -1;
308     }
309 }
310 
311 int Manager::updateKeywordOnHardware(
312     const types::Path i_fruPath,
313     const types::WriteVpdParams i_paramsToWriteData) noexcept
314 {
315     try
316     {
317         if (i_fruPath.empty())
318         {
319             throw std::runtime_error("Given FRU path is empty");
320         }
321 
322         nlohmann::json l_sysCfgJsonObj{};
323 
324         if (m_worker.get() != nullptr)
325         {
326             l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
327         }
328 
329         std::shared_ptr<Parser> l_parserObj =
330             std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
331         return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
332     }
333     catch (const std::exception& l_exception)
334     {
335         EventLogger::createAsyncPel(
336             types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
337             __FILE__, __FUNCTION__, 0,
338             "Update keyword on hardware failed for file[" + i_fruPath +
339                 "], reason: " + std::string(l_exception.what()),
340             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
341 
342         return constants::FAILURE;
343     }
344 }
345 
346 types::DbusVariantType Manager::readKeyword(
347     const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
348 {
349     try
350     {
351         nlohmann::json l_jsonObj{};
352 
353         if (m_worker.get() != nullptr)
354         {
355             l_jsonObj = m_worker->getSysCfgJsonObj();
356         }
357 
358         std::error_code ec;
359 
360         // Check if given path is filesystem path
361         if (!std::filesystem::exists(i_fruPath, ec) && (ec))
362         {
363             throw std::runtime_error(
364                 "Given file path " + i_fruPath + " not found.");
365         }
366 
367         std::shared_ptr<vpd::Parser> l_parserObj =
368             std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
369 
370         std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
371             l_parserObj->getVpdParserInstance();
372 
373         return (
374             l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
375     }
376     catch (const std::exception& e)
377     {
378         logging::logMessage(
379             e.what() + std::string(". VPD manager read operation failed for ") +
380             i_fruPath);
381         throw types::DeviceError::ReadFailure();
382     }
383 }
384 
385 void Manager::collectSingleFruVpd(
386     const sdbusplus::message::object_path& i_dbusObjPath)
387 {
388     if (m_vpdCollectionStatus != constants::vpdCollectionCompleted)
389     {
390         logging::logMessage(
391             "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
392             std::string(i_dbusObjPath));
393         return;
394     }
395 
396     if (m_worker.get() != nullptr)
397     {
398         m_worker->collectSingleFruVpd(i_dbusObjPath);
399     }
400 }
401 
402 void Manager::deleteSingleFruVpd(
403     const sdbusplus::message::object_path& i_dbusObjPath)
404 {
405     try
406     {
407         if (std::string(i_dbusObjPath).empty())
408         {
409             throw std::runtime_error(
410                 "Given DBus object path is empty. Aborting FRU VPD deletion.");
411         }
412 
413         if (m_worker.get() == nullptr)
414         {
415             throw std::runtime_error(
416                 "Worker object not found, can't perform FRU VPD deletion for: " +
417                 std::string(i_dbusObjPath));
418         }
419 
420         m_worker->deleteFruVpd(std::string(i_dbusObjPath));
421     }
422     catch (const std::exception& l_ex)
423     {
424         // TODO: Log PEL
425         logging::logMessage(l_ex.what());
426     }
427 }
428 
429 bool Manager::isValidUnexpandedLocationCode(
430     const std::string& i_unexpandedLocationCode)
431 {
432     if ((i_unexpandedLocationCode.length() <
433          constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
434         ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
435           constants::STR_CMP_SUCCESS) &&
436          (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
437           constants::STR_CMP_SUCCESS)) ||
438         ((i_unexpandedLocationCode.length() >
439           constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
440          (i_unexpandedLocationCode.find("-") != 4)))
441     {
442         return false;
443     }
444 
445     return true;
446 }
447 
448 std::string Manager::getExpandedLocationCode(
449     const std::string& i_unexpandedLocationCode,
450     [[maybe_unused]] const uint16_t i_nodeNumber)
451 {
452     if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
453     {
454         phosphor::logging::elog<types::DbusInvalidArgument>(
455             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
456             types::InvalidArgument::ARGUMENT_VALUE(
457                 i_unexpandedLocationCode.c_str()));
458     }
459 
460     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
461     if (!l_sysCfgJsonObj.contains("frus"))
462     {
463         logging::logMessage("Missing frus tag in system config JSON");
464     }
465 
466     const nlohmann::json& l_listOfFrus =
467         l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
468 
469     for (const auto& l_frus : l_listOfFrus.items())
470     {
471         for (const auto& l_aFru : l_frus.value())
472         {
473             if (l_aFru["extraInterfaces"].contains(
474                     constants::locationCodeInf) &&
475                 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
476                     "LocationCode", "") == i_unexpandedLocationCode)
477             {
478                 return std::get<std::string>(dbusUtility::readDbusProperty(
479                     l_aFru["serviceName"], l_aFru["inventoryPath"],
480                     constants::locationCodeInf, "LocationCode"));
481             }
482         }
483     }
484     phosphor::logging::elog<types::DbusInvalidArgument>(
485         types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
486         types::InvalidArgument::ARGUMENT_VALUE(
487             i_unexpandedLocationCode.c_str()));
488 }
489 
490 types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
491     const std::string& i_unexpandedLocationCode,
492     [[maybe_unused]] const uint16_t i_nodeNumber)
493 {
494     types::ListOfPaths l_inventoryPaths;
495 
496     if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
497     {
498         phosphor::logging::elog<types::DbusInvalidArgument>(
499             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
500             types::InvalidArgument::ARGUMENT_VALUE(
501                 i_unexpandedLocationCode.c_str()));
502     }
503 
504     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
505     if (!l_sysCfgJsonObj.contains("frus"))
506     {
507         logging::logMessage("Missing frus tag in system config JSON");
508     }
509 
510     const nlohmann::json& l_listOfFrus =
511         l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
512 
513     for (const auto& l_frus : l_listOfFrus.items())
514     {
515         for (const auto& l_aFru : l_frus.value())
516         {
517             if (l_aFru["extraInterfaces"].contains(
518                     constants::locationCodeInf) &&
519                 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
520                     "LocationCode", "") == i_unexpandedLocationCode)
521             {
522                 l_inventoryPaths.push_back(
523                     l_aFru.at("inventoryPath")
524                         .get_ref<const nlohmann::json::string_t&>());
525             }
526         }
527     }
528 
529     if (l_inventoryPaths.empty())
530     {
531         phosphor::logging::elog<types::DbusInvalidArgument>(
532             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
533             types::InvalidArgument::ARGUMENT_VALUE(
534                 i_unexpandedLocationCode.c_str()));
535     }
536 
537     return l_inventoryPaths;
538 }
539 
540 std::string Manager::getHwPath(
541     const sdbusplus::message::object_path& i_dbusObjPath)
542 {
543     // Dummy code to supress unused variable warning. To be removed.
544     logging::logMessage(std::string(i_dbusObjPath));
545 
546     return std::string{};
547 }
548 
549 std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
550     const std::string& i_expandedLocationCode)
551 {
552     /**
553      * Location code should always start with U and fulfil minimum length
554      * criteria.
555      */
556     if (i_expandedLocationCode[0] != 'U' ||
557         i_expandedLocationCode.length() <
558             constants::EXP_LOCATION_CODE_MIN_LENGTH)
559     {
560         phosphor::logging::elog<types::DbusInvalidArgument>(
561             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
562             types::InvalidArgument::ARGUMENT_VALUE(
563                 i_expandedLocationCode.c_str()));
564     }
565 
566     std::string l_fcKwd;
567 
568     auto l_fcKwdValue = dbusUtility::readDbusProperty(
569         "xyz.openbmc_project.Inventory.Manager",
570         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
571         "com.ibm.ipzvpd.VCEN", "FC");
572 
573     if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
574     {
575         l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
576     }
577 
578     // Get the first part of expanded location code to check for FC or TM.
579     std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
580 
581     std::string l_unexpandedLocationCode{};
582     uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
583 
584     // Check if this value matches the value of FC keyword.
585     if (l_fcKwd.substr(0, 4) == l_firstKwd)
586     {
587         /**
588          * Period(.) should be there in expanded location code to seggregate
589          * FC, node number and SE values.
590          */
591         size_t l_nodeStartPos = i_expandedLocationCode.find('.');
592         if (l_nodeStartPos == std::string::npos)
593         {
594             phosphor::logging::elog<types::DbusInvalidArgument>(
595                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
596                 types::InvalidArgument::ARGUMENT_VALUE(
597                     i_expandedLocationCode.c_str()));
598         }
599 
600         size_t l_nodeEndPos =
601             i_expandedLocationCode.find('.', l_nodeStartPos + 1);
602         if (l_nodeEndPos == std::string::npos)
603         {
604             phosphor::logging::elog<types::DbusInvalidArgument>(
605                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
606                 types::InvalidArgument::ARGUMENT_VALUE(
607                     i_expandedLocationCode.c_str()));
608         }
609 
610         // Skip 3 bytes for '.ND'
611         l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
612             l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
613 
614         /**
615          * Confirm if there are other details apart FC, node number and SE
616          * in location code
617          */
618         if (i_expandedLocationCode.length() >
619             constants::EXP_LOCATION_CODE_MIN_LENGTH)
620         {
621             l_unexpandedLocationCode =
622                 i_expandedLocationCode[0] + std::string("fcs") +
623                 i_expandedLocationCode.substr(
624                     l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
625                     std::string::npos);
626         }
627         else
628         {
629             l_unexpandedLocationCode = "Ufcs";
630         }
631     }
632     else
633     {
634         std::string l_tmKwd;
635         // Read TM keyword value.
636         auto l_tmKwdValue = dbusUtility::readDbusProperty(
637             "xyz.openbmc_project.Inventory.Manager",
638             "/xyz/openbmc_project/inventory/system/chassis/motherboard",
639             "com.ibm.ipzvpd.VSYS", "TM");
640 
641         if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
642         {
643             l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
644         }
645 
646         // Check if the substr matches to TM keyword value.
647         if (l_tmKwd.substr(0, 4) == l_firstKwd)
648         {
649             /**
650              * System location code will not have node number and any other
651              * details.
652              */
653             l_unexpandedLocationCode = "Umts";
654         }
655         // The given location code is neither "fcs" or "mts".
656         else
657         {
658             phosphor::logging::elog<types::DbusInvalidArgument>(
659                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
660                 types::InvalidArgument::ARGUMENT_VALUE(
661                     i_expandedLocationCode.c_str()));
662         }
663     }
664 
665     return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
666 }
667 
668 types::ListOfPaths Manager::getFrusByExpandedLocationCode(
669     const std::string& i_expandedLocationCode)
670 {
671     std::tuple<std::string, uint16_t> l_locationAndNodePair =
672         getUnexpandedLocationCode(i_expandedLocationCode);
673 
674     return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
675                                            std::get<1>(l_locationAndNodePair));
676 }
677 
678 void Manager::performVpdRecollection()
679 {
680     if (m_worker.get() != nullptr)
681     {
682         m_worker->performVpdRecollection();
683     }
684 }
685 
686 bool Manager::collectAllFruVpd() const noexcept
687 {
688     try
689     {
690         types::SeverityType l_severityType;
691         if (m_vpdCollectionStatus == constants::vpdCollectionNotStarted)
692         {
693             l_severityType = types::SeverityType::Informational;
694         }
695         else if (m_vpdCollectionStatus == constants::vpdCollectionCompleted ||
696                  m_vpdCollectionStatus == constants::vpdCollectionFailed)
697         {
698             l_severityType = types::SeverityType::Warning;
699         }
700         else
701         {
702             throw std::runtime_error(
703                 "Invalid collection status " + m_vpdCollectionStatus +
704                 ". Aborting all FRUs VPD collection.");
705         }
706 
707         EventLogger::createSyncPel(
708             types::ErrorType::FirmwareError, l_severityType, __FILE__,
709             __FUNCTION__, 0, "Collect all FRUs VPD is requested.", std::nullopt,
710             std::nullopt, std::nullopt, std::nullopt);
711 
712 // ToDo: Handle with OEM interface
713 #ifdef IBM_SYSTEM
714         if (m_ibmHandler.get() != nullptr)
715         {
716             m_ibmHandler->collectAllFruVpd();
717             return true;
718         }
719         else
720         {
721             throw std::runtime_error(
722                 "Not found any OEM handler to collect all FRUs VPD.");
723         }
724 #endif
725     }
726     catch (const std::exception& l_ex)
727     {
728         EventLogger::createSyncPel(
729             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
730             __FILE__, __FUNCTION__, 0, std::string(l_ex.what()), std::nullopt,
731             std::nullopt, std::nullopt, std::nullopt);
732     }
733     return false;
734 }
735 } // namespace vpd
736