Process FRU only when present with specific CCIN

Some FRUs are only supported  with specific CCINs in case of PowerVS
configuration.
The commit implements changes to check for presence of the FRU based on
its Present property, if found checks for specific CCIN, if found
updates the PN else skips.

Change-Id: I1f3ff08f82788aed4d8153474cd38352e5cc2394
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/configuration/ibm/50001_power_vs.json b/configuration/ibm/50001_power_vs.json
index b4064a6..e3b5e06 100644
--- a/configuration/ibm/50001_power_vs.json
+++ b/configuration/ibm/50001_power_vs.json
@@ -1,20 +1,24 @@
 {
     "/sys/bus/spi/drivers/at25/spi12.0/eeprom": {
+        "CCIN": ["5C85"],
         "VINI": {
             "PN": [48, 50, 89, 75, 57, 52, 48]
         }
     },
     "/sys/bus/spi/drivers/at25/spi22.0/eeprom": {
+        "CCIN": ["5C85"],
         "VINI": {
             "PN": [48, 50, 89, 75, 57, 52, 48]
         }
     },
     "/sys/bus/spi/drivers/at25/spi32.0/eeprom": {
+        "CCIN": ["5C85"],
         "VINI": {
             "PN": [48, 50, 89, 75, 57, 52, 48]
         }
     },
     "/sys/bus/spi/drivers/at25/spi42.0/eeprom": {
+        "CCIN": ["5C85"],
         "VINI": {
             "PN": [48, 50, 89, 75, 57, 52, 48]
         }
diff --git a/configuration/ibm/50003_power_vs.json b/configuration/ibm/50003_power_vs.json
index b8b5008..2866c32 100644
--- a/configuration/ibm/50003_power_vs.json
+++ b/configuration/ibm/50003_power_vs.json
@@ -1,40 +1,48 @@
 {
     "/sys/bus/spi/drivers/at25/spi12.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi22.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi32.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi42.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi52.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi62.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi72.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
     },
     "/sys/bus/spi/drivers/at25/spi82.0/eeprom": {
+        "CCIN": ["5CF8"],
         "VINI": {
             "PN": [48, 51, 85, 72, 48, 57, 56]
         }
diff --git a/vpd-manager/include/utility/dbus_utility.hpp b/vpd-manager/include/utility/dbus_utility.hpp
index 344a637..39a4bf3 100644
--- a/vpd-manager/include/utility/dbus_utility.hpp
+++ b/vpd-manager/include/utility/dbus_utility.hpp
@@ -685,5 +685,31 @@
         return std::string{};
     }
 }
+
+/**
+ * @brief API to read DBus present property for the given inventory.
+ *
+ * @param[in] i_invObjPath - Inventory path.
+ * @return Present property value, false in case of any error.
+ */
+inline bool isInventoryPresent(const std::string& i_invObjPath)
+{
+    if (i_invObjPath.empty())
+    {
+        return false;
+    }
+
+    const auto& l_retValue =
+        dbusUtility::readDbusProperty(constants::pimServiceName, i_invObjPath,
+                                      constants::inventoryItemInf, "Present");
+
+    auto l_ptrPresence = std::get_if<bool>(&l_retValue);
+    if (!l_ptrPresence)
+    {
+        return false;
+    }
+
+    return (*l_ptrPresence);
+}
 } // namespace dbusUtility
 } // namespace vpd
diff --git a/vpd-manager/include/utility/vpd_specific_utility.hpp b/vpd-manager/include/utility/vpd_specific_utility.hpp
index 4fc553d..09c4719 100644
--- a/vpd-manager/include/utility/vpd_specific_utility.hpp
+++ b/vpd-manager/include/utility/vpd_specific_utility.hpp
@@ -727,5 +727,41 @@
     }
     return false;
 }
+
+/**
+ * @brief API to get CCIN for a given FRU from DBus.
+ *
+ * The API reads the CCIN for a FRU based on its inventory path.
+ *
+ * @param[in] i_invObjPath - Inventory path of the FRU.
+ * @return CCIN of the FRU on success, empty string otherwise.
+ */
+inline std::string getCcinFromDbus(const std::string& i_invObjPath)
+{
+    try
+    {
+        if (i_invObjPath.empty())
+        {
+            throw std::runtime_error("Empty EEPROM path, can't read CCIN");
+        }
+
+        const auto& l_retValue = dbusUtility::readDbusProperty(
+            constants::pimServiceName, i_invObjPath, constants::viniInf,
+            constants::kwdCCIN);
+
+        auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
+        if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4)
+        {
+            throw DbusException("Invalid CCIN read from Dbus");
+        }
+
+        return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(l_ex.what());
+        return std::string{};
+    }
+}
 } // namespace vpdSpecificUtility
 } // namespace vpd
diff --git a/vpd-manager/src/manager.cpp b/vpd-manager/src/manager.cpp
index fd63ffc..437fdd9 100644
--- a/vpd-manager/src/manager.cpp
+++ b/vpd-manager/src/manager.cpp
@@ -329,9 +329,65 @@
 {
     for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
     {
-        // TODO add special handling for PROC CCIN check.
+        nlohmann::json l_sysCfgJsonObj{};
+        if (m_worker.get() != nullptr)
+        {
+            l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+        }
+
+        // The utility method will handle emty JSON case. No explicit
+        // handling required here.
+        auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
+            l_sysCfgJsonObj, l_fruPath);
+
+        // Mark it as failed if inventory path not found in JSON.
+        if (l_inventoryPath.empty())
+        {
+            o_failedPathList.push_back(l_fruPath);
+            continue;
+        }
+
+        // check if the FRU is present
+        if (!dbusUtility::isInventoryPresent(l_inventoryPath))
+        {
+            logging::logMessage(
+                "Inventory not present, skip updating part number. Path: " +
+                l_inventoryPath);
+            continue;
+        }
+
+        // check if the FRU needs CCIN check before updating PN.
+        if (l_recJson.contains("CCIN"))
+        {
+            const auto& l_ccinFromDbus =
+                vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
+
+            // Not an ideal situation as CCIN can't be empty.
+            if (l_ccinFromDbus.empty())
+            {
+                o_failedPathList.push_back(l_fruPath);
+                continue;
+            }
+
+            std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
+
+            if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
+                     l_ccinFromDbus) == l_ccinListFromJson.end())
+            {
+                // Don't update PN in this case.
+                continue;
+            }
+        }
+
         for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
         {
+            // Record name can't be CCIN, skip processing as it is there for PN
+            // update based on CCIN check.
+            if (l_recordName == constants::kwdCCIN)
+            {
+                continue;
+            }
+
             for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
             {
                 // Is value of type array.
@@ -341,24 +397,6 @@
                     continue;
                 }
 
-                nlohmann::json l_sysCfgJsonObj{};
-                if (m_worker.get() != nullptr)
-                {
-                    l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
-                }
-
-                // The utility method will handle empty JSON case. No explicit
-                // handling required here.
-                auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
-                    l_sysCfgJsonObj, l_fruPath);
-
-                // Mark it as failed if inventory path not found in JSON.
-                if (l_inventoryPath.empty())
-                {
-                    o_failedPathList.push_back(l_fruPath);
-                    continue;
-                }
-
                 // Get current Part number.
                 auto l_retVal = dbusUtility::readDbusProperty(
                     constants::pimServiceName, l_inventoryPath,