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/examples/inventory.json b/examples/inventory.json
index b5592cc..0101f21 100644
--- a/examples/inventory.json
+++ b/examples/inventory.json
@@ -26,6 +26,10 @@
     },
     "/sys/devices/path/to/bmc/eeprom": {
       "inventoryPath": "/bus/path/for/bmcfru",
+      "isReplacable": true,
+      "driverType": "at24",
+      "devAddress": "8-0051",
+      "busType": "i2c",
       "extraInterfaces": {
         "xyz.openbmc_project.Inventory.Item.Bmc": null
       }
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index ca1dd4d..fed83ad 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -280,48 +280,6 @@
     return vpdVector;
 }
 
-/* It does nothing. Just an empty function to return null
- * at the end of variadic template args
- */
-static string getCommand()
-{
-    return "";
-}
-
-/* This function to arrange all arguments to make command
- */
-template <typename T, typename... Types>
-static string getCommand(T arg1, Types... args)
-{
-    string cmd = " " + arg1 + getCommand(args...);
-
-    return cmd;
-}
-
-/* This API takes arguments and run that command
- * returns output of that command
- */
-template <typename T, typename... Types>
-static vector<string> executeCmd(T&& path, Types... args)
-{
-    vector<string> stdOutput;
-    array<char, 128> buffer;
-
-    string cmd = path + getCommand(args...);
-
-    unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
-    if (!pipe)
-    {
-        throw runtime_error("popen() failed!");
-    }
-    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
-    {
-        stdOutput.emplace_back(buffer.data());
-    }
-
-    return stdOutput;
-}
-
 /** This API will be called at the end of VPD collection to perform any post
  * actions.
  *
@@ -1031,6 +989,7 @@
 
             variant<KeywordVpdMap, Store> parseResult;
             parseResult = parser->parse();
+
             if (auto pVal = get_if<Store>(&parseResult))
             {
                 populateDbus(pVal->getVpdMap(), js, file);
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index 9b48021..9aaebee 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -113,5 +113,54 @@
  */
 constants::vpdType vpdTypeCheck(const Binary& vector);
 
+/*
+ * @brief This method does nothing. Just an empty function to return null
+ * at the end of variadic template args
+ */
+inline string getCommand()
+{
+    return "";
+}
+
+/**
+ * @brief This function to arrange all arguments to make commandy
+ * @param[in] arguments to create the command
+ * @return cmd - command string
+ */
+template <typename T, typename... Types>
+inline string getCommand(T arg1, Types... args)
+{
+    string cmd = " " + arg1 + getCommand(args...);
+
+    return cmd;
+}
+
+/**
+ * @brief This API takes arguments, creates a shell command line and executes
+ * them.
+ * @param[in] arguments for command
+ * @returns output of that command
+ */
+template <typename T, typename... Types>
+inline vector<string> executeCmd(T&& path, Types... args)
+{
+    vector<string> stdOutput;
+    array<char, 128> buffer;
+
+    string cmd = path + getCommand(args...);
+
+    unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
+    if (!pipe)
+    {
+        throw runtime_error("popen() failed!");
+    }
+    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
+    {
+        stdOutput.emplace_back(buffer.data());
+    }
+
+    return stdOutput;
+}
+
 } // namespace vpd
 } // namespace openpower
diff --git a/types.hpp b/types.hpp
index c297bf5..8c5c6e1 100644
--- a/types.hpp
+++ b/types.hpp
@@ -54,6 +54,7 @@
 using MapperResponse =
     std::map<Path, std::map<Service, std::vector<Interface>>>;
 using RestoredEeproms = std::tuple<Path, std::string, Keyword, Binary>;
+using ReplaceableFrus = std::vector<VPDfilepath>;
 } // namespace inventory
 
 } // namespace vpd
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
diff --git a/vpd-manager/manager.hpp b/vpd-manager/manager.hpp
index 6846c6e..41d3ad7 100644
--- a/vpd-manager/manager.hpp
+++ b/vpd-manager/manager.hpp
@@ -118,6 +118,12 @@
     /** @brief Start processing DBus messages. */
     void run();
 
+    /** @brief Api to perform VPD recollection.
+     * This api will trigger parser to perform VPD recollection for FRUs that
+     * can be replaced at standby.
+     */
+    void performVPDRecollection();
+
   private:
     /** @brief process the given JSON file
      */
@@ -138,6 +144,9 @@
 
     // map to hold the mapping of location code and inventory path
     inventory::LocationCodeMap fruLocationCode;
+
+    // map to hold FRUs which can be replaced at standby
+    inventory::ReplaceableFrus replaceableFrus;
 };
 
 } // namespace manager
diff --git a/vpd-manager/server.cpp b/vpd-manager/server.cpp
index 8291ecd..7ca72f6 100644
--- a/vpd-manager/server.cpp
+++ b/vpd-manager/server.cpp
@@ -4,10 +4,10 @@
 #include <map>
 #include <sdbusplus/exception.hpp>
 #include <sdbusplus/sdbus.hpp>
+#include <sdbusplus/sdbuspp_support/server.hpp>
 #include <sdbusplus/server.hpp>
 #include <string>
 #include <tuple>
-#include <variant>
 #include <xyz/openbmc_project/Common/error.hpp>
 
 namespace sdbusplus
@@ -30,55 +30,36 @@
 int Manager::_callback_WriteKeyword(sd_bus_message* msg, void* context,
                                     sd_bus_error* error)
 {
+    auto o = static_cast<Manager*>(context);
+
     try
     {
-        auto m = message::message(msg);
-#if 1
-        {
-            auto tbus = m.get_bus();
-            sdbusplus::server::transaction::Transaction t(tbus, m);
-            sdbusplus::server::transaction::set_id(
-                std::hash<sdbusplus::server::transaction::Transaction>{}(t));
-        }
-#endif
-
-        sdbusplus::message::object_path path{};
-        std::string record{};
-        std::string keyword{};
-        std::vector<uint8_t> value{};
-
-        m.read(path, record, keyword, value);
-
-        auto o = static_cast<Manager*>(context);
-        o->writeKeyword(path, record, keyword, value);
-
-        auto reply = m.new_method_return();
-        // No data to append on reply.
-
-        reply.method_return();
-    }
-    catch (sdbusplus::internal_exception_t& e)
-    {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return sdbusplus::sdbuspp::method_callback(
+            msg, o->_intf, error,
+            std::function([=](sdbusplus::message::object_path&& path,
+                              std::string&& record, std::string&& keyword,
+                              std::vector<uint8_t>&& value) {
+                return o->writeKeyword(path, record, keyword, value);
+            }));
     }
     catch (sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::PathNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::RecordNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::KeywordNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
 
-    return true;
+    return 0;
 }
 
 namespace details
@@ -97,49 +78,32 @@
                                                        void* context,
                                                        sd_bus_error* error)
 {
+    auto o = static_cast<Manager*>(context);
+
     try
     {
-        auto m = message::message(msg);
-#if 1
-        {
-            auto tbus = m.get_bus();
-            sdbusplus::server::transaction::Transaction t(tbus, m);
-            sdbusplus::server::transaction::set_id(
-                std::hash<sdbusplus::server::transaction::Transaction>{}(t));
-        }
-#endif
-
-        std::string locationCode{};
-        uint16_t nodeNumber{};
-
-        m.read(locationCode, nodeNumber);
-
-        auto o = static_cast<Manager*>(context);
-        auto r = o->getFRUsByUnexpandedLocationCode(locationCode, nodeNumber);
-
-        auto reply = m.new_method_return();
-        reply.append(std::move(r));
-
-        reply.method_return();
-    }
-    catch (sdbusplus::internal_exception_t& e)
-    {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return sdbusplus::sdbuspp::method_callback(
+            msg, o->_intf, error,
+            std::function(
+                [=](std::string&& locationCode, uint16_t&& nodeNumber) {
+                    return o->getFRUsByUnexpandedLocationCode(locationCode,
+                                                              nodeNumber);
+                }));
     }
     catch (sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::LocationNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::NodeNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
 
-    return true;
+    return 0;
 }
 
 namespace details
@@ -158,48 +122,30 @@
                                                      void* context,
                                                      sd_bus_error* error)
 {
+    auto o = static_cast<Manager*>(context);
+
     try
     {
-        auto m = message::message(msg);
-#if 1
-        {
-            auto tbus = m.get_bus();
-            sdbusplus::server::transaction::Transaction t(tbus, m);
-            sdbusplus::server::transaction::set_id(
-                std::hash<sdbusplus::server::transaction::Transaction>{}(t));
-        }
-#endif
-
-        std::string locationCode{};
-
-        m.read(locationCode);
-
-        auto o = static_cast<Manager*>(context);
-        auto r = o->getFRUsByExpandedLocationCode(locationCode);
-
-        auto reply = m.new_method_return();
-        reply.append(std::move(r));
-
-        reply.method_return();
-    }
-    catch (sdbusplus::internal_exception_t& e)
-    {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return sdbusplus::sdbuspp::method_callback(
+            msg, o->_intf, error,
+            std::function([=](std::string&& locationCode) {
+                return o->getFRUsByExpandedLocationCode(locationCode);
+            }));
     }
     catch (sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::LocationNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::NodeNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
 
-    return true;
+    return 0;
 }
 
 namespace details
@@ -218,49 +164,31 @@
                                                void* context,
                                                sd_bus_error* error)
 {
+    auto o = static_cast<Manager*>(context);
+
     try
     {
-        auto m = message::message(msg);
-#if 1
-        {
-            auto tbus = m.get_bus();
-            sdbusplus::server::transaction::Transaction t(tbus, m);
-            sdbusplus::server::transaction::set_id(
-                std::hash<sdbusplus::server::transaction::Transaction>{}(t));
-        }
-#endif
-
-        std::string locationCode{};
-        uint16_t nodeNumber{};
-
-        m.read(locationCode, nodeNumber);
-
-        auto o = static_cast<Manager*>(context);
-        auto r = o->getExpandedLocationCode(locationCode, nodeNumber);
-
-        auto reply = m.new_method_return();
-        reply.append(std::move(r));
-
-        reply.method_return();
-    }
-    catch (sdbusplus::internal_exception_t& e)
-    {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return sdbusplus::sdbuspp::method_callback(
+            msg, o->_intf, error,
+            std::function(
+                [=](std::string&& locationCode, uint16_t&& nodeNumber) {
+                    return o->getExpandedLocationCode(locationCode, nodeNumber);
+                }));
     }
     catch (sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::LocationNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
     catch (sdbusplus::com::ibm::VPD::Error::NodeNotFound& e)
     {
-        return sd_bus_error_set(error, e.name(), e.description());
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
     }
 
-    return true;
+    return 0;
 }
 
 namespace details
@@ -274,6 +202,37 @@
 } // namespace Manager
 } // namespace details
 
+int Manager::_callback_PerformVPDRecollection(sd_bus_message* msg,
+                                              void* context,
+                                              sd_bus_error* error)
+{
+    auto o = static_cast<Manager*>(context);
+
+    try
+    {
+        return sdbusplus::sdbuspp::method_callback(
+            msg, o->_intf, error,
+            std::function([=]() { return o->performVPDRecollection(); }));
+    }
+    catch (sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument& e)
+    {
+        return o->_intf->sd_bus_error_set(error, e.name(), e.description());
+    }
+
+    return 0;
+}
+
+namespace details
+{
+namespace Manager
+{
+static const auto _param_PerformVPDRecollection =
+    utility::tuple_to_array(std::make_tuple('\0'));
+static const auto _return_PerformVPDRecollection =
+    utility::tuple_to_array(std::make_tuple('\0'));
+} // namespace Manager
+} // namespace details
+
 const vtable::vtable_t Manager::_vtable[] = {
     vtable::start(),
 
@@ -297,6 +256,11 @@
                    details::Manager::_param_GetExpandedLocationCode.data(),
                    details::Manager::_return_GetExpandedLocationCode.data(),
                    _callback_GetExpandedLocationCode),
+
+    vtable::method("PerformVPDRecollection",
+                   details::Manager::_param_PerformVPDRecollection.data(),
+                   details::Manager::_return_PerformVPDRecollection.data(),
+                   _callback_PerformVPDRecollection),
     vtable::end()};
 
 } // namespace server