libpldmresponder: FRU: construct PDRs

Construct FRU record set and entity association PDRs for the FRUs for
which the BMC collects VPD (FRU information off of an EEPROM).

These PDRs are structured as per PLDM spec DSP0248 v1.2.0.

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I2c72d74dad449561b26c74482e00d1606546c5a2
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index 7b73a81..42babc3 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -15,7 +15,10 @@
 namespace responder
 {
 
-FruImpl::FruImpl(const std::string& configPath)
+FruImpl::FruImpl(const std::string& configPath, pldm_pdr* pdrRepo,
+                 pldm_entity_association_tree* entityTree) :
+    pdrRepo(pdrRepo),
+    entityTree(entityTree)
 {
     fru_parser::FruParser handle(configPath);
 
@@ -40,12 +43,7 @@
         return;
     }
 
-    // Populate all the interested Item types to a map for easy lookup
-    std::set<dbus::Interface> itemIntfsLookup;
-    auto itemIntfs = std::get<2>(dbusInfo);
-    std::transform(std::begin(itemIntfs), std::end(itemIntfs),
-                   std::inserter(itemIntfsLookup, itemIntfsLookup.end()),
-                   [](dbus::Interface intf) { return intf; });
+    auto itemIntfsLookup = std::get<2>(dbusInfo);
 
     for (const auto& object : objects)
     {
@@ -60,8 +58,39 @@
                 // not have corresponding config jsons
                 try
                 {
+                    pldm_entity entity{};
+                    entity.entity_type = handle.getEntityType(interface.first);
+                    pldm_entity_node* parent = nullptr;
+                    auto parentObj = pldm::utils::findParent(object.first.str);
+                    // To add a FRU to the entity association tree, we need to
+                    // determine if the FRU has a parent (D-Bus object). For eg
+                    // /system/backplane's parent is /system. /system has no
+                    // parent. Some D-Bus pathnames might just be namespaces
+                    // (not D-Bus objects), so we need to iterate upwards until
+                    // a parent is found, or we reach the root ("/").
+                    // Parents are always added first before children in the
+                    // entity association tree. We're relying on the fact that
+                    // the std::map containing object paths from the
+                    // GetManagedObjects call will have a sorted pathname list.
+                    do
+                    {
+                        auto iter = objToEntityNode.find(parentObj);
+                        if (iter != objToEntityNode.end())
+                        {
+                            parent = iter->second;
+                            break;
+                        }
+                        parentObj = pldm::utils::findParent(parentObj);
+                    } while (parentObj != "/");
+
+                    auto node = pldm_entity_association_tree_add(
+                        entityTree, &entity, parent,
+                        PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+                    objToEntityNode[object.first.str] = node;
+
                     auto recordInfos = handle.getRecordInfo(interface.first);
-                    populateRecords(interfaces, recordInfos);
+                    populateRecords(interfaces, recordInfos, entity);
+                    break;
                 }
                 catch (const std::exception& e)
                 {
@@ -74,6 +103,8 @@
         }
     }
 
+    pldm_entity_association_pdr_add(entityTree, pdrRepo);
+
     if (table.size())
     {
         padBytes = utils::getNumPadBytes(table.size());
@@ -88,7 +119,7 @@
 
 void FruImpl::populateRecords(
     const pldm::responder::dbus::InterfaceMap& interfaces,
-    const fru_parser::FruRecordInfos& recordInfos)
+    const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity)
 {
     // recordSetIdentifier for the FRU will be set when the first record gets
     // added for the FRU
@@ -144,6 +175,9 @@
             if (numRecs == numRecsCount)
             {
                 recordSetIdentifier = nextRSI();
+                pldm_pdr_add_fru_record_set(
+                    pdrRepo, 0, recordSetIdentifier, entity.entity_type,
+                    entity.entity_instance_num, entity.entity_container_id);
             }
             auto curSize = table.size();
             table.resize(curSize + recHeaderSize + tlvs.size());