PEL: Create SRC FRU callout for inventory path

When a BMC application creates an event log with the
CALLOUT_INVENTORY_PATH=<FRU inventory path> entry in the AdditionalData
property, create a FRU callout section for that FRU in the SRC.

Obtain the location code, part number, serial number, and CCIN VPD
keyword for that FRU to add to the callout.  If these aren't available,
as detected by catching an sdbusplus exception, create a callout with a
'no VPD for FRU' maintenance procedure instead.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I0d6680d87c325703df9e2a6d2e2715b18498fabc
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 8d2fd0b..b15c777 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -120,7 +120,7 @@
 
     _asciiString = std::make_unique<src::AsciiString>(regEntry);
 
-    // TODO: add callouts using the Callouts object
+    addCallouts(additionalData, dataIface);
 
     _size = baseSRCSize;
     _size += _callouts ? _callouts->flattenedSize() : 0;
@@ -502,5 +502,49 @@
     return ps;
 }
 
+void SRC::addCallouts(const AdditionalData& additionalData,
+                      const DataInterfaceBase& dataIface)
+{
+    auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
+    if (item)
+    {
+        addInventoryCallout(*item, dataIface);
+    }
+
+    // TODO: CALLOUT_DEVICE_PATH
+}
+
+void SRC::addInventoryCallout(const std::string& inventoryPath,
+                              const DataInterfaceBase& dataIface)
+{
+    std::string locCode;
+    std::string fn;
+    std::string ccin;
+    std::string sn;
+    std::unique_ptr<src::Callout> callout;
+
+    createCalloutsObject();
+
+    try
+    {
+        dataIface.getHWCalloutFields(inventoryPath, locCode, fn, ccin, sn);
+
+        callout = std::make_unique<src::Callout>(CalloutPriority::high, locCode,
+                                                 fn, ccin, sn);
+    }
+    catch (const SdBusError& e)
+    {
+        log<level::INFO>("No VPD found for FRU callout",
+                         entry("PATH=%s", inventoryPath.c_str()));
+
+        // Use the 'NoVPDforFRU' maintenance procedure instead
+        callout = std::make_unique<src::Callout>(CalloutPriority::high,
+                                                 MaintProcedure::noVPDforFRU);
+    }
+
+    _callouts->addCallout(std::move(callout));
+
+} // namespace pels
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index 51ded24..a9fecf8 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -339,6 +339,41 @@
     std::optional<std::string> getCallouts() const;
 
     /**
+     * @brief Checks the AdditionalData property and the message registry
+     *        JSON and adds any necessary callouts.
+     *
+     * The callout sources are the AdditionalData event log property
+     * and the message registry JSON.
+     *
+     * @param[in] additionalData - the AdditionalData values
+     * @param[in] dataIface - The DataInterface object
+     */
+    void addCallouts(const AdditionalData& additionalData,
+                     const DataInterfaceBase& dataIface);
+
+    /**
+     * @brief Adds a FRU callout based on an inventory path
+     *
+     * @param[in] inventoryPath - The inventory item to call out
+     * @param[in] dataIface - The DataInterface object
+     */
+    void addInventoryCallout(const std::string& inventoryPath,
+                             const DataInterfaceBase& dataIface);
+
+    /**
+     * @brief Creates the Callouts object _callouts
+     *        so that callouts can be added to it.
+     */
+    void createCalloutsObject()
+    {
+        if (!_callouts)
+        {
+            _callouts = std::make_unique<src::Callouts>();
+            _flags |= additionalSections;
+        }
+    }
+
+    /**
      * @brief The SRC version field
      */
     uint8_t _version;