PEL: Dynamic location code for registry callout

The PEL callout type 'symbolic FRU with trusted location code'
previously had both the symoblic FRU name and the location code
specified in the PEL message registry.  However, there are cases where
the location code is not known until runtime so it cannot be specified
in the registry.

To solve this, create a boolean 'UseInventoryLocCode` key that can be
used in the registry callout entry to specify that the location code to
use for the callout should come from the FRU passed in using the
CALLOUT_INVENTORY_PATH entry in the AdditionalData event log property.
In this case, that FRU will not be added as a normal FRU callout as it
would normally be otherwise.  The registry that uses this must be the
first one in the callout list.

For example:
{
    "Priority": "high",
    "SymbolicFRUTrusted": "air_mover",
    "UseInventoryLocCode": true
},

along with CALLOUT_INVENTORY_PATH=<processor 0 path>

would result in a symbolic FRU callout with the trusted location code
being the processor 0 location code.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Iaf65c523803662313f2ad5e197262b9f4e722332
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 0915a24..3fd62e8 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -744,18 +744,28 @@
                       const nlohmann::json& jsonCallouts,
                       const DataInterfaceBase& dataIface)
 {
+    auto registryCallouts =
+        getRegistryCallouts(regEntry, additionalData, dataIface);
+
     auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
-    if (item)
+
+    // If the first registry callout says to use the passed in inventory
+    // path to get the location code for a symbolic FRU callout with a
+    // trusted location code, then do not add the inventory path as a
+    // normal FRU callout.
+    bool useInvForSymbolicFRULocCode =
+        !registryCallouts.empty() && registryCallouts[0].useInventoryLocCode &&
+        !registryCallouts[0].symbolicFRUTrusted.empty();
+
+    if (item && !useInvForSymbolicFRULocCode)
     {
         addInventoryCallout(*item, std::nullopt, std::nullopt, dataIface);
     }
 
     addDevicePathCallouts(additionalData, dataIface);
 
-    if (regEntry.callouts)
-    {
-        addRegistryCallouts(regEntry, additionalData, dataIface);
-    }
+    addRegistryCallouts(registryCallouts, dataIface,
+                        (useInvForSymbolicFRULocCode) ? item : std::nullopt);
 
     if (!jsonCallouts.empty())
     {
@@ -822,20 +832,49 @@
     _callouts->addCallout(std::move(callout));
 }
 
-void SRC::addRegistryCallouts(const message::Entry& regEntry,
-                              const AdditionalData& additionalData,
-                              const DataInterfaceBase& dataIface)
+std::vector<message::RegistryCallout>
+    SRC::getRegistryCallouts(const message::Entry& regEntry,
+                             const AdditionalData& additionalData,
+                             const DataInterfaceBase& dataIface)
+{
+    std::vector<message::RegistryCallout> registryCallouts;
+
+    if (regEntry.callouts)
+    {
+        try
+        {
+            auto systemNames = dataIface.getSystemNames();
+
+            registryCallouts = message::Registry::getCallouts(
+                regEntry.callouts.value(), systemNames, additionalData);
+        }
+        catch (const std::exception& e)
+        {
+            addDebugData(fmt::format(
+                "Error parsing PEL message registry callout JSON: {}",
+                e.what()));
+        }
+    }
+
+    return registryCallouts;
+}
+
+void SRC::addRegistryCallouts(
+    const std::vector<message::RegistryCallout>& callouts,
+    const DataInterfaceBase& dataIface,
+    std::optional<std::string> trustedSymbolicFRUInvPath)
 {
     try
     {
-        auto systemNames = dataIface.getSystemNames();
-
-        auto regCallouts = message::Registry::getCallouts(
-            regEntry.callouts.value(), systemNames, additionalData);
-
-        for (const auto& regCallout : regCallouts)
+        for (const auto& callout : callouts)
         {
-            addRegistryCallout(regCallout, dataIface);
+            addRegistryCallout(callout, dataIface, trustedSymbolicFRUInvPath);
+
+            // Only the first callout gets the inventory path
+            if (trustedSymbolicFRUInvPath)
+            {
+                trustedSymbolicFRUInvPath = std::nullopt;
+            }
         }
     }
     catch (std::exception& e)
@@ -846,8 +885,10 @@
     }
 }
 
-void SRC::addRegistryCallout(const message::RegistryCallout& regCallout,
-                             const DataInterfaceBase& dataIface)
+void SRC::addRegistryCallout(
+    const message::RegistryCallout& regCallout,
+    const DataInterfaceBase& dataIface,
+    const std::optional<std::string>& trustedSymbolicFRUInvPath)
 {
     std::unique_ptr<src::Callout> callout;
     auto locCode = regCallout.locCode;
@@ -891,6 +932,22 @@
     {
         // Symbolic FRU with trusted location code callout
 
+        // Use the location code from the inventory path if there is one.
+        if (trustedSymbolicFRUInvPath)
+        {
+            try
+            {
+                locCode = dataIface.getLocationCode(*trustedSymbolicFRUInvPath);
+            }
+            catch (const std::exception& e)
+            {
+                addDebugData(
+                    fmt::format("Could not get location code for {}: {}",
+                                *trustedSymbolicFRUInvPath, e.what()));
+                locCode.clear();
+            }
+        }
+
         // The registry wants it to be trusted, but that requires a valid
         // location code for it to actually be.
         callout = std::make_unique<src::Callout>(