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