Implementation of priming in wait-vpd-parsers

As part of PST priming system inventory and FRUs VPD collection will be
part of systemd target, instead of being triggered internally by
vpd-manager.

This commit implements priming system blueprint in wait-vpd-parsers
application and removes the priming from vpd-manager initialization
flow.

```
Testing performed:
1. Removed PIM cache path in the system.
2. Restarted PIM service.
systemctl  restart xyz.openbmc_project.Inventory.Manager
3. Observed inventory paths are removed under PIM using below command
busctl  tree xyz.openbmc_project.Inventory.Manager
3. Restarted wait-vpd-parsers service
systemctl  restart wait-vpd-parsers
4. Observed that system is primed again with all inventory object paths, using below command
busctl  tree xyz.openbmc_project.Inventory.Manager
```

Change-Id: Ic93f54e909a247587208082830c5ebbe48f69f50
Signed-off-by: Anupama B R <anupama.b.r1@ibm.com>
diff --git a/wait-vpd-parser/include/prime_inventory.hpp b/wait-vpd-parser/include/prime_inventory.hpp
index 8dab2f8..4666ab5 100644
--- a/wait-vpd-parser/include/prime_inventory.hpp
+++ b/wait-vpd-parser/include/prime_inventory.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "logger.hpp"
 #include "types.hpp"
 
 #include <nlohmann/json.hpp>
@@ -57,11 +58,13 @@
     /**
      * @brief API to prime inventory Objects.
      *
-     * @param[in] i_vpdFilePath - EEPROM file path.
+     * @param[out] o_objectInterfaceMap - Interface and its properties map.
+     * @param[in] i_fruJsonObj - FRU json object.
      *
      * @return true if the prime inventory is success, false otherwise.
      */
-    bool primeInventory(const std::string& i_vpdFilePath) const noexcept;
+    bool primeInventory(vpd::types::ObjectMap& o_objectInterfaceMap,
+                        const nlohmann::json& i_fruJsonObj) const noexcept;
 
     /**
      * @brief API to populate all required interface for a FRU.
@@ -129,4 +132,6 @@
 
     // Parsed JSON file.
     nlohmann::json m_sysCfgJsonObj{};
+
+    std::shared_ptr<vpd::Logger> m_logger;
 };
diff --git a/wait-vpd-parser/src/prime_inventory.cpp b/wait-vpd-parser/src/prime_inventory.cpp
index 79125fe..b09b635 100644
--- a/wait-vpd-parser/src/prime_inventory.cpp
+++ b/wait-vpd-parser/src/prime_inventory.cpp
@@ -8,6 +8,7 @@
 #include "utility/vpd_specific_utility.hpp"
 
 #include <string>
+#include <vector>
 
 PrimeInventory::PrimeInventory()
 {
@@ -32,6 +33,8 @@
                 "Mandatory tag(s) missing from JSON file [" +
                 std::string(INVENTORY_JSON_SYM_LINK) + "]");
         }
+
+        m_logger = vpd::Logger::getLoggerInstance();
     }
     catch (const std::exception& l_ex)
     {
@@ -47,7 +50,42 @@
 
 bool PrimeInventory::isPrimingRequired() const noexcept
 {
-    // ToDo: Check if priming is required.
+    try
+    {
+        // get all object paths under PIM
+        const auto l_objectPaths = vpd::dbusUtility::GetSubTreePaths(
+            vpd::constants::systemInvPath, 0,
+            std::vector<std::string>{vpd::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)
+    {
+        m_logger->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;
 }
 
@@ -55,45 +93,353 @@
 {
     try
     {
-        /*ToDo:
-      * Check if priming is required.
-      * Traverse the system config JSON &
-      prime all the FRU paths which qualifies for priming.
-      */
+        if (m_sysCfgJsonObj.empty())
+        {
+            return;
+        }
+
+        const nlohmann::json& l_listOfFrus =
+            m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+        vpd::types::ObjectMap l_objectInterfaceMap;
+        for (const auto& l_itemFRUS : l_listOfFrus.items())
+        {
+            const std::string& l_vpdFilePath = l_itemFRUS.key();
+
+            if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
+            {
+                continue;
+            }
+
+            // Prime the inventry for FRUs
+            for (const auto& l_Fru : m_sysCfgJsonObj["frus"][l_vpdFilePath])
+            {
+                if (!primeInventory(l_objectInterfaceMap, l_Fru))
+                {
+                    m_logger->logMessage(
+                        "Priming of inventory failed for FRU " +
+                        std::string(l_Fru["inventoryPath"]));
+                }
+            }
+        }
+
+        // Notify PIM
+        if (!l_objectInterfaceMap.empty())
+        {
+            if (!vpd::dbusUtility::callPIM(move(l_objectInterfaceMap)))
+            {
+                m_logger->logMessage(
+                    "Call to PIM failed while priming inventory");
+            }
+        }
+        else
+        {
+            m_logger->logMessage("Priming inventory failed");
+        }
     }
     catch (const std::exception& l_ex)
     {
-        // ToDo: log an error
+        m_logger->logMessage("Prime system inventory failed, reason: " +
+                             std::string(l_ex.what()));
     }
 }
 
 bool PrimeInventory::primeInventory(
-    [[maybe_unused]] const std::string& i_vpdFilePath) const noexcept
+    vpd::types::ObjectMap& o_objectInterfaceMap,
+    const nlohmann::json& i_fruJsonObj) const noexcept
 {
-    // ToDo: Travers system config JSON & prime inventory objects found under
-    // the EEPROM.
+    if (i_fruJsonObj.empty())
+    {
+        m_logger->logMessage("Empty FRU JSON given");
+        return false;
+    }
+
+    vpd::types::InterfaceMap l_interfaces;
+    sdbusplus::message::object_path l_fruObjectPath(
+        i_fruJsonObj["inventoryPath"]);
+
+    if (i_fruJsonObj.contains("ccin"))
+    {
+        return true;
+    }
+
+    if (i_fruJsonObj.contains("noprime") &&
+        i_fruJsonObj.value("noprime", false))
+    {
+        return true;
+    }
+
+    // Reset data under PIM for this FRU only if the FRU is not synthesized
+    // and we handle it's Present property.
+    if (isPresentPropertyHandlingRequired(i_fruJsonObj))
+    {
+        // Clear data under PIM if already exists.
+        vpd::vpdSpecificUtility::resetDataUnderPIM(
+            std::string(i_fruJsonObj["inventoryPath"]), l_interfaces);
+    }
+
+    // Add extra interfaces mentioned in the Json config file
+    if (i_fruJsonObj.contains("extraInterfaces"))
+    {
+        populateInterfaces(i_fruJsonObj["extraInterfaces"], l_interfaces,
+                           std::monostate{});
+    }
+
+    vpd::types::PropertyMap l_propertyValueMap;
+
+    // Update Present property for this FRU only if we handle Present
+    // property for the FRU.
+    if (isPresentPropertyHandlingRequired(i_fruJsonObj))
+    {
+        l_propertyValueMap.emplace("Present", false);
+
+        // TODO: Present based on file will be taken care in future.
+        // By default present is set to false for FRU at the time of
+        // priming. Once collection goes through, it will be set to true in
+        // that flow.
+        /*if (std::filesystem::exists(i_vpdFilePath))
+        {
+            l_propertyValueMap["Present"] = true;
+        }*/
+    }
+
+    vpd::vpdSpecificUtility::insertOrMerge(
+        l_interfaces, "xyz.openbmc_project.Inventory.Item",
+        move(l_propertyValueMap));
+
+    if (i_fruJsonObj.value("inherit", true) &&
+        m_sysCfgJsonObj.contains("commonInterfaces"))
+    {
+        populateInterfaces(m_sysCfgJsonObj["commonInterfaces"], l_interfaces,
+                           std::monostate{});
+    }
+
+    processFunctionalProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
+    processEnabledProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
+
+    // Emplace the default state of FRU VPD collection
+    vpd::types::PropertyMap l_fruCollectionProperty = {
+        {"Status", vpd::constants::vpdCollectionNotStarted}};
+
+    vpd::vpdSpecificUtility::insertOrMerge(
+        l_interfaces, vpd::constants::vpdCollectionInterface,
+        std::move(l_fruCollectionProperty));
+
+    o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
+                                 std::move(l_interfaces));
+
     return true;
 }
 
 void PrimeInventory::populateInterfaces(
-    [[maybe_unused]] const nlohmann::json& i_interfaceJson,
-    [[maybe_unused]] vpd::types::InterfaceMap& io_interfaceMap,
-    [[maybe_unused]] const vpd::types::VPDMapVariant& i_parsedVpdMap)
-    const noexcept
+    const nlohmann::json& i_interfaceJson,
+    vpd::types::InterfaceMap& io_interfaceMap,
+    const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
 {
-    // ToDo: Populate interfaces needs to be published on Dbus.
+    for (const auto& l_interfacesPropPair : i_interfaceJson.items())
+    {
+        const std::string& l_interface = l_interfacesPropPair.key();
+        vpd::types::PropertyMap l_propertyMap;
+
+        for (const auto& l_propValuePair : l_interfacesPropPair.value().items())
+        {
+            const std::string l_property = l_propValuePair.key();
+
+            if (l_propValuePair.value().is_boolean())
+            {
+                l_propertyMap.emplace(l_property,
+                                      l_propValuePair.value().get<bool>());
+            }
+            else if (l_propValuePair.value().is_string())
+            {
+                if (l_property.compare("LocationCode") == 0 &&
+                    l_interface.compare("com.ibm.ipzvpd.Location") == 0)
+                {
+                    std::string l_value =
+                        vpd::vpdSpecificUtility::getExpandedLocationCode(
+                            l_propValuePair.value().get<std::string>(),
+                            i_parsedVpdMap);
+                    l_propertyMap.emplace(l_property, l_value);
+
+                    auto l_locCodeProperty = l_propertyMap;
+                    vpd::vpdSpecificUtility::insertOrMerge(
+                        io_interfaceMap,
+                        std::string(vpd::constants::xyzLocationCodeInf),
+                        move(l_locCodeProperty));
+                }
+                else
+                {
+                    l_propertyMap.emplace(
+                        l_property, l_propValuePair.value().get<std::string>());
+                }
+            }
+            else if (l_propValuePair.value().is_array())
+            {
+                try
+                {
+                    l_propertyMap.emplace(l_property,
+                                          l_propValuePair.value()
+                                              .get<vpd::types::BinaryVector>());
+                }
+                catch (const nlohmann::detail::type_error& e)
+                {
+                    std::cerr << "Type exception: " << e.what() << "\n";
+                }
+            }
+            else if (l_propValuePair.value().is_number())
+            {
+                // For now assume the value is a size_t.  In the future it would
+                // be nice to come up with a way to get the type from the JSON.
+                l_propertyMap.emplace(l_property,
+                                      l_propValuePair.value().get<size_t>());
+            }
+            else if (l_propValuePair.value().is_object())
+            {
+                const std::string& l_record =
+                    l_propValuePair.value().value("recordName", "");
+                const std::string& l_keyword =
+                    l_propValuePair.value().value("keywordName", "");
+                const std::string& l_encoding =
+                    l_propValuePair.value().value("encoding", "");
+
+                if (auto l_ipzVpdMap =
+                        std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap))
+                {
+                    if (!l_record.empty() && !l_keyword.empty() &&
+                        (*l_ipzVpdMap).count(l_record) &&
+                        (*l_ipzVpdMap).at(l_record).count(l_keyword))
+                    {
+                        auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword(
+                            ((*l_ipzVpdMap).at(l_record).at(l_keyword)),
+                            l_encoding);
+                        l_propertyMap.emplace(l_property, l_encoded);
+                    }
+                }
+                else if (auto l_kwdVpdMap =
+                             std::get_if<vpd::types::KeywordVpdMap>(
+                                 &i_parsedVpdMap))
+                {
+                    if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword))
+                    {
+                        if (auto l_kwValue =
+                                std::get_if<vpd::types::BinaryVector>(
+                                    &(*l_kwdVpdMap).at(l_keyword)))
+                        {
+                            auto l_encodedValue =
+                                vpd::vpdSpecificUtility::encodeKeyword(
+                                    std::string((*l_kwValue).begin(),
+                                                (*l_kwValue).end()),
+                                    l_encoding);
+
+                            l_propertyMap.emplace(l_property, l_encodedValue);
+                        }
+                        else if (auto l_kwValue = std::get_if<std::string>(
+                                     &(*l_kwdVpdMap).at(l_keyword)))
+                        {
+                            auto l_encodedValue =
+                                vpd::vpdSpecificUtility::encodeKeyword(
+                                    std::string((*l_kwValue).begin(),
+                                                (*l_kwValue).end()),
+                                    l_encoding);
+
+                            l_propertyMap.emplace(l_property, l_encodedValue);
+                        }
+                        else if (auto l_uintValue = std::get_if<size_t>(
+                                     &(*l_kwdVpdMap).at(l_keyword)))
+                        {
+                            l_propertyMap.emplace(l_property, *l_uintValue);
+                        }
+                        else
+                        {
+                            m_logger->logMessage(
+                                "Unknown keyword found, Keywrod = " +
+                                l_keyword);
+                        }
+                    }
+                }
+            }
+        }
+        vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface,
+                                               move(l_propertyMap));
+    }
 }
 
 void PrimeInventory::processFunctionalProperty(
-    [[maybe_unused]] const std::string& i_inventoryObjPath,
-    [[maybe_unused]] vpd::types::InterfaceMap& io_interfaces) const noexcept
+    const std::string& i_inventoryObjPath,
+    vpd::types::InterfaceMap& io_interfaces) const noexcept
 {
-    // ToDo: Populate interface to publish Functional property on Dbus.
+    if (!vpd::dbusUtility::isChassisPowerOn())
+    {
+        std::vector<std::string> l_operationalStatusInf{
+            vpd::constants::operationalStatusInf};
+
+        auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap(
+            i_inventoryObjPath, l_operationalStatusInf);
+
+        // If the object has been found. Check if it is under PIM.
+        if (l_mapperObjectMap.size() != 0)
+        {
+            for (const auto& [l_serviceName, l_interfaceLsit] :
+                 l_mapperObjectMap)
+            {
+                if (l_serviceName == vpd::constants::pimServiceName)
+                {
+                    // The object is already under PIM. No need to process
+                    // again. Retain the old value.
+                    return;
+                }
+            }
+        }
+
+        // Implies value is not there in D-Bus. Populate it with default
+        // value "true".
+        vpd::types::PropertyMap l_functionalProp;
+        l_functionalProp.emplace("Functional", true);
+        vpd::vpdSpecificUtility::insertOrMerge(
+            io_interfaces, vpd::constants::operationalStatusInf,
+            move(l_functionalProp));
+    }
+
+    // if chassis is power on. Functional property should be there on D-Bus.
+    // Don't process.
+    return;
 }
 
 void PrimeInventory::processEnabledProperty(
-    [[maybe_unused]] const std::string& i_inventoryObjPath,
-    [[maybe_unused]] vpd::types::InterfaceMap& io_interfaces) const noexcept
+    const std::string& i_inventoryObjPath,
+    vpd::types::InterfaceMap& io_interfaces) const noexcept
 {
-    // ToDo: Populate interface to publish Enabled property on Dbus.
+    if (!vpd::dbusUtility::isChassisPowerOn())
+    {
+        std::vector<std::string> l_enableInf{vpd::constants::enableInf};
+
+        auto l_mapperObjectMap =
+            vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
+
+        // If the object has been found. Check if it is under PIM.
+        if (l_mapperObjectMap.size() != 0)
+        {
+            for (const auto& [l_serviceName, l_interfaceLsit] :
+                 l_mapperObjectMap)
+            {
+                if (l_serviceName == vpd::constants::pimServiceName)
+                {
+                    // The object is already under PIM. No need to process
+                    // again. Retain the old value.
+                    return;
+                }
+            }
+        }
+
+        // Implies value is not there in D-Bus. Populate it with default
+        // value "true".
+        vpd::types::PropertyMap l_enabledProp;
+        l_enabledProp.emplace("Enabled", true);
+        vpd::vpdSpecificUtility::insertOrMerge(
+            io_interfaces, vpd::constants::enableInf, move(l_enabledProp));
+    }
+
+    // if chassis is power on. Enabled property should be there on D-Bus.
+    // Don't process.
+    return;
 }