PEL: Collect information for a FRU callout

Add an API to the DataInterface class to collect the following fields
needed to add a FRU callout to a PEL:
* Location code
* FRU part number (The VINI/FN keyword)
* FRU CCIN (The VINI/CC keyword)
* FRU serial number (The VINI/SN keyword)

For now, this code requires that both of the 2 D-Bus interfaces that
contain these properties be present on the inventory path being passed
in.  In the future, some smarts may need to be added to the code for
cases when the path isn't a FRU itself, or maybe doesn't have its own
VPD.  It's also possible there will be community supplied code in the
future that could do this mapping, which is why this code isn't trying
to do it itself now.

In the cases where one or both of these interfaces don't exist, an
exception will be thrown by the underlying sdbusplus code, and the
caller of this will handle it appropriately.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I8eb5752fa25fc0e2107df3c7145355bbb4f12cf4
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index 252dd95..95de4bb 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -57,6 +57,7 @@
 constexpr auto invMotherboard =
     "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
+constexpr auto locCode = "com.ibm.ipzvpd.Location";
 } // namespace interface
 
 using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
@@ -306,5 +307,39 @@
         [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
 }
 
+void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
+                                       std::string& locationCode,
+                                       std::string& fruPartNumber,
+                                       std::string& ccin,
+                                       std::string& serialNumber) const
+{
+    // For now, attempt to get all of the properties directly on the path
+    // passed in.  In the future, may need to make use of an algorithm
+    // to figure out which inventory objects actually hold these
+    // interfaces in the case of non FRUs, or possibly another service
+    // will provide this info.  Any missing interfaces will result
+    // in exceptions being thrown.
+
+    auto service = getService(inventoryPath, interface::locCode);
+
+    DBusValue locCode;
+    getProperty(service, inventoryPath, interface::locCode, "LocationCode",
+                locCode);
+
+    locationCode = std::get<std::string>(locCode);
+
+    auto properties =
+        getAllProperties(service, inventoryPath, interface::viniRecordVPD);
+
+    auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
+    fruPartNumber = std::string{value.begin(), value.end()};
+
+    value = std::get<std::vector<uint8_t>>(properties["CC"]);
+    ccin = std::string{value.begin(), value.end()};
+
+    value = std::get<std::vector<uint8_t>>(properties["SN"]);
+    serialNumber = std::string{value.begin(), value.end()};
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index 3fb4e1c..5b6ff94 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -210,6 +210,22 @@
         return _motherboardCCIN;
     }
 
+    /**
+     * @brief Get the fields from the inventory necessary for doing
+     *        a callout on an inventory path.
+     *
+     * @param[in] inventoryPath - The item to get the data for
+     * @param[out] locationCode - Filled in with the  location code
+     * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
+     * @param[out] ccin - Filled in with the VINI/CC keyword
+     * @param[out] serialNumber - Filled in with the VINI/SN keyword
+     */
+    virtual void getHWCalloutFields(const std::string& inventoryPath,
+                                    std::string& locationCode,
+                                    std::string& fruPartNumber,
+                                    std::string& ccin,
+                                    std::string& serialNumber) const = 0;
+
   protected:
     /**
      * @brief Sets the host on/off state and runs any
@@ -369,6 +385,21 @@
                      const std::string& interface, const std::string& property,
                      DBusValue& value) const;
 
+    /**
+     * @brief Get the fields from the inventory necessary for doing
+     *        a callout on an inventory path.
+     *
+     * @param[in] inventoryPath - The item to get the data for
+     * @param[out] locationCode - Filled in with the  location code
+     * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
+     * @param[out] ccin - Filled in with the VINI/CC keyword
+     * @param[out] serialNumber - Filled in with the VINI/SN keyword
+     */
+    void getHWCalloutFields(const std::string& inventoryPath,
+                            std::string& locationCode,
+                            std::string& fruPartNumber, std::string& ccin,
+                            std::string& serialNumber) const override;
+
   private:
     /**
      * @brief Reads the BMC firmware version string and puts it into
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index 5781233..1654a9d 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -29,6 +29,10 @@
     MOCK_METHOD(std::string, getChassisState, (), (const override));
     MOCK_METHOD(std::string, getHostState, (), (const override));
     MOCK_METHOD(std::string, getMotherboardCCIN, (), (const override));
+    MOCK_METHOD(void, getHWCalloutFields,
+                (const std::string&, std::string&, std::string&, std::string&,
+                 std::string&),
+                (const override));
 
     void changeHostState(bool newState)
     {