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/parser.cpp b/vpd-manager/src/parser.cpp
new file mode 100644
index 0000000..d6266cb
--- /dev/null
+++ b/vpd-manager/src/parser.cpp
@@ -0,0 +1,342 @@
+#include "parser.hpp"
+
+#include "constants.hpp"
+#include "event_logger.hpp"
+
+#include <utility/dbus_utility.hpp>
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+#include <fstream>
+
+namespace vpd
+{
+Parser::Parser(const std::string& vpdFilePath, nlohmann::json parsedJson) :
+    m_vpdFilePath(vpdFilePath), m_parsedJson(parsedJson)
+{
+    std::error_code l_errCode;
+
+    // ToDo: Add minimum file size check in all the concert praser classes,
+    // depends on their VPD type.
+    if (!std::filesystem::exists(m_vpdFilePath, l_errCode))
+    {
+        std::string l_message{"Parser object creation failed, file [" +
+                              m_vpdFilePath + "] doesn't exists."};
+
+        if (l_errCode)
+        {
+            l_message += " Error message: " + l_errCode.message();
+        }
+
+        throw std::runtime_error(l_message);
+    }
+
+    // Read VPD offset if applicable.
+    if (!m_parsedJson.empty())
+    {
+        m_vpdStartOffset = jsonUtility::getVPDOffset(m_parsedJson, vpdFilePath);
+    }
+}
+
+std::shared_ptr<vpd::ParserInterface> Parser::getVpdParserInstance()
+{
+    // Read the VPD data into a vector.
+    vpdSpecificUtility::getVpdDataInVector(m_vpdFilePath, m_vpdVector,
+                                           m_vpdStartOffset);
+
+    // This will detect the type of parser required.
+    std::shared_ptr<vpd::ParserInterface> l_parser =
+        ParserFactory::getParser(m_vpdVector, m_vpdFilePath, m_vpdStartOffset);
+
+    return l_parser;
+}
+
+types::VPDMapVariant Parser::parse()
+{
+    std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance();
+    return l_parser->parse();
+}
+
+int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
+{
+    int l_bytesUpdatedOnHardware = constants::FAILURE;
+
+    // A lambda to extract Record : Keyword string from i_paramsToWriteData
+    auto l_keyWordIdentifier =
+        [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
+        std::string l_keywordString{};
+        if (const types::IpzData* l_ipzData =
+                std::get_if<types::IpzData>(&i_paramsToWriteData))
+        {
+            l_keywordString =
+                std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
+        }
+        else if (const types::KwData* l_kwData =
+                     std::get_if<types::KwData>(&i_paramsToWriteData))
+        {
+            l_keywordString = std::get<0>(*l_kwData);
+        }
+        return l_keywordString;
+    };
+
+    try
+    {
+        // Enable Reboot Guard
+        if (constants::FAILURE == dbusUtility::EnableRebootGuard())
+        {
+            EventLogger::createAsyncPel(
+                types::ErrorType::DbusFailure,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                std::string(
+                    "Failed to enable BMC Reboot Guard while updating " +
+                    l_keyWordIdentifier(i_paramsToWriteData)),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+            return constants::FAILURE;
+        }
+
+        // Update keyword's value on hardware
+        try
+        {
+            std::shared_ptr<ParserInterface> l_vpdParserInstance =
+                getVpdParserInstance();
+            l_bytesUpdatedOnHardware =
+                l_vpdParserInstance->writeKeywordOnHardware(
+                    i_paramsToWriteData);
+        }
+        catch (const std::exception& l_exception)
+        {
+            std::string l_errMsg(
+                "Error while updating keyword's value on hardware path " +
+                m_vpdFilePath + ", error: " + std::string(l_exception.what()));
+
+            // TODO : Log PEL
+
+            throw std::runtime_error(l_errMsg);
+        }
+
+        auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] =
+            jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson,
+                                                    m_vpdFilePath);
+
+        // If inventory D-bus object path is present, update keyword's value on
+        // DBus
+        if (!l_inventoryObjPath.empty())
+        {
+            types::Record l_recordName;
+            std::string l_interfaceName;
+            std::string l_propertyName;
+            types::DbusVariantType l_keywordValue;
+
+            if (const types::IpzData* l_ipzData =
+                    std::get_if<types::IpzData>(&i_paramsToWriteData))
+            {
+                l_recordName = std::get<0>(*l_ipzData);
+                l_interfaceName = constants::ipzVpdInf + l_recordName;
+                l_propertyName = std::get<1>(*l_ipzData);
+
+                try
+                {
+                    // Read keyword's value from hardware to write the same on
+                    // D-bus.
+                    std::shared_ptr<ParserInterface> l_vpdParserInstance =
+                        getVpdParserInstance();
+
+                    logging::logMessage(
+                        "Performing VPD read on " + m_vpdFilePath);
+
+                    l_keywordValue =
+                        l_vpdParserInstance->readKeywordFromHardware(
+                            types::ReadVpdParams(
+                                std::make_tuple(l_recordName, l_propertyName)));
+                }
+                catch (const std::exception& l_exception)
+                {
+                    // Unable to read keyword's value from hardware.
+                    std::string l_errMsg(
+                        "Error while reading keyword's value from hadware path " +
+                        m_vpdFilePath +
+                        ", error: " + std::string(l_exception.what()));
+
+                    // TODO: Log PEL
+
+                    throw std::runtime_error(l_errMsg);
+                }
+            }
+            else
+            {
+                // Input parameter type provided isn't compatible to perform
+                // update.
+                std::string l_errMsg(
+                    "Input parameter type isn't compatible to update keyword's value on DBus for object path: " +
+                    l_inventoryObjPath);
+                throw std::runtime_error(l_errMsg);
+            }
+
+            // Get D-bus name for the given keyword
+            l_propertyName =
+                vpdSpecificUtility::getDbusPropNameForGivenKw(l_propertyName);
+
+            // Create D-bus object map
+            types::ObjectMap l_dbusObjMap = {std::make_pair(
+                l_inventoryObjPath,
+                types::InterfaceMap{std::make_pair(
+                    l_interfaceName, types::PropertyMap{std::make_pair(
+                                         l_propertyName, l_keywordValue)})})};
+
+            // Call PIM's Notify method to perform update
+            if (!dbusUtility::callPIM(std::move(l_dbusObjMap)))
+            {
+                // Call to PIM's Notify method failed.
+                std::string l_errMsg("Notify PIM is failed for object path: " +
+                                     l_inventoryObjPath);
+                throw std::runtime_error(l_errMsg);
+            }
+        }
+
+        // Update keyword's value on redundant hardware if present
+        if (!l_redundantFruPath.empty())
+        {
+            if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
+                                                i_paramsToWriteData) < 0)
+            {
+                std::string l_errMsg(
+                    "Error while updating keyword's value on redundant path " +
+                    l_redundantFruPath);
+                throw std::runtime_error(l_errMsg);
+            }
+        }
+
+        // TODO: Check if revert is required when any of the writes fails.
+        // TODO: Handle error logging
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage("Update VPD Keyword failed for : " +
+                            l_keyWordIdentifier(i_paramsToWriteData) +
+                            " failed due to error: " + l_ex.what());
+
+        // update failed, set return value to failure
+        l_bytesUpdatedOnHardware = constants::FAILURE;
+    }
+
+    // Disable Reboot Guard
+    if (constants::FAILURE == dbusUtility::DisableRebootGuard())
+    {
+        EventLogger::createAsyncPel(
+            types::ErrorType::DbusFailure, types::SeverityType::Critical,
+            __FILE__, __FUNCTION__, 0,
+            std::string("Failed to disable BMC Reboot Guard while updating " +
+                        l_keyWordIdentifier(i_paramsToWriteData)),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+
+    return l_bytesUpdatedOnHardware;
+}
+
+int Parser::updateVpdKeywordOnRedundantPath(
+    const std::string& i_fruPath,
+    const types::WriteVpdParams& i_paramsToWriteData)
+{
+    try
+    {
+        std::shared_ptr<Parser> l_parserObj =
+            std::make_shared<Parser>(i_fruPath, m_parsedJson);
+
+        std::shared_ptr<ParserInterface> l_vpdParserInstance =
+            l_parserObj->getVpdParserInstance();
+
+        return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
+    }
+    catch (const std::exception& l_exception)
+    {
+        EventLogger::createSyncPel(
+            types::ErrorType::InvalidVpdMessage,
+            types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+            "Error while updating keyword's value on redundant path " +
+                i_fruPath + ", error: " + std::string(l_exception.what()),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        return -1;
+    }
+}
+
+int Parser::updateVpdKeywordOnHardware(
+    const types::WriteVpdParams& i_paramsToWriteData)
+{
+    int l_bytesUpdatedOnHardware = constants::FAILURE;
+
+    // A lambda to extract Record : Keyword string from i_paramsToWriteData
+    auto l_keyWordIdentifier =
+        [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
+        std::string l_keywordString{};
+        if (const types::IpzData* l_ipzData =
+                std::get_if<types::IpzData>(&i_paramsToWriteData))
+        {
+            l_keywordString =
+                std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
+        }
+        else if (const types::KwData* l_kwData =
+                     std::get_if<types::KwData>(&i_paramsToWriteData))
+        {
+            l_keywordString = std::get<0>(*l_kwData);
+        }
+        return l_keywordString;
+    };
+
+    try
+    {
+        // Enable Reboot Guard
+        if (constants::FAILURE == dbusUtility::EnableRebootGuard())
+        {
+            EventLogger::createAsyncPel(
+                types::ErrorType::DbusFailure,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                std::string(
+                    "Failed to enable BMC Reboot Guard while updating " +
+                    l_keyWordIdentifier(i_paramsToWriteData)),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+            return constants::FAILURE;
+        }
+
+        std::shared_ptr<ParserInterface> l_vpdParserInstance =
+            getVpdParserInstance();
+        l_bytesUpdatedOnHardware =
+            l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
+    }
+    catch (const std::exception& l_exception)
+    {
+        types::ErrorType l_errorType;
+
+        if (typeid(l_exception) == typeid(EccException))
+        {
+            l_errorType = types::ErrorType::EccCheckFailed;
+        }
+        else
+        {
+            l_errorType = types::ErrorType::InvalidVpdMessage;
+        }
+
+        EventLogger::createAsyncPel(
+            l_errorType, types::SeverityType::Informational, __FILE__,
+            __FUNCTION__, 0,
+            "Error while updating keyword's value on hardware path [" +
+                m_vpdFilePath + "], error: " + std::string(l_exception.what()),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+
+    // Disable Reboot Guard
+    if (constants::FAILURE == dbusUtility::DisableRebootGuard())
+    {
+        EventLogger::createAsyncPel(
+            types::ErrorType::DbusFailure, types::SeverityType::Critical,
+            __FILE__, __FUNCTION__, 0,
+            std::string("Failed to disable BMC Reboot Guard while updating " +
+                        l_keyWordIdentifier(i_paramsToWriteData)),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+
+    return l_bytesUpdatedOnHardware;
+}
+
+} // namespace vpd