xref: /openbmc/openpower-vpd-parser/vpd-manager/manager.cpp (revision 6555e7efc40c305e5c3c8863c3d1793f474ec7e2)
1 #include "config.h"
2 
3 #include "manager.hpp"
4 
5 #include "common_utility.hpp"
6 #include "editor_impl.hpp"
7 #include "ibm_vpd_utils.hpp"
8 #include "ipz_parser.hpp"
9 #include "parser_factory.hpp"
10 #include "reader_impl.hpp"
11 #include "vpd_exceptions.hpp"
12 
13 #include <filesystem>
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <xyz/openbmc_project/Common/error.hpp>
16 
17 using namespace openpower::vpd::constants;
18 using namespace openpower::vpd::inventory;
19 using namespace openpower::vpd::manager::editor;
20 using namespace openpower::vpd::manager::reader;
21 using namespace std;
22 using namespace openpower::vpd::parser;
23 using namespace openpower::vpd::parser::factory;
24 using namespace openpower::vpd::ipz::parser;
25 using namespace openpower::vpd::exceptions;
26 using namespace phosphor::logging;
27 
28 namespace openpower
29 {
30 namespace vpd
31 {
32 namespace manager
33 {
34 Manager::Manager(std::shared_ptr<boost::asio::io_context>& ioCon,
35                  std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
36                  std::shared_ptr<sdbusplus::asio::connection>& conn) :
37     ioContext(ioCon),
38     interface(iFace), conn(conn)
39 {
40     interface->register_method(
41         "WriteKeyword",
42         [this](const sdbusplus::message::object_path& path,
43                const std::string& recordName, const std::string& keyword,
44                const Binary& value) {
45             this->writeKeyword(path, recordName, keyword, value);
46         });
47 
48     interface->register_method(
49         "GetFRUsByUnexpandedLocationCode",
50         [this](const std::string& locationCode,
51                const uint16_t nodeNumber) -> inventory::ListOfPaths {
52             return this->getFRUsByUnexpandedLocationCode(locationCode,
53                                                          nodeNumber);
54         });
55 
56     interface->register_method(
57         "GetFRUsByExpandedLocationCode",
58         [this](const std::string& locationCode) -> inventory::ListOfPaths {
59             return this->getFRUsByExpandedLocationCode(locationCode);
60         });
61 
62     interface->register_method(
63         "GetExpandedLocationCode",
64         [this](const std::string& locationCode,
65                const uint16_t nodeNumber) -> std::string {
66             return this->getExpandedLocationCode(locationCode, nodeNumber);
67         });
68 
69     interface->register_method("PerformVPDRecollection",
70                                [this]() { this->performVPDRecollection(); });
71 
72     interface->register_method(
73         "deleteFRUVPD", [this](const sdbusplus::message::object_path& path) {
74             this->deleteFRUVPD(path);
75         });
76 
77     interface->register_method(
78         "CollectFRUVPD", [this](const sdbusplus::message::object_path& path) {
79             this->collectFRUVPD(path);
80         });
81 
82     sd_bus_default(&sdBus);
83     initManager();
84 }
85 
86 void Manager::initManager()
87 {
88     try
89     {
90         processJSON();
91         restoreSystemVpd();
92         listenHostState();
93         listenAssetTag();
94 
95         // Create an instance of the BIOS handler
96         biosHandler = std::make_shared<BiosHandler>(conn, *this);
97 
98         // instantiate gpioMonitor class
99         gpioMon = std::make_shared<GpioMonitor>(jsonFile, ioContext);
100     }
101     catch (const std::exception& e)
102     {
103         std::cerr << e.what() << "\n";
104     }
105 }
106 
107 /**
108  * @brief An api to get list of blank system VPD properties.
109  * @param[in] vpdMap - IPZ vpd map.
110  * @param[in] objectPath - Object path for the FRU.
111  * @param[out] blankPropertyList - Properties which are blank in System VPD and
112  * needs to be updated as standby.
113  */
114 static void
115     getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
116                             std::vector<RestoredEeproms>& blankPropertyList)
117 {
118     for (const auto& systemRecKwdPair : svpdKwdMap)
119     {
120         auto it = vpdMap.find(systemRecKwdPair.first);
121 
122         // check if record is found in map we got by parser
123         if (it != vpdMap.end())
124         {
125             const auto& kwdListForRecord = systemRecKwdPair.second;
126             for (const auto& keywordInfo : kwdListForRecord)
127             {
128                 const auto& keyword = get<0>(keywordInfo);
129 
130                 DbusPropertyMap& kwdValMap = it->second;
131                 auto iterator = kwdValMap.find(keyword);
132 
133                 if (iterator != kwdValMap.end())
134                 {
135                     string& kwdValue = iterator->second;
136 
137                     // check bus data
138                     const string& recordName = systemRecKwdPair.first;
139                     const string& busValue = readBusProperty(
140                         objectPath, ipzVpdInf + recordName, keyword);
141 
142                     if (busValue.find_first_not_of(' ') != string::npos)
143                     {
144                         if (kwdValue.find_first_not_of(' ') == string::npos)
145                         {
146                             // implies data is blank on EEPROM but not on cache.
147                             // So EEPROM vpd update is required.
148                             Binary busData(busValue.begin(), busValue.end());
149 
150                             blankPropertyList.push_back(std::make_tuple(
151                                 objectPath, recordName, keyword, busData));
152                         }
153                     }
154                 }
155             }
156         }
157     }
158 }
159 
160 void Manager::restoreSystemVpd()
161 {
162     std::cout << "Attempting system VPD restore" << std::endl;
163     ParserInterface* parser = nullptr;
164     try
165     {
166         auto vpdVector = getVpdDataInVector(jsonFile, systemVpdFilePath);
167         const auto& inventoryPath =
168             jsonFile["frus"][systemVpdFilePath][0]["inventoryPath"]
169                 .get_ref<const nlohmann::json::string_t&>();
170 
171         parser = ParserFactory::getParser(vpdVector, (pimPath + inventoryPath));
172         auto parseResult = parser->parse();
173 
174         if (auto pVal = std::get_if<Store>(&parseResult))
175         {
176             // map to hold all the keywords whose value is blank and
177             // needs to be updated at standby.
178             std::vector<RestoredEeproms> blankSystemVpdProperties{};
179             getListOfBlankSystemVpd(pVal->getVpdMap(), SYSTEM_OBJECT,
180                                     blankSystemVpdProperties);
181 
182             // if system VPD restore is required, update the
183             // EEPROM
184             for (const auto& item : blankSystemVpdProperties)
185             {
186                 std::cout << "Restoring keyword: " << std::get<2>(item)
187                           << std::endl;
188                 writeKeyword(std::get<0>(item), std::get<1>(item),
189                              std::get<2>(item), std::get<3>(item));
190             }
191         }
192         else
193         {
194             std::cerr << "Not a valid format to restore system VPD"
195                       << std::endl;
196         }
197     }
198     catch (const std::exception& e)
199     {
200         std::cerr << "Failed to restore system VPD due to exception: "
201                   << e.what() << std::endl;
202     }
203     // release the parser object
204     ParserFactory::freeParser(parser);
205 }
206 
207 void Manager::listenHostState()
208 {
209     static std::shared_ptr<sdbusplus::bus::match_t> hostState =
210         std::make_shared<sdbusplus::bus::match_t>(
211             *conn,
212             sdbusplus::bus::match::rules::propertiesChanged(
213                 "/xyz/openbmc_project/state/host0",
214                 "xyz.openbmc_project.State.Host"),
215             [this](sdbusplus::message_t& msg) { hostStateCallBack(msg); });
216 }
217 
218 void Manager::checkEssentialFrus()
219 {
220     for (const auto& invPath : essentialFrus)
221     {
222         const auto res = readBusProperty(invPath, invItemIntf, "Present");
223 
224         // implies the essential FRU is missing. Log PEL.
225         if (res == "false")
226         {
227             auto rc = sd_bus_call_method_async(
228                 sdBus, NULL, loggerService, loggerObjectPath,
229                 loggerCreateInterface, "Create", NULL, NULL, "ssa{ss}",
230                 errIntfForEssentialFru,
231                 "xyz.openbmc_project.Logging.Entry.Level.Warning", 2,
232                 "DESCRIPTION", "Essential fru missing from the system.",
233                 "CALLOUT_INVENTORY_PATH", (pimPath + invPath).c_str());
234 
235             if (rc < 0)
236             {
237                 log<level::ERR>("Error calling sd_bus_call_method_async",
238                                 entry("RC=%d", rc),
239                                 entry("MSG=%s", strerror(-rc)));
240             }
241         }
242     }
243 }
244 
245 void Manager::hostStateCallBack(sdbusplus::message_t& msg)
246 {
247     if (msg.is_method_error())
248     {
249         std::cerr << "Error in reading signal " << std::endl;
250     }
251 
252     Path object;
253     PropertyMap propMap;
254     msg.read(object, propMap);
255     const auto itr = propMap.find("CurrentHostState");
256     if (itr != propMap.end())
257     {
258         if (auto hostState = std::get_if<std::string>(&(itr->second)))
259         {
260             // implies system is moving from standby to power on state
261             if (*hostState == "xyz.openbmc_project.State.Host.HostState."
262                               "TransitioningToRunning")
263             {
264                 // detect if essential frus are present in the system.
265                 checkEssentialFrus();
266 
267                 // check and perfrom recollection for FRUs replaceable at
268                 // standby.
269                 performVPDRecollection();
270                 return;
271             }
272         }
273     }
274 }
275 
276 void Manager::listenAssetTag()
277 {
278     static std::shared_ptr<sdbusplus::bus::match_t> assetMatcher =
279         std::make_shared<sdbusplus::bus::match_t>(
280             *conn,
281             sdbusplus::bus::match::rules::propertiesChanged(
282                 "/xyz/openbmc_project/inventory/system",
283                 "xyz.openbmc_project.Inventory.Decorator.AssetTag"),
284             [this](sdbusplus::message_t& msg) { assetTagCallback(msg); });
285 }
286 
287 void Manager::assetTagCallback(sdbusplus::message_t& msg)
288 {
289     if (msg.is_method_error())
290     {
291         std::cerr << "Error in reading signal " << std::endl;
292     }
293 
294     Path object;
295     PropertyMap propMap;
296     msg.read(object, propMap);
297     const auto itr = propMap.find("AssetTag");
298     if (itr != propMap.end())
299     {
300         if (auto assetTag = std::get_if<std::string>(&(itr->second)))
301         {
302             // Call Notify to persist the AssetTag
303             inventory::ObjectMap objectMap = {
304                 {std::string{"/system"},
305                  {{"xyz.openbmc_project.Inventory.Decorator.AssetTag",
306                    {{"AssetTag", *assetTag}}}}}};
307 
308             common::utility::callPIM(std::move(objectMap));
309         }
310         else
311         {
312             std::cerr << "Failed to read asset tag" << std::endl;
313         }
314     }
315 }
316 
317 void Manager::processJSON()
318 {
319     std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary);
320 
321     if (!json)
322     {
323         throw std::runtime_error("json file not found");
324     }
325 
326     jsonFile = nlohmann::json::parse(json);
327     if (jsonFile.find("frus") == jsonFile.end())
328     {
329         throw std::runtime_error("frus group not found in json");
330     }
331 
332     const nlohmann::json& groupFRUS =
333         jsonFile["frus"].get_ref<const nlohmann::json::object_t&>();
334     for (const auto& itemFRUS : groupFRUS.items())
335     {
336         const std::vector<nlohmann::json>& groupEEPROM =
337             itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
338         for (const auto& itemEEPROM : groupEEPROM)
339         {
340             bool isMotherboard = false;
341             std::string redundantPath;
342 
343             if (itemEEPROM["extraInterfaces"].find(
344                     "xyz.openbmc_project.Inventory.Item.Board.Motherboard") !=
345                 itemEEPROM["extraInterfaces"].end())
346             {
347                 isMotherboard = true;
348             }
349             if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end())
350             {
351                 redundantPath = itemEEPROM["redundantEeprom"]
352                                     .get_ref<const nlohmann::json::string_t&>();
353             }
354             frus.emplace(
355                 itemEEPROM["inventoryPath"]
356                     .get_ref<const nlohmann::json::string_t&>(),
357                 std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard));
358 
359             if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
360                 itemEEPROM["extraInterfaces"].end())
361             {
362                 fruLocationCode.emplace(
363                     itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF]
364                               ["LocationCode"]
365                                   .get_ref<const nlohmann::json::string_t&>(),
366                     itemEEPROM["inventoryPath"]
367                         .get_ref<const nlohmann::json::string_t&>());
368             }
369 
370             if (itemEEPROM.value("replaceableAtStandby", false))
371             {
372                 replaceableFrus.emplace_back(itemFRUS.key());
373             }
374 
375             if (itemEEPROM.value("essentialFru", false))
376             {
377                 essentialFrus.emplace_back(itemEEPROM["inventoryPath"]);
378             }
379         }
380     }
381 }
382 
383 void Manager::writeKeyword(const sdbusplus::message::object_path& path,
384                            const std::string& recordName,
385                            const std::string& keyword, const Binary& value)
386 {
387     try
388     {
389         std::string objPath{path};
390         // Strip any inventory prefix in path
391         if (objPath.find(INVENTORY_PATH) == 0)
392         {
393             objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
394         }
395 
396         if (frus.find(objPath) == frus.end())
397         {
398             throw std::runtime_error("Inventory path not found");
399         }
400 
401         inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
402 
403         // instantiate editor class to update the data
404         EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath);
405 
406         uint32_t offset = 0;
407         // Setup offset, if any
408         for (const auto& item : jsonFile["frus"][vpdFilePath])
409         {
410             if (item.find("offset") != item.end())
411             {
412                 offset = item["offset"];
413                 break;
414             }
415         }
416 
417         edit.updateKeyword(value, offset, true);
418 
419         // If we have a redundant EEPROM to update, then update just the EEPROM,
420         // not the cache since that is already done when we updated the primary
421         if (!std::get<1>(frus.find(objPath)->second).empty())
422         {
423             EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile,
424                             recordName, keyword, objPath);
425             edit.updateKeyword(value, offset, false);
426         }
427 
428         // if it is a motehrboard FRU need to check for location expansion
429         if (std::get<2>(frus.find(objPath)->second))
430         {
431             if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE"))
432             {
433                 edit.expandLocationCode("fcs");
434             }
435             else if (recordName == "VSYS" &&
436                      (keyword == "TM" || keyword == "SE"))
437             {
438                 edit.expandLocationCode("mts");
439             }
440         }
441 
442         return;
443     }
444     catch (const std::exception& e)
445     {
446         std::cerr << e.what() << std::endl;
447     }
448 }
449 
450 ListOfPaths
451     Manager::getFRUsByUnexpandedLocationCode(const LocationCode& locationCode,
452                                              const NodeNumber nodeNumber)
453 {
454     ReaderImpl read;
455     return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode);
456 }
457 
458 ListOfPaths
459     Manager::getFRUsByExpandedLocationCode(const LocationCode& locationCode)
460 {
461     ReaderImpl read;
462     return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode);
463 }
464 
465 LocationCode Manager::getExpandedLocationCode(const LocationCode& locationCode,
466                                               const NodeNumber nodeNumber)
467 {
468     ReaderImpl read;
469     return read.getExpandedLocationCode(locationCode, nodeNumber,
470                                         fruLocationCode);
471 }
472 
473 void Manager::performVPDRecollection()
474 {
475     // get list of FRUs replaceable at standby
476     for (const auto& item : replaceableFrus)
477     {
478         const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item];
479         const nlohmann::json& singleFru = groupEEPROM[0];
480 
481         const string& inventoryPath =
482             singleFru["inventoryPath"]
483                 .get_ref<const nlohmann::json::string_t&>();
484 
485         bool prePostActionRequired = false;
486 
487         if ((jsonFile["frus"][item].at(0)).find("preAction") !=
488             jsonFile["frus"][item].at(0).end())
489         {
490             try
491             {
492                 if (!executePreAction(jsonFile, item))
493                 {
494                     // if the FRU has preAction defined then its execution
495                     // should pass to ensure bind/unbind of data.
496                     // preAction execution failed. should not call
497                     // bind/unbind.
498                     log<level::ERR>(
499                         "Pre-Action execution failed for the FRU",
500                         entry("ERROR=%s",
501                               ("Inventory path: " + inventoryPath).c_str()));
502                     continue;
503                 }
504             }
505             catch (const GpioException& e)
506             {
507                 log<level::ERR>(e.what());
508                 PelAdditionalData additionalData{};
509                 additionalData.emplace("DESCRIPTION", e.what());
510                 createPEL(additionalData, PelSeverity::WARNING,
511                           errIntfForGpioError, sdBus);
512                 continue;
513             }
514             prePostActionRequired = true;
515         }
516 
517         // unbind, bind the driver to trigger parser.
518         triggerVpdCollection(singleFru, inventoryPath);
519 
520         // this check is added to avoid file system expensive call in case not
521         // required.
522         if (prePostActionRequired)
523         {
524             // Check if device showed up (test for file)
525             if (!filesystem::exists(item))
526             {
527                 try
528                 {
529                     // If not, then take failure postAction
530                     executePostFailAction(jsonFile, item);
531                 }
532                 catch (const GpioException& e)
533                 {
534                     PelAdditionalData additionalData{};
535                     additionalData.emplace("DESCRIPTION", e.what());
536                     createPEL(additionalData, PelSeverity::WARNING,
537                               errIntfForGpioError, sdBus);
538                 }
539             }
540             else
541             {
542                 // bind the LED driver
543                 string chipAddr = singleFru.value("pcaChipAddress", "");
544                 cout << "performVPDRecollection: Executing driver binding for "
545                         "chip "
546                         "address - "
547                      << chipAddr << endl;
548 
549                 executeCmd(createBindUnbindDriverCmnd(chipAddr, "i2c",
550                                                       "leds-pca955x", "/bind"));
551             }
552         }
553     }
554 }
555 
556 void Manager::collectFRUVPD(const sdbusplus::message::object_path& path)
557 {
558     std::cout << "Manager called to collect vpd for fru: " << std::string{path}
559               << std::endl;
560 
561     using InvalidArgument =
562         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
563     using Argument = xyz::openbmc_project::Common::InvalidArgument;
564 
565     std::string objPath{path};
566 
567     // Strip any inventory prefix in path
568     if (objPath.find(INVENTORY_PATH) == 0)
569     {
570         objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
571     }
572 
573     // if path not found in Json.
574     if (frus.find(objPath) == frus.end())
575     {
576         elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"),
577                               Argument::ARGUMENT_VALUE(objPath.c_str()));
578     }
579 
580     inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
581 
582     const std::vector<nlohmann::json>& groupEEPROM =
583         jsonFile["frus"][vpdFilePath].get_ref<const nlohmann::json::array_t&>();
584 
585     nlohmann::json singleFru{};
586     for (const auto& item : groupEEPROM)
587     {
588         if (item["inventoryPath"] == objPath)
589         {
590             // this is the inventory we are looking for
591             singleFru = item;
592             break;
593         }
594     }
595 
596     // check if the device qualifies for CM.
597     if (singleFru.value("concurrentlyMaintainable", false))
598     {
599         bool prePostActionRequired = false;
600 
601         if ((jsonFile["frus"][vpdFilePath].at(0)).find("preAction") !=
602             jsonFile["frus"][vpdFilePath].at(0).end())
603         {
604             if (!executePreAction(jsonFile, vpdFilePath))
605             {
606                 // if the FRU has preAction defined then its execution should
607                 // pass to ensure bind/unbind of data.
608                 // preAction execution failed. should not call bind/unbind.
609                 log<level::ERR>("Pre-Action execution failed for the FRU");
610                 return;
611             }
612 
613             prePostActionRequired = true;
614         }
615 
616         // unbind, bind the driver to trigger parser.
617         triggerVpdCollection(singleFru, objPath);
618 
619         // this check is added to avoid file system expensive call in case not
620         // required.
621         if (prePostActionRequired)
622         {
623             // Check if device showed up (test for file)
624             if (!filesystem::exists(vpdFilePath))
625             {
626                 try
627                 {
628                     // If not, then take failure postAction
629                     executePostFailAction(jsonFile, vpdFilePath);
630                 }
631                 catch (const GpioException& e)
632                 {
633                     PelAdditionalData additionalData{};
634                     additionalData.emplace("DESCRIPTION", e.what());
635                     createPEL(additionalData, PelSeverity::WARNING,
636                               errIntfForGpioError, sdBus);
637                 }
638             }
639             else
640             {
641                 // bind the LED driver
642                 string chipAddr = jsonFile["frus"][vpdFilePath].at(0).value(
643                     "pcaChipAddress", "");
644                 cout << "Executing driver binding for chip address - "
645                      << chipAddr << endl;
646 
647                 executeCmd(createBindUnbindDriverCmnd(chipAddr, "i2c",
648                                                       "leds-pca955x", "/bind"));
649             }
650         }
651         return;
652     }
653     else
654     {
655         elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"),
656                               Argument::ARGUMENT_VALUE(objPath.c_str()));
657     }
658 }
659 
660 void Manager::triggerVpdCollection(const nlohmann::json& singleFru,
661                                    const std::string& path)
662 {
663     if ((singleFru.find("devAddress") == singleFru.end()) ||
664         (singleFru.find("driverType") == singleFru.end()) ||
665         (singleFru.find("busType") == singleFru.end()))
666     {
667         // The FRUs is marked for collection but missing mandatory
668         // fields for collection. Log error and return.
669         log<level::ERR>(
670             "Collection Failed as mandatory field missing in Json",
671             entry("ERROR=%s", ("Recollection failed for " + (path)).c_str()));
672 
673         return;
674     }
675 
676     string deviceAddress = singleFru["devAddress"];
677     const string& driverType = singleFru["driverType"];
678     const string& busType = singleFru["busType"];
679 
680     // devTreeStatus flag is present in json as false to mention
681     // that the EEPROM is not mentioned in device tree. If this flag
682     // is absent consider the value to be true, i.e EEPROM is
683     // mentioned in device tree
684     if (!singleFru.value("devTreeStatus", true))
685     {
686         auto pos = deviceAddress.find('-');
687         if (pos != string::npos)
688         {
689             string busNum = deviceAddress.substr(0, pos);
690             deviceAddress = "0x" + deviceAddress.substr(pos + 1, string::npos);
691 
692             string deleteDevice = "echo" + deviceAddress + " > /sys/bus/" +
693                                   busType + "/devices/" + busType + "-" +
694                                   busNum + "/delete_device";
695             executeCmd(deleteDevice);
696 
697             string addDevice = "echo" + driverType + " " + deviceAddress +
698                                " > /sys/bus/" + busType + "/devices/" +
699                                busType + "-" + busNum + "/new_device";
700             executeCmd(addDevice);
701         }
702         else
703         {
704             const string& inventoryPath =
705                 singleFru["inventoryPath"]
706                     .get_ref<const nlohmann::json::string_t&>();
707 
708             log<level::ERR>(
709                 "Wrong format of device address in Json",
710                 entry("ERROR=%s",
711                       ("Recollection failed for " + inventoryPath).c_str()));
712         }
713     }
714     else
715     {
716         executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
717                                               driverType, "/unbind"));
718         executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
719                                               driverType, "/bind"));
720     }
721 }
722 
723 void Manager::deleteFRUVPD(const sdbusplus::message::object_path& path)
724 {
725     std::cout << "Manager called to delete vpd for fru: " << std::string{path}
726               << std::endl;
727 
728     using InvalidArgument =
729         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
730     using Argument = xyz::openbmc_project::Common::InvalidArgument;
731 
732     std::string objPath{path};
733 
734     // Strip any inventory prefix in path
735     if (objPath.find(INVENTORY_PATH) == 0)
736     {
737         objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
738     }
739 
740     // if path not found in Json.
741     if (frus.find(objPath) == frus.end())
742     {
743         elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"),
744                               Argument::ARGUMENT_VALUE(objPath.c_str()));
745     }
746 
747     inventory::Path& vpdFilePath = std::get<0>(frus.find(objPath)->second);
748 
749     string chipAddress =
750         jsonFile["frus"][vpdFilePath].at(0).value("pcaChipAddress", "");
751 
752     // Unbind the LED driver for this FRU
753     cout << "Unbinding device- " << chipAddress << endl;
754     executeCmd(createBindUnbindDriverCmnd(chipAddress, "i2c", "leds-pca955x",
755                                           "/unbind"));
756 
757     // if the FRU is not present then log error
758     if (readBusProperty(objPath, "xyz.openbmc_project.Inventory.Item",
759                         "Present") == "false")
760     {
761         elog<InvalidArgument>(Argument::ARGUMENT_NAME("FRU not preset"),
762                               Argument::ARGUMENT_VALUE(objPath.c_str()));
763     }
764     else
765     {
766         // Set present property of FRU as false as it has been removed.
767         // CC data for FRU is also removed as
768         // a) FRU is not there so CC does not make sense.
769         // b) Sensors dependent on Panel uses CC data.
770         inventory::InterfaceMap interfaces{
771             {"xyz.openbmc_project.Inventory.Item", {{"Present", false}}},
772             {"com.ibm.ipzvpd.VINI", {{"CC", Binary{}}}}};
773 
774         inventory::ObjectMap objectMap;
775         objectMap.emplace(objPath, move(interfaces));
776 
777         common::utility::callPIM(move(objectMap));
778     }
779 }
780 
781 } // namespace manager
782 } // namespace vpd
783 } // namespace openpower