Prime FRUs if only required

As vpd-manager primes the FRUs listed in the system config JSON on each
system boot, which takes significant time.

To improve the efficiency, this commit implements priming of FRUs only
if the Dbus object paths having “com.ibm.VPD.Collection" interface
count does not match with the inventoryPaths count from the system
config JSON.

Change-Id: I506503b8e8ad64342352a9aae3c889b9fd5a22d8
Signed-off-by: Anupama B R <anupama.b.r1@ibm.com>
diff --git a/vpd-manager/include/utility/dbus_utility.hpp b/vpd-manager/include/utility/dbus_utility.hpp
index e2bf6c1..3d0ba61 100644
--- a/vpd-manager/include/utility/dbus_utility.hpp
+++ b/vpd-manager/include/utility/dbus_utility.hpp
@@ -711,5 +711,49 @@
 
     return (*l_ptrPresence);
 }
+
+/**
+ * @brief API to get list of sub tree paths for a given object path
+ *
+ * Given a DBus object path, this API returns a list of object paths under that
+ * object path in the DBus tree. This API calls DBus method GetSubTreePaths
+ * hosted by ObjectMapper DBus service.
+ *
+ * @param[in] i_objectPath - DBus object path.
+ * @param[in] i_depth - The maximum subtree depth for which results should be
+ * fetched. For unconstrained fetches use a depth of zero.
+ * @param[in] i_constrainingInterfaces - An array of result set constraining
+ * interfaces.
+ *
+ * @return On success, returns a std::vector<std::string> of object paths,
+ * else returns an empty vector.
+ *
+ * Note: The caller of this API should check for empty vector.
+ */
+inline std::vector<std::string> GetSubTreePaths(
+    const std::string i_objectPath, const int i_depth = 0,
+    const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept
+{
+    std::vector<std::string> l_objectPaths;
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method = l_bus.new_method_call(
+            constants::objectMapperService, constants::objectMapperPath,
+            constants::objectMapperInf, "GetSubTreePaths");
+
+        l_method.append(i_objectPath, i_depth, i_constrainingInterfaces);
+
+        auto l_result = l_bus.call(l_method);
+        l_result.read(l_objectPaths);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        logging::logMessage(
+            "Error while getting GetSubTreePaths for path [" + i_objectPath +
+            "], error: " + std::string(l_ex.what()));
+    }
+    return l_objectPaths;
+}
 } // namespace dbusUtility
 } // namespace vpd
diff --git a/vpd-manager/oem-handler/ibm_handler.cpp b/vpd-manager/oem-handler/ibm_handler.cpp
index 250f815..49fafe2 100644
--- a/vpd-manager/oem-handler/ibm_handler.cpp
+++ b/vpd-manager/oem-handler/ibm_handler.cpp
@@ -586,7 +586,10 @@
             // here.
             m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
 
-            primeSystemBlueprint();
+            if (isPrimingRequired())
+            {
+                primeSystemBlueprint();
+            }
         }
 
         // Enable all mux which are used for connecting to the i2c on the
@@ -609,4 +612,44 @@
     }
 }
 
+bool IbmHandler::isPrimingRequired() const noexcept
+{
+    try
+    {
+        // get all object paths under PIM
+        const auto l_objectPaths = dbusUtility::GetSubTreePaths(
+            constants::systemInvPath, 0,
+            std::vector<std::string>{constants::vpdCollectionInterface});
+
+        const nlohmann::json& l_listOfFrus =
+            m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+        size_t l_invPathCount = 0;
+
+        for (const auto& l_itemFRUS : l_listOfFrus.items())
+        {
+            for (const auto& l_Fru : l_itemFRUS.value())
+            {
+                if (l_Fru.contains("ccin") || (l_Fru.contains("noprime") &&
+                                               l_Fru.value("noprime", false)))
+                {
+                    continue;
+                }
+
+                l_invPathCount += 1;
+            }
+        }
+        return ((l_objectPaths.size() >= l_invPathCount) ? false : true);
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(
+            "Error while checking is priming required or not, error: " +
+            std::string(l_ex.what()));
+    }
+
+    // In case of any error, perform priming, as it's unclear whether priming is
+    // required.
+    return true;
+}
 } // namespace vpd
diff --git a/vpd-manager/oem-handler/ibm_handler.hpp b/vpd-manager/oem-handler/ibm_handler.hpp
index 3a6ea42..a397f06 100644
--- a/vpd-manager/oem-handler/ibm_handler.hpp
+++ b/vpd-manager/oem-handler/ibm_handler.hpp
@@ -145,6 +145,19 @@
      */
     void enableMuxChips();
 
+    /**
+     * @brief API to check if priming is required.
+     *
+     * The API will traverse the system config JSON and counts the FRU
+     * paths which qualifies for priming and compares with count of object paths
+     * found under PIM which hosts the "com.ibm.VPD.Collection" interface. If
+     * the dbus count is equal to or greater than the count from JSON config
+     * consider as priming is not required.
+     *
+     * @return true if priming is required, false otherwise.
+     */
+    bool isPrimingRequired() const noexcept;
+
     // Parsed system config json object.
     nlohmann::json m_sysCfgJsonObj{};