Move OEM specific code

IBM specific implementation is being moved to OEM handler.
This is being done to keep OEM specific code under specific OEM file,
keeping rest of the code generic.

Change-Id: I316bd6835566f79ffae1a4e1f804a986ee4b8983
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/oem-handler/ibm_handler.cpp b/vpd-manager/oem-handler/ibm_handler.cpp
index d9e193a..fa3f064 100644
--- a/vpd-manager/oem-handler/ibm_handler.cpp
+++ b/vpd-manager/oem-handler/ibm_handler.cpp
@@ -2,6 +2,7 @@
 
 #include "ibm_handler.hpp"
 
+#include "configuration.hpp"
 #include "listener.hpp"
 #include "parser.hpp"
 
@@ -57,7 +58,9 @@
     // Set up minimal things that is needed before bus name is claimed.
     performInitialSetup();
 
-    if (!m_sysCfgJsonObj.empty() &&
+    // If the object is created, implies back up and restore took place in
+    // system VPD flow.
+    if ((m_backupAndRestoreObj == nullptr) && !m_sysCfgJsonObj.empty() &&
         jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode))
     {
         try
@@ -430,6 +433,308 @@
     }
 }
 
+void IbmHandler::getSystemJson(std::string& o_systemJson,
+                               const types::VPDMapVariant& i_parsedVpdMap)
+{
+    if (auto l_pVal = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
+    {
+        uint16_t l_errCode = 0;
+        std::string l_hwKWdValue =
+            vpdSpecificUtility::getHWVersion(*l_pVal, l_errCode);
+        if (l_hwKWdValue.empty())
+        {
+            if (l_errCode)
+            {
+                throw DataException("Failed to fetch HW value. Reason: " +
+                                    commonUtility::getErrCodeMsg(l_errCode));
+            }
+            throw DataException("HW value fetched is empty.");
+        }
+
+        const std::string& l_imKwdValue =
+            vpdSpecificUtility::getIMValue(*l_pVal, l_errCode);
+        if (l_imKwdValue.empty())
+        {
+            if (l_errCode)
+            {
+                throw DataException("Failed to fetch IM value. Reason: " +
+                                    commonUtility::getErrCodeMsg(l_errCode));
+            }
+            throw DataException("IM value fetched is empty.");
+        }
+
+        auto l_itrToIM = config::systemType.find(l_imKwdValue);
+        if (l_itrToIM == config::systemType.end())
+        {
+            throw DataException("IM keyword does not map to any system type");
+        }
+
+        const types::HWVerList l_hwVersionList = l_itrToIM->second.second;
+        if (!l_hwVersionList.empty())
+        {
+            transform(l_hwKWdValue.begin(), l_hwKWdValue.end(),
+                      l_hwKWdValue.begin(), ::toupper);
+
+            auto l_itrToHW =
+                std::find_if(l_hwVersionList.begin(), l_hwVersionList.end(),
+                             [&l_hwKWdValue](const auto& l_aPair) {
+                                 return l_aPair.first == l_hwKWdValue;
+                             });
+
+            if (l_itrToHW != l_hwVersionList.end())
+            {
+                if (!(*l_itrToHW).second.empty())
+                {
+                    o_systemJson += (*l_itrToIM).first + "_" +
+                                    (*l_itrToHW).second + ".json";
+                }
+                else
+                {
+                    o_systemJson += (*l_itrToIM).first + ".json";
+                }
+                return;
+            }
+        }
+        o_systemJson += l_itrToIM->second.first + ".json";
+        return;
+    }
+
+    throw DataException(
+        "Invalid VPD type returned from Parser. Can't get system JSON.");
+}
+
+static void setEnvAndReboot(const std::string& i_key,
+                            const std::string& i_value)
+{
+    // set env and reboot and break.
+    commonUtility::executeCmd("/sbin/fw_setenv", i_key, i_value);
+    logging::logMessage("Rebooting BMC to pick up new device tree");
+
+    // make dbus call to reboot
+    auto l_bus = sdbusplus::bus::new_default_system();
+    auto l_method = l_bus.new_method_call(
+        "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
+        "org.freedesktop.systemd1.Manager", "Reboot");
+    l_bus.call_noreply(l_method);
+}
+
+static std::string readFitConfigValue()
+{
+    std::vector<std::string> l_output =
+        commonUtility::executeCmd("/sbin/fw_printenv");
+    std::string l_fitConfigValue;
+
+    for (const auto& l_entry : l_output)
+    {
+        auto l_pos = l_entry.find("=");
+        auto l_key = l_entry.substr(0, l_pos);
+        if (l_key != "fitconfig")
+        {
+            continue;
+        }
+
+        if (l_pos + 1 < l_entry.size())
+        {
+            l_fitConfigValue = l_entry.substr(l_pos + 1);
+        }
+    }
+
+    return l_fitConfigValue;
+}
+
+bool IbmHandler::isBackupOnCache()
+{
+    try
+    {
+        uint16_t l_errCode = 0;
+        std::string l_backupAndRestoreCfgFilePath =
+            m_sysCfgJsonObj.value("backupRestoreConfigPath", "");
+
+        if (l_backupAndRestoreCfgFilePath.empty())
+        {
+            m_logger->logMessage(
+                "backupRestoreConfigPath is not found in JSON. Can't determne the backup path.");
+            return false;
+        }
+
+        nlohmann::json l_backupAndRestoreCfgJsonObj =
+            jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
+                                       l_errCode);
+        if (l_backupAndRestoreCfgJsonObj.empty() || l_errCode)
+        {
+            m_logger->logMessage(
+                "JSON parsing failed for file [ " +
+                std::string(l_backupAndRestoreCfgFilePath) +
+                " ], error : " + commonUtility::getErrCodeMsg(l_errCode));
+            return false;
+        }
+
+        // check if either of "source" or "destination" has inventory path.
+        // this indicates that this sytem has System VPD on hardware
+        // and other copy on D-Bus (BMC cache).
+        if (!l_backupAndRestoreCfgJsonObj.empty() &&
+            ((l_backupAndRestoreCfgJsonObj.contains("source") &&
+              l_backupAndRestoreCfgJsonObj["source"].contains(
+                  "inventoryPath")) ||
+             (l_backupAndRestoreCfgJsonObj.contains("destination") &&
+              l_backupAndRestoreCfgJsonObj["destination"].contains(
+                  "inventoryPath"))))
+        {
+            return true;
+        }
+    }
+    catch (const std::exception& l_ex)
+    {
+        m_logger->logMessage(
+            "Exception while checking for backup on cache. Reason:" +
+            std::string(l_ex.what()));
+    }
+
+    // In case of any failure/ambiguity. Don't perform back up and restore.
+    return false;
+}
+
+void IbmHandler::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
+{
+    try
+    {
+        m_backupAndRestoreObj =
+            std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
+        auto [l_srcVpdVariant,
+              l_dstVpdVariant] = m_backupAndRestoreObj->backupAndRestore();
+
+        // ToDo: Revisit is this check is required or not.
+        if (auto l_srcVpdMap = std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
+            l_srcVpdMap && !(*l_srcVpdMap).empty())
+        {
+            io_srcVpdMap = std::move(l_srcVpdVariant);
+        }
+    }
+    catch (const std::exception& l_ex)
+    {
+        EventLogger::createSyncPel(
+            EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
+            __FILE__, __FUNCTION__, 0,
+            std::string(
+                "Exception caught while backup and restore VPD keyword's.") +
+                EventLogger::getErrorMsg(l_ex),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+}
+
+void IbmHandler::setDeviceTreeAndJson()
+{
+    // JSON is madatory for processing of this API.
+    if (m_sysCfgJsonObj.empty())
+    {
+        throw JsonException("System config JSON is empty", m_sysCfgJsonObj);
+    }
+
+    // parse system VPD
+    auto l_parsedVpdMap = m_worker->parseVpdFile(SYSTEM_VPD_FILE_PATH);
+
+    // Implies it is default JSON.
+    std::string l_systemJson{JSON_ABSOLUTE_PATH_PREFIX};
+
+    // get system JSON as per the system configuration.
+    getSystemJson(l_systemJson, l_parsedVpdMap);
+
+    if (!l_systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
+    {
+        throw DataException(
+            "No system JSON found corresponding to IM read from VPD.");
+    }
+
+    uint16_t l_errCode = 0;
+    // re-parse the JSON once appropriate JSON has been selected.
+    m_sysCfgJsonObj = jsonUtility::getParsedJson(l_systemJson, l_errCode);
+
+    if (l_errCode)
+    {
+        throw(JsonException(
+            "JSON parsing failed for file [ " + l_systemJson +
+                " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
+            l_systemJson));
+    }
+
+    m_worker->setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
+                                          constants::vpdCollectionInProgress);
+
+    std::string l_devTreeFromJson;
+    if (m_sysCfgJsonObj.contains("devTree"))
+    {
+        l_devTreeFromJson = m_sysCfgJsonObj["devTree"];
+
+        if (l_devTreeFromJson.empty())
+        {
+            EventLogger::createSyncPel(
+                types::ErrorType::JsonFailure, types::SeverityType::Error,
+                __FILE__, __FUNCTION__, 0,
+                "Mandatory value for device tree missing from JSON[" +
+                    l_systemJson + "]",
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        }
+    }
+
+    auto l_fitConfigVal = readFitConfigValue();
+
+    if (l_devTreeFromJson.empty() ||
+        l_fitConfigVal.find(l_devTreeFromJson) != std::string::npos)
+    { // Skipping setting device tree as either devtree info is missing from
+        // Json or it is rightly set.
+
+        m_worker->setJsonSymbolicLink(l_systemJson);
+
+        const std::string& l_systemVpdInvPath =
+            jsonUtility::getInventoryObjPathFromJson(
+                m_sysCfgJsonObj, SYSTEM_VPD_FILE_PATH, l_errCode);
+
+        if (l_systemVpdInvPath.empty())
+        {
+            if (l_errCode)
+            {
+                throw JsonException(
+                    "System vpd inventory path not found in JSON. Reason:" +
+                        commonUtility::getErrCodeMsg(l_errCode),
+                    INVENTORY_JSON_SYM_LINK);
+            }
+            throw JsonException("System vpd inventory path is missing in JSON",
+                                INVENTORY_JSON_SYM_LINK);
+        }
+
+        // TODO: for backward compatibility this should also support motherboard
+        // interface.
+        std::vector<std::string> l_interfaceList{
+            constants::motherboardInterface};
+        const types::MapperGetObject& l_sysVpdObjMap =
+            dbusUtility::getObjectMap(l_systemVpdInvPath, l_interfaceList);
+
+        if (!l_sysVpdObjMap.empty())
+        {
+            if (isBackupOnCache() && jsonUtility::isBackupAndRestoreRequired(
+                                         m_sysCfgJsonObj, l_errCode))
+            {
+                performBackupAndRestore(l_parsedVpdMap);
+            }
+            else if (l_errCode)
+            {
+                logging::logMessage(
+                    "Failed to check if backup and restore required. Reason : " +
+                    commonUtility::getErrCodeMsg(l_errCode));
+            }
+        }
+
+        // proceed to publish system VPD.
+        m_worker->publishSystemVPD(l_parsedVpdMap);
+        m_worker->setCollectionStatusProperty(
+            SYSTEM_VPD_FILE_PATH, constants::vpdCollectionCompleted);
+        return;
+    }
+
+    setEnvAndReboot("fitconfig", l_devTreeFromJson);
+    exit(EXIT_SUCCESS);
+}
+
 void IbmHandler::performInitialSetup()
 {
     try
@@ -443,12 +748,7 @@
         m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
         if (!dbusUtility::isChassisPowerOn())
         {
-            m_worker->setDeviceTreeAndJson();
-
-            // Since the above function setDeviceTreeAndJson can change the json
-            // which is used, we would need to reacquire the json object again
-            // here.
-            m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+            setDeviceTreeAndJson();
         }
 
         // Update BMC postion for RBMC prototype system