Implement VPD recollection

This commit implements functionality to recollect VPD for a hardware
by triggering VPD parser for that hardware in case there is a
replacement.

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: Ia0d377b554299faac3b46a4dc8dd96f964f07bd2
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index feb6115..42b1bbd 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -6,11 +6,18 @@
 #include "ibm_vpd_utils.hpp"
 #include "ipz_parser.hpp"
 #include "reader_impl.hpp"
+#include "vpd_exceptions.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
 
 using namespace openpower::vpd::constants;
 using namespace openpower::vpd::inventory;
 using namespace openpower::vpd::manager::editor;
 using namespace openpower::vpd::manager::reader;
+using namespace std;
+using namespace openpower::vpd::parser;
+using namespace openpower::vpd::exceptions;
+using namespace phosphor::logging;
 
 namespace openpower
 {
@@ -90,6 +97,11 @@
                     itemEEPROM["inventoryPath"]
                         .get_ref<const nlohmann::json::string_t&>());
             }
+
+            if (itemEEPROM.value("isReplaceable", false))
+            {
+                replaceableFrus.emplace_back(itemFRUS.key());
+            }
         }
     }
 }
@@ -156,6 +168,80 @@
                                         fruLocationCode);
 }
 
+void Manager::performVPDRecollection()
+{
+    // get list of FRUs replaceable at standby
+    for (const auto& item : replaceableFrus)
+    {
+        const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item];
+        const nlohmann::json& singleFru = groupEEPROM[0];
+
+        const string& inventoryPath =
+            singleFru["inventoryPath"]
+                .get_ref<const nlohmann::json::string_t&>();
+
+        if ((singleFru.find("devAddress") == singleFru.end()) ||
+            (singleFru.find("driverType") == singleFru.end()) ||
+            (singleFru.find("busType") == singleFru.end()))
+        {
+            // The FRUs is marked for replacement but missing mandatory
+            // fields for recollection. Skip to another replaceable fru.
+            log<level::ERR>(
+                "Recollection Failed as mandatory field missing in Json",
+                entry("ERROR=%s",
+                      ("Recollection failed for " + inventoryPath).c_str()));
+            continue;
+        }
+
+        string str = "echo ";
+        string deviceAddress = singleFru["devAddress"];
+        const string& driverType = singleFru["driverType"];
+        const string& busType = singleFru["busType"];
+
+        // devTreeStatus flag is present in json as false to mention
+        // that the EEPROM is not mentioned in device tree. If this flag
+        // is absent consider the value to be true, i.e EEPROM is
+        // mentioned in device tree
+        if (!singleFru.value("devTreeStatus", true))
+        {
+            auto pos = deviceAddress.find('-');
+            if (pos != string::npos)
+            {
+                string busNum = deviceAddress.substr(0, pos);
+                deviceAddress =
+                    "0x" + deviceAddress.substr(pos + 1, string::npos);
+
+                string deleteDevice = str + deviceAddress + " > /sys/bus/" +
+                                      busType + "/devices/" + busType + "-" +
+                                      busNum + "/delete_device";
+                executeCmd(deleteDevice);
+
+                string addDevice = str + driverType + " " + deviceAddress +
+                                   " > /sys/bus/" + busType + "/devices/" +
+                                   busType + "-" + busNum + "/new_device";
+                executeCmd(addDevice);
+            }
+            else
+            {
+                log<level::ERR>(
+                    "Wrong format of device address in Json",
+                    entry(
+                        "ERROR=%s",
+                        ("Recollection failed for " + inventoryPath).c_str()));
+                continue;
+            }
+        }
+        else
+        {
+            string cmd = str + deviceAddress + " > /sys/bus/" + busType +
+                         "/drivers/" + driverType;
+
+            executeCmd(cmd + "/unbind");
+            executeCmd(cmd + "/bind");
+        }
+    }
+}
+
 } // namespace manager
 } // namespace vpd
 } // namespace openpower