VPD Tool: Emplace Present property

This commit adds logic which emplaces fru's
"Present" property in vpd-tool output.

Some frus doesn't have Present property by itself.
In such case, vpd-tool emplaces the fru's parent's
present property value and displays it to the user.

Test:
Tested on rainier.
1.
root@rain148bmc:/tmp# ./vpd-tool -i

[
    {
        "/system": {
            "LocationCode": "U9105.42B.13BE920",
            "Model": "9105-42B",
            "Present": "true",
            "SerialNumber": "13BE920",
            "SubModel": "S0",
            "TYPE": "FRU",
            "type": "xyz.openbmc_project.Inventory.Item.System"
        },
       ..
        .
     }
]

2.
root@p10bmc:/tmp# ./vpd-tool -o -O "/system/chassis/motherboard/vdd_vrm1"
[
    {
        "/system/chassis/motherboard/vdd_vrm1": {
            "CC": "ABCD",
            "DR": "CABCDARD  ",
            "FN": "1234",
            "LocationCode": "U7123.ABC.12342-P0-C23",
            "PN": "02CW34F",
            "Present": "true",
            "SN": "YH30W3R8X12D",
            "TYPE": "FRU",
            "type": "xyz.openbmc_project.Inventory.Item.Vrm"
        }
    }
]

3.
root@rain148bmc:/tmp# ./vpd-tool -o -O /system
[
    {
        "/system": {
            "LocationCode": "U9105.42B.13BE920",
            "Model": "9105-42B",
            "Present": "true",
            "SerialNumber": "13BE920",
            "SubModel": "S0",
            "TYPE": "FRU",
            "type": "xyz.openbmc_project.Inventory.Item.System"
        }
    }
]

Signed-off-by: Priyanga Ramasamy <priyanga24@in.ibm.com>
Change-Id: Ic2ff978a499f13b21e34fe37aa7ebd067ffcd34e
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index 8c5a92e..4d9f1a3 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -193,7 +193,6 @@
         throw runtime_error("Extra Interfaces not found");
     }
 
-    json output = json::object({});
     json subOutput = json::object({});
     fruType = "FRU";
 
@@ -249,8 +248,34 @@
             subOutput.insert(js.begin(), js.end());
         }
     }
-    output.emplace(invPath, subOutput);
-    return output;
+    return subOutput;
+}
+
+json VpdTool::getPresentPropJson(const std::string& invPath,
+                                 std::string& parentPresence)
+{
+    std::variant<bool> response;
+    makeDBusCall(invPath, "xyz.openbmc_project.Inventory.Item", "Present")
+        .read(response);
+
+    std::string presence{};
+
+    if (auto pVal = get_if<bool>(&response))
+    {
+        presence = *pVal ? "true" : "false";
+        if (parentPresence.empty())
+        {
+            parentPresence = presence;
+        }
+    }
+    else
+    {
+        presence = parentPresence;
+    }
+
+    json js;
+    js.emplace("Present", presence);
+    return js;
 }
 
 json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
@@ -266,8 +291,10 @@
     {
         for (const auto& itemFRUS : jsObject["frus"].items())
         {
+            string parentPresence{};
             for (auto itemEEPROM : itemFRUS.value())
             {
+                json subOutput = json::object({});
                 try
                 {
                     if (flag == 'O')
@@ -277,19 +304,59 @@
                         {
                             throw runtime_error("Inventory Path not found");
                         }
-
                         else if (itemEEPROM.at("inventoryPath") == fruPath)
                         {
                             validObject = true;
-                            json j = interfaceDecider(itemEEPROM);
-                            output.insert(j.begin(), j.end());
+                            subOutput = interfaceDecider(itemEEPROM);
+                            json presentJs = getPresentPropJson(
+                                "/xyz/openbmc_project/inventory" + fruPath,
+                                parentPresence);
+                            subOutput.insert(presentJs.begin(),
+                                             presentJs.end());
+                            output.emplace(fruPath, subOutput);
                             return output;
                         }
+                        else // this else is to keep track of parent present
+                             // property.
+                        {
+                            json presentJs = getPresentPropJson(
+                                "/xyz/openbmc_project/inventory" +
+                                    string(itemEEPROM.at("inventoryPath")),
+                                parentPresence);
+                        }
                     }
                     else
                     {
-                        json j = interfaceDecider(itemEEPROM);
-                        output.insert(j.begin(), j.end());
+                        subOutput = interfaceDecider(itemEEPROM);
+                        json presentJs = getPresentPropJson(
+                            "/xyz/openbmc_project/inventory" +
+                                string(itemEEPROM.at("inventoryPath")),
+                            parentPresence);
+                        subOutput.insert(presentJs.begin(), presentJs.end());
+                        output.emplace(string(itemEEPROM.at("inventoryPath")),
+                                       subOutput);
+                    }
+                }
+                catch (const sdbusplus::exception::SdBusError& e)
+                {
+                    // if any of frupath doesn't have Present property of its
+                    // own, emplace its parent's present property value.
+                    if (e.name() == std::string("org.freedesktop.DBus.Error."
+                                                "UnknownProperty") &&
+                        (((flag == 'O') && validObject) || flag == 'I'))
+                    {
+                        json presentJs;
+                        presentJs.emplace("Present", parentPresence);
+                        subOutput.insert(presentJs.begin(), presentJs.end());
+                        output.emplace(string(itemEEPROM.at("inventoryPath")),
+                                       subOutput);
+                    }
+
+                    // for the user given child frupath which doesn't have
+                    // Present prop (vpd-tool -o).
+                    if ((flag == 'O') && validObject)
+                    {
+                        return output;
                     }
                 }
                 catch (const exception& e)
@@ -441,4 +508,4 @@
         throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
     }
     return rc;
-}
+}
\ No newline at end of file
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
index ac0b2f5..5ae9284 100644
--- a/vpd_tool_impl.hpp
+++ b/vpd_tool_impl.hpp
@@ -123,6 +123,15 @@
      */
     openpower::vpd::Binary toBinary(const std::string& value);
 
+    /**
+     * @brief Get the json which has Present property value of the given fru.
+     * @param[in] invPath - inventory path of the fru.
+     * @param[out] parentPresence - Update the parent fru's present property.
+     * @return output json which has the Present property value.
+     */
+    json getPresentPropJson(const std::string& invPath,
+                            std::string& parentPresence);
+
   public:
     /**
      * @brief Dump the complete inventory in JSON format