host-bmc: refine remote terminus functionality

This commit introduces support for migrating remote terminus-related
functionalities into the host-bmc directory, a move aimed at optimizing
remote terminus operations for future endeavors. By consolidating these
functionalities, tracking and implementing future enhancements become
more streamlined and manageable.

Change-Id: I52b6ced7acacb004b6055ae710193a959d986659
Signed-off-by: Kamalkumar Patel <kamalkumar.patel@ibm.com>
diff --git a/host-bmc/host_pdr_handler.cpp b/host-bmc/host_pdr_handler.cpp
index 3f3b964..71e3812 100644
--- a/host-bmc/host_pdr_handler.cpp
+++ b/host-bmc/host_pdr_handler.cpp
@@ -628,7 +628,8 @@
     }
     if (!nextRecordHandle)
     {
-        updateEntityAssociation(entityAssociations, entityTree, objPathMap);
+        pldm::hostbmc::utils::updateEntityAssociation(entityAssociations,
+                                                      entityTree, objPathMap);
 
         /*received last record*/
         this->parseStateSensorPDRs(stateSensorPDRs);
diff --git a/host-bmc/host_pdr_handler.hpp b/host-bmc/host_pdr_handler.hpp
index 3b05f59..053cfef 100644
--- a/host-bmc/host_pdr_handler.hpp
+++ b/host-bmc/host_pdr_handler.hpp
@@ -7,6 +7,7 @@
 #include "libpldmresponder/oem_handler.hpp"
 #include "libpldmresponder/pdr_utils.hpp"
 #include "requester/handler.hpp"
+#include "utils.hpp"
 
 #include <libpldm/base.h>
 #include <libpldm/platform.h>
@@ -314,11 +315,11 @@
     /** @brief maps an object path to pldm_entity from the BMC's entity
      *         association tree
      */
-    utils::ObjectPathMaps objPathMap;
+    ObjectPathMaps objPathMap;
 
     /** @brief maps an entity name to map, maps to entity name to pldm_entity
      */
-    utils::EntityAssociations entityAssociations;
+    EntityAssociations entityAssociations;
 
     /** @brief the vector of FRU Record Data Format
      */
diff --git a/host-bmc/test/meson.build b/host-bmc/test/meson.build
index 30f6624..66d75b7 100644
--- a/host-bmc/test/meson.build
+++ b/host-bmc/test/meson.build
@@ -5,6 +5,7 @@
 test_sources = [
   '../../common/utils.cpp',
   '../custom_dbus.cpp',
+  '../utils.cpp'
 ]
 
 tests = [
diff --git a/host-bmc/test/utils_test.cpp b/host-bmc/test/utils_test.cpp
index ab64aa1..701f4cb 100644
--- a/host-bmc/test/utils_test.cpp
+++ b/host-bmc/test/utils_test.cpp
@@ -1,4 +1,4 @@
-#include "common/utils.hpp"
+#include "../utils.hpp"
 
 #include <libpldm/pdr.h>
 
@@ -80,7 +80,8 @@
          l5b}};
 
     ObjectPathMaps objPathMap;
-    updateEntityAssociation(entityAssociations, tree, objPathMap);
+    pldm::hostbmc::utils::updateEntityAssociation(entityAssociations, tree,
+                                                  objPathMap);
 
     EXPECT_EQ(objPathMap.size(), retObjectMaps.size());
 
diff --git a/host-bmc/utils.cpp b/host-bmc/utils.cpp
new file mode 100644
index 0000000..f025ada
--- /dev/null
+++ b/host-bmc/utils.cpp
@@ -0,0 +1,208 @@
+#include "common/utils.hpp"
+
+#include "libpldm/entity.h"
+
+#include "utils.hpp"
+
+#include <iostream>
+
+namespace pldm
+{
+namespace hostbmc
+{
+namespace utils
+{
+Entities getParentEntites(const EntityAssociations& entityAssoc)
+{
+    Entities parents{};
+    for (const auto& et : entityAssoc)
+    {
+        parents.push_back(et[0]);
+    }
+
+    bool found = false;
+    for (auto it = parents.begin(); it != parents.end();
+         it = found ? parents.erase(it) : std::next(it))
+    {
+        uint16_t parent_contained_id =
+            pldm_entity_node_get_remote_container_id(*it);
+        found = false;
+        for (const auto& evs : entityAssoc)
+        {
+            for (size_t i = 1; i < evs.size() && !found; i++)
+            {
+                uint16_t node_contained_id =
+                    pldm_entity_node_get_remote_container_id(evs[i]);
+
+                pldm_entity parent_entity = pldm_entity_extract(*it);
+                pldm_entity node_entity = pldm_entity_extract(evs[i]);
+
+                if (node_entity.entity_type == parent_entity.entity_type &&
+                    node_entity.entity_instance_num ==
+                        parent_entity.entity_instance_num &&
+                    node_contained_id == parent_contained_id)
+                {
+                    found = true;
+                }
+            }
+            if (found)
+            {
+                break;
+            }
+        }
+    }
+
+    return parents;
+}
+
+void addObjectPathEntityAssociations(const EntityAssociations& entityAssoc,
+                                     pldm_entity_node* entity,
+                                     const fs::path& path,
+                                     ObjectPathMaps& objPathMap)
+{
+    if (entity == nullptr)
+    {
+        return;
+    }
+
+    bool found = false;
+    pldm_entity node_entity = pldm_entity_extract(entity);
+    if (!entityMaps.contains(node_entity.entity_type))
+    {
+        lg2::info(
+            "{ENTITY_TYPE} Entity fetched from remote PLDM terminal does not exist.",
+            "ENTITY_TYPE", (int)node_entity.entity_type);
+        return;
+    }
+
+    std::string entityName = entityMaps.at(node_entity.entity_type);
+    for (const auto& ev : entityAssoc)
+    {
+        pldm_entity ev_entity = pldm_entity_extract(ev[0]);
+        if (ev_entity.entity_instance_num == node_entity.entity_instance_num &&
+            ev_entity.entity_type == node_entity.entity_type)
+        {
+            uint16_t node_contained_id =
+                pldm_entity_node_get_remote_container_id(ev[0]);
+            uint16_t entity_contained_id =
+                pldm_entity_node_get_remote_container_id(entity);
+
+            if (node_contained_id != entity_contained_id)
+            {
+                continue;
+            }
+
+            fs::path p = path / fs::path{entityName +
+                                         std::to_string(
+                                             node_entity.entity_instance_num)};
+            std::string entity_path = p.string();
+            // If the entity obtained from the remote PLDM terminal is not in
+            // the MAP, or there is no auxiliary name PDR, add it directly.
+            // Otherwise, check whether the DBus service of entity_path exists,
+            // and overwrite the entity if it does not exist.
+            if (!objPathMap.contains(entity_path))
+            {
+                objPathMap[entity_path] = entity;
+            }
+            else
+            {
+                try
+                {
+                    pldm::utils::DBusHandler().getService(entity_path.c_str(),
+                                                          nullptr);
+                }
+                catch (const std::exception& e)
+                {
+                    objPathMap[entity_path] = entity;
+                }
+            }
+
+            for (size_t i = 1; i < ev.size(); i++)
+            {
+                addObjectPathEntityAssociations(entityAssoc, ev[i], p,
+                                                objPathMap);
+            }
+            found = true;
+        }
+    }
+
+    if (!found)
+    {
+        std::string dbusPath =
+            path / fs::path{entityName +
+                            std::to_string(node_entity.entity_instance_num)};
+
+        try
+        {
+            pldm::utils::DBusHandler().getService(dbusPath.c_str(), nullptr);
+        }
+        catch (const std::exception& e)
+        {
+            objPathMap[dbusPath] = entity;
+        }
+    }
+}
+
+void updateEntityAssociation(const EntityAssociations& entityAssoc,
+                             pldm_entity_association_tree* entityTree,
+                             ObjectPathMaps& objPathMap)
+{
+    std::vector<pldm_entity_node*> parentsEntity =
+        getParentEntites(entityAssoc);
+    for (const auto& entity : parentsEntity)
+    {
+        fs::path path{"/xyz/openbmc_project/inventory"};
+        std::deque<std::string> paths{};
+        pldm_entity node_entity = pldm_entity_extract(entity);
+        auto node = pldm_entity_association_tree_find_with_locality(
+            entityTree, &node_entity, false);
+        if (!node)
+        {
+            continue;
+        }
+
+        bool found = true;
+        while (node)
+        {
+            if (!pldm_entity_is_exist_parent(node))
+            {
+                break;
+            }
+
+            pldm_entity parent = pldm_entity_get_parent(node);
+            try
+            {
+                paths.push_back(entityMaps.at(parent.entity_type) +
+                                std::to_string(parent.entity_instance_num));
+            }
+            catch (const std::exception& e)
+            {
+                lg2::error(
+                    "Parent entity not found in the entityMaps, type: {ENTITY_TYPE}, num: {NUM}, e: {ERROR}",
+                    "ENTITY_TYPE", (int)parent.entity_type, "NUM",
+                    (int)parent.entity_instance_num, "ERROR", e);
+                found = false;
+                break;
+            }
+
+            node = pldm_entity_association_tree_find_with_locality(
+                entityTree, &parent, false);
+        }
+
+        if (!found)
+        {
+            continue;
+        }
+
+        while (!paths.empty())
+        {
+            path = path / fs::path{paths.back()};
+            paths.pop_back();
+        }
+
+        addObjectPathEntityAssociations(entityAssoc, entity, path, objPathMap);
+    }
+}
+} // namespace utils
+} // namespace hostbmc
+} // namespace pldm
diff --git a/host-bmc/utils.hpp b/host-bmc/utils.hpp
new file mode 100644
index 0000000..857d6c7
--- /dev/null
+++ b/host-bmc/utils.hpp
@@ -0,0 +1,57 @@
+#include "libpldm/pdr.h"
+
+#include "libpldmresponder/oem_handler.hpp"
+
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/lg2.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <map>
+#include <string>
+#include <vector>
+
+PHOSPHOR_LOG2_USING;
+namespace pldm
+{
+using EntityName = std::string;
+using EntityType = uint16_t;
+
+using Entities = std::vector<pldm_entity_node*>;
+using EntityAssociations = std::vector<Entities>;
+using ObjectPathMaps = std::map<fs::path, pldm_entity_node*>;
+namespace hostbmc
+{
+namespace utils
+{
+
+const std::map<EntityType, EntityName> entityMaps = {
+    {PLDM_ENTITY_SYSTEM_CHASSIS, "chassis"},
+    {PLDM_ENTITY_BOARD, "io_board"},
+    {PLDM_ENTITY_SYS_BOARD, "motherboard"},
+    {PLDM_ENTITY_POWER_SUPPLY, "powersupply"},
+    {PLDM_ENTITY_PROC, "cpu"},
+    {PLDM_ENTITY_SYSTEM_CHASSIS | 0x8000, "system"},
+    {PLDM_ENTITY_PROC_MODULE, "dcm"},
+    {PLDM_ENTITY_PROC | 0x8000, "core"},
+    {PLDM_ENTITY_IO_MODULE, "io_module"},
+    {PLDM_ENTITY_FAN, "fan"},
+    {PLDM_ENTITY_SYS_MGMT_MODULE, "system_management_module"},
+    {PLDM_ENTITY_POWER_CONVERTER, "power_converter"},
+    {PLDM_ENTITY_SLOT, "slot"},
+    {PLDM_ENTITY_CONNECTOR, "connector"}};
+
+/** @brief Vector a entity name to pldm_entity from entity association tree
+ *  @param[in]  entityAssoc    - Vector of associated pldm entities
+ *  @param[in]  entityTree     - entity association tree
+ *  @param[out] objPathMap     - maps an object path to pldm_entity from the
+ *                               BMC's entity association tree
+ *  @return
+ */
+void updateEntityAssociation(const EntityAssociations& entityAssoc,
+                             pldm_entity_association_tree* entityTree,
+                             ObjectPathMaps& objPathMap);
+
+} // namespace utils
+} // namespace hostbmc
+} // namespace pldm