Revamped code for VPD parser

The commit removes all the pre-existing code from the branch
and pushes the revamped code.

Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.

Note: This code supports all the existing functionalities as it is.

Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/src/backup_restore.cpp b/vpd-manager/src/backup_restore.cpp
new file mode 100644
index 0000000..294efc4
--- /dev/null
+++ b/vpd-manager/src/backup_restore.cpp
@@ -0,0 +1,384 @@
+#include "backup_restore.hpp"
+
+#include "constants.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "types.hpp"
+
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+namespace vpd
+{
+BackupAndRestoreStatus BackupAndRestore::m_backupAndRestoreStatus =
+    BackupAndRestoreStatus::NotStarted;
+
+BackupAndRestore::BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj) :
+    m_sysCfgJsonObj(i_sysCfgJsonObj)
+{
+    std::string l_backupAndRestoreCfgFilePath =
+        i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
+    try
+    {
+        m_backupAndRestoreCfgJsonObj =
+            jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
+    }
+    catch (const std::exception& ex)
+    {
+        logging::logMessage(
+            "Failed to intialize backup and restore object for file = " +
+            l_backupAndRestoreCfgFilePath);
+        throw(ex);
+    }
+}
+
+std::tuple<types::VPDMapVariant, types::VPDMapVariant>
+    BackupAndRestore::backupAndRestore()
+{
+    auto l_emptyVariantPair =
+        std::make_tuple(std::monostate{}, std::monostate{});
+
+    if (m_backupAndRestoreStatus >= BackupAndRestoreStatus::Invoked)
+    {
+        logging::logMessage("Backup and restore invoked already.");
+        return l_emptyVariantPair;
+    }
+
+    m_backupAndRestoreStatus = BackupAndRestoreStatus::Invoked;
+    try
+    {
+        if (m_backupAndRestoreCfgJsonObj.empty() ||
+            !m_backupAndRestoreCfgJsonObj.contains("source") ||
+            !m_backupAndRestoreCfgJsonObj.contains("destination") ||
+            !m_backupAndRestoreCfgJsonObj.contains("type") ||
+            !m_backupAndRestoreCfgJsonObj.contains("backupMap"))
+        {
+            logging::logMessage(
+                "Backup restore config JSON is missing necessary tag(s), can't initiate backup and restore.");
+            return l_emptyVariantPair;
+        }
+
+        std::string l_srcVpdPath;
+        types::VPDMapVariant l_srcVpdVariant;
+        if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value(
+                "hardwarePath", "");
+            !l_srcVpdPath.empty() && std::filesystem::exists(l_srcVpdPath))
+        {
+            std::shared_ptr<Parser> l_vpdParser =
+                std::make_shared<Parser>(l_srcVpdPath, m_sysCfgJsonObj);
+            l_srcVpdVariant = l_vpdParser->parse();
+        }
+        else if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value(
+                     "inventoryPath", "");
+                 l_srcVpdPath.empty())
+        {
+            logging::logMessage(
+                "Couldn't extract source path, can't initiate backup and restore.");
+            return l_emptyVariantPair;
+        }
+
+        std::string l_dstVpdPath;
+        types::VPDMapVariant l_dstVpdVariant;
+        if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"].value(
+                "hardwarePath", "");
+            !l_dstVpdPath.empty() && std::filesystem::exists(l_dstVpdPath))
+        {
+            std::shared_ptr<Parser> l_vpdParser =
+                std::make_shared<Parser>(l_dstVpdPath, m_sysCfgJsonObj);
+            l_dstVpdVariant = l_vpdParser->parse();
+        }
+        else if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"]
+                                    .value("inventoryPath", "");
+                 l_dstVpdPath.empty())
+        {
+            logging::logMessage(
+                "Couldn't extract destination path, can't initiate backup and restore.");
+            return l_emptyVariantPair;
+        }
+
+        // Implement backup and restore for IPZ type VPD
+        auto l_backupAndRestoreType =
+            m_backupAndRestoreCfgJsonObj.value("type", "");
+        if (l_backupAndRestoreType.compare("IPZ") == constants::STR_CMP_SUCCESS)
+        {
+            types::IPZVpdMap l_srcVpdMap;
+            if (auto l_srcVpdPtr =
+                    std::get_if<types::IPZVpdMap>(&l_srcVpdVariant))
+            {
+                l_srcVpdMap = *l_srcVpdPtr;
+            }
+            else if (!std::holds_alternative<std::monostate>(l_srcVpdVariant))
+            {
+                logging::logMessage("Source VPD is not of IPZ type.");
+                return l_emptyVariantPair;
+            }
+
+            types::IPZVpdMap l_dstVpdMap;
+            if (auto l_dstVpdPtr =
+                    std::get_if<types::IPZVpdMap>(&l_dstVpdVariant))
+            {
+                l_dstVpdMap = *l_dstVpdPtr;
+            }
+            else if (!std::holds_alternative<std::monostate>(l_dstVpdVariant))
+            {
+                logging::logMessage("Destination VPD is not of IPZ type.");
+                return l_emptyVariantPair;
+            }
+
+            backupAndRestoreIpzVpd(l_srcVpdMap, l_dstVpdMap, l_srcVpdPath,
+                                   l_dstVpdPath);
+            m_backupAndRestoreStatus = BackupAndRestoreStatus::Completed;
+
+            return std::make_tuple(l_srcVpdMap, l_dstVpdMap);
+        }
+        // Note: add implementation here to support any other VPD type.
+    }
+    catch (const std::exception& ex)
+    {
+        logging::logMessage("Back up and restore failed with exception: " +
+                            std::string(ex.what()));
+    }
+    return l_emptyVariantPair;
+}
+
+void BackupAndRestore::backupAndRestoreIpzVpd(
+    types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap,
+    const std::string& i_srcPath, const std::string& i_dstPath)
+{
+    if (!m_backupAndRestoreCfgJsonObj["backupMap"].is_array())
+    {
+        logging::logMessage(
+            "Invalid value found for tag backupMap, in backup and restore config JSON.");
+        return;
+    }
+
+    const std::string l_srcFruPath =
+        jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath);
+    const std::string l_dstFruPath =
+        jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath);
+    if (l_srcFruPath.empty() || l_dstFruPath.empty())
+    {
+        logging::logMessage(
+            "Couldn't find either source or destination FRU path.");
+        return;
+    }
+
+    const std::string l_srcInvPath =
+        jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_srcPath);
+    const std::string l_dstInvPath =
+        jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_dstPath);
+    if (l_srcInvPath.empty() || l_dstInvPath.empty())
+    {
+        logging::logMessage(
+            "Couldn't find either source or destination inventory path.");
+        return;
+    }
+
+    const std::string l_srcServiceName =
+        jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath);
+    const std::string l_dstServiceName =
+        jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath);
+    if (l_srcServiceName.empty() || l_dstServiceName.empty())
+    {
+        logging::logMessage(
+            "Couldn't find either source or destination DBus service name.");
+        return;
+    }
+
+    for (const auto& l_aRecordKwInfo :
+         m_backupAndRestoreCfgJsonObj["backupMap"])
+    {
+        const std::string& l_srcRecordName =
+            l_aRecordKwInfo.value("sourceRecord", "");
+        const std::string& l_srcKeywordName =
+            l_aRecordKwInfo.value("sourceKeyword", "");
+        const std::string& l_dstRecordName =
+            l_aRecordKwInfo.value("destinationRecord", "");
+        const std::string& l_dstKeywordName =
+            l_aRecordKwInfo.value("destinationKeyword", "");
+
+        if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
+            l_srcKeywordName.empty() || l_dstKeywordName.empty())
+        {
+            logging::logMessage(
+                "Record or keyword not found in the backup and restore config JSON.");
+            continue;
+        }
+
+        if (!io_srcVpdMap.empty() &&
+            io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end())
+        {
+            logging::logMessage(
+                "Record: " + l_srcRecordName +
+                ", is not found in the source path: " + i_srcPath);
+            continue;
+        }
+
+        if (!io_dstVpdMap.empty() &&
+            io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end())
+        {
+            logging::logMessage(
+                "Record: " + l_dstRecordName +
+                ", is not found in the destination path: " + i_dstPath);
+            continue;
+        }
+
+        types::BinaryVector l_defaultBinaryValue;
+        if (l_aRecordKwInfo.contains("defaultValue") &&
+            l_aRecordKwInfo["defaultValue"].is_array())
+        {
+            l_defaultBinaryValue =
+                l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>();
+        }
+        else
+        {
+            logging::logMessage(
+                "Couldn't read default value for record name: " +
+                l_srcRecordName + ", keyword name: " + l_srcKeywordName +
+                " from backup and restore config JSON file.");
+            continue;
+        }
+
+        bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false);
+
+        types::BinaryVector l_srcBinaryValue;
+        std::string l_srcStrValue;
+        if (!io_srcVpdMap.empty())
+        {
+            vpdSpecificUtility::getKwVal(io_srcVpdMap.at(l_srcRecordName),
+                                         l_srcKeywordName, l_srcStrValue);
+            l_srcBinaryValue =
+                types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end());
+        }
+        else
+        {
+            // Read keyword value from DBus
+            const auto l_value = dbusUtility::readDbusProperty(
+                l_srcServiceName, l_srcInvPath,
+                constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName);
+            if (const auto l_binaryValue =
+                    std::get_if<types::BinaryVector>(&l_value))
+            {
+                l_srcBinaryValue = *l_binaryValue;
+                l_srcStrValue = std::string(l_srcBinaryValue.begin(),
+                                            l_srcBinaryValue.end());
+            }
+        }
+
+        types::BinaryVector l_dstBinaryValue;
+        std::string l_dstStrValue;
+        if (!io_dstVpdMap.empty())
+        {
+            vpdSpecificUtility::getKwVal(io_dstVpdMap.at(l_dstRecordName),
+                                         l_dstKeywordName, l_dstStrValue);
+            l_dstBinaryValue =
+                types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end());
+        }
+        else
+        {
+            // Read keyword value from DBus
+            const auto l_value = dbusUtility::readDbusProperty(
+                l_dstServiceName, l_dstInvPath,
+                constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName);
+            if (const auto l_binaryValue =
+                    std::get_if<types::BinaryVector>(&l_value))
+            {
+                l_dstBinaryValue = *l_binaryValue;
+                l_dstStrValue = std::string(l_dstBinaryValue.begin(),
+                                            l_dstBinaryValue.end());
+            }
+        }
+
+        if (l_srcBinaryValue != l_dstBinaryValue)
+        {
+            // ToDo: Handle if there is no valid default value in the backup and
+            // restore config JSON.
+            if (l_dstBinaryValue == l_defaultBinaryValue)
+            {
+                // Update keyword's value on hardware
+                auto l_vpdParser =
+                    std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj);
+
+                auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword(
+                    types::IpzData(l_dstRecordName, l_dstKeywordName,
+                                   l_srcBinaryValue));
+
+                /* To keep the data in sync between hardware and parsed map
+                 updating the io_dstVpdMap. This should only be done if write
+                 on hardware returns success.*/
+                if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0)
+                {
+                    io_dstVpdMap[l_dstRecordName][l_dstKeywordName] =
+                        l_srcStrValue;
+                }
+                continue;
+            }
+
+            if (l_srcBinaryValue == l_defaultBinaryValue)
+            {
+                // Update keyword's value on hardware
+                auto l_vpdParser =
+                    std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj);
+
+                auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword(
+                    types::IpzData(l_srcRecordName, l_srcKeywordName,
+                                   l_dstBinaryValue));
+
+                /* To keep the data in sync between hardware and parsed map
+                 updating the io_srcVpdMap. This should only be done if write
+                 on hardware returns success.*/
+                if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0)
+                {
+                    io_srcVpdMap[l_srcRecordName][l_srcKeywordName] =
+                        l_dstStrValue;
+                }
+            }
+            else
+            {
+                /**
+                 * Update io_srcVpdMap to publish the same data on DBus, which
+                 * is already present on the DBus. Because after calling
+                 * backupAndRestore API the map value will get published to DBus
+                 * in the worker flow.
+                 */
+                if (!io_srcVpdMap.empty() && io_dstVpdMap.empty())
+                {
+                    io_srcVpdMap[l_srcRecordName][l_srcKeywordName] =
+                        l_dstStrValue;
+                }
+
+                std::string l_errorMsg(
+                    "Mismatch found between source and destination VPD for record : " +
+                    l_srcRecordName + " and keyword : " + l_srcKeywordName +
+                    " . Value read from source : " + l_srcStrValue +
+                    " . Value read from destination : " + l_dstStrValue);
+
+                EventLogger::createSyncPel(
+                    types::ErrorType::VpdMismatch, types::SeverityType::Warning,
+                    __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt,
+                    std::nullopt, std::nullopt, std::nullopt);
+            }
+        }
+        else if (l_srcBinaryValue == l_defaultBinaryValue &&
+                 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired)
+        {
+            std::string l_errorMsg(
+                "Default value found on both source and destination VPD, for record: " +
+                l_srcRecordName + " and keyword: " + l_srcKeywordName);
+
+            EventLogger::createSyncPel(
+                types::ErrorType::DefaultValue, types::SeverityType::Error,
+                __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt,
+                std::nullopt, std::nullopt, std::nullopt);
+        }
+    }
+}
+
+void BackupAndRestore::setBackupAndRestoreStatus(
+    const BackupAndRestoreStatus& i_status)
+{
+    m_backupAndRestoreStatus = i_status;
+}
+} // namespace vpd